safari-pilot 0.1.4 → 0.1.26

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.
Files changed (231) hide show
  1. package/.claude-plugin/commands/start.md +41 -0
  2. package/.claude-plugin/commands/stop.md +57 -0
  3. package/.claude-plugin/plugin.json +4 -0
  4. package/README.md +105 -26
  5. package/bin/Safari Pilot.app/Contents/CodeResources +0 -0
  6. package/bin/Safari Pilot.app/Contents/Info.plist +7 -7
  7. package/bin/Safari Pilot.app/Contents/MacOS/Safari Pilot +0 -0
  8. package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Info.plist +6 -6
  9. package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/MacOS/Safari Pilot Extension +0 -0
  10. package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/background.js +1008 -262
  11. package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/build.config.js +14 -0
  12. package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/content-isolated.js +516 -0
  13. package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/content-main.js +269 -0
  14. package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/lib/handshake-machine.js +48 -0
  15. package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/lib/route-command.js +27 -0
  16. package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/lib/storage-keys.js +18 -0
  17. package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/manifest.json +14 -6
  18. package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/native/SafariWebExtensionHandler.swift +23 -0
  19. package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/_CodeSignature/CodeResources +63 -8
  20. package/bin/Safari Pilot.app/Contents/Resources/Assets.car +0 -0
  21. package/bin/Safari Pilot.app/Contents/Resources/Base.lproj/Main.storyboardc/Info.plist +0 -0
  22. package/bin/Safari Pilot.app/Contents/Resources/Base.lproj/Main.storyboardc/MainMenu.nib +0 -0
  23. package/bin/Safari Pilot.app/Contents/Resources/Base.lproj/Main.storyboardc/NSWindowController-B8D-0N-5wS.nib +0 -0
  24. package/bin/Safari Pilot.app/Contents/_CodeSignature/CodeResources +9 -9
  25. package/bin/Safari Pilot.zip +0 -0
  26. package/bin/SafariPilotd +0 -0
  27. package/daemon/com.safari-pilot.daemon.plist +20 -0
  28. package/dist/aria.d.ts +50 -0
  29. package/dist/aria.js +466 -0
  30. package/dist/aria.js.map +1 -0
  31. package/dist/auto-wait.d.ts +38 -0
  32. package/dist/auto-wait.js +386 -0
  33. package/dist/auto-wait.js.map +1 -0
  34. package/dist/benchmark/eval.d.ts +30 -0
  35. package/dist/benchmark/eval.js +234 -0
  36. package/dist/benchmark/eval.js.map +1 -0
  37. package/dist/benchmark/fixture-server.d.ts +11 -0
  38. package/dist/benchmark/fixture-server.js +163 -0
  39. package/dist/benchmark/fixture-server.js.map +1 -0
  40. package/dist/benchmark/reporter.d.ts +59 -0
  41. package/dist/benchmark/reporter.js +333 -0
  42. package/dist/benchmark/reporter.js.map +1 -0
  43. package/dist/benchmark/runner.d.ts +22 -0
  44. package/dist/benchmark/runner.js +486 -0
  45. package/dist/benchmark/runner.js.map +1 -0
  46. package/dist/benchmark/stream-parser.d.ts +36 -0
  47. package/dist/benchmark/stream-parser.js +199 -0
  48. package/dist/benchmark/stream-parser.js.map +1 -0
  49. package/dist/benchmark/task-loader.d.ts +26 -0
  50. package/dist/benchmark/task-loader.js +147 -0
  51. package/dist/benchmark/task-loader.js.map +1 -0
  52. package/dist/benchmark/types.d.ts +129 -0
  53. package/dist/benchmark/types.js +67 -0
  54. package/dist/benchmark/types.js.map +1 -0
  55. package/dist/benchmark/worker.d.ts +32 -0
  56. package/dist/benchmark/worker.js +209 -0
  57. package/dist/benchmark/worker.js.map +1 -0
  58. package/dist/config.d.ts +52 -0
  59. package/dist/config.js +181 -0
  60. package/dist/config.js.map +1 -0
  61. package/dist/engine-selector.d.ts +16 -0
  62. package/dist/engine-selector.js +40 -6
  63. package/dist/engine-selector.js.map +1 -1
  64. package/dist/engines/applescript.d.ts +21 -5
  65. package/dist/engines/applescript.js +85 -86
  66. package/dist/engines/applescript.js.map +1 -1
  67. package/dist/engines/daemon.d.ts +49 -1
  68. package/dist/engines/daemon.js +264 -17
  69. package/dist/engines/daemon.js.map +1 -1
  70. package/dist/engines/engine-proxy.d.ts +53 -0
  71. package/dist/engines/engine-proxy.js +84 -0
  72. package/dist/engines/engine-proxy.js.map +1 -0
  73. package/dist/engines/engine.d.ts +14 -0
  74. package/dist/engines/engine.js +21 -0
  75. package/dist/engines/engine.js.map +1 -1
  76. package/dist/engines/extension.d.ts +25 -2
  77. package/dist/engines/extension.js +130 -5
  78. package/dist/engines/extension.js.map +1 -1
  79. package/dist/engines/js-helpers.d.ts +20 -0
  80. package/dist/engines/js-helpers.js +112 -0
  81. package/dist/engines/js-helpers.js.map +1 -0
  82. package/dist/errors.d.ts +190 -5
  83. package/dist/errors.js +320 -4
  84. package/dist/errors.js.map +1 -1
  85. package/dist/escape.d.ts +16 -0
  86. package/dist/escape.js +32 -0
  87. package/dist/escape.js.map +1 -0
  88. package/dist/index.js +78 -2
  89. package/dist/index.js.map +1 -1
  90. package/dist/locator.d.ts +62 -0
  91. package/dist/locator.js +472 -0
  92. package/dist/locator.js.map +1 -0
  93. package/dist/path-resolve.d.ts +6 -0
  94. package/dist/path-resolve.js +97 -0
  95. package/dist/path-resolve.js.map +1 -0
  96. package/dist/security/audit-log.d.ts +3 -3
  97. package/dist/security/audit-log.js +2 -0
  98. package/dist/security/audit-log.js.map +1 -1
  99. package/dist/security/circuit-breaker.d.ts +33 -0
  100. package/dist/security/circuit-breaker.js +74 -15
  101. package/dist/security/circuit-breaker.js.map +1 -1
  102. package/dist/security/domain-policy.d.ts +10 -3
  103. package/dist/security/domain-policy.js +32 -9
  104. package/dist/security/domain-policy.js.map +1 -1
  105. package/dist/security/human-approval.d.ts +11 -0
  106. package/dist/security/human-approval.js +19 -3
  107. package/dist/security/human-approval.js.map +1 -1
  108. package/dist/security/idpi-annotator.d.ts +30 -0
  109. package/dist/security/{idpi-scanner.js → idpi-annotator.js} +39 -14
  110. package/dist/security/idpi-annotator.js.map +1 -0
  111. package/dist/security/rate-limiter.d.ts +7 -0
  112. package/dist/security/rate-limiter.js +17 -8
  113. package/dist/security/rate-limiter.js.map +1 -1
  114. package/dist/security/screenshot-policy.d.ts +7 -0
  115. package/dist/security/screenshot-policy.js +37 -0
  116. package/dist/security/screenshot-policy.js.map +1 -0
  117. package/dist/security/tab-ownership.d.ts +28 -10
  118. package/dist/security/tab-ownership.js +57 -27
  119. package/dist/security/tab-ownership.js.map +1 -1
  120. package/dist/server.d.ts +124 -4
  121. package/dist/server.js +1113 -69
  122. package/dist/server.js.map +1 -1
  123. package/dist/tools/_frame-routing-helper.d.ts +6 -0
  124. package/dist/tools/_frame-routing-helper.js +25 -0
  125. package/dist/tools/_frame-routing-helper.js.map +1 -0
  126. package/dist/tools/auth.d.ts +20 -0
  127. package/dist/tools/auth.js +135 -0
  128. package/dist/tools/auth.js.map +1 -0
  129. package/dist/tools/clipboard.d.ts +2 -2
  130. package/dist/tools/clipboard.js +4 -6
  131. package/dist/tools/clipboard.js.map +1 -1
  132. package/dist/tools/compound.d.ts +2 -0
  133. package/dist/tools/compound.js +27 -8
  134. package/dist/tools/compound.js.map +1 -1
  135. package/dist/tools/downloads.d.ts +33 -0
  136. package/dist/tools/downloads.js +491 -0
  137. package/dist/tools/downloads.js.map +1 -0
  138. package/dist/tools/extension-diagnostics.d.ts +31 -0
  139. package/dist/tools/extension-diagnostics.js +130 -0
  140. package/dist/tools/extension-diagnostics.js.map +1 -0
  141. package/dist/tools/extraction.d.ts +6 -2
  142. package/dist/tools/extraction.js +226 -135
  143. package/dist/tools/extraction.js.map +1 -1
  144. package/dist/tools/file-upload.d.ts +21 -0
  145. package/dist/tools/file-upload.js +212 -0
  146. package/dist/tools/file-upload.js.map +1 -0
  147. package/dist/tools/frames.d.ts +2 -3
  148. package/dist/tools/frames.js +49 -55
  149. package/dist/tools/frames.js.map +1 -1
  150. package/dist/tools/har.d.ts +105 -0
  151. package/dist/tools/har.js +215 -0
  152. package/dist/tools/har.js.map +1 -0
  153. package/dist/tools/interaction.d.ts +14 -2
  154. package/dist/tools/interaction.js +324 -121
  155. package/dist/tools/interaction.js.map +1 -1
  156. package/dist/tools/mime.d.ts +5 -0
  157. package/dist/tools/mime.js +103 -0
  158. package/dist/tools/mime.js.map +1 -0
  159. package/dist/tools/navigation.d.ts +5 -0
  160. package/dist/tools/navigation.js +78 -29
  161. package/dist/tools/navigation.js.map +1 -1
  162. package/dist/tools/network.d.ts +4 -2
  163. package/dist/tools/network.js +195 -16
  164. package/dist/tools/network.js.map +1 -1
  165. package/dist/tools/pdf.d.ts +70 -0
  166. package/dist/tools/pdf.js +560 -0
  167. package/dist/tools/pdf.js.map +1 -0
  168. package/dist/tools/performance.d.ts +2 -2
  169. package/dist/tools/performance.js +6 -9
  170. package/dist/tools/performance.js.map +1 -1
  171. package/dist/tools/permissions.d.ts +2 -2
  172. package/dist/tools/permissions.js +11 -10
  173. package/dist/tools/permissions.js.map +1 -1
  174. package/dist/tools/service-workers.d.ts +2 -2
  175. package/dist/tools/service-workers.js +4 -6
  176. package/dist/tools/service-workers.js.map +1 -1
  177. package/dist/tools/shadow.d.ts +2 -2
  178. package/dist/tools/shadow.js +14 -8
  179. package/dist/tools/shadow.js.map +1 -1
  180. package/dist/tools/storage.d.ts +2 -2
  181. package/dist/tools/storage.js +106 -37
  182. package/dist/tools/storage.js.map +1 -1
  183. package/dist/tools/structured-extraction.d.ts +2 -2
  184. package/dist/tools/structured-extraction.js +8 -7
  185. package/dist/tools/structured-extraction.js.map +1 -1
  186. package/dist/tools/wait.d.ts +3 -3
  187. package/dist/tools/wait.js +6 -8
  188. package/dist/tools/wait.js.map +1 -1
  189. package/dist/trace-collector.d.ts +231 -0
  190. package/dist/trace-collector.js +584 -0
  191. package/dist/trace-collector.js.map +1 -0
  192. package/dist/trace.d.ts +3 -0
  193. package/dist/trace.js +27 -0
  194. package/dist/trace.js.map +1 -0
  195. package/dist/types.d.ts +68 -1
  196. package/extension/background.js +1131 -260
  197. package/extension/build.config.js +14 -0
  198. package/extension/content-isolated.js +556 -0
  199. package/extension/content-main.js +269 -0
  200. package/extension/lib/handshake-machine.js +48 -0
  201. package/extension/lib/route-command.js +27 -0
  202. package/extension/lib/storage-keys.js +18 -0
  203. package/extension/manifest.json +14 -6
  204. package/extension/native/SafariWebExtensionHandler.swift +23 -0
  205. package/hooks/distribution-check.sh +75 -0
  206. package/hooks/e2e-coverage-check.sh +33 -0
  207. package/hooks/e2e-no-mocks.sh +37 -0
  208. package/hooks/pre-publish-verify.sh +59 -0
  209. package/hooks/safari-pilot-guard.sh +82 -0
  210. package/hooks/session-end.sh +12 -0
  211. package/launchagents/com.safari-pilot.health-check.plist +22 -0
  212. package/package.json +21 -7
  213. package/safari-pilot.config.json +44 -0
  214. package/scripts/build-extension.sh +89 -13
  215. package/scripts/health-check.sh +36 -0
  216. package/scripts/postinstall.sh +104 -19
  217. package/scripts/pre-tag-check.sh +149 -0
  218. package/scripts/preuninstall.sh +4 -0
  219. package/scripts/promote-stable.sh +44 -0
  220. package/scripts/test-e2e-harness.sh +64 -0
  221. package/scripts/trace-merge.sh +13 -0
  222. package/scripts/trace-rotate.sh +13 -0
  223. package/scripts/update-daemon.sh +23 -4
  224. package/scripts/verify-artifact-integrity.sh +85 -0
  225. package/scripts/verify-extension-smoke.sh +43 -0
  226. package/skills/safari-pilot/SKILL.md +32 -1
  227. package/dist/security/idpi-scanner.d.ts +0 -20
  228. package/dist/security/idpi-scanner.js.map +0 -1
  229. package/dist/security/screenshot-redaction.d.ts +0 -42
  230. package/dist/security/screenshot-redaction.js +0 -134
  231. package/dist/security/screenshot-redaction.js.map +0 -1
