safari-pilot 0.1.0
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/.claude-plugin/plugin.json +35 -0
- package/.mcp.json +11 -0
- package/LICENSE +21 -0
- package/README.md +324 -0
- package/bin/.gitkeep +0 -0
- package/bin/Safari Pilot.app/Contents/CodeResources +0 -0
- package/bin/Safari Pilot.app/Contents/Info.plist +58 -0
- package/bin/Safari Pilot.app/Contents/MacOS/Safari Pilot +0 -0
- package/bin/Safari Pilot.app/Contents/PkgInfo +1 -0
- package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Info.plist +55 -0
- package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/MacOS/Safari Pilot Extension +0 -0
- package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/background.js +294 -0
- package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/content-isolated.js +80 -0
- package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/content-main.js +310 -0
- package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/icons/icon-128.png +0 -0
- package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/icons/icon-48.png +0 -0
- package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/icons/icon-96.png +0 -0
- package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/manifest.json +39 -0
- package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/_CodeSignature/CodeResources +194 -0
- package/bin/Safari Pilot.app/Contents/Resources/AppIcon.icns +0 -0
- package/bin/Safari Pilot.app/Contents/Resources/Assets.car +0 -0
- package/bin/Safari Pilot.app/Contents/Resources/Base.lproj/Main.html +19 -0
- package/bin/Safari Pilot.app/Contents/Resources/Base.lproj/Main.storyboardc/Info.plist +0 -0
- package/bin/Safari Pilot.app/Contents/Resources/Base.lproj/Main.storyboardc/MainMenu.nib +0 -0
- package/bin/Safari Pilot.app/Contents/Resources/Base.lproj/Main.storyboardc/NSWindowController-B8D-0N-5wS.nib +0 -0
- package/bin/Safari Pilot.app/Contents/Resources/Base.lproj/Main.storyboardc/XfG-lQ-9wD-view-m2S-Jp-Qdl.nib +0 -0
- package/bin/Safari Pilot.app/Contents/Resources/Icon.png +0 -0
- package/bin/Safari Pilot.app/Contents/Resources/Script.js +22 -0
- package/bin/Safari Pilot.app/Contents/Resources/Style.css +45 -0
- package/bin/Safari Pilot.app/Contents/_CodeSignature/CodeResources +236 -0
- package/bin/Safari Pilot.zip +0 -0
- package/bin/SafariPilotd +0 -0
- package/dist/engine-selector.d.ts +10 -0
- package/dist/engine-selector.js +55 -0
- package/dist/engine-selector.js.map +1 -0
- package/dist/engines/applescript.d.ts +53 -0
- package/dist/engines/applescript.js +290 -0
- package/dist/engines/applescript.js.map +1 -0
- package/dist/engines/daemon.d.ts +19 -0
- package/dist/engines/daemon.js +187 -0
- package/dist/engines/daemon.js.map +1 -0
- package/dist/engines/engine.d.ts +15 -0
- package/dist/engines/engine.js +42 -0
- package/dist/engines/engine.js.map +1 -0
- package/dist/engines/extension.d.ts +34 -0
- package/dist/engines/extension.js +66 -0
- package/dist/engines/extension.js.map +1 -0
- package/dist/errors.d.ts +128 -0
- package/dist/errors.js +250 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/security/audit-log.d.ts +23 -0
- package/dist/security/audit-log.js +68 -0
- package/dist/security/audit-log.js.map +1 -0
- package/dist/security/circuit-breaker.d.ts +29 -0
- package/dist/security/circuit-breaker.js +114 -0
- package/dist/security/circuit-breaker.js.map +1 -0
- package/dist/security/domain-policy.d.ts +29 -0
- package/dist/security/domain-policy.js +96 -0
- package/dist/security/domain-policy.js.map +1 -0
- package/dist/security/human-approval.d.ts +20 -0
- package/dist/security/human-approval.js +150 -0
- package/dist/security/human-approval.js.map +1 -0
- package/dist/security/idpi-scanner.d.ts +20 -0
- package/dist/security/idpi-scanner.js +102 -0
- package/dist/security/idpi-scanner.js.map +1 -0
- package/dist/security/kill-switch.d.ts +51 -0
- package/dist/security/kill-switch.js +103 -0
- package/dist/security/kill-switch.js.map +1 -0
- package/dist/security/rate-limiter.d.ts +30 -0
- package/dist/security/rate-limiter.js +70 -0
- package/dist/security/rate-limiter.js.map +1 -0
- package/dist/security/screenshot-redaction.d.ts +42 -0
- package/dist/security/screenshot-redaction.js +134 -0
- package/dist/security/screenshot-redaction.js.map +1 -0
- package/dist/security/tab-ownership.d.ts +46 -0
- package/dist/security/tab-ownership.js +85 -0
- package/dist/security/tab-ownership.js.map +1 -0
- package/dist/server.d.ts +53 -0
- package/dist/server.js +347 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/clipboard.d.ts +15 -0
- package/dist/tools/clipboard.js +128 -0
- package/dist/tools/clipboard.js.map +1 -0
- package/dist/tools/compound.d.ts +68 -0
- package/dist/tools/compound.js +491 -0
- package/dist/tools/compound.js.map +1 -0
- package/dist/tools/extraction.d.ts +26 -0
- package/dist/tools/extraction.js +414 -0
- package/dist/tools/extraction.js.map +1 -0
- package/dist/tools/frames.d.ts +22 -0
- package/dist/tools/frames.js +165 -0
- package/dist/tools/frames.js.map +1 -0
- package/dist/tools/interaction.d.ts +30 -0
- package/dist/tools/interaction.js +651 -0
- package/dist/tools/interaction.js.map +1 -0
- package/dist/tools/navigation.d.ts +41 -0
- package/dist/tools/navigation.js +316 -0
- package/dist/tools/navigation.js.map +1 -0
- package/dist/tools/network.d.ts +27 -0
- package/dist/tools/network.js +721 -0
- package/dist/tools/network.js.map +1 -0
- package/dist/tools/performance.d.ts +16 -0
- package/dist/tools/performance.js +240 -0
- package/dist/tools/performance.js.map +1 -0
- package/dist/tools/permissions.d.ts +25 -0
- package/dist/tools/permissions.js +308 -0
- package/dist/tools/permissions.js.map +1 -0
- package/dist/tools/service-workers.d.ts +15 -0
- package/dist/tools/service-workers.js +136 -0
- package/dist/tools/service-workers.js.map +1 -0
- package/dist/tools/shadow.d.ts +21 -0
- package/dist/tools/shadow.js +126 -0
- package/dist/tools/shadow.js.map +1 -0
- package/dist/tools/storage.d.ts +30 -0
- package/dist/tools/storage.js +679 -0
- package/dist/tools/storage.js.map +1 -0
- package/dist/tools/structured-extraction.d.ts +22 -0
- package/dist/tools/structured-extraction.js +433 -0
- package/dist/tools/structured-extraction.js.map +1 -0
- package/dist/tools/wait.d.ts +18 -0
- package/dist/tools/wait.js +182 -0
- package/dist/tools/wait.js.map +1 -0
- package/dist/types.d.ts +85 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/extension/background.js +294 -0
- package/extension/content-isolated.js +80 -0
- package/extension/content-main.js +310 -0
- package/extension/icons/icon-128.png +0 -0
- package/extension/icons/icon-48.png +0 -0
- package/extension/icons/icon-96.png +0 -0
- package/extension/manifest.json +39 -0
- package/hooks/session-end.sh +67 -0
- package/hooks/session-start.sh +66 -0
- package/package.json +46 -0
- package/scripts/build-extension.sh +135 -0
- package/scripts/postinstall.sh +91 -0
- package/scripts/preuninstall.sh +25 -0
- package/scripts/update-daemon.sh +62 -0
- package/skills/safari-pilot/SKILL.md +157 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "safari-pilot",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Native Safari browser automation for AI agents on macOS",
|
|
5
|
+
"author": "Aakash Kumar",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"homepage": "https://github.com/aakashkumar/safari-pilot",
|
|
8
|
+
"components": {
|
|
9
|
+
"mcpServers": {
|
|
10
|
+
"safari": {
|
|
11
|
+
"command": "node",
|
|
12
|
+
"args": ["${CLAUDE_PLUGIN_ROOT}/dist/index.js"],
|
|
13
|
+
"env": {
|
|
14
|
+
"SAFARI_PILOT_DATA": "${CLAUDE_PLUGIN_DATA}",
|
|
15
|
+
"SAFARI_PILOT_DAEMON": "${CLAUDE_PLUGIN_ROOT}/bin/SafariPilotd"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"skills": ["skills/safari-pilot/SKILL.md"],
|
|
20
|
+
"hooks": [
|
|
21
|
+
{
|
|
22
|
+
"event": "SessionStart",
|
|
23
|
+
"script": "${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"event": "Stop",
|
|
27
|
+
"script": "${CLAUDE_PLUGIN_ROOT}/hooks/session-end.sh"
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
"requirements": {
|
|
32
|
+
"os": ["darwin"],
|
|
33
|
+
"minOsVersion": "12.0"
|
|
34
|
+
}
|
|
35
|
+
}
|
package/.mcp.json
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Aakash Kumar
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
# Safari Pilot
|
|
2
|
+
|
|
3
|
+
**Native Safari browser automation for AI agents on macOS.**
|
|
4
|
+
|
|
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
|
+
|
|
7
|
+
> **74 tools** | **3 engine tiers** | **92x faster than raw AppleScript** | **9 security layers** | **macOS 12+**
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Why Safari Pilot?
|
|
12
|
+
|
|
13
|
+
| Problem with Chrome-based tools | Safari Pilot solution |
|
|
14
|
+
|---|---|
|
|
15
|
+
| Chrome heats your Mac to 97C during automation | Native WebKit — ~60% less CPU |
|
|
16
|
+
| Playwright/Puppeteer launch clean browsers without your logins | Uses your **real Safari** — Gmail, GitHub, Slack already signed in |
|
|
17
|
+
| Chrome steals window focus mid-work | Safari stays in background, never interrupts |
|
|
18
|
+
| Third-party MCP packages have MAIN world access to your banking tabs | Every line of code is first-party, auditable, open source |
|
|
19
|
+
| 80ms per command via osascript | Persistent Swift daemon: **p50 = 1ms** |
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
### As a Claude Code Plugin (Recommended)
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
claude plugin add safari-pilot
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Manual Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
git clone https://github.com/aakashkumar/safari-pilot.git
|
|
33
|
+
cd safari-pilot
|
|
34
|
+
npm install
|
|
35
|
+
npm run build
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Prerequisites
|
|
39
|
+
|
|
40
|
+
- **macOS 12.0 (Monterey)** or later
|
|
41
|
+
- **Safari** (pre-installed on every Mac)
|
|
42
|
+
- **Node.js 20+**
|
|
43
|
+
|
|
44
|
+
### Required Safari Setting
|
|
45
|
+
|
|
46
|
+
Enable "Allow JavaScript from Apple Events":
|
|
47
|
+
|
|
48
|
+
1. Open **Safari > Settings > Advanced**
|
|
49
|
+
2. Check **"Show features for web developers"**
|
|
50
|
+
3. Go to **Safari > Develop** menu
|
|
51
|
+
4. Check **"Allow JavaScript from Apple Events"**
|
|
52
|
+
|
|
53
|
+
This is a one-time setting that persists across Safari restarts.
|
|
54
|
+
|
|
55
|
+
### Optional: Safari Web Extension
|
|
56
|
+
|
|
57
|
+
For advanced features (closed Shadow DOM traversal, CSP bypass, dialog interception, network mocking), install the bundled Safari extension:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# Build the extension (requires Xcode)
|
|
61
|
+
bash scripts/build-extension.sh
|
|
62
|
+
|
|
63
|
+
# Open the app to trigger Safari's extension enable prompt
|
|
64
|
+
open "bin/Safari Pilot.app"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Then enable it in **Safari > Settings > Extensions > Safari Pilot**.
|
|
68
|
+
|
|
69
|
+
## Quick Start
|
|
70
|
+
|
|
71
|
+
Once installed, Safari Pilot activates when Claude Code detects browser tasks:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
Browse to github.com/trending and extract the top 10 repositories
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
Go to my company dashboard at app.example.com and download the monthly report
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
Test the checkout flow on staging.mystore.com — add to cart, fill payment, verify confirmation
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
Monitor news.ycombinator.com for any post about our company
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Tool Catalog (74 Tools)
|
|
90
|
+
|
|
91
|
+
### Navigation (7)
|
|
92
|
+
`safari_navigate` | `safari_navigate_back` | `safari_navigate_forward` | `safari_reload` | `safari_new_tab` | `safari_close_tab` | `safari_list_tabs`
|
|
93
|
+
|
|
94
|
+
### Interaction (11)
|
|
95
|
+
`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`
|
|
96
|
+
|
|
97
|
+
### Extraction (7)
|
|
98
|
+
`safari_snapshot` | `safari_get_text` | `safari_get_html` | `safari_get_attribute` | `safari_evaluate` | `safari_take_screenshot` | `safari_get_console_messages`
|
|
99
|
+
|
|
100
|
+
### Network (8)
|
|
101
|
+
`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`
|
|
102
|
+
|
|
103
|
+
### Storage (11)
|
|
104
|
+
`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`
|
|
105
|
+
|
|
106
|
+
### Shadow DOM (2)
|
|
107
|
+
`safari_query_shadow` | `safari_click_shadow`
|
|
108
|
+
|
|
109
|
+
### Frames (3)
|
|
110
|
+
`safari_list_frames` | `safari_switch_frame` | `safari_eval_in_frame`
|
|
111
|
+
|
|
112
|
+
### Permissions & Overrides (6)
|
|
113
|
+
`safari_permission_get` | `safari_permission_set` | `safari_override_geolocation` | `safari_override_timezone` | `safari_override_locale` | `safari_override_useragent`
|
|
114
|
+
|
|
115
|
+
### Clipboard (2)
|
|
116
|
+
`safari_clipboard_read` | `safari_clipboard_write`
|
|
117
|
+
|
|
118
|
+
### Service Workers (2)
|
|
119
|
+
`safari_sw_list` | `safari_sw_unregister`
|
|
120
|
+
|
|
121
|
+
### Performance (3)
|
|
122
|
+
`safari_begin_trace` | `safari_end_trace` | `safari_get_page_metrics`
|
|
123
|
+
|
|
124
|
+
### Structured Extraction (5)
|
|
125
|
+
`safari_smart_scrape` | `safari_extract_tables` | `safari_extract_links` | `safari_extract_images` | `safari_extract_metadata`
|
|
126
|
+
|
|
127
|
+
### Compound Workflows (4)
|
|
128
|
+
`safari_test_flow` | `safari_monitor_page` | `safari_paginate_scrape` | `safari_media_control`
|
|
129
|
+
|
|
130
|
+
### Wait (1)
|
|
131
|
+
`safari_wait_for` — 7 condition types: selector, selectorHidden, text, textGone, urlMatch, networkidle, function
|
|
132
|
+
|
|
133
|
+
### System (2)
|
|
134
|
+
`safari_health_check` | `safari_emergency_stop`
|
|
135
|
+
|
|
136
|
+
## Architecture
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
Claude Code
|
|
140
|
+
|
|
|
141
|
+
| MCP Protocol (stdio)
|
|
142
|
+
v
|
|
143
|
+
+--------------------------------------------------+
|
|
144
|
+
| Safari Pilot MCP Server (TypeScript) |
|
|
145
|
+
| |
|
|
146
|
+
| Security Pipeline: |
|
|
147
|
+
| Kill Switch -> Tab Ownership -> Domain Policy |
|
|
148
|
+
| -> Rate Limiter -> Circuit Breaker -> Audit Log |
|
|
149
|
+
| |
|
|
150
|
+
| Engine Selector: |
|
|
151
|
+
| +-----------+ +-----------+ +----------------+ |
|
|
152
|
+
| | Extension | | Daemon | | AppleScript | |
|
|
153
|
+
| | (deep DOM)| | (1ms p50) | | (fallback) | |
|
|
154
|
+
| +-----------+ +-----------+ +----------------+ |
|
|
155
|
+
+--------------------------------------------------+
|
|
156
|
+
| | |
|
|
157
|
+
v v v
|
|
158
|
+
+--------------------------------------------------+
|
|
159
|
+
| Safari (macOS native) |
|
|
160
|
+
| Your real browser with all your sessions |
|
|
161
|
+
+--------------------------------------------------+
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Three Engine Tiers
|
|
165
|
+
|
|
166
|
+
| Engine | Latency | Capabilities | When Used |
|
|
167
|
+
|---|---|---|---|
|
|
168
|
+
| **Safari Web Extension** | ~10ms | Shadow DOM, CSP bypass, dialog interception, network mocking | Extension installed + feature requires it |
|
|
169
|
+
| **Swift Daemon** | **1ms p50** | All AppleScript capabilities, persistent process | Default when daemon is running |
|
|
170
|
+
| **AppleScript (osascript)** | ~90ms | Basic navigation, forms, extraction, screenshots | Fallback when daemon unavailable |
|
|
171
|
+
|
|
172
|
+
The engine selector automatically picks the best available engine for each command. If the daemon isn't running, it falls back to raw AppleScript. If a command needs Shadow DOM access and the extension isn't installed, it returns a clear error with instructions.
|
|
173
|
+
|
|
174
|
+
## Performance
|
|
175
|
+
|
|
176
|
+
Benchmarked on Apple Silicon (M-series), 20 consecutive commands:
|
|
177
|
+
|
|
178
|
+
| Metric | Daemon | AppleScript | Speedup |
|
|
179
|
+
|---|---|---|---|
|
|
180
|
+
| p50 | 0-1ms | 81-92ms | **92x** |
|
|
181
|
+
| p95 | 62ms | 116ms | ~2x |
|
|
182
|
+
| avg | 5ms | 85ms | **17x** |
|
|
183
|
+
|
|
184
|
+
Over a 500-command session, the daemon saves ~40 seconds of pure overhead vs raw AppleScript.
|
|
185
|
+
|
|
186
|
+
## Security Model
|
|
187
|
+
|
|
188
|
+
Safari Pilot runs on your local machine with access to your real browser sessions. The security model is defense-in-depth:
|
|
189
|
+
|
|
190
|
+
### Tab Ownership
|
|
191
|
+
The agent can **only** interact with tabs it created via `safari_new_tab`. Your existing tabs (banking, email, personal) are untouchable. Ownership is enforced at the server level — there is no bypass.
|
|
192
|
+
|
|
193
|
+
### Domain Policy
|
|
194
|
+
Per-domain rate limits prevent runaway automation. Banking and financial domains are flagged as untrusted by default.
|
|
195
|
+
|
|
196
|
+
### Rate Limiter + Circuit Breaker
|
|
197
|
+
Global limit of 120 actions/minute. Per-domain limits configurable. Circuit breaker trips after 5 consecutive errors, backs off for 120 seconds.
|
|
198
|
+
|
|
199
|
+
### IDPI Scanner
|
|
200
|
+
Indirect Prompt Injection defense. Scans extracted text for 9 known injection patterns (role reassignment, fake system prompts, base64 payloads, hidden text, etc.).
|
|
201
|
+
|
|
202
|
+
### Kill Switch
|
|
203
|
+
`safari_emergency_stop` immediately halts all automation and blocks further calls. One command, full stop.
|
|
204
|
+
|
|
205
|
+
### Human Approval
|
|
206
|
+
Sensitive actions (OAuth consent, financial forms, downloads) are flagged and require explicit approval.
|
|
207
|
+
|
|
208
|
+
### Audit Logging
|
|
209
|
+
Every tool call is logged with timestamp, tool name, URL, parameters (passwords redacted), result, and latency. Session-end hook produces a summary.
|
|
210
|
+
|
|
211
|
+
### Screenshot Redaction
|
|
212
|
+
Cross-origin iframes are blurred in screenshots. Password fields are redacted.
|
|
213
|
+
|
|
214
|
+
### No Credential Access
|
|
215
|
+
Safari Pilot **never** accesses the macOS Keychain. Authentication works through real browser interaction, same as you clicking.
|
|
216
|
+
|
|
217
|
+
## Development
|
|
218
|
+
|
|
219
|
+
### Project Structure
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
safari-pilot/
|
|
223
|
+
├── src/ # TypeScript MCP server
|
|
224
|
+
│ ├── server.ts # Main server + tool registration
|
|
225
|
+
│ ├── engines/ # AppleScript, Daemon, Extension engines
|
|
226
|
+
│ ├── security/ # 9 security modules
|
|
227
|
+
│ └── tools/ # 14 tool category modules
|
|
228
|
+
├── daemon/ # Swift persistent daemon
|
|
229
|
+
│ ├── Sources/ # Swift source code
|
|
230
|
+
│ └── Tests/ # Swift tests (custom runner)
|
|
231
|
+
├── extension/ # Safari Web Extension
|
|
232
|
+
│ ├── manifest.json # Manifest V3
|
|
233
|
+
│ ├── content-main.js # MAIN world (Shadow DOM, React filling)
|
|
234
|
+
│ ├── content-isolated.js # ISOLATED world relay
|
|
235
|
+
│ └── background.js # Service worker + native messaging
|
|
236
|
+
├── skills/ # Claude Code skill definition
|
|
237
|
+
├── hooks/ # Session start/end hooks
|
|
238
|
+
├── scripts/ # Install, update, build scripts
|
|
239
|
+
└── test/ # 1,167 tests
|
|
240
|
+
├── unit/ # 705 unit tests
|
|
241
|
+
├── integration/ # 372 integration tests
|
|
242
|
+
├── security/ # 27 security tests
|
|
243
|
+
├── e2e/ # 31 E2E tests (real Safari)
|
|
244
|
+
├── canary/ # Deployment canary test
|
|
245
|
+
└── fixtures/ # HTML test pages
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Building
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
# TypeScript server
|
|
252
|
+
npm run build
|
|
253
|
+
|
|
254
|
+
# Swift daemon
|
|
255
|
+
cd daemon && swift build -c release
|
|
256
|
+
cp .build/release/SafariPilotd ../bin/
|
|
257
|
+
|
|
258
|
+
# Safari extension (requires Xcode)
|
|
259
|
+
bash scripts/build-extension.sh
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Testing
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
# All tests
|
|
266
|
+
npm test
|
|
267
|
+
|
|
268
|
+
# By category
|
|
269
|
+
npm run test:unit # 705 tests, no Safari needed
|
|
270
|
+
npm run test:integration # 372 tests, some need Safari
|
|
271
|
+
npm run test:security # 27 security-focused tests
|
|
272
|
+
npm run test:e2e # 31 tests against real Safari
|
|
273
|
+
|
|
274
|
+
# Swift daemon tests
|
|
275
|
+
cd daemon && swift run SafariPilotdTests
|
|
276
|
+
|
|
277
|
+
# Canary deployment
|
|
278
|
+
bash test/canary/install-test.sh
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Adding a New Tool
|
|
282
|
+
|
|
283
|
+
1. Add the handler to the appropriate module in `src/tools/`
|
|
284
|
+
2. Follow the pattern: `getDefinitions()` returns schema, `getHandler()` returns handler
|
|
285
|
+
3. Write tests in `test/unit/tools/`
|
|
286
|
+
4. The server auto-registers tools from all modules in `initialize()`
|
|
287
|
+
5. Add the tool name to `skills/safari-pilot/SKILL.md` allowed-tools
|
|
288
|
+
|
|
289
|
+
## What Safari Pilot Does NOT Replace
|
|
290
|
+
|
|
291
|
+
| Use Case | Keep Using |
|
|
292
|
+
|---|---|
|
|
293
|
+
| Lighthouse / Core Web Vitals auditing | Chrome DevTools MCP |
|
|
294
|
+
| Cross-platform automation (Linux/Windows) | Playwright MCP |
|
|
295
|
+
| Headless CI browser testing | Playwright |
|
|
296
|
+
| Pure text extraction (no interaction needed) | Jina Reader / Firecrawl |
|
|
297
|
+
| Visual regression testing | Playwright snapshots |
|
|
298
|
+
|
|
299
|
+
Safari Pilot is for **interactive browsing on Mac** — especially authenticated sessions.
|
|
300
|
+
|
|
301
|
+
## FAQ
|
|
302
|
+
|
|
303
|
+
**Q: Does this work on Linux/Windows?**
|
|
304
|
+
No. Safari is macOS only. The plugin gracefully disables itself on non-macOS systems.
|
|
305
|
+
|
|
306
|
+
**Q: Can the agent see my banking tabs?**
|
|
307
|
+
No. Tab ownership enforcement means the agent can only interact with tabs it opened via `safari_new_tab`. Your existing tabs are invisible to tool calls.
|
|
308
|
+
|
|
309
|
+
**Q: What if Safari crashes during automation?**
|
|
310
|
+
The daemon detects Safari crashes (error codes -600/-609) and retries with exponential backoff. If Safari restarts, automation resumes.
|
|
311
|
+
|
|
312
|
+
**Q: How is this different from safari-mcp?**
|
|
313
|
+
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.
|
|
314
|
+
|
|
315
|
+
**Q: Does the Swift daemon run all the time?**
|
|
316
|
+
Only when Claude Code is active. The LaunchAgent starts the daemon on demand and it shuts down with the session.
|
|
317
|
+
|
|
318
|
+
## License
|
|
319
|
+
|
|
320
|
+
MIT — see [LICENSE](LICENSE).
|
|
321
|
+
|
|
322
|
+
## Author
|
|
323
|
+
|
|
324
|
+
Built by [Aakash Kumar](https://github.com/aakashkumar) with Claude.
|
package/bin/.gitkeep
ADDED
|
File without changes
|
|
Binary file
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>BuildMachineOSBuild</key>
|
|
6
|
+
<string>25E5233c</string>
|
|
7
|
+
<key>CFBundleDevelopmentRegion</key>
|
|
8
|
+
<string>en</string>
|
|
9
|
+
<key>CFBundleDisplayName</key>
|
|
10
|
+
<string>Safari Pilot</string>
|
|
11
|
+
<key>CFBundleExecutable</key>
|
|
12
|
+
<string>Safari Pilot</string>
|
|
13
|
+
<key>CFBundleIconFile</key>
|
|
14
|
+
<string>AppIcon</string>
|
|
15
|
+
<key>CFBundleIconName</key>
|
|
16
|
+
<string>AppIcon</string>
|
|
17
|
+
<key>CFBundleIdentifier</key>
|
|
18
|
+
<string>com.safari-pilot.app</string>
|
|
19
|
+
<key>CFBundleInfoDictionaryVersion</key>
|
|
20
|
+
<string>6.0</string>
|
|
21
|
+
<key>CFBundleName</key>
|
|
22
|
+
<string>Safari Pilot</string>
|
|
23
|
+
<key>CFBundlePackageType</key>
|
|
24
|
+
<string>APPL</string>
|
|
25
|
+
<key>CFBundleShortVersionString</key>
|
|
26
|
+
<string>1.0</string>
|
|
27
|
+
<key>CFBundleSupportedPlatforms</key>
|
|
28
|
+
<array>
|
|
29
|
+
<string>MacOSX</string>
|
|
30
|
+
</array>
|
|
31
|
+
<key>CFBundleVersion</key>
|
|
32
|
+
<string>1</string>
|
|
33
|
+
<key>DTCompiler</key>
|
|
34
|
+
<string>com.apple.compilers.llvm.clang.1_0</string>
|
|
35
|
+
<key>DTPlatformBuild</key>
|
|
36
|
+
<string>25E236</string>
|
|
37
|
+
<key>DTPlatformName</key>
|
|
38
|
+
<string>macosx</string>
|
|
39
|
+
<key>DTPlatformVersion</key>
|
|
40
|
+
<string>26.4</string>
|
|
41
|
+
<key>DTSDKBuild</key>
|
|
42
|
+
<string>25E236</string>
|
|
43
|
+
<key>DTSDKName</key>
|
|
44
|
+
<string>macosx26.4</string>
|
|
45
|
+
<key>DTXcode</key>
|
|
46
|
+
<string>2640</string>
|
|
47
|
+
<key>DTXcodeBuild</key>
|
|
48
|
+
<string>17E192</string>
|
|
49
|
+
<key>LSMinimumSystemVersion</key>
|
|
50
|
+
<string>26.4</string>
|
|
51
|
+
<key>NSMainStoryboardFile</key>
|
|
52
|
+
<string>Main</string>
|
|
53
|
+
<key>NSPrincipalClass</key>
|
|
54
|
+
<string>NSApplication</string>
|
|
55
|
+
<key>SFSafariWebExtensionConverterVersion</key>
|
|
56
|
+
<string>26.4</string>
|
|
57
|
+
</dict>
|
|
58
|
+
</plist>
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
APPL????
|
package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Info.plist
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>BuildMachineOSBuild</key>
|
|
6
|
+
<string>25E5233c</string>
|
|
7
|
+
<key>CFBundleDevelopmentRegion</key>
|
|
8
|
+
<string>en</string>
|
|
9
|
+
<key>CFBundleDisplayName</key>
|
|
10
|
+
<string>Safari Pilot Extension</string>
|
|
11
|
+
<key>CFBundleExecutable</key>
|
|
12
|
+
<string>Safari Pilot Extension</string>
|
|
13
|
+
<key>CFBundleIdentifier</key>
|
|
14
|
+
<string>com.safari-pilot.app.Extension</string>
|
|
15
|
+
<key>CFBundleInfoDictionaryVersion</key>
|
|
16
|
+
<string>6.0</string>
|
|
17
|
+
<key>CFBundleName</key>
|
|
18
|
+
<string>Safari Pilot Extension</string>
|
|
19
|
+
<key>CFBundlePackageType</key>
|
|
20
|
+
<string>XPC!</string>
|
|
21
|
+
<key>CFBundleShortVersionString</key>
|
|
22
|
+
<string>1.0</string>
|
|
23
|
+
<key>CFBundleSupportedPlatforms</key>
|
|
24
|
+
<array>
|
|
25
|
+
<string>MacOSX</string>
|
|
26
|
+
</array>
|
|
27
|
+
<key>CFBundleVersion</key>
|
|
28
|
+
<string>1</string>
|
|
29
|
+
<key>DTCompiler</key>
|
|
30
|
+
<string>com.apple.compilers.llvm.clang.1_0</string>
|
|
31
|
+
<key>DTPlatformBuild</key>
|
|
32
|
+
<string>25E236</string>
|
|
33
|
+
<key>DTPlatformName</key>
|
|
34
|
+
<string>macosx</string>
|
|
35
|
+
<key>DTPlatformVersion</key>
|
|
36
|
+
<string>26.4</string>
|
|
37
|
+
<key>DTSDKBuild</key>
|
|
38
|
+
<string>25E236</string>
|
|
39
|
+
<key>DTSDKName</key>
|
|
40
|
+
<string>macosx26.4</string>
|
|
41
|
+
<key>DTXcode</key>
|
|
42
|
+
<string>2640</string>
|
|
43
|
+
<key>DTXcodeBuild</key>
|
|
44
|
+
<string>17E192</string>
|
|
45
|
+
<key>LSMinimumSystemVersion</key>
|
|
46
|
+
<string>10.14</string>
|
|
47
|
+
<key>NSExtension</key>
|
|
48
|
+
<dict>
|
|
49
|
+
<key>NSExtensionPointIdentifier</key>
|
|
50
|
+
<string>com.apple.Safari.web-extension</string>
|
|
51
|
+
<key>NSExtensionPrincipalClass</key>
|
|
52
|
+
<string>Safari_Pilot_Extension.SafariWebExtensionHandler</string>
|
|
53
|
+
</dict>
|
|
54
|
+
</dict>
|
|
55
|
+
</plist>
|