safari-mcp 2.22.6 → 2.22.7
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 +24 -8
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1861,10 +1861,16 @@ function _safeUrlForOutput(rawUrl) {
|
|
|
1861
1861
|
// Receipts this daemon rotated on the caller's behalf (a cross-origin safari_navigate) stay
|
|
1862
1862
|
// usable under their old name: callers routinely keep the first receipt they saw and ignore
|
|
1863
1863
|
// the fresh one in the navigate result (nine consecutive "not valid for this origin" failures
|
|
1864
|
-
// in one session on 4.9.26). Whoever held the old capability held the tab.
|
|
1864
|
+
// in one session on 4.9.26). Whoever held the old capability held the tab. Every session that
|
|
1865
|
+
// held it follows the alias, other clients of an HTTP daemon included (_getActiveReceipt), so an
|
|
1866
|
+
// alias may only join two receipts of one tab: each rotation names the receipt it replaces.
|
|
1865
1867
|
const _receiptAliases = new Map(); // old token → newer token
|
|
1866
1868
|
function _aliasReceipt(oldToken, newToken) {
|
|
1867
1869
|
if (!oldToken || !newToken || oldToken === newToken) return;
|
|
1870
|
+
// Every older name of the tab points straight at its newest receipt. As a chain of one link per
|
|
1871
|
+
// rotation, a receipt stopped resolving after eight (_receiptToken's hop limit): a handful of
|
|
1872
|
+
// cross-origin navigations in one tab.
|
|
1873
|
+
for (const [older, newer] of _receiptAliases) if (newer === oldToken) _receiptAliases.set(older, newToken);
|
|
1868
1874
|
_receiptAliases.set(oldToken, newToken);
|
|
1869
1875
|
if (_receiptAliases.size > 500) _receiptAliases.delete(_receiptAliases.keys().next().value);
|
|
1870
1876
|
}
|
|
@@ -1920,8 +1926,10 @@ const _activeReceipts = new Map();
|
|
|
1920
1926
|
function _receiptSessionKey() {
|
|
1921
1927
|
return `${SESSION_ID}:${currentSessionId()}`;
|
|
1922
1928
|
}
|
|
1929
|
+
// Resolved on every read: another client of an HTTP daemon can rotate this session's receipt
|
|
1930
|
+
// after it was stored, and the extension then knows only the new one.
|
|
1923
1931
|
function _getActiveReceipt() {
|
|
1924
|
-
return _activeReceipts.get(_receiptSessionKey())
|
|
1932
|
+
return _receiptToken(_activeReceipts.get(_receiptSessionKey()));
|
|
1925
1933
|
}
|
|
1926
1934
|
function _setActiveReceipt(receipt) {
|
|
1927
1935
|
_activeReceipts.set(_receiptSessionKey(), _receiptToken(receipt));
|
|
@@ -2089,7 +2097,7 @@ async function _runExtensionBatchAction(action, args = {}) {
|
|
|
2089
2097
|
if (safeValue?.receipt) {
|
|
2090
2098
|
// Rotation retires the old token. Keep it resolving to the new one, as navigate does,
|
|
2091
2099
|
// so the tab stays reachable — and closable by the tab cap — by the name it was opened with.
|
|
2092
|
-
_aliasReceipt(previous, safeValue.receipt);
|
|
2100
|
+
_aliasReceipt(_receiptToken(previous), safeValue.receipt); // the receipt extensionOrFallback sent
|
|
2093
2101
|
_setActiveReceipt(safeValue.receipt);
|
|
2094
2102
|
}
|
|
2095
2103
|
return safeValue;
|
|
@@ -2388,9 +2396,13 @@ async function extensionOrFallback(extensionType, extensionPayload, fallbackFn)
|
|
|
2388
2396
|
const reloadHandoff = extensionType === "reload_extension" && _isExtensionHost
|
|
2389
2397
|
? _prepareReloadHttpWorkerHandoff()
|
|
2390
2398
|
: null;
|
|
2399
|
+
const namedReceipt = _receiptToken(extensionPayload.receipt);
|
|
2391
2400
|
const payload = {
|
|
2392
2401
|
...(attachActiveReceipt && activeReceipt ? { receipt: activeReceipt } : {}),
|
|
2393
2402
|
...extensionPayload,
|
|
2403
|
+
// A receipt named when the call started follows a rotation of its tab made while the call
|
|
2404
|
+
// waited above (by another client of an HTTP daemon): an alias joins receipts of one tab.
|
|
2405
|
+
...(namedReceipt ? { receipt: namedReceipt } : {}),
|
|
2394
2406
|
...(reloadHandoff ? { reloadHandoff: reloadHandoff.token } : {}),
|
|
2395
2407
|
sessionId: `${SESSION_ID}:${currentSessionId()}`,
|
|
2396
2408
|
};
|
|
@@ -2554,8 +2566,11 @@ server.tool(
|
|
|
2554
2566
|
}
|
|
2555
2567
|
const oldUrl = safari.getActiveTabURL();
|
|
2556
2568
|
// extensionOrFallback claims the destination for this session once its guard passes.
|
|
2569
|
+
// Read once, before anything waits: a call alongside that names another tab makes that tab the
|
|
2570
|
+
// session's current one, and the rotation below has to name the tab this call navigated.
|
|
2571
|
+
const usedReceipt = _receiptToken(receipt || _getActiveReceipt());
|
|
2557
2572
|
let result = await extensionOrFallback(
|
|
2558
|
-
"navigate", { url, ..._explicitReceipt({ receipt }) },
|
|
2573
|
+
"navigate", { url, ..._explicitReceipt({ receipt: usedReceipt }) },
|
|
2559
2574
|
() => safari.navigate(url)
|
|
2560
2575
|
);
|
|
2561
2576
|
// Tab kept its identity, just changed URL — drop the stale old URL from ownership.
|
|
@@ -2565,14 +2580,15 @@ server.tool(
|
|
|
2565
2580
|
// origin", ~80×/week). The caller chose the destination, so rotating here is safe —
|
|
2566
2581
|
// hand back the new receipt instead of letting the caller discover the trap.
|
|
2567
2582
|
const landed = result && typeof result === "object" ? result.url : "";
|
|
2568
|
-
|
|
2569
|
-
if (landed && _originOf(landed) !== (_receiptOrigins.get(usedReceipt) || _originOf(oldUrl)) && usedReceipt) {
|
|
2583
|
+
// The origin of the receipt the navigation carried: extensionOrFallback sends the newest one.
|
|
2584
|
+
if (landed && _originOf(landed) !== (_receiptOrigins.get(_receiptToken(usedReceipt)) || _originOf(oldUrl)) && usedReceipt) {
|
|
2570
2585
|
try {
|
|
2586
|
+
// Named for the alias below, as run_script getReceipt names it (see _receiptAliases).
|
|
2571
2587
|
const fresh = _sanitizeTabResult(await extensionOrFallback(
|
|
2572
|
-
"get_tab_receipt", { ..._explicitReceipt({ receipt }) }, () => null
|
|
2588
|
+
"get_tab_receipt", { ..._explicitReceipt({ receipt: usedReceipt }) }, () => null
|
|
2573
2589
|
));
|
|
2574
2590
|
if (fresh?.receipt) {
|
|
2575
|
-
_aliasReceipt(usedReceipt, fresh.receipt);
|
|
2591
|
+
_aliasReceipt(_receiptToken(usedReceipt), fresh.receipt); // the receipt extensionOrFallback sent
|
|
2576
2592
|
_setActiveReceipt(fresh.receipt);
|
|
2577
2593
|
result = { ...result, receipt: fresh.receipt };
|
|
2578
2594
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "safari-mcp",
|
|
3
|
-
"version": "2.22.
|
|
3
|
+
"version": "2.22.7",
|
|
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",
|