@@ -0,0 +1,41 @@
1
+ ---
2
+ name: start
3
+ description: Start the Safari Pilot daemon (idempotent — safe to call if already running)
4
+ allowed-tools: Bash
5
+ ---
6
+
7
+ Start the SafariPilotd daemon process. This is idempotent — if the daemon is already running, report its PID and exit.
8
+
9
+ ## Steps
10
+
11
+ 1. Check if the daemon is already running:
12
+ ```bash
13
+ pgrep -f SafariPilotd
14
+ ```
15
+
16
+ 2. **If already running**, report the PID and exit:
17
+ > Safari Pilot daemon already running (PID: {pid})
18
+
19
+ 3. **If not running**, start it:
20
+ ```bash
21
+ DAEMON_BIN="${CLAUDE_PLUGIN_ROOT}/bin/SafariPilotd"
22
+ DATA_DIR="${HOME}/.safari-pilot"
23
+ mkdir -p "$DATA_DIR"
24
+ "$DAEMON_BIN" --daemon >> "$DATA_DIR/daemon.log" 2>&1 &
25
+ echo $! > "$DATA_DIR/daemon.pid"
26
+ ```
27
+
28
+ 4. Verify it started (check PID is alive):
29
+ ```bash
30
+ kill -0 $(cat ~/.safari-pilot/daemon.pid) 2>/dev/null
31
+ ```
32
+
33
+ 5. Report the result:
34
+ > Safari Pilot daemon started (PID: {pid})
35
+ >
36
+ > The Safari extension is managed separately in Safari > Settings > Extensions.
37
+
38
+ ## If the daemon binary is missing
39
+
40
+ Report:
41
+ > SafariPilotd not found at {path}. Run `npm install` in the safari-pilot directory to build it, or install Xcode Command Line Tools if Swift is not available.
@@ -0,0 +1,57 @@
1
+ ---
2
+ name: stop
3
+ description: Stop the Safari Pilot daemon gracefully
4
+ allowed-tools: Bash
5
+ ---
6
+
7
+ Stop the SafariPilotd daemon process. Attempts graceful SIGTERM shutdown, falls back to SIGKILL if needed.
8
+
9
+ ## Steps
10
+
11
+ 1. Find the daemon PID from the PID file or process list:
12
+ ```bash
13
+ PID_FILE="${HOME}/.safari-pilot/daemon.pid"
14
+ if [ -f "$PID_FILE" ]; then
15
+ PID=$(cat "$PID_FILE")
16
+ else
17
+ PID=$(pgrep -f SafariPilotd)
18
+ fi
19
+ ```
20
+
21
+ 2. **If PID was found**, verify the process is actually alive:
22
+ ```bash
23
+ kill -0 "$PID" 2>/dev/null
24
+ ```
25
+ If the process is not alive, clean up the stale PID file and report:
26
+ ```bash
27
+ rm -f "${HOME}/.safari-pilot/daemon.pid"
28
+ ```
29
+ > Safari Pilot daemon is not running (cleaned up stale PID file).
30
+
31
+ 3. **If no PID was found or process is dead**, report and exit:
32
+ > Safari Pilot daemon is not running.
33
+
34
+ 4. **If running**, send SIGTERM for graceful shutdown:
35
+ ```bash
36
+ kill "$PID" 2>/dev/null
37
+ ```
38
+
39
+ 5. Wait up to 3 seconds for the process to exit:
40
+ ```bash
41
+ for i in 1 2 3; do
42
+ kill -0 "$PID" 2>/dev/null || break
43
+ sleep 1
44
+ done
45
+ ```
46
+
47
+ 6. Check if it stopped:
48
+ - **If stopped**: clean up the PID file and report:
49
+ ```bash
50
+ rm -f "${HOME}/.safari-pilot/daemon.pid"
51
+ ```
52
+ > Safari Pilot daemon stopped (was PID: {pid})
53
+ - **If still running after 3s**: report the fallback command:
54
+ > Daemon did not stop gracefully. To force kill: `kill -9 {pid}`
55
+
56
+ 7. Always note:
57
+ > The Safari extension remains active independently in Safari > Settings > Extensions.
@@ -6,6 +6,10 @@
6
6
  "license": "MIT",
