safari-mcp 2.1.3 → 2.1.5
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/index.js +18 -2
- package/package.json +1 -1
- package/safari.js +3 -0
package/index.js
CHANGED
|
@@ -15,14 +15,30 @@ import { randomUUID } from "node:crypto";
|
|
|
15
15
|
import { execFileSync } from "node:child_process";
|
|
16
16
|
|
|
17
17
|
// ========== SINGLETON: kill stale instances from previous sessions ==========
|
|
18
|
+
// NOTE: Claude Code VSCode may start 2 instances simultaneously (~40ms apart).
|
|
19
|
+
// Only kill instances running >10 seconds (truly stale), not fresh siblings.
|
|
18
20
|
try {
|
|
19
21
|
const myPid = process.pid;
|
|
20
22
|
const lines = execFileSync("pgrep", ["-af", "node.*/safari-mcp/index\\.js"], { encoding: "utf8" }).trim().split("\n");
|
|
21
23
|
for (const line of lines) {
|
|
22
24
|
const pid = parseInt(line.split(/\s+/)[0], 10);
|
|
23
25
|
if (pid && pid !== myPid) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
// Check how long the process has been running (elapsed time in seconds)
|
|
27
|
+
let elapsedSec = 0;
|
|
28
|
+
try {
|
|
29
|
+
const etime = execFileSync("ps", ["-o", "etime=", "-p", String(pid)], { encoding: "utf8" }).trim();
|
|
30
|
+
// etime format: [[DD-]HH:]MM:SS — parse to seconds
|
|
31
|
+
const parts = etime.replace(/-/g, ":").split(":").map(Number);
|
|
32
|
+
if (parts.length === 2) elapsedSec = parts[0] * 60 + parts[1];
|
|
33
|
+
else if (parts.length === 3) elapsedSec = parts[0] * 3600 + parts[1] * 60 + parts[2];
|
|
34
|
+
else if (parts.length === 4) elapsedSec = parts[0] * 86400 + parts[1] * 3600 + parts[2] * 60 + parts[3];
|
|
35
|
+
} catch {}
|
|
36
|
+
if (elapsedSec > 10) {
|
|
37
|
+
try { process.kill(pid, "SIGTERM"); } catch {}
|
|
38
|
+
console.error(`[Safari MCP] Killed stale instance PID ${pid} (running ${elapsedSec}s)`);
|
|
39
|
+
} else {
|
|
40
|
+
console.error(`[Safari MCP] Skipping fresh instance PID ${pid} (running ${elapsedSec}s)`);
|
|
41
|
+
}
|
|
26
42
|
}
|
|
27
43
|
}
|
|
28
44
|
} catch {}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "safari-mcp",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.5",
|
|
4
4
|
"mcpName": "io.github.achiya-automation/safari-mcp",
|
|
5
5
|
"description": "Safari browser automation for AI agents — native macOS, zero Chrome overhead. 80 tools via AppleScript + JavaScript.",
|
|
6
6
|
"type": "module",
|
package/safari.js
CHANGED
|
@@ -1152,6 +1152,9 @@ export async function fill({ selector, value, ref }) {
|
|
|
1152
1152
|
`var pm=el.closest('.ProseMirror')||el.querySelector('.ProseMirror');if(pm){try{var v=null;if(pm.pmViewDesc&&pm.pmViewDesc.view)v=pm.pmViewDesc.view;else if(pm.cmView&&pm.cmView.view)v=pm.cmView.view;else{var keys=Object.keys(pm);for(var ki=0;ki<keys.length;ki++){var o=pm[keys[ki]];if(o&&o.state&&o.dispatch){v=o;break;}}}if(v&&v.state&&v.dispatch){var doc=v.state.doc;var hasContent=doc.textContent&&doc.textContent.trim().length>0;if(hasContent){var endPos=doc.content.size>1?doc.content.size-1:doc.content.size;v.dispatch(v.state.tr.insertText(' ${val}',endPos));v.focus();return 'Filled CE (ProseMirror append)';}else{var tr=v.state.tr;tr.replaceWith(0,doc.content.size,v.state.schema.text('${val}'));v.dispatch(tr);v.focus();return 'Filled CE (ProseMirror replace)';}}}catch(e){}}` +
|
|
1153
1153
|
// Closure/Medium detection — signal for native paste
|
|
1154
1154
|
`var isClosure=Object.keys(el).some(function(k){return k.startsWith('closure_uid_');})||location.hostname.includes('medium.com');if(isClosure){return '__CLOSURE_NATIVE_PASTE__';}` +
|
|
1155
|
+
// Synthetic ClipboardEvent paste — works on ProseMirror, TipTap, Slate, and most modern editors
|
|
1156
|
+
// that don't respond to execCommand but DO handle paste events
|
|
1157
|
+
`try{el.focus();var sel2=window.getSelection();if(sel2.rangeCount){var rng=document.createRange();rng.selectNodeContents(el);sel2.removeAllRanges();sel2.addRange(rng);}var dt=new DataTransfer();dt.setData('text/plain','${val}');var pe=new ClipboardEvent('paste',{bubbles:true,cancelable:true,clipboardData:dt});var handled=!el.dispatchEvent(pe);if(handled||el.textContent.indexOf('${val.substring(0, 20)}')>=0){return 'Filled CE (synthetic paste)';}}catch(ep){}` +
|
|
1155
1158
|
// Default contenteditable: selectAll+delete+insert (safer than textContent='')
|
|
1156
1159
|
`document.execCommand('selectAll');document.execCommand('delete');document.execCommand('insertText',false,'${val}');el.dispatchEvent(new Event('input',{bubbles:true}));el.dispatchEvent(new Event('blur',{bubbles:true}));return 'Filled contenteditable';}` +
|
|
1157
1160
|
// Standard input/textarea with _valueTracker
|