buddy-workbench 0.1.91 → 0.1.93
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/server/routes/pr-conflicts.js +3 -1
- package/server/services/clipboard-history.js +73 -29
- package/ui/dist/assets/index-B7RKG5wD.css +1 -0
- package/ui/dist/assets/index-DM76pj0U.js +590 -0
- package/ui/dist/assets/{xmindAdapter-CcQJiPhQ.js → xmindAdapter-DpfS6snl.js} +1 -1
- package/ui/dist/index.html +2 -2
- package/ui/dist/sw.js +2 -2
- package/ui/dist/assets/index-4ZrOzy5e.js +0 -590
- package/ui/dist/assets/index-CMDqLDOG.css +0 -1
package/package.json
CHANGED
|
@@ -310,7 +310,9 @@ export function UserProfile({ userId }: { userId: string }) {
|
|
|
310
310
|
router.get('/my-conflicts', async (_req, res) => {
|
|
311
311
|
const host = configuredBitbucketHost();
|
|
312
312
|
if (!host) return res.json({ values: [] });
|
|
313
|
-
|
|
313
|
+
// Bitbucket Server does not accept `state=ALL` for this endpoint; use the
|
|
314
|
+
// supported open state, matching the PR review/dashboard routes.
|
|
315
|
+
const dashboardUrl = `https://${host}/rest/api/latest/dashboard/pull-requests?role=author&state=OPEN&limit=100`;
|
|
314
316
|
try {
|
|
315
317
|
const response = await client.get(dashboardUrl, { headers: authHeaders() });
|
|
316
318
|
if (response.status < 200 || response.status >= 300) return res.status(response.status).json({ error: `Unable to load authored Pull Requests (${response.status}).` });
|
|
@@ -15,15 +15,34 @@ const previewLimit = 2000;
|
|
|
15
15
|
let lastValue = '';
|
|
16
16
|
let lastImageHash = '';
|
|
17
17
|
let suppressImageCaptureUntil = 0;
|
|
18
|
+
// Swift startup/permission failures can persist for the lifetime of the
|
|
19
|
+
// process (for example when the app has no GUI/clipboard access). Avoid
|
|
20
|
+
// recompiling Swift and incrementing the same error counter every poll.
|
|
21
|
+
let swiftClipboardRetryAt = 0;
|
|
18
22
|
let mozjpegWasmModule = null;
|
|
23
|
+
const clipboardErrorCounts = new Map();
|
|
19
24
|
const clipboardErrorLogTimes = new Map();
|
|
20
25
|
|
|
21
|
-
function
|
|
26
|
+
function markClipboardSuccess(...stages) {
|
|
27
|
+
for (const stage of stages) {
|
|
28
|
+
clipboardErrorCounts.delete(stage);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function logClipboardError(stage, error, threshold = 3) {
|
|
22
33
|
// Ignore benign failures due to timeout or process termination (e.g., system sleep / screen lock / display off)
|
|
23
34
|
if (error?.killed || error?.timedOut || error?.signal === 'SIGTERM' || error?.code === 'ETIMEDOUT') {
|
|
24
35
|
return;
|
|
25
36
|
}
|
|
26
37
|
|
|
38
|
+
const count = (clipboardErrorCounts.get(stage) || 0) + 1;
|
|
39
|
+
clipboardErrorCounts.set(stage, count);
|
|
40
|
+
|
|
41
|
+
// Transient filter: only log if failures happen consecutively past threshold
|
|
42
|
+
if (count < threshold) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
27
46
|
// Clipboard polling runs continuously. Avoid filling Error Log with the
|
|
28
47
|
// same native-tool failure every two seconds while retaining diagnostics.
|
|
29
48
|
const now = Date.now();
|
|
@@ -33,7 +52,7 @@ function logClipboardError(stage, error) {
|
|
|
33
52
|
const details = String(error?.stderr || error?.stdout || error?.stack || error?.message || error || '').trim();
|
|
34
53
|
addErrorRecord({
|
|
35
54
|
source: 'Clipboard Monitor',
|
|
36
|
-
message: `Failed during ${stage}.`,
|
|
55
|
+
message: `Failed during ${stage} (consecutive failures: ${count}).`,
|
|
37
56
|
details: details || 'The native clipboard operation returned an unknown error.'
|
|
38
57
|
});
|
|
39
58
|
}
|
|
@@ -85,6 +104,7 @@ async function getMacClipboardImageData() {
|
|
|
85
104
|
try {
|
|
86
105
|
const { stdout } = await execFileAsync('osascript', ['-l', 'JavaScript', '-e', jsaScript], { timeout: 3000 });
|
|
87
106
|
const str = stdout.trim();
|
|
107
|
+
markClipboardSuccess('macOS image clipboard read (osascript)');
|
|
88
108
|
if (!str) return null;
|
|
89
109
|
const firstColon = str.indexOf(':');
|
|
90
110
|
const secondColon = str.indexOf(':', firstColon + 1);
|
|
@@ -96,11 +116,17 @@ async function getMacClipboardImageData() {
|
|
|
96
116
|
return { width, height, rgbaBuf: Buffer.from(base64, 'base64') };
|
|
97
117
|
} catch (error) {
|
|
98
118
|
logClipboardError('macOS image clipboard read (osascript)', error);
|
|
119
|
+
// If osascript failed with an error, attempt the Swift fallback for image capture
|
|
120
|
+
const rawBuffer = await getMacClipboardImageBuffer();
|
|
121
|
+
if (rawBuffer && rawBuffer.length > 0) {
|
|
122
|
+
return { rawBuffer };
|
|
123
|
+
}
|
|
99
124
|
return null;
|
|
100
125
|
}
|
|
101
126
|
}
|
|
102
127
|
|
|
103
128
|
async function getMacClipboardImageBuffer() {
|
|
129
|
+
if (Date.now() < swiftClipboardRetryAt) return null;
|
|
104
130
|
const swiftCode = `
|
|
105
131
|
import Cocoa
|
|
106
132
|
let pb = NSPasteboard.general
|
|
@@ -117,9 +143,19 @@ if let data = pb.data(forType: .png) {
|
|
|
117
143
|
`;
|
|
118
144
|
try {
|
|
119
145
|
const { stdout } = await execFileAsync('swift', ['-e', swiftCode], { encoding: 'buffer', timeout: 3000 });
|
|
120
|
-
if (stdout && stdout.length > 0)
|
|
146
|
+
if (stdout && stdout.length > 0) {
|
|
147
|
+
markClipboardSuccess('macOS image clipboard read (Swift)');
|
|
148
|
+
swiftClipboardRetryAt = 0;
|
|
149
|
+
return stdout;
|
|
150
|
+
}
|
|
121
151
|
} catch (error) {
|
|
152
|
+
// Back off before trying Swift again. A failed `swift -e` invocation is
|
|
153
|
+
// commonly persistent and clipboard polling runs every two seconds.
|
|
154
|
+
swiftClipboardRetryAt = Date.now() + 5 * 60 * 1000;
|
|
122
155
|
logClipboardError('macOS image clipboard read (Swift)', error);
|
|
156
|
+
// This failure is now rate-limited by the retry window; don't let the
|
|
157
|
+
// displayed "consecutive failures" grow without bound.
|
|
158
|
+
clipboardErrorCounts.delete('macOS image clipboard read (Swift)');
|
|
123
159
|
try {
|
|
124
160
|
const jsaScript = `
|
|
125
161
|
ObjC.import("AppKit");
|
|
@@ -141,7 +177,10 @@ if let data = pb.data(forType: .png) {
|
|
|
141
177
|
`;
|
|
142
178
|
const { stdout } = await execFileAsync('osascript', ['-l', 'JavaScript', '-e', jsaScript], { timeout: 3000 });
|
|
143
179
|
const str = stdout.trim();
|
|
144
|
-
if (str)
|
|
180
|
+
if (str) {
|
|
181
|
+
markClipboardSuccess('macOS image clipboard fallback read (osascript)');
|
|
182
|
+
return Buffer.from(str, 'base64');
|
|
183
|
+
}
|
|
145
184
|
} catch (fallbackError) {
|
|
146
185
|
logClipboardError('macOS image clipboard fallback read (osascript)', fallbackError);
|
|
147
186
|
}
|
|
@@ -334,7 +373,12 @@ export async function captureClipboard() {
|
|
|
334
373
|
if (process.platform === 'darwin' && settings.clipboardImageEnabled !== false && Date.now() >= suppressImageCaptureUntil) {
|
|
335
374
|
try {
|
|
336
375
|
const imageData = await getMacClipboardImageData();
|
|
337
|
-
if (imageData
|
|
376
|
+
if (!imageData) {
|
|
377
|
+
// No image on clipboard (or transient capture failed and will retry next tick)
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (imageData.rgbaBuf && imageData.rgbaBuf.length > 0) {
|
|
338
382
|
const hash = createHash('md5').update(imageData.rgbaBuf).digest('hex');
|
|
339
383
|
if (hash !== lastImageHash) {
|
|
340
384
|
let isDuplicate = false;
|
|
@@ -356,12 +400,14 @@ export async function captureClipboard() {
|
|
|
356
400
|
try {
|
|
357
401
|
const mozjpegBuf = await compressMozjpeg(imageData.rgbaBuf, imageData.width, imageData.height, 75);
|
|
358
402
|
saveClipboardImage(mozjpegBuf, 'jpg', hash);
|
|
403
|
+
markClipboardSuccess('image compression or save (WASM/JPEG)', 'image save');
|
|
359
404
|
} catch (error) {
|
|
360
405
|
logClipboardError('image compression or save (WASM/JPEG)', error);
|
|
361
406
|
const rawBuf = await getMacClipboardImageBuffer();
|
|
362
407
|
if (rawBuf) {
|
|
363
408
|
try {
|
|
364
409
|
saveClipboardImage(rawBuf, 'png', hash);
|
|
410
|
+
markClipboardSuccess('raw image save after compression fallback', 'image save');
|
|
365
411
|
} catch (fallbackError) {
|
|
366
412
|
logClipboardError('raw image save after compression fallback', fallbackError);
|
|
367
413
|
}
|
|
@@ -369,32 +415,30 @@ export async function captureClipboard() {
|
|
|
369
415
|
}
|
|
370
416
|
}
|
|
371
417
|
}
|
|
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
|
-
}
|
|
418
|
+
} else if (imageData.rawBuffer && imageData.rawBuffer.length > 0) {
|
|
419
|
+
const hash = createHash('md5').update(imageData.rawBuffer).digest('hex');
|
|
420
|
+
if (hash !== lastImageHash) {
|
|
421
|
+
let isDuplicate = false;
|
|
422
|
+
const dates = clipboardDates();
|
|
423
|
+
for (const d of dates) {
|
|
424
|
+
const dayList = dayItems(d);
|
|
425
|
+
if (dayList.some(item => {
|
|
426
|
+
if (item.imageHash === hash || item.fileHash === hash) return true;
|
|
427
|
+
if (Array.isArray(item.imageHashes) && item.imageHashes.includes(hash)) return true;
|
|
428
|
+
return false;
|
|
429
|
+
})) {
|
|
430
|
+
isDuplicate = true;
|
|
431
|
+
break;
|
|
389
432
|
}
|
|
433
|
+
}
|
|
390
434
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
435
|
+
lastImageHash = hash;
|
|
436
|
+
if (!isDuplicate) {
|
|
437
|
+
try {
|
|
438
|
+
saveClipboardImage(imageData.rawBuffer, 'png', hash);
|
|
439
|
+
markClipboardSuccess('image save');
|
|
440
|
+
} catch (error) {
|
|
441
|
+
logClipboardError('image save', error);
|
|
398
442
|
}
|
|
399
443
|
}
|
|
400
444
|
}
|