safari-mcp 2.22.13 → 2.22.14
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/safari.js +25 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "safari-mcp",
|
|
3
|
-
"version": "2.22.
|
|
3
|
+
"version": "2.22.14",
|
|
4
4
|
"mcpName": "io.github.achiya-automation/safari-mcp",
|
|
5
5
|
"description": "Safari browser automation for AI agents — native macOS, zero Chrome overhead. 98 tools via AppleScript + JavaScript.",
|
|
6
6
|
"type": "module",
|
package/safari.js
CHANGED
|
@@ -8,7 +8,7 @@ import { promisify } from "node:util";
|
|
|
8
8
|
import { tmpdir, homedir } from "node:os";
|
|
9
9
|
import { join, dirname, resolve as resolvePath } from "node:path";
|
|
10
10
|
import { readFile, writeFile, unlink, appendFile, mkdir } from "node:fs/promises";
|
|
11
|
-
import { readFileSync, realpathSync } from "node:fs";
|
|
11
|
+
import { existsSync, readFileSync, realpathSync } from "node:fs";
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
13
|
import { randomUUID } from "node:crypto";
|
|
14
14
|
import { VIEWPORT_SCRIPT, SAFE_AREA_SCRIPT, PWA_SCRIPT, WEBKIT_COMPAT_SCRIPT, ALL_SHEETS_FN } from "./injected-validators.js";
|
|
@@ -1426,9 +1426,16 @@ export const SCREEN_LOCKED_MSG =
|
|
|
1426
1426
|
"safari_type_text / safari_press_key, and safari_upload_file without forceNative.";
|
|
1427
1427
|
|
|
1428
1428
|
export async function isScreenLocked() {
|
|
1429
|
-
|
|
1430
|
-
"ioreg -n Root -d1 -r -a
|
|
1431
|
-
|
|
1429
|
+
try {
|
|
1430
|
+
const { stdout } = await execFileAsync("/usr/sbin/ioreg", ["-n", "Root", "-d1", "-r", "-a"], { timeout: 2000 });
|
|
1431
|
+
const matches = [...String(stdout).matchAll(/<key>\s*(?:CGSSessionScreenIsLocked|IOConsoleLocked)\s*<\/key>\s*<(true|false)\s*\/>/g)];
|
|
1432
|
+
if (matches.length) return matches.some(match => match[1] === "true");
|
|
1433
|
+
// ioreg omits the key on an unlocked session. A present but unreadable value or
|
|
1434
|
+
// a broken probe must fail toward blocked input, never toward CGEvent.
|
|
1435
|
+
return /<key>\s*(?:CGSSessionScreenIsLocked|IOConsoleLocked)\s*<\/key>/.test(String(stdout));
|
|
1436
|
+
} catch {
|
|
1437
|
+
return true;
|
|
1438
|
+
}
|
|
1432
1439
|
}
|
|
1433
1440
|
|
|
1434
1441
|
export async function assertScreenUnlocked(probe = isScreenLocked) {
|
|
@@ -6610,6 +6617,17 @@ export function macosCompatNote(productVersion) {
|
|
|
6610
6617
|
return { version: raw, major, risky, line };
|
|
6611
6618
|
}
|
|
6612
6619
|
|
|
6620
|
+
// A Homebrew upgrade deletes the node binary a long-running server still executes, and System
|
|
6621
|
+
// Settings cannot grant anything to a file that is gone. Seen on 9.10.26: node 26.9.0 was replaced
|
|
6622
|
+
// by 26.11.0 under a LaunchAgent daemon started two days earlier, and doctor went from 6/6 to 3/6
|
|
6623
|
+
// (Apple Events -1743, both helper permissions false). Only a restart onto the current node helps.
|
|
6624
|
+
export function staleNodeNote(execPath = process.execPath, exists = existsSync) {
|
|
6625
|
+
if (exists(execPath)) return null;
|
|
6626
|
+
return `⚠ This server still runs node from ${execPath}, which no longer exists: node was upgraded or removed while the server kept running. ` +
|
|
6627
|
+
"macOS permission checks can fail for a binary that is gone, and System Settings cannot grant anything to it. " +
|
|
6628
|
+
"Restart the server on the current node, then grant the permissions to the new binary if macOS asks.";
|
|
6629
|
+
}
|
|
6630
|
+
|
|
6613
6631
|
export async function doctor() {
|
|
6614
6632
|
const checks = [];
|
|
6615
6633
|
const add = (ok, label, detail, fix) => checks.push({ ok, label, detail, fix: ok ? null : fix });
|
|
@@ -6657,7 +6675,7 @@ export async function doctor() {
|
|
|
6657
6675
|
// Homebrew node upgrade changes that path, so the grant has to follow the new binary.
|
|
6658
6676
|
add(!!pf && pf.screenRecording === true, "Screen Recording (screenshots)",
|
|
6659
6677
|
pf ? (pf.screenRecording ? "permitted" : "NOT permitted — screenshots taken through AppleScript and safari_save_pdf fail (extension screenshots don't need it)") : "unknown (helper not responding)",
|
|
6660
|
-
`System Settings > Privacy & Security > Screen & System Audio Recording → enable ${process.ppid
|
|
6678
|
+
`System Settings > Privacy & Security > Screen & System Audio Recording → enable ${process.ppid !== 1 ? "the terminal/IDE that launched this server" : staleNodeNote() ? "the node this server runs on after a restart (see the warning above)" : `node at ${process.execPath} (this server runs under launchd)`}.`);
|
|
6661
6679
|
|
|
6662
6680
|
// 6. Helper codesign identity — a stale/ad-hoc id breaks the Accessibility grant on reinstall
|
|
6663
6681
|
let idOk = false, idDetail = "";
|
|
@@ -6685,6 +6703,8 @@ export async function doctor() {
|
|
|
6685
6703
|
const passed = checks.filter((c) => c.ok).length;
|
|
6686
6704
|
const lines = [`Safari MCP doctor — ${passed}/${checks.length} checks passed`, ""];
|
|
6687
6705
|
if (osLine) lines.push(osLine, "");
|
|
6706
|
+
const nodeNote = staleNodeNote();
|
|
6707
|
+
if (nodeNote) lines.push(nodeNote, "");
|
|
6688
6708
|
// Not a pass/fail check — a state the user has to be able to see here rather than dig out
|
|
6689
6709
|
// of the host's env, so "why did it touch my tab" has an answer in the same report (#92).
|
|
6690
6710
|
lines.push(
|