getaimeter 0.1.4 → 0.1.5
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/package.json +1 -1
- package/watcher.js +35 -69
package/package.json
CHANGED
package/watcher.js
CHANGED
|
@@ -137,39 +137,20 @@ async function reportEvents(events) {
|
|
|
137
137
|
// File watcher
|
|
138
138
|
// ---------------------------------------------------------------------------
|
|
139
139
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
// Skip if already being processed
|
|
151
|
-
if (_processing.has(normalizedKey)) return;
|
|
152
|
-
|
|
153
|
-
// Debounce: wait 2000ms after last change before processing
|
|
154
|
-
const existing = _debounceTimers.get(normalizedKey);
|
|
155
|
-
if (existing) clearTimeout(existing);
|
|
156
|
-
|
|
157
|
-
_debounceTimers.set(normalizedKey, setTimeout(async () => {
|
|
158
|
-
_debounceTimers.delete(normalizedKey);
|
|
159
|
-
if (_processing.has(normalizedKey)) return;
|
|
160
|
-
_processing.add(normalizedKey);
|
|
161
|
-
try {
|
|
162
|
-
const events = extractNewUsage(filePath);
|
|
163
|
-
if (events.length > 0) {
|
|
164
|
-
await reportEvents(events);
|
|
165
|
-
saveState();
|
|
166
|
-
}
|
|
167
|
-
} catch (err) {
|
|
168
|
-
logError(`Processing ${filePath}:`, err.message);
|
|
169
|
-
} finally {
|
|
170
|
-
_processing.delete(normalizedKey);
|
|
140
|
+
/**
|
|
141
|
+
* Process a single file: extract new usage and report it.
|
|
142
|
+
* Called only from the poll loop — single-threaded, no races.
|
|
143
|
+
*/
|
|
144
|
+
async function processFile(filePath) {
|
|
145
|
+
try {
|
|
146
|
+
const events = extractNewUsage(filePath);
|
|
147
|
+
if (events.length > 0) {
|
|
148
|
+
await reportEvents(events);
|
|
149
|
+
saveState();
|
|
171
150
|
}
|
|
172
|
-
}
|
|
151
|
+
} catch (err) {
|
|
152
|
+
logError(`Processing ${filePath}:`, err.message);
|
|
153
|
+
}
|
|
173
154
|
}
|
|
174
155
|
|
|
175
156
|
/**
|
|
@@ -248,45 +229,32 @@ function startWatching() {
|
|
|
248
229
|
}
|
|
249
230
|
saveState();
|
|
250
231
|
|
|
251
|
-
//
|
|
252
|
-
|
|
253
|
-
for (const watchPath of watchPaths) {
|
|
254
|
-
try {
|
|
255
|
-
const w = fs.watch(watchPath, { recursive: true }, (eventType, filename) => {
|
|
256
|
-
if (!filename) return;
|
|
257
|
-
const fullPath = path.join(watchPath, filename);
|
|
258
|
-
handleFileChange(fullPath);
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
w.on('error', (err) => {
|
|
262
|
-
logError(`Watcher error on ${watchPath}:`, err.message);
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
watchers.push(w);
|
|
266
|
-
log('Watching (fs.watch):', watchPath);
|
|
267
|
-
} catch (err) {
|
|
268
|
-
logError(`Could not watch ${watchPath}:`, err.message);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// Polling fallback — fs.watch is unreliable on Windows for deeply nested dirs.
|
|
273
|
-
// Every 5 seconds, scan all known JSONL files for size changes.
|
|
232
|
+
// Poll every 5 seconds — simple, reliable, no race conditions.
|
|
233
|
+
// fs.watch is unreliable on Windows for deeply nested dirs and fires duplicates.
|
|
274
234
|
const POLL_INTERVAL = 5_000;
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
235
|
+
let polling = false;
|
|
236
|
+
|
|
237
|
+
const pollInterval = setInterval(async () => {
|
|
238
|
+
if (polling) return; // skip if previous poll still running
|
|
239
|
+
polling = true;
|
|
240
|
+
try {
|
|
241
|
+
for (const watchPath of watchPaths) {
|
|
242
|
+
const files = findJsonlFiles(watchPath);
|
|
243
|
+
for (const file of files) {
|
|
244
|
+
try {
|
|
245
|
+
const currentSize = fs.statSync(file).size;
|
|
246
|
+
const lastOffset = getOffset(file);
|
|
247
|
+
if (currentSize > lastOffset) {
|
|
248
|
+
await processFile(file);
|
|
249
|
+
}
|
|
250
|
+
} catch {}
|
|
251
|
+
}
|
|
286
252
|
}
|
|
253
|
+
} finally {
|
|
254
|
+
polling = false;
|
|
287
255
|
}
|
|
288
256
|
}, POLL_INTERVAL);
|
|
289
|
-
log(`Polling every ${POLL_INTERVAL / 1000}s
|
|
257
|
+
log(`Polling every ${POLL_INTERVAL / 1000}s`);
|
|
290
258
|
|
|
291
259
|
// Periodic state save
|
|
292
260
|
const saveInterval = setInterval(() => saveState(), 30_000);
|
|
@@ -295,8 +263,6 @@ function startWatching() {
|
|
|
295
263
|
return () => {
|
|
296
264
|
clearInterval(saveInterval);
|
|
297
265
|
clearInterval(pollInterval);
|
|
298
|
-
for (const w of watchers) w.close();
|
|
299
|
-
for (const t of _debounceTimers.values()) clearTimeout(t);
|
|
300
266
|
saveState();
|
|
301
267
|
log('Watcher stopped.');
|
|
302
268
|
};
|