buddy-workbench 0.1.15 → 0.1.16
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/repositories/settings.js +1 -1
- package/server/routes/clipboard.js +10 -1
- package/server/services/bookmark-sync.js +10 -3
- package/server/services/clipboard-history.js +63 -3
- package/server/services/process-manager.js +17 -1
- package/ui/dist/assets/index-C8IWgbWG.js +524 -0
- package/ui/dist/index.html +1 -1
- package/ui/dist/assets/index-Dfww9Tti.js +0 -524
package/package.json
CHANGED
|
@@ -53,7 +53,7 @@ export function saveDefaultEditor(defaultEditor) {
|
|
|
53
53
|
|
|
54
54
|
export function saveDefaultBrowser(defaultBrowser) {
|
|
55
55
|
const settings = readSettings();
|
|
56
|
-
const valid = ['chrome', 'edge', 'safari'];
|
|
56
|
+
const valid = process.platform === 'win32' ? ['chrome', 'edge'] : ['chrome', 'edge', 'safari'];
|
|
57
57
|
settings.defaultBrowser = valid.includes(defaultBrowser) ? defaultBrowser : 'chrome';
|
|
58
58
|
mkdirSync(dirname(paths.settings), { recursive: true });
|
|
59
59
|
writeFileSync(paths.settings, JSON.stringify(settings, null, 2), { mode: 0o600 });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import express, { Router } from 'express';
|
|
2
|
-
import { clipboardDates, clipboardItems, clipboardOriginal, deleteClipboardItem, saveClipboardImage, clipboardImagePath, allTaggedClipboardItems, updateClipboardItemTags } from '../services/clipboard-history.js';
|
|
2
|
+
import { clipboardDates, clipboardItems, clipboardOriginal, deleteClipboardItem, saveClipboardImage, clipboardImagePath, allTaggedClipboardItems, updateClipboardItemTags, markClipboardCopied } from '../services/clipboard-history.js';
|
|
3
3
|
|
|
4
4
|
const router = Router();
|
|
5
5
|
router.get('/', (req, res) => res.json({ dates: clipboardDates(), items: clipboardItems(req.query.date) }));
|
|
@@ -36,5 +36,14 @@ router.put('/:date/:id/tags', (req, res) => {
|
|
|
36
36
|
if (!updated) return res.status(404).json({ error: 'Clipboard entry not found.' });
|
|
37
37
|
res.json(updated);
|
|
38
38
|
});
|
|
39
|
+
router.post('/:date/:id/copied', async (req, res) => {
|
|
40
|
+
try {
|
|
41
|
+
const updated = await markClipboardCopied(req.params.date, req.params.id);
|
|
42
|
+
res.json(updated || { ok: true });
|
|
43
|
+
} catch (error) {
|
|
44
|
+
res.status(500).json({ error: error.message });
|
|
45
|
+
}
|
|
46
|
+
});
|
|
39
47
|
router.delete('/:date/:id', (req, res) => { if (!deleteClipboardItem(req.params.date, req.params.id)) return res.status(404).json({ error: 'Clipboard entry not found.' }); res.status(204).end(); });
|
|
40
48
|
export default router;
|
|
49
|
+
|
|
@@ -33,10 +33,17 @@ export function getBrowserBookmarkPaths() {
|
|
|
33
33
|
* Checks if a browser process is currently running.
|
|
34
34
|
*/
|
|
35
35
|
export function checkBrowserRunning(browserName) {
|
|
36
|
+
const platform = os.platform();
|
|
36
37
|
try {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
if (platform === 'win32') {
|
|
39
|
+
const exeName = browserName === 'chrome' ? 'chrome.exe' : 'msedge.exe';
|
|
40
|
+
const output = execSync(`tasklist /FI "IMAGENAME eq ${exeName}"`, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
41
|
+
return output.toLowerCase().includes(exeName.toLowerCase());
|
|
42
|
+
} else {
|
|
43
|
+
const processPattern = browserName === 'chrome' ? 'Google Chrome' : 'Microsoft Edge';
|
|
44
|
+
const output = execSync(`pgrep -f "${processPattern}"`, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
45
|
+
return output.trim().length > 0;
|
|
46
|
+
}
|
|
40
47
|
} catch {
|
|
41
48
|
return false;
|
|
42
49
|
}
|
|
@@ -287,7 +287,11 @@ export async function captureClipboard() {
|
|
|
287
287
|
const dates = clipboardDates();
|
|
288
288
|
for (const d of dates) {
|
|
289
289
|
const dayList = dayItems(d);
|
|
290
|
-
if (dayList.some(item =>
|
|
290
|
+
if (dayList.some(item => {
|
|
291
|
+
if (item.imageHash === hash || item.fileHash === hash) return true;
|
|
292
|
+
if (Array.isArray(item.imageHashes) && item.imageHashes.includes(hash)) return true;
|
|
293
|
+
return false;
|
|
294
|
+
})) {
|
|
291
295
|
isDuplicate = true;
|
|
292
296
|
break;
|
|
293
297
|
}
|
|
@@ -313,7 +317,11 @@ export async function captureClipboard() {
|
|
|
313
317
|
const dates = clipboardDates();
|
|
314
318
|
for (const d of dates) {
|
|
315
319
|
const dayList = dayItems(d);
|
|
316
|
-
if (dayList.some(item =>
|
|
320
|
+
if (dayList.some(item => {
|
|
321
|
+
if (item.imageHash === hash || item.fileHash === hash) return true;
|
|
322
|
+
if (Array.isArray(item.imageHashes) && item.imageHashes.includes(hash)) return true;
|
|
323
|
+
return false;
|
|
324
|
+
})) {
|
|
317
325
|
isDuplicate = true;
|
|
318
326
|
break;
|
|
319
327
|
}
|
|
@@ -332,11 +340,61 @@ export async function captureClipboard() {
|
|
|
332
340
|
|
|
333
341
|
export function startClipboardCapture() { captureClipboard(); const timer = setInterval(captureClipboard, 2000); timer.unref(); }
|
|
334
342
|
|
|
343
|
+
export async function markClipboardCopied(date, id) {
|
|
344
|
+
let targetDate = date;
|
|
345
|
+
let items = dayItems(targetDate);
|
|
346
|
+
let item = items.find((entry) => entry.id === id);
|
|
347
|
+
|
|
348
|
+
if (!item) {
|
|
349
|
+
const dates = clipboardDates();
|
|
350
|
+
for (const d of dates) {
|
|
351
|
+
const dayList = dayItems(d);
|
|
352
|
+
const found = dayList.find((entry) => entry.id === id);
|
|
353
|
+
if (found) {
|
|
354
|
+
targetDate = d;
|
|
355
|
+
items = dayList;
|
|
356
|
+
item = found;
|
|
357
|
+
break;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (!item) return null;
|
|
363
|
+
|
|
364
|
+
if (item.imageFile) {
|
|
365
|
+
try {
|
|
366
|
+
const imageData = await getMacClipboardImageData();
|
|
367
|
+
if (imageData && imageData.rgbaBuf.length > 0) {
|
|
368
|
+
const currentHash = createHash('md5').update(imageData.rgbaBuf).digest('hex');
|
|
369
|
+
lastImageHash = currentHash;
|
|
370
|
+
|
|
371
|
+
const existingHashes = Array.isArray(item.imageHashes)
|
|
372
|
+
? item.imageHashes
|
|
373
|
+
: [item.imageHash, item.fileHash].filter(Boolean);
|
|
374
|
+
|
|
375
|
+
if (!existingHashes.includes(currentHash)) {
|
|
376
|
+
const updatedItem = { ...item, imageHashes: [...existingHashes, currentHash] };
|
|
377
|
+
const nextItems = items.map((entry) => (entry.id === id ? updatedItem : entry));
|
|
378
|
+
writeJson(dayFile(targetDate), nextItems);
|
|
379
|
+
return updatedItem;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
} catch {}
|
|
383
|
+
} else {
|
|
384
|
+
const text = clipboardOriginal(targetDate, id) || item.text || item.preview || '';
|
|
385
|
+
if (text) {
|
|
386
|
+
lastValue = text.trim();
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return item;
|
|
390
|
+
}
|
|
391
|
+
|
|
335
392
|
export function saveClipboardImage(buffer, ext = 'png', imageHash = '') {
|
|
336
393
|
const date = today();
|
|
337
394
|
const id = randomUUID();
|
|
338
395
|
const filename = `${id}.${ext}`;
|
|
339
|
-
const
|
|
396
|
+
const fileHash = createHash('md5').update(buffer).digest('hex');
|
|
397
|
+
const hash = imageHash || fileHash;
|
|
340
398
|
|
|
341
399
|
mkdirSync(join(paths.clipboardDir, 'content'), { recursive: true });
|
|
342
400
|
writeFileSync(join(paths.clipboardDir, 'content', filename), buffer);
|
|
@@ -345,6 +403,8 @@ export function saveClipboardImage(buffer, ext = 'png', imageHash = '') {
|
|
|
345
403
|
id,
|
|
346
404
|
imageFile: filename,
|
|
347
405
|
imageHash: hash,
|
|
406
|
+
fileHash,
|
|
407
|
+
imageHashes: Array.from(new Set([hash, fileHash].filter(Boolean))),
|
|
348
408
|
createdAt: new Date().toISOString()
|
|
349
409
|
};
|
|
350
410
|
|
|
@@ -103,7 +103,23 @@ export function scriptErrorLogs(launcherId, scriptId) { return logs.get(keyFor(l
|
|
|
103
103
|
export function clearScriptLogs(launcherId, scriptId) { logs.set(keyFor(launcherId, scriptId), { output: '', error: '', errorCount: 0 }); }
|
|
104
104
|
|
|
105
105
|
async function listeningProcesses() {
|
|
106
|
-
if (process.platform === 'win32')
|
|
106
|
+
if (process.platform === 'win32') {
|
|
107
|
+
try {
|
|
108
|
+
const { stdout } = await execFileAsync('netstat', ['-ano', '-p', 'tcp']);
|
|
109
|
+
const rows = [];
|
|
110
|
+
const matches = stdout.matchAll(/TCP\s+(?:\[::\]|[\d.]+):(\d+)\s+.*?LISTENING\s+(\d+)/gi);
|
|
111
|
+
for (const m of matches) {
|
|
112
|
+
const port = Number(m[1]);
|
|
113
|
+
const pid = Number(m[2]);
|
|
114
|
+
if (port && pid) {
|
|
115
|
+
rows.push({ pid, port, command: 'node' });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return rows;
|
|
119
|
+
} catch {
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
}
|
|
107
123
|
try {
|
|
108
124
|
const { stdout } = await execFileAsync('lsof', ['-nP', '-iTCP', '-sTCP:LISTEN', '-F', 'pcn'], { maxBuffer: 1024 * 1024 });
|
|
109
125
|
const rows = [];
|