buddy-workbench 0.1.90 → 0.1.92

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "buddy-workbench",
3
- "version": "0.1.90",
3
+ "version": "0.1.92",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,9 +16,29 @@ let lastValue = '';
16
16
  let lastImageHash = '';
17
17
  let suppressImageCaptureUntil = 0;
18
18
  let mozjpegWasmModule = null;
19
+ const clipboardErrorCounts = new Map();
19
20
  const clipboardErrorLogTimes = new Map();
20
21
 
21
- function logClipboardError(stage, error) {
22
+ function markClipboardSuccess(...stages) {
23
+ for (const stage of stages) {
24
+ clipboardErrorCounts.delete(stage);
25
+ }
26
+ }
27
+
28
+ function logClipboardError(stage, error, threshold = 3) {
29
+ // Ignore benign failures due to timeout or process termination (e.g., system sleep / screen lock / display off)
30
+ if (error?.killed || error?.timedOut || error?.signal === 'SIGTERM' || error?.code === 'ETIMEDOUT') {
31
+ return;
32
+ }
33
+
34
+ const count = (clipboardErrorCounts.get(stage) || 0) + 1;
35
+ clipboardErrorCounts.set(stage, count);
36
+
37
+ // Transient filter: only log if failures happen consecutively past threshold
38
+ if (count < threshold) {
39
+ return;
40
+ }
41
+
22
42
  // Clipboard polling runs continuously. Avoid filling Error Log with the
23
43
  // same native-tool failure every two seconds while retaining diagnostics.
24
44
  const now = Date.now();
@@ -28,7 +48,7 @@ function logClipboardError(stage, error) {
28
48
  const details = String(error?.stderr || error?.stdout || error?.stack || error?.message || error || '').trim();
29
49
  addErrorRecord({
30
50
  source: 'Clipboard Monitor',
31
- message: `Failed during ${stage}.`,
51
+ message: `Failed during ${stage} (consecutive failures: ${count}).`,
32
52
  details: details || 'The native clipboard operation returned an unknown error.'
33
53
  });
34
54
  }
@@ -80,6 +100,7 @@ async function getMacClipboardImageData() {
80
100
  try {
81
101
  const { stdout } = await execFileAsync('osascript', ['-l', 'JavaScript', '-e', jsaScript], { timeout: 3000 });
82
102
  const str = stdout.trim();
103
+ markClipboardSuccess('macOS image clipboard read (osascript)');
83
104
  if (!str) return null;
84
105
  const firstColon = str.indexOf(':');
85
106
  const secondColon = str.indexOf(':', firstColon + 1);
@@ -91,6 +112,11 @@ async function getMacClipboardImageData() {
91
112
  return { width, height, rgbaBuf: Buffer.from(base64, 'base64') };
92
113
  } catch (error) {
93
114
  logClipboardError('macOS image clipboard read (osascript)', error);
115
+ // If osascript failed with an error, attempt the Swift fallback for image capture
116
+ const rawBuffer = await getMacClipboardImageBuffer();
117
+ if (rawBuffer && rawBuffer.length > 0) {
118
+ return { rawBuffer };
119
+ }
94
120
  return null;
95
121
  }
96
122
  }
@@ -112,7 +138,10 @@ if let data = pb.data(forType: .png) {
112
138
  `;
113
139
  try {
114
140
  const { stdout } = await execFileAsync('swift', ['-e', swiftCode], { encoding: 'buffer', timeout: 3000 });
115
- if (stdout && stdout.length > 0) return stdout;
141
+ if (stdout && stdout.length > 0) {
142
+ markClipboardSuccess('macOS image clipboard read (Swift)');
143
+ return stdout;
144
+ }
116
145
  } catch (error) {
117
146
  logClipboardError('macOS image clipboard read (Swift)', error);
118
147
  try {
@@ -136,7 +165,10 @@ if let data = pb.data(forType: .png) {
136
165
  `;
137
166
  const { stdout } = await execFileAsync('osascript', ['-l', 'JavaScript', '-e', jsaScript], { timeout: 3000 });
138
167
  const str = stdout.trim();
139
- if (str) return Buffer.from(str, 'base64');
168
+ if (str) {
169
+ markClipboardSuccess('macOS image clipboard fallback read (osascript)');
170
+ return Buffer.from(str, 'base64');
171
+ }
140
172
  } catch (fallbackError) {
141
173
  logClipboardError('macOS image clipboard fallback read (osascript)', fallbackError);
142
174
  }
@@ -329,7 +361,12 @@ export async function captureClipboard() {
329
361
  if (process.platform === 'darwin' && settings.clipboardImageEnabled !== false && Date.now() >= suppressImageCaptureUntil) {
330
362
  try {
331
363
  const imageData = await getMacClipboardImageData();
332
- if (imageData && imageData.rgbaBuf.length > 0) {
364
+ if (!imageData) {
365
+ // No image on clipboard (or transient capture failed and will retry next tick)
366
+ return;
367
+ }
368
+
369
+ if (imageData.rgbaBuf && imageData.rgbaBuf.length > 0) {
333
370
  const hash = createHash('md5').update(imageData.rgbaBuf).digest('hex');
334
371
  if (hash !== lastImageHash) {
335
372
  let isDuplicate = false;
@@ -351,12 +388,14 @@ export async function captureClipboard() {
351
388
  try {
352
389
  const mozjpegBuf = await compressMozjpeg(imageData.rgbaBuf, imageData.width, imageData.height, 75);
353
390
  saveClipboardImage(mozjpegBuf, 'jpg', hash);
391
+ markClipboardSuccess('image compression or save (WASM/JPEG)', 'image save');
354
392
  } catch (error) {
355
393
  logClipboardError('image compression or save (WASM/JPEG)', error);
356
394
  const rawBuf = await getMacClipboardImageBuffer();
357
395
  if (rawBuf) {
358
396
  try {
359
397
  saveClipboardImage(rawBuf, 'png', hash);
398
+ markClipboardSuccess('raw image save after compression fallback', 'image save');
360
399
  } catch (fallbackError) {
361
400
  logClipboardError('raw image save after compression fallback', fallbackError);
362
401
  }
@@ -364,32 +403,30 @@ export async function captureClipboard() {
364
403
  }
365
404
  }
366
405
  }
367
- } else {
368
- const imageBuffer = await getMacClipboardImageBuffer();
369
- if (imageBuffer && imageBuffer.length > 0) {
370
- const hash = createHash('md5').update(imageBuffer).digest('hex');
371
- if (hash !== lastImageHash) {
372
- let isDuplicate = false;
373
- const dates = clipboardDates();
374
- for (const d of dates) {
375
- const dayList = dayItems(d);
376
- if (dayList.some(item => {
377
- if (item.imageHash === hash || item.fileHash === hash) return true;
378
- if (Array.isArray(item.imageHashes) && item.imageHashes.includes(hash)) return true;
379
- return false;
380
- })) {
381
- isDuplicate = true;
382
- break;
383
- }
406
+ } else if (imageData.rawBuffer && imageData.rawBuffer.length > 0) {
407
+ const hash = createHash('md5').update(imageData.rawBuffer).digest('hex');
408
+ if (hash !== lastImageHash) {
409
+ let isDuplicate = false;
410
+ const dates = clipboardDates();
411
+ for (const d of dates) {
412
+ const dayList = dayItems(d);
413
+ if (dayList.some(item => {
414
+ if (item.imageHash === hash || item.fileHash === hash) return true;
415
+ if (Array.isArray(item.imageHashes) && item.imageHashes.includes(hash)) return true;
416
+ return false;
417
+ })) {
418
+ isDuplicate = true;
419
+ break;
384
420
  }
421
+ }
385
422
 
386
- lastImageHash = hash;
387
- if (!isDuplicate) {
388
- try {
389
- saveClipboardImage(imageBuffer, 'png', hash);
390
- } catch (error) {
391
- logClipboardError('image save', error);
392
- }
423
+ lastImageHash = hash;
424
+ if (!isDuplicate) {
425
+ try {
426
+ saveClipboardImage(imageData.rawBuffer, 'png', hash);
427
+ markClipboardSuccess('image save');
428
+ } catch (error) {
429
+ logClipboardError('image save', error);
393
430
  }
394
431
  }
395
432
  }