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
|
@@ -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
|
|
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)
|
|
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)
|
|
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
|
|
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
|
|
374
|
-
if (
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
const
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
if (
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
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
|
}
|