safari-pilot 0.1.24 → 0.1.28
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/README.md +63 -14
- package/bin/Safari Pilot.app/Contents/CodeResources +0 -0
- package/bin/Safari Pilot.app/Contents/Info.plist +2 -2
- package/bin/Safari Pilot.app/Contents/MacOS/Safari Pilot +0 -0
- package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Info.plist +2 -2
- 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 +220 -1
- package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/manifest.json +1 -1
- package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/_CodeSignature/CodeResources +4 -4
- 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/_CodeSignature/CodeResources +7 -7
- package/bin/Safari Pilot.zip +0 -0
- package/bin/SafariPilotd +0 -0
- package/dist/aria.js +4 -0
- package/dist/aria.js.map +1 -1
- package/dist/config.d.ts +6 -0
- package/dist/config.js +9 -0
- package/dist/config.js.map +1 -1
- package/dist/engines/daemon.js +25 -4
- package/dist/engines/daemon.js.map +1 -1
- package/dist/errors.d.ts +18 -0
- package/dist/errors.js +27 -0
- package/dist/errors.js.map +1 -1
- package/dist/locator.d.ts +80 -0
- package/dist/locator.js +324 -8
- package/dist/locator.js.map +1 -1
- package/dist/security/human-approval.js +15 -0
- package/dist/security/human-approval.js.map +1 -1
- package/dist/security/selector-pack-validator.d.ts +4 -0
- package/dist/security/selector-pack-validator.js +32 -0
- package/dist/security/selector-pack-validator.js.map +1 -0
- package/dist/server.js +3 -0
- package/dist/server.js.map +1 -1
- package/dist/tools/extraction.d.ts +1 -0
- package/dist/tools/extraction.js +126 -2
- package/dist/tools/extraction.js.map +1 -1
- package/dist/tools/interaction.js +29 -10
- package/dist/tools/interaction.js.map +1 -1
- package/dist/tools/selector-pack.d.ts +23 -0
- package/dist/tools/selector-pack.js +98 -0
- package/dist/tools/selector-pack.js.map +1 -0
- package/extension/background.js +231 -8
- package/extension/manifest.json +1 -1
- package/hooks/pre-publish-verify.sh +18 -1
- package/package.json +2 -1
- package/safari-pilot.config.json +4 -0
- package/scripts/pre-tag-check.sh +149 -0
- package/scripts/test-e2e-harness.sh +64 -0
- package/skills/safari-pilot/SKILL.md +30 -1
- package/dist/security/idpi-scanner.d.ts +0 -27
- package/dist/security/idpi-scanner.js +0 -111
- package/dist/security/idpi-scanner.js.map +0 -1
- package/dist/security/screenshot-redaction.d.ts +0 -42
- package/dist/security/screenshot-redaction.js +0 -134
- package/dist/security/screenshot-redaction.js.map +0 -1
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
|
-
> **
|
|
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
|
|
|
@@ -91,7 +91,8 @@ Without the extension, Safari Pilot still works for ~80% of use cases (navigatio
|
|
|
91
91
|
|
|
92
92
|
### System Requirements
|
|
93
93
|
|
|
94
|
-
- **macOS
|
|
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
|
|
95
96
|
- **Safari** (pre-installed on every Mac)
|
|
96
97
|
- **Node.js 20+**
|
|
97
98
|
|
|
@@ -119,7 +120,7 @@ Monitor news.ycombinator.com for any post about our company
|
|
|
119
120
|
Open my X.com bookmarks and extract the top 5 posts with author profiles
|
|
120
121
|
```
|
|
121
122
|
|
|
122
|
-
## Tool Catalog (
|
|
123
|
+
## Tool Catalog (82 Tools)
|
|
123
124
|
|
|
124
125
|
### Navigation (7)
|
|
125
126
|
`safari_navigate` | `safari_navigate_back` | `safari_navigate_forward` | `safari_reload` | `safari_new_tab` | `safari_close_tab` | `safari_list_tabs`
|
|
@@ -127,15 +128,21 @@ Open my X.com bookmarks and extract the top 5 posts with author profiles
|
|
|
127
128
|
### Interaction (11)
|
|
128
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`
|
|
129
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
|
+
|
|
130
134
|
### Extraction (7)
|
|
131
135
|
`safari_snapshot` | `safari_get_text` | `safari_get_html` | `safari_get_attribute` | `safari_evaluate` | `safari_take_screenshot` | `safari_get_console_messages`
|
|
132
136
|
|
|
133
|
-
### Network (
|
|
134
|
-
`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`
|
|
135
139
|
|
|
136
140
|
### Storage (11)
|
|
137
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`
|
|
138
142
|
|
|
143
|
+
### Authentication (2)
|
|
144
|
+
`safari_authenticate` | `safari_clear_authentication` — HTTP Basic auth via DNR header injection (extension required).
|
|
145
|
+
|
|
139
146
|
### Shadow DOM (2)
|
|
140
147
|
`safari_query_shadow` | `safari_click_shadow`
|
|
141
148
|
|
|
@@ -160,9 +167,18 @@ Open my X.com bookmarks and extract the top 5 posts with author profiles
|
|
|
160
167
|
### Compound Workflows (4)
|
|
161
168
|
`safari_test_flow` | `safari_monitor_page` | `safari_paginate_scrape` | `safari_media_control`
|
|
162
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
|
+
|
|
163
176
|
### Wait (1)
|
|
164
177
|
`safari_wait_for` — 7 condition types: selector, selectorHidden, text, textGone, urlMatch, networkidle, function
|
|
165
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
|
+
|
|
166
182
|
### System (2)
|
|
167
183
|
`safari_health_check` | `safari_emergency_stop`
|
|
168
184
|
|
|
@@ -290,19 +306,27 @@ bash scripts/build-extension.sh
|
|
|
290
306
|
### Testing
|
|
291
307
|
|
|
292
308
|
```bash
|
|
293
|
-
#
|
|
294
|
-
npm test
|
|
309
|
+
# Default — unit tests, no Safari required
|
|
310
|
+
npm test # 398 unit tests
|
|
311
|
+
npm run test:unit # alias for above
|
|
295
312
|
|
|
296
|
-
#
|
|
297
|
-
npm run test:
|
|
298
|
-
npm run test:
|
|
299
|
-
npm run test:security # 27 security-focused tests
|
|
300
|
-
npm run test:e2e # Against real Safari (optional)
|
|
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)
|
|
301
316
|
|
|
302
|
-
#
|
|
303
|
-
|
|
317
|
+
# Both
|
|
318
|
+
npm run test:all # unit + e2e
|
|
319
|
+
|
|
320
|
+
# Swift daemon (real Swift types, mocked at NSAppleScript boundary only)
|
|
321
|
+
cd daemon && swift test # 153 tests
|
|
304
322
|
```
|
|
305
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
|
+
|
|
306
330
|
### Adding a New Tool
|
|
307
331
|
|
|
308
332
|
1. Add the handler to the appropriate module in `src/tools/`
|
|
@@ -310,6 +334,31 @@ cd daemon && swift run SafariPilotdTests
|
|
|
310
334
|
3. Write tests in `test/unit/tools/`
|
|
311
335
|
4. The server auto-registers tools from all modules in `initialize()`
|
|
312
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.
|
|
313
362
|
|
|
314
363
|
## What Safari Pilot Does NOT Replace
|
|
315
364
|
|
|
Binary file
|
|
@@ -23,13 +23,13 @@
|
|
|
23
23
|
<key>CFBundlePackageType</key>
|
|
24
24
|
<string>APPL</string>
|
|
25
25
|
<key>CFBundleShortVersionString</key>
|
|
26
|
-
<string>0.1.
|
|
26
|
+
<string>0.1.28</string>
|
|
27
27
|
<key>CFBundleSupportedPlatforms</key>
|
|
28
28
|
<array>
|
|
29
29
|
<string>MacOSX</string>
|
|
30
30
|
</array>
|
|
31
31
|
<key>CFBundleVersion</key>
|
|
32
|
-
<string>
|
|
32
|
+
<string>202605050412</string>
|
|
33
33
|
<key>DTCompiler</key>
|
|
34
34
|
<string>com.apple.compilers.llvm.clang.1_0</string>
|
|
35
35
|
<key>DTPlatformBuild</key>
|
|
Binary file
|
package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Info.plist
CHANGED
|
@@ -19,13 +19,13 @@
|
|
|
19
19
|
<key>CFBundlePackageType</key>
|
|
20
20
|
<string>XPC!</string>
|
|
21
21
|
<key>CFBundleShortVersionString</key>
|
|
22
|
-
<string>0.1.
|
|
22
|
+
<string>0.1.28</string>
|
|
23
23
|
<key>CFBundleSupportedPlatforms</key>
|
|
24
24
|
<array>
|
|
25
25
|
<string>MacOSX</string>
|
|
26
26
|
</array>
|
|
27
27
|
<key>CFBundleVersion</key>
|
|
28
|
-
<string>
|
|
28
|
+
<string>202605050412</string>
|
|
29
29
|
<key>DTCompiler</key>
|
|
30
30
|
<string>com.apple.compilers.llvm.clang.1_0</string>
|
|
31
31
|
<key>DTPlatformBuild</key>
|
|
Binary file
|
|
@@ -22,6 +22,12 @@ let wakePending = false;
|
|
|
22
22
|
// initialize() is awaiting a fetch promise that will never resolve, and
|
|
23
23
|
// its `finally` never runs to clear isWakeRunning).
|
|
24
24
|
let pollLoopController = null;
|
|
25
|
+
// T73: timestamp of the last successful httpPoll resolution. Updated inside
|
|
26
|
+
// pollLoop's success try-block, read by supersedePollLoop to skip the abort
|
|
27
|
+
// cascade when the prior pollLoop is healthy. Default 0 → first supersede
|
|
28
|
+
// after extension load always runs (correct: there's nothing to be healthy
|
|
29
|
+
// about yet).
|
|
30
|
+
let lastSuccessfulPoll = 0;
|
|
25
31
|
|
|
26
32
|
// ─── Tab Cache ──────────────────────────────────────────────────────────────
|
|
27
33
|
// Safari's browser.tabs.query({}) returns [] when called from alarm-triggered
|
|
@@ -82,6 +88,72 @@ browser.tabs.onRemoved.addListener((tabId) => {
|
|
|
82
88
|
saveTabCache();
|
|
83
89
|
});
|
|
84
90
|
|
|
91
|
+
// T79: clear tab-scoped selectorPack storage on tab close. Keys live under
|
|
92
|
+
// prefix `sp_pack_<tabId>_<name>`. Multiple listeners can be added to the
|
|
93
|
+
// same event; this one runs independently of the tab-cache listener above.
|
|
94
|
+
browser.tabs.onRemoved.addListener(async (tabId) => {
|
|
95
|
+
try {
|
|
96
|
+
const all = await browser.storage.local.get(null);
|
|
97
|
+
const toRemove = Object.keys(all).filter((k) => k.startsWith('sp_pack_' + tabId + '_'));
|
|
98
|
+
if (toRemove.length > 0) {
|
|
99
|
+
await browser.storage.local.remove(toRemove);
|
|
100
|
+
emitTrace('__cleanup__', 'selector_pack_cleared', { layer: 'extension-bg', data: { tabId, count: toRemove.length } });
|
|
101
|
+
}
|
|
102
|
+
} catch (e) {
|
|
103
|
+
emitTrace('__cleanup__', 'selector_pack_clear_failed', { layer: 'extension-bg', data: { tabId, error: e && e.message ? e.message : String(e) } });
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// T79 Cluster D: re-inject persisted packs into window.__sp_pack on every
|
|
108
|
+
// completed navigation. Runs purely from the extension-bg context — no MCP
|
|
109
|
+
// command involved. Reads sp_pack_<tabId>_<name> keys and re-injects each
|
|
110
|
+
// via the same storage-bus execute_script flow used everywhere else.
|
|
111
|
+
browser.tabs.onUpdated.addListener(async (tabId, changeInfo) => {
|
|
112
|
+
if (changeInfo.status !== 'complete') return;
|
|
113
|
+
let stored;
|
|
114
|
+
try {
|
|
115
|
+
stored = await browser.storage.local.get(null);
|
|
116
|
+
} catch (e) {
|
|
117
|
+
emitTrace('__rehydrate__', 'pack_rehydrate_storage_get_failed', { layer: 'extension-bg', data: { tabId, error: e && e.message ? e.message : String(e) } });
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const prefix = 'sp_pack_' + tabId + '_';
|
|
121
|
+
const keys = Object.keys(stored).filter((k) => k.startsWith(prefix));
|
|
122
|
+
if (keys.length === 0) return;
|
|
123
|
+
for (const key of keys) {
|
|
124
|
+
const name = key.slice(prefix.length);
|
|
125
|
+
const body = stored[key];
|
|
126
|
+
if (typeof body !== 'string' || !body) continue;
|
|
127
|
+
const injectionScript =
|
|
128
|
+
'window.__sp_pack=window.__sp_pack||{};' +
|
|
129
|
+
'try{' +
|
|
130
|
+
'window.__sp_pack[' + JSON.stringify(name) + ']=new Function(\'root\',\'arg\',' + JSON.stringify(body) + ');' +
|
|
131
|
+
'return JSON.stringify({ok:true,rehydrated:true});' +
|
|
132
|
+
'}catch(e){' +
|
|
133
|
+
'return JSON.stringify({ok:false,error:e&&e.message?e.message:String(e)});' +
|
|
134
|
+
'}';
|
|
135
|
+
const rehydrateCmdId = '__rehydrate_' + tabId + '_' + name + '_' + Date.now();
|
|
136
|
+
const cmdKey = 'sp_cmd_' + rehydrateCmdId;
|
|
137
|
+
const storageCmd = {
|
|
138
|
+
commandId: rehydrateCmdId,
|
|
139
|
+
tabId,
|
|
140
|
+
method: 'execute_script',
|
|
141
|
+
params: { script: injectionScript, commandId: rehydrateCmdId },
|
|
142
|
+
timestamp: Date.now(),
|
|
143
|
+
deadline: Date.now() + 5000,
|
|
144
|
+
};
|
|
145
|
+
try {
|
|
146
|
+
await browser.storage.local.set({ [cmdKey]: storageCmd });
|
|
147
|
+
emitTrace(rehydrateCmdId, 'pack_rehydrated', { layer: 'extension-bg', data: { tabId, name } });
|
|
148
|
+
// Fire-and-forget — content-isolated picks it up, runs the injection.
|
|
149
|
+
// Cleanup the cmd key after a short delay to keep storage lean.
|
|
150
|
+
setTimeout(() => { browser.storage.local.remove([cmdKey, 'sp_result_' + rehydrateCmdId]).catch(() => {}); }, 6000);
|
|
151
|
+
} catch (e) {
|
|
152
|
+
emitTrace(rehydrateCmdId, 'pack_rehydrate_failed', { layer: 'extension-bg', data: { tabId, name, error: e && e.message ? e.message : String(e) } });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
85
157
|
// ─── HTTP IPC to daemon ─────────────────────────────────────────────────────
|
|
86
158
|
const HTTP_URL = 'http://127.0.0.1:19475';
|
|
87
159
|
|
|
@@ -471,6 +543,98 @@ async function executeCommand(cmd) {
|
|
|
471
543
|
return result;
|
|
472
544
|
}
|
|
473
545
|
|
|
546
|
+
// T79 Cluster D: selectorPack register/unregister sentinels.
|
|
547
|
+
// Pack registration writes both to (a) page-scope window.__sp_pack[name] for
|
|
548
|
+
// immediate use AND (b) browser.storage.local sp_pack_<tabId>_<name>=body for
|
|
549
|
+
// persistence across navigations. The tabs.onUpdated listener below
|
|
550
|
+
// re-injects (a) from (b) on every navigation. The existing tabs.onRemoved
|
|
551
|
+
// listener cleans up (b) on tab close.
|
|
552
|
+
//
|
|
553
|
+
// Body must be a string already validated upstream by validatePackBody (the
|
|
554
|
+
// MCP-side selector-pack tool). The extension does NOT re-validate — it
|
|
555
|
+
// trusts that ANY script that reaches this sentinel passed validation.
|
|
556
|
+
// Single source of truth on the MCP side avoids drift.
|
|
557
|
+
if (typeof cmd.script === 'string' && cmd.script.startsWith('__SP_PACK_')) {
|
|
558
|
+
const colonIdx = cmd.script.indexOf(':');
|
|
559
|
+
const sentinel = colonIdx > 0 ? cmd.script.slice(0, colonIdx) : cmd.script;
|
|
560
|
+
let parsed = {};
|
|
561
|
+
if (colonIdx > 0) {
|
|
562
|
+
try { parsed = JSON.parse(cmd.script.slice(colonIdx + 1)); }
|
|
563
|
+
catch (e) {
|
|
564
|
+
const result = { ok: false, error: { name: 'PACK_PARAM_PARSE', message: `Failed to parse pack params: ${e?.message ?? String(e)}` } };
|
|
565
|
+
await updatePendingEntry(commandId, { status: 'completed', result });
|
|
566
|
+
return result;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
const name = parsed.name;
|
|
571
|
+
const body = parsed.body;
|
|
572
|
+
if (typeof name !== 'string' || !name) {
|
|
573
|
+
const result = { ok: false, error: { name: 'PACK_INVALID_NAME', message: 'pack sentinel requires non-empty name' } };
|
|
574
|
+
await updatePendingEntry(commandId, { status: 'completed', result });
|
|
575
|
+
return result;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
const storageKey = 'sp_pack_' + tab.id + '_' + name;
|
|
579
|
+
|
|
580
|
+
if (sentinel === '__SP_PACK_REGISTER__') {
|
|
581
|
+
if (typeof body !== 'string' || !body) {
|
|
582
|
+
const result = { ok: false, error: { name: 'PACK_INVALID_BODY', message: 'pack register sentinel requires non-empty body' } };
|
|
583
|
+
await updatePendingEntry(commandId, { status: 'completed', result });
|
|
584
|
+
return result;
|
|
585
|
+
}
|
|
586
|
+
try {
|
|
587
|
+
// Persist for re-injection on navigation.
|
|
588
|
+
await browser.storage.local.set({ [storageKey]: body });
|
|
589
|
+
} catch (e) {
|
|
590
|
+
const result = { ok: false, error: { name: 'PACK_STORAGE_WRITE_FAILED', message: e?.message ?? String(e) } };
|
|
591
|
+
await updatePendingEntry(commandId, { status: 'completed', result });
|
|
592
|
+
return result;
|
|
593
|
+
}
|
|
594
|
+
// Inject into page now via the standard storage-bus execute path. The
|
|
595
|
+
// page-side evaluator (content-main.js) wraps scripts in `new
|
|
596
|
+
// Function(script)()`, so the script must use a top-level `return` —
|
|
597
|
+
// an IIFE expression statement would have its result discarded.
|
|
598
|
+
// Embed name + body via JSON.stringify so quotes / backslashes survive
|
|
599
|
+
// the round-trip without bespoke escaping.
|
|
600
|
+
const injectionScript =
|
|
601
|
+
'window.__sp_pack=window.__sp_pack||{};' +
|
|
602
|
+
'try{' +
|
|
603
|
+
'window.__sp_pack[' + JSON.stringify(name) + ']=new Function(\'root\',\'arg\',' + JSON.stringify(body) + ');' +
|
|
604
|
+
'return JSON.stringify({ok:true,name:' + JSON.stringify(name) + '});' +
|
|
605
|
+
'}catch(e){' +
|
|
606
|
+
'return JSON.stringify({ok:false,error:e&&e.message?e.message:String(e)});' +
|
|
607
|
+
'}';
|
|
608
|
+
// Replace cmd.script and fall through to the regular execute path so
|
|
609
|
+
// the inject runs through the storage-bus content-script flow.
|
|
610
|
+
cmd.script = injectionScript;
|
|
611
|
+
emitTrace(commandId, 'pack_registered', { layer: 'extension-bg', data: { tabId: tab.id, name, bodyBytes: body.length } });
|
|
612
|
+
// Fall through — the regular execute path runs `cmd.script` in-page.
|
|
613
|
+
} else if (sentinel === '__SP_PACK_UNREGISTER__') {
|
|
614
|
+
try {
|
|
615
|
+
await browser.storage.local.remove([storageKey]);
|
|
616
|
+
} catch (e) {
|
|
617
|
+
const result = { ok: false, error: { name: 'PACK_STORAGE_REMOVE_FAILED', message: e?.message ?? String(e) } };
|
|
618
|
+
await updatePendingEntry(commandId, { status: 'completed', result });
|
|
619
|
+
return result;
|
|
620
|
+
}
|
|
621
|
+
const removalScript =
|
|
622
|
+
'var __removed=false;' +
|
|
623
|
+
'if(window.__sp_pack&&window.__sp_pack[' + JSON.stringify(name) + ']){' +
|
|
624
|
+
'delete window.__sp_pack[' + JSON.stringify(name) + '];' +
|
|
625
|
+
'__removed=true;' +
|
|
626
|
+
'}' +
|
|
627
|
+
'return JSON.stringify({ok:true,removed:__removed});';
|
|
628
|
+
cmd.script = removalScript;
|
|
629
|
+
emitTrace(commandId, 'pack_unregistered', { layer: 'extension-bg', data: { tabId: tab.id, name } });
|
|
630
|
+
// Fall through — regular execute path runs the removal.
|
|
631
|
+
} else {
|
|
632
|
+
const r = { ok: false, error: { name: 'UNKNOWN_PACK_SENTINEL', message: `Unknown pack sentinel: ${sentinel}` } };
|
|
633
|
+
await updatePendingEntry(commandId, { status: 'completed', result: r });
|
|
634
|
+
return r;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
474
638
|
// T55a: validate frameId at dispatch time. Missing frame → fast-fail
|
|
475
639
|
// before any storage-bus traffic. Re-resolve frame.url so content-isolated.js's
|
|
476
640
|
// mutation guard has the authoritative value when comparing to location.href.
|
|
@@ -790,12 +954,32 @@ async function pollLoop(abortSignal) {
|
|
|
790
954
|
const BACKOFF_MS = [0, 250, 500, 1000, 2000];
|
|
791
955
|
const MAX_ATTEMPTS = 5;
|
|
792
956
|
let attempts = 0;
|
|
957
|
+
// T72: pre-launch the FIRST /poll fetch BEFORE entering the while loop so
|
|
958
|
+
// a fetch is always in flight by the time the loop body runs. Combined
|
|
959
|
+
// with the in-loop reassignment below, this guarantees that MV3's event
|
|
960
|
+
// page never sees an idle moment between iterations — preventing the
|
|
961
|
+
// suspension that drove T71's 80% multi-file e2e sweep flake rate. The
|
|
962
|
+
// in-flight variable is awaited inside the existing BACKOFF_MS retry
|
|
963
|
+
// ladder so cold-start fetch errors still hit the retry path.
|
|
964
|
+
let inflightPoll = httpPoll(abortSignal);
|
|
793
965
|
// T60: honor the abort signal threaded from pollLoopController so a fresh
|
|
794
966
|
// alarm wake can forcibly stop a prior pollLoop instance — even one whose
|
|
795
967
|
// fetch is wedged from event-page suspension recovery.
|
|
796
968
|
while (!(abortSignal && abortSignal.aborted)) {
|
|
797
969
|
try {
|
|
798
|
-
const data = await
|
|
970
|
+
const data = await inflightPoll;
|
|
971
|
+
// T73: mark this pollLoop as healthy. supersedePollLoop reads this
|
|
972
|
+
// timestamp on each keepalive alarm and skips the abort cascade when
|
|
973
|
+
// recent (<30s). Set BEFORE the next-fetch reassignment so a successful
|
|
974
|
+
// resolution is recorded even if the next httpPoll throws synchronously.
|
|
975
|
+
lastSuccessfulPoll = Date.now();
|
|
976
|
+
// T72: kick the NEXT /poll fetch IMMEDIATELY — before processing this
|
|
977
|
+
// batch's commands — so a fetch is in flight while executeCommand
|
|
978
|
+
// runs. The MV3 event page stays alive on the in-flight fetch even
|
|
979
|
+
// when a tool call takes seconds (e.g. safari_navigate waiting for
|
|
980
|
+
// page load). Without this, a long-running executeCommand can let
|
|
981
|
+
// the page suspend before the next /poll iteration starts.
|
|
982
|
+
inflightPoll = httpPoll(abortSignal);
|
|
799
983
|
if (attempts > 0) {
|
|
800
984
|
emitTrace('__pollloop__', 'pollloop_recovered', { attempts });
|
|
801
985
|
attempts = 0;
|
|
@@ -816,8 +1000,13 @@ async function pollLoop(abortSignal) {
|
|
|
816
1000
|
// TimeoutError = the per-fetch 10 s timeout fired with no command —
|
|
817
1001
|
// this is the NORMAL idle case (the daemon long-polls up to 5 s).
|
|
818
1002
|
// Pre-T22 this killed the loop, requiring an alarm to re-arm: bug.
|
|
1003
|
+
// T72: re-arm the inflight fetch before continuing so the next iter
|
|
1004
|
+
// has a fetch to await (otherwise we'd await a stale resolved value).
|
|
819
1005
|
if (err.name === 'TimeoutError') {
|
|
820
1006
|
attempts = 0;
|
|
1007
|
+
if (!(abortSignal && abortSignal.aborted)) {
|
|
1008
|
+
inflightPoll = httpPoll(abortSignal);
|
|
1009
|
+
}
|
|
821
1010
|
continue;
|
|
822
1011
|
}
|
|
823
1012
|
attempts++;
|
|
@@ -829,6 +1018,11 @@ async function pollLoop(abortSignal) {
|
|
|
829
1018
|
const baseMs = BACKOFF_MS[Math.min(attempts, BACKOFF_MS.length - 1)];
|
|
830
1019
|
const jitter = Math.random() * 250;
|
|
831
1020
|
await new Promise((r) => setTimeout(r, baseMs + jitter));
|
|
1021
|
+
// T72: re-arm after backoff so the next iter awaits a fresh fetch
|
|
1022
|
+
// rather than the (already-rejected) prior one.
|
|
1023
|
+
if (!(abortSignal && abortSignal.aborted)) {
|
|
1024
|
+
inflightPoll = httpPoll(abortSignal);
|
|
1025
|
+
}
|
|
832
1026
|
}
|
|
833
1027
|
}
|
|
834
1028
|
emitTrace('__pollloop__', 'pollloop_aborted', { reason: 'signal_aborted_pre_iter' });
|
|
@@ -837,7 +1031,32 @@ async function pollLoop(abortSignal) {
|
|
|
837
1031
|
// T60: idempotent supersede. Aborts any prior pollLoop instance (releasing
|
|
838
1032
|
// stuck fetches) and starts a fresh one with a new AbortController. The
|
|
839
1033
|
// previous loop returns via its AbortError catch; this one runs free.
|
|
1034
|
+
//
|
|
1035
|
+
// T73: skip the abort cascade when the prior pollLoop is healthy. A healthy
|
|
1036
|
+
// pollLoop has resolved an httpPoll within the last 30s — its in-flight
|
|
1037
|
+
// /poll is registered in the daemon's waitingPolls and ready to fast-path
|
|
1038
|
+
// the next execute. Aborting it kills that registration; commands queued
|
|
1039
|
+
// during the supersede transition wait until the new pollLoop establishes
|
|
1040
|
+
// a fresh waitingPoll AND a new execute arrives — observed as a 10s gap in
|
|
1041
|
+
// e2e sweeps (T72-partial validation, 40% residual flake rate). Skipping
|
|
1042
|
+
// preserves the daemon-side registration through the keepalive nudge.
|
|
1043
|
+
//
|
|
1044
|
+
// 30s threshold rationale: daemon long-poll holds 5s normally; bursts of
|
|
1045
|
+
// activity reset lastSuccessfulPoll on every resolution. Idle systems with
|
|
1046
|
+
// no commands resolve every 5s (TimeoutError doesn't update the timestamp,
|
|
1047
|
+
// but the SUCCESS try-block does — and 204 responses count as success here:
|
|
1048
|
+
// they reach the resolution point in pollLoop). 30s gives 6x headroom.
|
|
840
1049
|
function supersedePollLoop(reason) {
|
|
1050
|
+
// T73 health check — skip when prior pollLoop is alive AND has resolved
|
|
1051
|
+
// recently. Only the unhealthy path (wedged fetch, never-resolved poll,
|
|
1052
|
+
// or first-load) goes through the abort+restart cascade.
|
|
1053
|
+
if (pollLoopController && lastSuccessfulPoll > 0 && Date.now() - lastSuccessfulPoll < 30_000) {
|
|
1054
|
+
emitTrace('__pollloop__', 'pollloop_supersede_skipped', {
|
|
1055
|
+
reason,
|
|
1056
|
+
sinceLastPollMs: Date.now() - lastSuccessfulPoll,
|
|
1057
|
+
});
|
|
1058
|
+
return;
|
|
1059
|
+
}
|
|
841
1060
|
if (pollLoopController) {
|
|
842
1061
|
try { pollLoopController.abort(); } catch { /* ignore */ }
|
|
843
1062
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<dict>
|
|
7
7
|
<key>Resources/background.js</key>
|
|
8
8
|
<data>
|
|
9
|
-
|
|
9
|
+
S+yhMSyT4yM0o29NjtVWT9pto7E=
|
|
10
10
|
</data>
|
|
11
11
|
<key>Resources/build.config.js</key>
|
|
12
12
|
<data>
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
</data>
|
|
47
47
|
<key>Resources/manifest.json</key>
|
|
48
48
|
<data>
|
|
49
|
-
|
|
49
|
+
N9cYkZyI/ckF3TWIR2w2ujwqOSQ=
|
|
50
50
|
</data>
|
|
51
51
|
<key>Resources/native/SafariWebExtensionHandler.swift</key>
|
|
52
52
|
<data>
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
<dict>
|
|
60
60
|
<key>hash2</key>
|
|
61
61
|
<data>
|
|
62
|
-
|
|
62
|
+
wGGys1VHHjcourMlcj2w6IUjnjahEI8V1cj6WRb+/lU=
|
|
63
63
|
</data>
|
|
64
64
|
</dict>
|
|
65
65
|
<key>Resources/build.config.js</key>
|
|
@@ -129,7 +129,7 @@
|
|
|
129
129
|
<dict>
|
|
130
130
|
<key>hash2</key>
|
|
131
131
|
<data>
|
|
132
|
-
|
|
132
|
+
Vh4l0sBEAXf+Jc3DaBwYM5wwB9LUiM1LhuR2p1VYK/4=
|
|
133
133
|
</data>
|
|
134
134
|
</dict>
|
|
135
135
|
<key>Resources/native/SafariWebExtensionHandler.swift</key>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -18,15 +18,15 @@
|
|
|
18
18
|
</data>
|
|
19
19
|
<key>Resources/Base.lproj/Main.storyboardc/Info.plist</key>
|
|
20
20
|
<data>
|
|
21
|
-
|
|
21
|
+
Lk+CPfhfIGRA8ShZZFI+A0rJ2Fk=
|
|
22
22
|
</data>
|
|
23
23
|
<key>Resources/Base.lproj/Main.storyboardc/MainMenu.nib</key>
|
|
24
24
|
<data>
|
|
25
|
-
|
|
25
|
+
gfejsxd2k48cV+t1Ave1GMgeWeU=
|
|
26
26
|
</data>
|
|
27
27
|
<key>Resources/Base.lproj/Main.storyboardc/NSWindowController-B8D-0N-5wS.nib</key>
|
|
28
28
|
<data>
|
|
29
|
-
|
|
29
|
+
9ykXQmnGGwCAJL2MPMLC7qPCpkQ=
|
|
30
30
|
</data>
|
|
31
31
|
<key>Resources/Base.lproj/Main.storyboardc/XfG-lQ-9wD-view-m2S-Jp-Qdl.nib</key>
|
|
32
32
|
<data>
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
<dict>
|
|
52
52
|
<key>cdhash</key>
|
|
53
53
|
<data>
|
|
54
|
-
|
|
54
|
+
bWtJ8Gsg4i9gGuKEQ4SYB+FcDso=
|
|
55
55
|
</data>
|
|
56
56
|
<key>requirement</key>
|
|
57
57
|
<string>anchor apple generic and identifier "com.safari-pilot.app.Extension" and (certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */ or certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = V37WLKRXUJ)</string>
|
|
@@ -81,21 +81,21 @@
|
|
|
81
81
|
<dict>
|
|
82
82
|
<key>hash2</key>
|
|
83
83
|
<data>
|
|
84
|
-
|
|
84
|
+
8J9pOXoi9UK5I6qZRMloMaWPxQoW+OciXbGvdnqtq0Q=
|
|
85
85
|
</data>
|
|
86
86
|
</dict>
|
|
87
87
|
<key>Resources/Base.lproj/Main.storyboardc/MainMenu.nib</key>
|
|
88
88
|
<dict>
|
|
89
89
|
<key>hash2</key>
|
|
90
90
|
<data>
|
|
91
|
-
|
|
91
|
+
pXqB05PG+ZwnFVMoJVsDjhgig8YjRG8grrBPWsZG/YI=
|
|
92
92
|
</data>
|
|
93
93
|
</dict>
|
|
94
94
|
<key>Resources/Base.lproj/Main.storyboardc/NSWindowController-B8D-0N-5wS.nib</key>
|
|
95
95
|
<dict>
|
|
96
96
|
<key>hash2</key>
|
|
97
97
|
<data>
|
|
98
|
-
|
|
98
|
+
LAAQ0mIs7zxLtn87eO30xhqHEkEP/lblzyrpTD3gv0s=
|
|
99
99
|
</data>
|
|
100
100
|
</dict>
|
|
101
101
|
<key>Resources/Base.lproj/Main.storyboardc/XfG-lQ-9wD-view-m2S-Jp-Qdl.nib</key>
|
package/bin/Safari Pilot.zip
CHANGED
|
Binary file
|
package/bin/SafariPilotd
CHANGED
|
Binary file
|
package/dist/aria.js
CHANGED
|
@@ -15,6 +15,10 @@
|
|
|
15
15
|
* Safe for use in `querySelector()` or tool parameters.
|
|
16
16
|
*/
|
|
17
17
|
export function buildRefSelector(ref) {
|
|
18
|
+
// T78: passthrough for fully-qualified data-sp-ref selectors so callers can
|
|
19
|
+
// hand in either the bare ref or the resolved selector form.
|
|
20
|
+
if (ref.startsWith('[data-sp-ref='))
|
|
21
|
+
return ref;
|
|
18
22
|
return '[data-sp-ref="' + ref + '"]';
|
|
19
23
|
}
|
|
20
24
|
/**
|
package/dist/aria.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aria.js","sourceRoot":"","sources":["../src/aria.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"aria.js","sourceRoot":"","sources":["../src/aria.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
CHANGED
|
@@ -31,6 +31,11 @@ export interface ExtensionConfig {
|
|
|
31
31
|
enabled: boolean;
|
|
32
32
|
killSwitchVersion: string;
|
|
33
33
|
}
|
|
34
|
+
export interface SelectorPackConfig {
|
|
35
|
+
/** T79: feature-flag for safari_register_selector / safari_unregister_selector tools.
|
|
36
|
+
* Defaults to false. JS-injection surface — only enable for trusted environments. */
|
|
37
|
+
enabled: boolean;
|
|
38
|
+
}
|
|
34
39
|
export interface SafariPilotConfig {
|
|
35
40
|
schemaVersion: string;
|
|
36
41
|
rateLimit: RateLimitConfig;
|
|
@@ -41,6 +46,7 @@ export interface SafariPilotConfig {
|
|
|
41
46
|
daemon: DaemonConfig;
|
|
42
47
|
healthCheck: HealthCheckConfig;
|
|
43
48
|
extension: ExtensionConfig;
|
|
49
|
+
selectorPack: SelectorPackConfig;
|
|
44
50
|
screenshotPolicy?: {
|
|
45
51
|
blockedPatterns?: string[];
|
|
46
52
|
};
|
package/dist/config.js
CHANGED
|
@@ -37,6 +37,9 @@ export const DEFAULT_CONFIG = {
|
|
|
37
37
|
enabled: true,
|
|
38
38
|
killSwitchVersion: '0.1.5',
|
|
39
39
|
},
|
|
40
|
+
selectorPack: {
|
|
41
|
+
enabled: false,
|
|
42
|
+
},
|
|
40
43
|
};
|
|
41
44
|
// ─── Deep merge ──────────────────────────────────────────────────────────────
|
|
42
45
|
function isPlainObject(val) {
|
|
@@ -166,6 +169,12 @@ export function loadConfig(configPath) {
|
|
|
166
169
|
// File not found or unreadable → use defaults silently
|
|
167
170
|
}
|
|
168
171
|
const merged = deepMerge(DEFAULT_CONFIG, userConfig);
|
|
172
|
+
// T79: selectorPack.enabled is a JS-injection-surface flag. Enforce strict
|
|
173
|
+
// boolean — anything other than literal `true` reverts to false (fail-safe).
|
|
174
|
+
const mergedRecord = merged;
|
|
175
|
+
const sp = mergedRecord['selectorPack'];
|
|
176
|
+
if (sp)
|
|
177
|
+
sp['enabled'] = sp['enabled'] === true;
|
|
169
178
|
validate(merged);
|
|
170
179
|
return deepFreeze(resolveConfigPaths(merged));
|
|
171
180
|
}
|