buddy-workbench 0.1.96 → 0.1.98

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.96",
3
+ "version": "0.1.98",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {
@@ -12,6 +12,8 @@ import { readWindowsClipboard } from './windows.js';
12
12
 
13
13
  const execFileAsync = promisify(execFile);
14
14
  const previewLimit = 2000;
15
+ const macClipboardCommandMaxBuffer = 100 * 1024 * 1024;
16
+ const macClipboardCommandTimeout = 10 * 1000;
15
17
  let lastValue = '';
16
18
  let lastImageHash = '';
17
19
  let suppressImageCaptureUntil = 0;
@@ -101,7 +103,10 @@ async function getMacClipboardImageData() {
101
103
  }
102
104
  `;
103
105
  try {
104
- const { stdout } = await execFileAsync('osascript', ['-l', 'JavaScript', '-e', jsaScript], { timeout: 3000 });
106
+ const { stdout } = await execFileAsync('osascript', ['-l', 'JavaScript', '-e', jsaScript], {
107
+ timeout: macClipboardCommandTimeout,
108
+ maxBuffer: macClipboardCommandMaxBuffer
109
+ });
105
110
  const str = stdout.trim();
106
111
  markClipboardSuccess('macOS image clipboard read (osascript)');
107
112
  if (!str) return null;
@@ -113,8 +118,7 @@ async function getMacClipboardImageData() {
113
118
  const base64 = str.slice(secondColon + 1);
114
119
  if (!width || !height || !base64) return null;
115
120
  return { width, height, rgbaBuf: Buffer.from(base64, 'base64') };
116
- } catch (error) {
117
- logClipboardError('macOS image clipboard read (osascript)', error);
121
+ } catch {
118
122
  // If osascript failed with an error, attempt the Swift fallback for image capture
119
123
  const rawBuffer = await getMacClipboardImageBuffer();
120
124
  if (rawBuffer && rawBuffer.length > 0) {
@@ -141,20 +145,20 @@ if let data = pb.data(forType: .png) {
141
145
  }
142
146
  `;
143
147
  try {
144
- const { stdout } = await execFileAsync('swift', ['-e', swiftCode], { encoding: 'buffer', timeout: 3000 });
148
+ const { stdout } = await execFileAsync('swift', ['-e', swiftCode], {
149
+ encoding: 'buffer',
150
+ timeout: macClipboardCommandTimeout,
151
+ maxBuffer: macClipboardCommandMaxBuffer
152
+ });
145
153
  if (stdout && stdout.length > 0) {
146
154
  markClipboardSuccess('macOS image clipboard read (Swift)');
147
155
  swiftClipboardRetryAt = 0;
148
156
  return stdout;
149
157
  }
150
- } catch (error) {
158
+ } catch {
151
159
  // Back off before trying Swift again. A failed `swift -e` invocation is
152
160
  // commonly persistent and clipboard polling runs every two seconds.
153
161
  swiftClipboardRetryAt = Date.now() + 5 * 60 * 1000;
154
- logClipboardError('macOS image clipboard read (Swift)', error);
155
- // This failure is now rate-limited by the retry window; don't let the
156
- // displayed "consecutive failures" grow without bound.
157
- clipboardErrorCounts.delete('macOS image clipboard read (Swift)');
158
162
  try {
159
163
  const jsaScript = `
160
164
  ObjC.import("AppKit");
@@ -174,15 +178,16 @@ if let data = pb.data(forType: .png) {
174
178
  "";
175
179
  }
176
180
  `;
177
- const { stdout } = await execFileAsync('osascript', ['-l', 'JavaScript', '-e', jsaScript], { timeout: 3000 });
181
+ const { stdout } = await execFileAsync('osascript', ['-l', 'JavaScript', '-e', jsaScript], {
182
+ timeout: macClipboardCommandTimeout,
183
+ maxBuffer: macClipboardCommandMaxBuffer
184
+ });
178
185
  const str = stdout.trim();
179
186
  if (str) {
180
187
  markClipboardSuccess('macOS image clipboard fallback read (osascript)');
181
188
  return Buffer.from(str, 'base64');
182
189
  }
183
- } catch (fallbackError) {
184
- logClipboardError('macOS image clipboard fallback read (osascript)', fallbackError);
185
- }
190
+ } catch {}
186
191
  }
187
192
  return null;
188
193
  }
@@ -394,11 +399,11 @@ export async function captureClipboard() {
394
399
  }
395
400
  }
396
401
 
397
- lastImageHash = hash;
398
402
  if (!isDuplicate) {
399
403
  try {
400
404
  const mozjpegBuf = await compressMozjpeg(imageData.rgbaBuf, imageData.width, imageData.height, 75);
401
405
  saveClipboardImage(mozjpegBuf, 'jpg', hash);
406
+ lastImageHash = hash;
402
407
  markClipboardSuccess('image compression or save (WASM/JPEG)', 'image save');
403
408
  } catch (error) {
404
409
  logClipboardError('image compression or save (WASM/JPEG)', error);
@@ -406,12 +411,15 @@ export async function captureClipboard() {
406
411
  if (rawBuf) {
407
412
  try {
408
413
  saveClipboardImage(rawBuf, 'png', hash);
414
+ lastImageHash = hash;
409
415
  markClipboardSuccess('raw image save after compression fallback', 'image save');
410
416
  } catch (fallbackError) {
411
417
  logClipboardError('raw image save after compression fallback', fallbackError);
412
418
  }
413
419
  }
414
420
  }
421
+ } else {
422
+ lastImageHash = hash;
415
423
  }
416
424
  }
417
425
  } else if (imageData.rawBuffer && imageData.rawBuffer.length > 0) {
@@ -431,14 +439,16 @@ export async function captureClipboard() {
431
439
  }
432
440
  }
433
441
 
434
- lastImageHash = hash;
435
442
  if (!isDuplicate) {
436
443
  try {
437
444
  saveClipboardImage(imageData.rawBuffer, 'png', hash);
445
+ lastImageHash = hash;
438
446
  markClipboardSuccess('image save');
439
447
  } catch (error) {
440
448
  logClipboardError('image save', error);
441
449
  }
450
+ } else {
451
+ lastImageHash = hash;
442
452
  }
443
453
  }
444
454
  }
@@ -446,7 +456,22 @@ export async function captureClipboard() {
446
456
  }
447
457
  }
448
458
 
449
- export function startClipboardCapture() { captureClipboard(); const timer = setInterval(captureClipboard, 2000); timer.unref(); }
459
+ export function startClipboardCapture() {
460
+ let captureInProgress = false;
461
+ const capture = async () => {
462
+ if (captureInProgress) return;
463
+ captureInProgress = true;
464
+ try {
465
+ await captureClipboard();
466
+ } finally {
467
+ captureInProgress = false;
468
+ }
469
+ };
470
+
471
+ void capture();
472
+ const timer = setInterval(() => { void capture(); }, 2000);
473
+ timer.unref();
474
+ }
450
475
 
451
476
  export async function markClipboardCopied(date, id) {
452
477
  let targetDate = date;