buddy-workbench 0.1.91 → 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.91",
3
+ "version": "0.1.92",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,14 +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) {
22
29
  // Ignore benign failures due to timeout or process termination (e.g., system sleep / screen lock / display off)
23
30
  if (error?.killed || error?.timedOut || error?.signal === 'SIGTERM' || error?.code === 'ETIMEDOUT') {
24
31
  return;
25
32
  }
26
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
+
27
42
  // Clipboard polling runs continuously. Avoid filling Error Log with the
28
43
  // same native-tool failure every two seconds while retaining diagnostics.
29
44
  const now = Date.now();
@@ -33,7 +48,7 @@ function logClipboardError(stage, error) {
33
48
  const details = String(error?.stderr || error?.stdout || error?.stack || error?.message || error || '').trim();
34
49
  addErrorRecord({
35
50
  source: 'Clipboard Monitor',
36
- message: `Failed during ${stage}.`,
51
+ message: `Failed during ${stage} (consecutive failures: ${count}).`,
37
52
  details: details || 'The native clipboard operation returned an unknown error.'
38
53
  });
39
54
  }
@@ -85,6 +100,7 @@ async function getMacClipboardImageData() {
85
100
  try {
86
101
  const { stdout } = await execFileAsync('osascript', ['-l', 'JavaScript', '-e', jsaScript], { timeout: 3000 });
87
102
  const str = stdout.trim();
103
+ markClipboardSuccess('macOS image clipboard read (osascript)');
88
104
  if (!str) return null;
89
105
  const firstColon = str.indexOf(':');
90
106
  const secondColon = str.indexOf(':', firstColon + 1);
@@ -96,6 +112,11 @@ async function getMacClipboardImageData() {
96
112
  return { width, height, rgbaBuf: Buffer.from(base64, 'base64') };
97
113
  } catch (error) {
98
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
+ }
99
120
  return null;
100
121
  }
101
122
  }
@@ -117,7 +138,10 @@ if let data = pb.data(forType: .png) {
117
138
  `;
118
139
  try {
119
140
  const { stdout } = await execFileAsync('swift', ['-e', swiftCode], { encoding: 'buffer', timeout: 3000 });
120
- if (stdout && stdout.length > 0) return stdout;
141
+ if (stdout && stdout.length > 0) {
142
+ markClipboardSuccess('macOS image clipboard read (Swift)');
143
+ return stdout;
144
+ }
121
145
  } catch (error) {
122
146
  logClipboardError('macOS image clipboard read (Swift)', error);
123
147
  try {
@@ -141,7 +165,10 @@ if let data = pb.data(forType: .png) {
141
165
  `;
142
166
  const { stdout } = await execFileAsync('osascript', ['-l', 'JavaScript', '-e', jsaScript], { timeout: 3000 });
143
167
  const str = stdout.trim();
144
- 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
+ }
145
172
  } catch (fallbackError) {
146
173
  logClipboardError('macOS image clipboard fallback read (osascript)', fallbackError);
147
174
  }
@@ -334,7 +361,12 @@ export async function captureClipboard() {
334
361
  if (process.platform === 'darwin' && settings.clipboardImageEnabled !== false && Date.now() >= suppressImageCaptureUntil) {
335
362
  try {
336
363
  const imageData = await getMacClipboardImageData();
337
- 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) {
338
370
  const hash = createHash('md5').update(imageData.rgbaBuf).digest('hex');
339
371
  if (hash !== lastImageHash) {
340
372
  let isDuplicate = false;
@@ -356,12 +388,14 @@ export async function captureClipboard() {
356
388
  try {
357
389
  const mozjpegBuf = await compressMozjpeg(imageData.rgbaBuf, imageData.width, imageData.height, 75);
358
390
  saveClipboardImage(mozjpegBuf, 'jpg', hash);
391
+ markClipboardSuccess('image compression or save (WASM/JPEG)', 'image save');
359
392
  } catch (error) {
360
393
  logClipboardError('image compression or save (WASM/JPEG)', error);
361
394
  const rawBuf = await getMacClipboardImageBuffer();
362
395
  if (rawBuf) {
363
396
  try {
364
397
  saveClipboardImage(rawBuf, 'png', hash);
398
+ markClipboardSuccess('raw image save after compression fallback', 'image save');
365
399
  } catch (fallbackError) {
366
400
  logClipboardError('raw image save after compression fallback', fallbackError);
367
401
  }
@@ -369,32 +403,30 @@ export async function captureClipboard() {
369
403
  }
370
404
  }
371
405
  }
372
- } else {
373
- const imageBuffer = await getMacClipboardImageBuffer();
374
- if (imageBuffer && imageBuffer.length > 0) {
375
- const hash = createHash('md5').update(imageBuffer).digest('hex');
376
- if (hash !== lastImageHash) {
377
- let isDuplicate = false;
378
- const dates = clipboardDates();
379
- for (const d of dates) {
380
- const dayList = dayItems(d);
381
- if (dayList.some(item => {
382
- if (item.imageHash === hash || item.fileHash === hash) return true;
383
- if (Array.isArray(item.imageHashes) && item.imageHashes.includes(hash)) return true;
384
- return false;
385
- })) {
386
- isDuplicate = true;
387
- break;
388
- }
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;
389
420
  }
421
+ }
390
422
 
391
- lastImageHash = hash;
392
- if (!isDuplicate) {
393
- try {
394
- saveClipboardImage(imageBuffer, 'png', hash);
395
- } catch (error) {
396
- logClipboardError('image save', error);
397
- }
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);
398
430
  }
399
431
  }
400
432
  }