7
7
  "homepage": "https://github.com/RTinkslinger/safari-pilot",
8
8
  "components": {
9
+ "commands": [
10
+ "commands/start.md",
11
+ "commands/stop.md"
12
+ ],
9
13
  "mcpServers": {
10
14
  "safari": {
11
15
  "command": "node",
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  Safari Pilot gives Claude Code direct control of Safari through AppleScript and a persistent Swift daemon — no Chrome, no Playwright, no third-party code touching your browser. Your real Safari, with all your logins, automated natively.
6
6
 
7
- > **74 tools** | **3 engine tiers** | **92x faster than raw AppleScript** | **9 security layers** | **macOS 12+**
7
+ > **82 tools** | **3 engine tiers** | **92x faster than raw AppleScript** | **9 security layers** | **macOS 14+ recommended** (12+ minimum)
8
8
 
9
9
  ---
10
10
 
@@ -69,7 +69,13 @@ The extension unlocks advanced features that are impossible without it.
69
69
  6. Set to **"Allow on all websites"** when prompted
70
70
  7. Click **"Manage Profiles"** and enable for your active profile
71
71
 
72
- The extension is signed with Developer ID and notarized by Apple — it persists permanently across Safari restarts. No "Allow Unsigned Extensions" needed.
72
+ The extension is signed with Developer ID and notarized by Apple — it persists permanently across Safari restarts.
73
+
74
+ > **Troubleshooting:** If Safari shows **"Safari detected an app or service that interfered with clicking"** when you try to enable the extension, this is a Safari security feature triggered by other apps on your Mac that have Accessibility, Screen Recording, or Input Monitoring permissions (e.g., terminal emulators, screen sharing tools, window managers). To work around it:
75
+ > 1. Go to **Safari > Develop > Allow Unsigned Extensions** (check it temporarily)
76
+ > 2. Enable Safari Pilot in **Safari > Settings > Extensions**
77
+ > 3. Quit and reopen Safari
78
+ > 4. Optionally uncheck "Allow Unsigned Extensions" — the notarized extension stays enabled
73
79
 
74
80
  **What the extension adds:**
75
81
 
@@ -85,7 +91,8 @@ Without the extension, Safari Pilot still works for ~80% of use cases (navigatio
85
91
 
86
92
  ### System Requirements
87
93
 
88
- - **macOS 12.0 (Monterey)** or later
94
+ - **macOS 14.0 (Sonoma)** or later — recommended; required for the extension engine (the daemon's HTTP poll server uses Hummingbird, which requires macOS 14+)
95
+ - **macOS 12.0 (Monterey)** — minimum; daemon + AppleScript engines work, extension features are unavailable
89
96
  - **Safari** (pre-installed on every Mac)
90
97
  - **Node.js 20+**
91
98
 
@@ -113,7 +120,7 @@ Monitor news.ycombinator.com for any post about our company
113
120
  Open my X.com bookmarks and extract the top 5 posts with author profiles
114
121
  ```
115
122
 
116
- ## Tool Catalog (74 Tools)
123
+ ## Tool Catalog (82 Tools)
117
124
 
118
125
  ### Navigation (7)
119
126
  `safari_navigate` | `safari_navigate_back` | `safari_navigate_forward` | `safari_reload` | `safari_new_tab` | `safari_close_tab` | `safari_list_tabs`
@@ -121,20 +128,26 @@ Open my X.com bookmarks and extract the top 5 posts with author profiles
121
128
  ### Interaction (11)
122
129
  `safari_click` | `safari_double_click` | `safari_fill` | `safari_select_option` | `safari_check` | `safari_hover` | `safari_type` | `safari_press_key` | `safari_scroll` | `safari_drag` | `safari_handle_dialog`
123
130
 
131
+ ### File Upload (1)
132
+ `safari_file_upload` — programmatic upload to standard `<input type=file>` elements, including hidden inputs behind `<label>` (use `force: true`). 25 MiB / file × 4 / call. Path B architecture: out-of-band byte transport via daemon staging → extension fetch. Does NOT support drag-and-drop dropzones, custom pickers, or native OS dialogs.
133
+
124
134
  ### Extraction (7)
125
135
  `safari_snapshot` | `safari_get_text` | `safari_get_html` | `safari_get_attribute` | `safari_evaluate` | `safari_take_screenshot` | `safari_get_console_messages`
126
136
 
127
- ### Network (8)
128
- `safari_list_network_requests` | `safari_get_network_request` | `safari_intercept_requests` | `safari_network_throttle` | `safari_network_offline` | `safari_mock_request` | `safari_websocket_listen` | `safari_websocket_filter`
137
+ ### Network (10)
138
+ `safari_list_network_requests` | `safari_get_network_request` | `safari_intercept_requests` | `safari_network_throttle` | `safari_network_offline` | `safari_mock_request` | `safari_websocket_listen` | `safari_websocket_filter` | `safari_dump_har` | `safari_route_from_har`
129
139
 
130
140
  ### Storage (11)
131
141
  `safari_get_cookies` | `safari_set_cookie` | `safari_delete_cookie` | `safari_storage_state_export` | `safari_storage_state_import` | `safari_local_storage_get` | `safari_local_storage_set` | `safari_session_storage_get` | `safari_session_storage_set` | `safari_idb_list` | `safari_idb_get`
132
142
 
143
+ ### Authentication (2)
144
+ `safari_authenticate` | `safari_clear_authentication` — HTTP Basic auth via DNR header injection (extension required).
145
+
133
146
  ### Shadow DOM (2)
134
147
  `safari_query_shadow` | `safari_click_shadow`
135
148
 
136
- ### Frames (3)
137
- `safari_list_frames` | `safari_switch_frame` | `safari_eval_in_frame`
149
+ ### Frames (2)
150
+ `safari_list_frames` | `safari_eval_in_frame`
138
151
 
139
152
  ### Permissions & Overrides (6)
140
153
  `safari_permission_get` | `safari_permission_set` | `safari_override_geolocation` | `safari_override_timezone` | `safari_override_locale` | `safari_override_useragent`
@@ -154,9 +167,18 @@ Open my X.com bookmarks and extract the top 5 posts with author profiles
154
167
  ### Compound Workflows (4)
155
168
  `safari_test_flow` | `safari_monitor_page` | `safari_paginate_scrape` | `safari_media_control`
156
169
 
170
+ ### Downloads (1)
171
+ `safari_wait_for_download` — wait for download triggered by a click, capture metadata + optional `saveAs`.
172
+
173
+ ### PDF (1)
174
+ `safari_export_pdf` — export the frontmost Safari tab as a PDF via WKWebView.
175
+
157
176
  ### Wait (1)
158
177
  `safari_wait_for` — 7 condition types: selector, selectorHidden, text, textGone, urlMatch, networkidle, function
159
178
 
179
+ ### Diagnostics (2)
180
+ `safari_extension_health` | `safari_extension_debug_dump` — observability for the extension engine. Read-only; safe to call any time.
181
+
160
182
  ### System (2)
161
183
  `safari_health_check` | `safari_emergency_stop`
162
184
 
@@ -224,11 +246,11 @@ Safari Pilot runs on your local machine with access to your real browser session
224
246
 
225
247
  **Domain Policy** — Per-domain rate limits prevent runaway automation. Banking and financial domains flagged as untrusted by default.
226
248
 
227
- **Rate Limiter + Circuit Breaker** — Global limit of 120 actions/minute. Circuit breaker trips after 5 consecutive errors, backs off for 120 seconds.
249
+ **Rate Limiter + Circuit Breaker** — Configurable via `safari-pilot.config.json`. Defaults: 120 actions/minute, circuit breaker trips at 5 errors with 120s cooldown.
228
250
 
229
251
  **IDPI Scanner** — Indirect Prompt Injection defense. Scans extracted text for 9 known injection patterns.
230
252
 
231
- **Kill Switch** — `safari_emergency_stop` immediately halts all automation. One command, full stop.
253
+ **Kill Switch** — `safari_emergency_stop` immediately halts all automation. Configurable auto-activation on error threshold.
232
254
 
233
255
  **Human Approval** — Sensitive actions (OAuth consent, financial forms, downloads) flagged for explicit approval.
234
256
 
@@ -238,6 +260,34 @@ Safari Pilot runs on your local machine with access to your real browser session
238
260
 
239
261
  **No Credential Access** — Safari Pilot **never** accesses the macOS Keychain. Authentication works through real browser interaction.
240
262
 
263
+ ## Configuration
264
+
265
+ All security settings are tunable via `safari-pilot.config.json` in the package root:
266
+
267
+ ```json
268
+ {
269
+ "schemaVersion": "1.0",
270
+ "rateLimit": { "maxActionsPerMinute": 120, "windowMs": 60000 },
271
+ "circuitBreaker": { "errorThreshold": 5, "windowMs": 60000, "cooldownMs": 120000 },
272
+ "domainPolicy": { "defaultMaxActionsPerMinute": 60, "blocked": [], "trusted": [] },
273
+ "killSwitch": { "autoActivation": false, "maxErrors": 5, "windowSeconds": 60 },
274
+ "audit": { "maxEntries": 10000, "logPath": "~/.safari-pilot/audit.log" },
275
+ "daemon": { "timeoutMs": 30000 },
276
+ "healthCheck": { "timeoutMs": 3000 }
277
+ }
278
+ ```
279
+
280
+ Missing file → all defaults. Partial file → deep-merge with defaults. Sensitive domain protections (banking, PayPal, etc.) cannot be overridden via config.
281
+
282
+ Set `SAFARI_PILOT_CONFIG` env var to use a custom config path.
283
+
284
+ ### Daemon Lifecycle
285
+
286
+ ```bash
287
+ /safari-pilot start # Start daemon, report PID (idempotent)
288
+ /safari-pilot stop # Graceful shutdown with SIGKILL fallback
289
+ ```
290
+
241
291
  ## Development
242
292
 
243
293
  ### Building from Source
@@ -246,33 +296,37 @@ Safari Pilot runs on your local machine with access to your real browser session
246
296
  # TypeScript server
247
297
  npm run build
248
298
 
249
- # Swift daemon
250
- cd daemon && swift build -c release
251
- cp .build/release/SafariPilotd ../bin/
299
+ # Swift daemon (rebuild + atomic swap + launchctl restart)
300
+ bash scripts/update-daemon.sh
252
301
 
253
- # Safari extension (requires Xcode)
302
+ # Safari extension (Xcode archive → sign → notarize)
254
303
  bash scripts/build-extension.sh
255
304
  ```
256
305
 
257
306
  ### Testing
258
307
 
259
308
  ```bash
260
- # All tests
261
- npm test
309
+ # Default — unit tests, no Safari required
310
+ npm test # 398 unit tests
311
+ npm run test:unit # alias for above
262
312
 
263
- # By category
264
- npm run test:unit # 705 tests, no Safari needed
265
- npm run test:integration # 372 tests, some need Safari
266
- npm run test:security # 27 security-focused tests
267
- npm run test:e2e # 31 tests against real Safari
313
+ # Real Safari required (production stack must be running)
314
+ npm run test:e2e # ~30 e2e tests across 12+ files
315
+ npm run test:e2e:harness # 5 tests requiring DEBUG_HARNESS build (auto-rebuilds release after)
268
316
 
269
- # Swift daemon tests
270
- cd daemon && swift run SafariPilotdTests
317
+ # Both
318
+ npm run test:all # unit + e2e
271
319
 
272
- # Canary deployment
273
- bash test/canary/install-test.sh
320
+ # Swift daemon (real Swift types, mocked at NSAppleScript boundary only)
321
+ cd daemon && swift test # 153 tests
274
322
  ```
275
323
 
324
+ **Test policy:**
325
+ - Unit tests (`test/unit/`) cover pure logic; can mock Node boundaries (`fs`, `net`, `child_process`) but never internal modules.
326
+ - E2E tests (`test/e2e/`) spawn a real MCP server, drive Safari through the real stack, and use ZERO mocks (enforced by pre-commit hook). They fail closed on any `vi.mock` or direct `import from '../../src/'`.
327
+ - The harness-dependent tests (`t21`, `t22`, `t27`, `t44`, `t55a`) require `SAFARI_PILOT_TEST_MODE=1` build markers stripped from production. `npm run test:e2e:harness` automates the test build → run → release-rebuild flow. Local-only (refuses on CI).
328
+ - See `CLAUDE.md` "End-to-End Testing (HARD RULES)" for the full contract.
329
+
276
330
  ### Adding a New Tool
277
331
 
278
332
  1. Add the handler to the appropriate module in `src/tools/`
@@ -280,6 +334,31 @@ bash test/canary/install-test.sh
280
334
  3. Write tests in `test/unit/tools/`
281
335
  4. The server auto-registers tools from all modules in `initialize()`
282
336
  5. Add the tool name to `skills/safari-pilot/SKILL.md` allowed-tools
337
+ 6. If touching `extension/*` or `daemon/Sources/*`, follow `CLAUDE.md` "Extension Build: Hard Rules" — version bump in lockstep, ditto with metadata-stripping flags, run `bash scripts/pre-tag-check.sh` before any tag push.
338
+
339
+ ### Releasing a new version
340
+
341
+ The release pipeline is automated via `.github/workflows/release.yml` on tag push. Before tagging, run the local SOP gate:
342
+
343
+ ```bash
344
+ # 1. Bump versions in lockstep
345
+ # Edit package.json + extension/manifest.json (must match)
346
+
347
+ # 2. Rebuild extension if extension/* changed
348
+ bash scripts/build-extension.sh
349
+
350
+ # 3. Local install rehearsal
351
+ open "bin/Safari Pilot.app" # verify in Safari Settings
352
+
353
+ # 4. Mandatory pre-tag check (mirrors every CI verify step)
354
+ bash scripts/pre-tag-check.sh # must print "ALL CHECKS PASSED"
355
+
356
+ # 5. Commit, tag, push
357
+ git tag -a v0.1.X -m "..."
358
+ git push origin main && git push origin v0.1.X
359
+ ```
360
+
361
+ The pre-tag check catches: AppleDouble (`._*`) metadata in zip, codesign --deep --strict failures, missing entitlements, version mismatch, dangling tag, prepublish hook misconfiguration, unit test regressions. It runs in seconds and saves CI round-trips.
283
362
 
284
363
  ## What Safari Pilot Does NOT Replace
285
364
 
@@ -308,7 +387,7 @@ The daemon detects Safari crashes (error codes -600/-609) and retries with expon
308
387
  Safari Pilot is built from scratch — no code from third-party Safari MCP packages. Every line that touches your browser is first-party and auditable. We also add 9 security layers, a persistent Swift daemon (92x faster), and structured extraction tools.
309
388
 
310
389
  **Q: Does the Swift daemon run all the time?**
311
- Only when Claude Code is active. The LaunchAgent starts the daemon on demand and it shuts down with the session.
390
+ The daemon starts on Claude Code session start (via the SessionStart hook) and stays running between sessions for fast restart. Use `/safari-pilot stop` to shut it down manually. The LaunchAgent auto-restarts it if it crashes.
312
391
 
313
392
  **Q: Do I need the Safari extension?**
314
393
  No — Safari Pilot works without it for ~80% of use cases. The extension adds Shadow DOM traversal, CSP bypass, dialog interception, and network mocking. Install it from the [GitHub Release](https://github.com/RTinkslinger/safari-pilot/releases/latest) if you need those features.
@@ -23,29 +23,29 @@
23
23
  <key>CFBundlePackageType</key>
24
24
  <string>APPL</string>
25
25
  <key>CFBundleShortVersionString</key>
26
- <string>0.1.3</string>
26
+ <string>0.1.26</string>
27
27
  <key>CFBundleSupportedPlatforms</key>
28
28
  <array>
29
29
  <string>MacOSX</string>
30
30
  </array>
31
31
  <key>CFBundleVersion</key>
32
- <string>202604130112</string>
32
+ <string>202605041322</string>
33
33
  <key>DTCompiler</key>
34
34
  <string>com.apple.compilers.llvm.clang.1_0</string>
35
35
  <key>DTPlatformBuild</key>
36
- <string>25E236</string>
36
+ <string>25E251</string>
37
37
  <key>DTPlatformName</key>
38
38
  <string>macosx</string>
39
39
  <key>DTPlatformVersion</key>
40
40
  <string>26.4</string>
41
41
  <key>DTSDKBuild</key>
42
- <string>25E236</string>
42
+ <string>25E251</string>
43
43
  <key>DTSDKName</key>
44
44
  <string>macosx26.4</string>
45
45
  <key>DTXcode</key>
46
- <string>2640</string>
46
+ <string>2641</string>
47
47
  <key>DTXcodeBuild</key>
48
- <string>17E192</string>
48
+ <string>17E202</string>
49
49
  <key>LSMinimumSystemVersion</key>
50
50
  <string>26.4</string>
51
51
  <key>NSMainStoryboardFile</key>
@@ -53,6 +53,6 @@
53
53
  <key>NSPrincipalClass</key>
54
54
  <string>NSApplication</string>
55
55
  <key>SFSafariWebExtensionConverterVersion</key>
56
- <string>26.4</string>
56
+ <string>26.4.1</string>
57
57
  </dict>
58
58
  </plist>
@@ -19,29 +19,29 @@
19
19
  <key>CFBundlePackageType</key>
20
20
  <string>XPC!</string>
21
21
  <key>CFBundleShortVersionString</key>
22
- <string>0.1.3</string>
22
+ <string>0.1.26</string>
23
23
  <key>CFBundleSupportedPlatforms</key>
24
24
  <array>
25
25
  <string>MacOSX</string>
26
26
  </array>
27
27
  <key>CFBundleVersion</key>
28
- <string>202604130112</string>
28
+ <string>202605041322</string>
29
29
  <key>DTCompiler</key>
30
30
  <string>com.apple.compilers.llvm.clang.1_0</string>
31
31
  <key>DTPlatformBuild</key>
32
- <string>25E236</string>
32
+ <string>25E251</string>
33
33
  <key>DTPlatformName</key>
34
34
  <string>macosx</string>
35
35
  <key>DTPlatformVersion</key>
36
36
  <string>26.4</string>
37
37
  <key>DTSDKBuild</key>
38
- <string>25E236</string>
38
+ <string>25E251</string>
39
39
  <key>DTSDKName</key>
40
40
  <string>macosx26.4</string>
41
41
  <key>DTXcode</key>
42
- <string>2640</string>
42
+ <string>2641</string>
43
43
  <key>DTXcodeBuild</key>
44
- <string>17E192</string>
44
+ <string>17E202</string>
45
45
  <key>LSMinimumSystemVersion</key>
46
46
  <string>10.14</string>
47
47
  <key>NSExtension</key>