ios-webkit-mcp 0.18.3
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/CONTRIBUTING.md +61 -0
- package/LICENSE +202 -0
- package/NOTICE +16 -0
- package/README.md +187 -0
- package/build/src/bin/ios-webkit-mcp.d.ts +2 -0
- package/build/src/bin/ios-webkit-mcp.js +14 -0
- package/build/src/bin/ios-webkit-mcp.js.map +1 -0
- package/build/src/capability.d.ts +61 -0
- package/build/src/capability.js +146 -0
- package/build/src/capability.js.map +1 -0
- package/build/src/index.d.ts +1 -0
- package/build/src/index.js +2 -0
- package/build/src/index.js.map +1 -0
- package/build/src/page-session.d.ts +108 -0
- package/build/src/page-session.js +331 -0
- package/build/src/page-session.js.map +1 -0
- package/build/src/server.d.ts +11 -0
- package/build/src/server.js +144 -0
- package/build/src/server.js.map +1 -0
- package/build/src/tools/analyze_performance.d.ts +40 -0
- package/build/src/tools/analyze_performance.js +112 -0
- package/build/src/tools/analyze_performance.js.map +1 -0
- package/build/src/tools/breakpoints.d.ts +80 -0
- package/build/src/tools/breakpoints.js +167 -0
- package/build/src/tools/breakpoints.js.map +1 -0
- package/build/src/tools/clear_console.d.ts +11 -0
- package/build/src/tools/clear_console.js +32 -0
- package/build/src/tools/clear_console.js.map +1 -0
- package/build/src/tools/click.d.ts +20 -0
- package/build/src/tools/click.js +88 -0
- package/build/src/tools/click.js.map +1 -0
- package/build/src/tools/debug_element.d.ts +36 -0
- package/build/src/tools/debug_element.js +66 -0
- package/build/src/tools/debug_element.js.map +1 -0
- package/build/src/tools/debugger.d.ts +44 -0
- package/build/src/tools/debugger.js +117 -0
- package/build/src/tools/debugger.js.map +1 -0
- package/build/src/tools/delete_cookie.d.ts +18 -0
- package/build/src/tools/delete_cookie.js +33 -0
- package/build/src/tools/delete_cookie.js.map +1 -0
- package/build/src/tools/drag.d.ts +40 -0
- package/build/src/tools/drag.js +202 -0
- package/build/src/tools/drag.js.map +1 -0
- package/build/src/tools/evaluate_script.d.ts +24 -0
- package/build/src/tools/evaluate_script.js +172 -0
- package/build/src/tools/evaluate_script.js.map +1 -0
- package/build/src/tools/fill.d.ts +22 -0
- package/build/src/tools/fill.js +104 -0
- package/build/src/tools/fill.js.map +1 -0
- package/build/src/tools/fill_form.d.ts +34 -0
- package/build/src/tools/fill_form.js +110 -0
- package/build/src/tools/fill_form.js.map +1 -0
- package/build/src/tools/gc_heap.d.ts +16 -0
- package/build/src/tools/gc_heap.js +66 -0
- package/build/src/tools/gc_heap.js.map +1 -0
- package/build/src/tools/get_capability_snapshot.d.ts +17 -0
- package/build/src/tools/get_capability_snapshot.js +50 -0
- package/build/src/tools/get_capability_snapshot.js.map +1 -0
- package/build/src/tools/get_console_message.d.ts +18 -0
- package/build/src/tools/get_console_message.js +92 -0
- package/build/src/tools/get_console_message.js.map +1 -0
- package/build/src/tools/get_cookies.d.ts +20 -0
- package/build/src/tools/get_cookies.js +59 -0
- package/build/src/tools/get_cookies.js.map +1 -0
- package/build/src/tools/get_event_listeners.d.ts +29 -0
- package/build/src/tools/get_event_listeners.js +129 -0
- package/build/src/tools/get_event_listeners.js.map +1 -0
- package/build/src/tools/get_network_request.d.ts +22 -0
- package/build/src/tools/get_network_request.js +121 -0
- package/build/src/tools/get_network_request.js.map +1 -0
- package/build/src/tools/get_performance_metrics.d.ts +27 -0
- package/build/src/tools/get_performance_metrics.js +185 -0
- package/build/src/tools/get_performance_metrics.js.map +1 -0
- package/build/src/tools/get_resource_tree.d.ts +18 -0
- package/build/src/tools/get_resource_tree.js +82 -0
- package/build/src/tools/get_resource_tree.js.map +1 -0
- package/build/src/tools/handle_dialog.d.ts +39 -0
- package/build/src/tools/handle_dialog.js +182 -0
- package/build/src/tools/handle_dialog.js.map +1 -0
- package/build/src/tools/highlight_node.d.ts +41 -0
- package/build/src/tools/highlight_node.js +95 -0
- package/build/src/tools/highlight_node.js.map +1 -0
- package/build/src/tools/hover.d.ts +20 -0
- package/build/src/tools/hover.js +89 -0
- package/build/src/tools/hover.js.map +1 -0
- package/build/src/tools/intercept.d.ts +81 -0
- package/build/src/tools/intercept.js +161 -0
- package/build/src/tools/intercept.js.map +1 -0
- package/build/src/tools/list_console_messages.d.ts +20 -0
- package/build/src/tools/list_console_messages.js +105 -0
- package/build/src/tools/list_console_messages.js.map +1 -0
- package/build/src/tools/list_indexeddb.d.ts +16 -0
- package/build/src/tools/list_indexeddb.js +55 -0
- package/build/src/tools/list_indexeddb.js.map +1 -0
- package/build/src/tools/list_network_requests.d.ts +25 -0
- package/build/src/tools/list_network_requests.js +108 -0
- package/build/src/tools/list_network_requests.js.map +1 -0
- package/build/src/tools/list_pages.d.ts +17 -0
- package/build/src/tools/list_pages.js +113 -0
- package/build/src/tools/list_pages.js.map +1 -0
- package/build/src/tools/navigate_page.d.ts +18 -0
- package/build/src/tools/navigate_page.js +73 -0
- package/build/src/tools/navigate_page.js.map +1 -0
- package/build/src/tools/network_runtime.d.ts +51 -0
- package/build/src/tools/network_runtime.js +101 -0
- package/build/src/tools/network_runtime.js.map +1 -0
- package/build/src/tools/press_key.d.ts +32 -0
- package/build/src/tools/press_key.js +108 -0
- package/build/src/tools/press_key.js.map +1 -0
- package/build/src/tools/query_dom_node.d.ts +22 -0
- package/build/src/tools/query_dom_node.js +103 -0
- package/build/src/tools/query_dom_node.js.map +1 -0
- package/build/src/tools/record_cpu_profile.d.ts +38 -0
- package/build/src/tools/record_cpu_profile.js +174 -0
- package/build/src/tools/record_cpu_profile.js.map +1 -0
- package/build/src/tools/record_memory_tracking.d.ts +32 -0
- package/build/src/tools/record_memory_tracking.js +103 -0
- package/build/src/tools/record_memory_tracking.js.map +1 -0
- package/build/src/tools/record_timeline.d.ts +20 -0
- package/build/src/tools/record_timeline.js +110 -0
- package/build/src/tools/record_timeline.js.map +1 -0
- package/build/src/tools/reload_page.d.ts +18 -0
- package/build/src/tools/reload_page.js +59 -0
- package/build/src/tools/reload_page.js.map +1 -0
- package/build/src/tools/select_page.d.ts +18 -0
- package/build/src/tools/select_page.js +54 -0
- package/build/src/tools/select_page.js.map +1 -0
- package/build/src/tools/set_extra_http_headers.d.ts +16 -0
- package/build/src/tools/set_extra_http_headers.js +55 -0
- package/build/src/tools/set_extra_http_headers.js.map +1 -0
- package/build/src/tools/set_init_script.d.ts +16 -0
- package/build/src/tools/set_init_script.js +43 -0
- package/build/src/tools/set_init_script.js.map +1 -0
- package/build/src/tools/set_outer_html.d.ts +18 -0
- package/build/src/tools/set_outer_html.js +52 -0
- package/build/src/tools/set_outer_html.js.map +1 -0
- package/build/src/tools/set_user_agent.d.ts +40 -0
- package/build/src/tools/set_user_agent.js +83 -0
- package/build/src/tools/set_user_agent.js.map +1 -0
- package/build/src/tools/summarize_console_errors.d.ts +40 -0
- package/build/src/tools/summarize_console_errors.js +131 -0
- package/build/src/tools/summarize_console_errors.js.map +1 -0
- package/build/src/tools/take_memory_snapshot.d.ts +18 -0
- package/build/src/tools/take_memory_snapshot.js +88 -0
- package/build/src/tools/take_memory_snapshot.js.map +1 -0
- package/build/src/tools/take_node_screenshot.d.ts +29 -0
- package/build/src/tools/take_node_screenshot.js +57 -0
- package/build/src/tools/take_node_screenshot.js.map +1 -0
- package/build/src/tools/take_screenshot.d.ts +51 -0
- package/build/src/tools/take_screenshot.js +108 -0
- package/build/src/tools/take_screenshot.js.map +1 -0
- package/build/src/tools/take_snapshot.d.ts +22 -0
- package/build/src/tools/take_snapshot.js +142 -0
- package/build/src/tools/take_snapshot.js.map +1 -0
- package/build/src/tools/type_text.d.ts +34 -0
- package/build/src/tools/type_text.js +203 -0
- package/build/src/tools/type_text.js.map +1 -0
- package/build/src/tools/wait_for.d.ts +24 -0
- package/build/src/tools/wait_for.js +93 -0
- package/build/src/tools/wait_for.js.map +1 -0
- package/build/src/tools/wip_send.d.ts +22 -0
- package/build/src/tools/wip_send.js +81 -0
- package/build/src/tools/wip_send.js.map +1 -0
- package/build/src/wip-client/iwdp-http.d.ts +17 -0
- package/build/src/wip-client/iwdp-http.js +98 -0
- package/build/src/wip-client/iwdp-http.js.map +1 -0
- package/build/src/wip-client/ws-session.d.ts +41 -0
- package/build/src/wip-client/ws-session.js +290 -0
- package/build/src/wip-client/ws-session.js.map +1 -0
- package/docs/spec.md +689 -0
- package/package.json +77 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* WIP has neither `Page.javascriptDialogOpening` events nor
|
|
4
|
+
* `Page.handleJavaScriptDialog` method — verified `-32601` for both. The iOS
|
|
5
|
+
* WebView surfaces `window.alert/confirm/prompt` as native UI handled by the
|
|
6
|
+
* host app's WKUIDelegate, completely outside WIP's reach.
|
|
7
|
+
*
|
|
8
|
+
* This tool overrides `window.alert/confirm/prompt` from JS land:
|
|
9
|
+
* - `install` mode: monkey-patches the three globals to record each call in
|
|
10
|
+
* `window.__wdm_dialogs` and short-circuit return preset responses
|
|
11
|
+
* - `list` mode: returns the captured queue
|
|
12
|
+
* - `set_response` mode: updates the preset response for subsequent confirm/prompt
|
|
13
|
+
* - `uninstall` mode: restores the natives
|
|
14
|
+
*
|
|
15
|
+
* Because the override returns synchronously, no native iOS dialog UI appears.
|
|
16
|
+
* Pages that depend on the actual dialog blocking (e.g. "wait for user to dismiss
|
|
17
|
+
* the alert") may behave differently than in a real user session.
|
|
18
|
+
*/
|
|
19
|
+
export declare const handleDialogTool: {
|
|
20
|
+
name: string;
|
|
21
|
+
description: string;
|
|
22
|
+
inputSchema: {
|
|
23
|
+
mode: z.ZodEnum<["install", "list", "set_response", "uninstall"]>;
|
|
24
|
+
confirmResponse: z.ZodOptional<z.ZodBoolean>;
|
|
25
|
+
promptResponse: z.ZodOptional<z.ZodString>;
|
|
26
|
+
clearQueue: z.ZodOptional<z.ZodBoolean>;
|
|
27
|
+
};
|
|
28
|
+
handler: ({ mode, confirmResponse, promptResponse, clearQueue, }: {
|
|
29
|
+
mode: "install" | "list" | "set_response" | "uninstall";
|
|
30
|
+
confirmResponse?: boolean;
|
|
31
|
+
promptResponse?: string;
|
|
32
|
+
clearQueue?: boolean;
|
|
33
|
+
}) => Promise<{
|
|
34
|
+
content: {
|
|
35
|
+
type: "text";
|
|
36
|
+
text: string;
|
|
37
|
+
}[];
|
|
38
|
+
}>;
|
|
39
|
+
};
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { PageSession } from '../page-session.js';
|
|
3
|
+
/**
|
|
4
|
+
* WIP has neither `Page.javascriptDialogOpening` events nor
|
|
5
|
+
* `Page.handleJavaScriptDialog` method — verified `-32601` for both. The iOS
|
|
6
|
+
* WebView surfaces `window.alert/confirm/prompt` as native UI handled by the
|
|
7
|
+
* host app's WKUIDelegate, completely outside WIP's reach.
|
|
8
|
+
*
|
|
9
|
+
* This tool overrides `window.alert/confirm/prompt` from JS land:
|
|
10
|
+
* - `install` mode: monkey-patches the three globals to record each call in
|
|
11
|
+
* `window.__wdm_dialogs` and short-circuit return preset responses
|
|
12
|
+
* - `list` mode: returns the captured queue
|
|
13
|
+
* - `set_response` mode: updates the preset response for subsequent confirm/prompt
|
|
14
|
+
* - `uninstall` mode: restores the natives
|
|
15
|
+
*
|
|
16
|
+
* Because the override returns synchronously, no native iOS dialog UI appears.
|
|
17
|
+
* Pages that depend on the actual dialog blocking (e.g. "wait for user to dismiss
|
|
18
|
+
* the alert") may behave differently than in a real user session.
|
|
19
|
+
*/
|
|
20
|
+
export const handleDialogTool = {
|
|
21
|
+
name: 'handle_dialog',
|
|
22
|
+
description: [
|
|
23
|
+
'Manage `window.alert` / `confirm` / `prompt` interception on the inspected iOS WebView page.',
|
|
24
|
+
'NOTE: WIP has no `Page.javascriptDialogOpening` event or `Page.handleJavaScriptDialog` method (verified `-32601`). This tool monkey-patches the three globals from JS — synthetic, no real iOS dialog UI fires after install. Use this to capture dialog text the page would have shown and to pre-set the return value for `confirm` / `prompt`.',
|
|
25
|
+
'Modes: `install` (overrides + start recording), `list` (read captured queue), `set_response` (update default return), `uninstall` (restore natives).',
|
|
26
|
+
].join(' '),
|
|
27
|
+
inputSchema: {
|
|
28
|
+
mode: z
|
|
29
|
+
.enum(['install', 'list', 'set_response', 'uninstall'])
|
|
30
|
+
.describe('install | list | set_response | uninstall.'),
|
|
31
|
+
confirmResponse: z
|
|
32
|
+
.boolean()
|
|
33
|
+
.optional()
|
|
34
|
+
.describe('install / set_response: value returned to `confirm()`. Default true.'),
|
|
35
|
+
promptResponse: z
|
|
36
|
+
.string()
|
|
37
|
+
.optional()
|
|
38
|
+
.describe('install / set_response: value returned to `prompt()`. Default "".'),
|
|
39
|
+
clearQueue: z
|
|
40
|
+
.boolean()
|
|
41
|
+
.optional()
|
|
42
|
+
.describe('list: also clear the queue after reading. Default false.'),
|
|
43
|
+
},
|
|
44
|
+
handler: async ({ mode, confirmResponse, promptResponse, clearQueue = false, }) => {
|
|
45
|
+
let ps;
|
|
46
|
+
try {
|
|
47
|
+
ps = await PageSession.get();
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
return errorResult(`Unable to attach to a WKWebView page: ${describe(e)}`);
|
|
51
|
+
}
|
|
52
|
+
let expression;
|
|
53
|
+
if (mode === 'install') {
|
|
54
|
+
const cr = confirmResponse === undefined ? 'true' : String(confirmResponse);
|
|
55
|
+
const pr = JSON.stringify(promptResponse ?? '');
|
|
56
|
+
expression = `
|
|
57
|
+
(() => {
|
|
58
|
+
const state = window.__wdm_dialog_state = window.__wdm_dialog_state || {
|
|
59
|
+
installed: false, alert: null, confirm: null, prompt: null,
|
|
60
|
+
response: { confirm: true, prompt: '' },
|
|
61
|
+
};
|
|
62
|
+
state.response.confirm = ${cr};
|
|
63
|
+
state.response.prompt = ${pr};
|
|
64
|
+
if (state.installed) return { ok: true, alreadyInstalled: true, response: state.response };
|
|
65
|
+
state.alert = window.alert; state.confirm = window.confirm; state.prompt = window.prompt;
|
|
66
|
+
window.__wdm_dialogs = window.__wdm_dialogs || [];
|
|
67
|
+
window.alert = function (msg) {
|
|
68
|
+
window.__wdm_dialogs.push({ type: 'alert', text: String(msg ?? ''), ts: Date.now() });
|
|
69
|
+
};
|
|
70
|
+
window.confirm = function (msg) {
|
|
71
|
+
window.__wdm_dialogs.push({ type: 'confirm', text: String(msg ?? ''), ts: Date.now() });
|
|
72
|
+
return state.response.confirm;
|
|
73
|
+
};
|
|
74
|
+
window.prompt = function (msg, defaultValue) {
|
|
75
|
+
window.__wdm_dialogs.push({ type: 'prompt', text: String(msg ?? ''), defaultValue: defaultValue == null ? null : String(defaultValue), ts: Date.now() });
|
|
76
|
+
return state.response.prompt;
|
|
77
|
+
};
|
|
78
|
+
state.installed = true;
|
|
79
|
+
return { ok: true, installed: true, response: state.response };
|
|
80
|
+
})()
|
|
81
|
+
`;
|
|
82
|
+
}
|
|
83
|
+
else if (mode === 'list') {
|
|
84
|
+
expression = `
|
|
85
|
+
(() => {
|
|
86
|
+
const queue = (window.__wdm_dialogs || []).slice();
|
|
87
|
+
if (${clearQueue}) window.__wdm_dialogs = [];
|
|
88
|
+
return { ok: true, queue, installed: (window.__wdm_dialog_state || {}).installed === true };
|
|
89
|
+
})()
|
|
90
|
+
`;
|
|
91
|
+
}
|
|
92
|
+
else if (mode === 'set_response') {
|
|
93
|
+
const cr = confirmResponse === undefined ? 'undefined' : String(confirmResponse);
|
|
94
|
+
const pr = promptResponse === undefined ? 'undefined' : JSON.stringify(promptResponse);
|
|
95
|
+
expression = `
|
|
96
|
+
(() => {
|
|
97
|
+
const state = window.__wdm_dialog_state;
|
|
98
|
+
if (!state || !state.installed) return { ok: false, reason: 'not_installed' };
|
|
99
|
+
if (${cr} !== undefined) state.response.confirm = ${cr};
|
|
100
|
+
if (${pr} !== undefined) state.response.prompt = ${pr};
|
|
101
|
+
return { ok: true, response: state.response };
|
|
102
|
+
})()
|
|
103
|
+
`;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
expression = `
|
|
107
|
+
(() => {
|
|
108
|
+
const state = window.__wdm_dialog_state;
|
|
109
|
+
if (!state || !state.installed) return { ok: true, alreadyUninstalled: true };
|
|
110
|
+
if (state.alert) window.alert = state.alert;
|
|
111
|
+
if (state.confirm) window.confirm = state.confirm;
|
|
112
|
+
if (state.prompt) window.prompt = state.prompt;
|
|
113
|
+
state.installed = false;
|
|
114
|
+
return { ok: true, uninstalled: true };
|
|
115
|
+
})()
|
|
116
|
+
`;
|
|
117
|
+
}
|
|
118
|
+
let result;
|
|
119
|
+
try {
|
|
120
|
+
result = await ps.session.send('Runtime.evaluate', { expression, returnByValue: true, awaitPromise: false }, 10_000);
|
|
121
|
+
}
|
|
122
|
+
catch (e) {
|
|
123
|
+
return errorResult(`Runtime.evaluate (handle_dialog ${mode}) failed: ${describe(e)}`);
|
|
124
|
+
}
|
|
125
|
+
const v = result?.result?.value;
|
|
126
|
+
if (!v || v.ok !== true) {
|
|
127
|
+
return errorResult(`# handle_dialog (${mode}): failed\n\n${JSON.stringify(v)}`);
|
|
128
|
+
}
|
|
129
|
+
if (mode === 'list') {
|
|
130
|
+
const queue = v.queue ?? [];
|
|
131
|
+
const lines = [
|
|
132
|
+
`# handle_dialog list — ${queue.length} captured · installed: ${v.installed}`,
|
|
133
|
+
'',
|
|
134
|
+
];
|
|
135
|
+
if (queue.length === 0) {
|
|
136
|
+
lines.push('(No dialogs intercepted. Either install first or page hasn\'t triggered one.)');
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
queue.forEach((d, i) => {
|
|
140
|
+
const ts = typeof d.ts === 'number' ? new Date(d.ts).toISOString() : '';
|
|
141
|
+
lines.push(`## [${i + 1}] ${d.type}${ts ? ` · ${ts}` : ''}`);
|
|
142
|
+
lines.push(`text: ${JSON.stringify(d.text)}`);
|
|
143
|
+
if (d.defaultValue != null)
|
|
144
|
+
lines.push(`defaultValue: ${JSON.stringify(d.defaultValue)}`);
|
|
145
|
+
lines.push('');
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
return textResult(lines.join('\n'));
|
|
149
|
+
}
|
|
150
|
+
if (mode === 'install') {
|
|
151
|
+
const resp = v.response;
|
|
152
|
+
return textResult([
|
|
153
|
+
`# handle_dialog install ✓${v.alreadyInstalled ? ' (already installed — response updated)' : ''}`,
|
|
154
|
+
'',
|
|
155
|
+
`confirm() default response: ${resp.confirm}`,
|
|
156
|
+
`prompt() default response: ${JSON.stringify(resp.prompt)}`,
|
|
157
|
+
'',
|
|
158
|
+
'⚠ Synthetic: alert/confirm/prompt return immediately, no native UI fires. Pages depending on real dialog blocking semantics may diverge.',
|
|
159
|
+
].join('\n'));
|
|
160
|
+
}
|
|
161
|
+
if (mode === 'set_response') {
|
|
162
|
+
const resp = v.response;
|
|
163
|
+
return textResult([
|
|
164
|
+
`# handle_dialog set_response ✓`,
|
|
165
|
+
'',
|
|
166
|
+
`confirm() response: ${resp.confirm}`,
|
|
167
|
+
`prompt() response: ${JSON.stringify(resp.prompt)}`,
|
|
168
|
+
].join('\n'));
|
|
169
|
+
}
|
|
170
|
+
return textResult([`# handle_dialog uninstall ✓${v.alreadyUninstalled ? ' (was already uninstalled)' : ''}`].join('\n'));
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
function textResult(text) {
|
|
174
|
+
return { content: [{ type: 'text', text }] };
|
|
175
|
+
}
|
|
176
|
+
function errorResult(text) {
|
|
177
|
+
return { content: [{ type: 'text', text }], isError: true };
|
|
178
|
+
}
|
|
179
|
+
function describe(e) {
|
|
180
|
+
return e instanceof Error ? e.message : String(e);
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=handle_dialog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handle_dialog.js","sourceRoot":"","sources":["../../../src/tools/handle_dialog.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `highlight_node` + `hide_highlight` (batch-13 Should) — visual feedback loop
|
|
3
|
+
* for AI agents picking DOM elements.
|
|
4
|
+
*
|
|
5
|
+
* Probe-confirmed (2026-05-17):
|
|
6
|
+
* - DOM.highlightNode ✅ (needs valid nodeId)
|
|
7
|
+
* - DOM.hideHighlight ✅
|
|
8
|
+
*
|
|
9
|
+
* The Apple WIP `DOM.highlightNode` schema differs from CDP — uses
|
|
10
|
+
* `highlightConfig: { contentColor: { r, g, b, a } }` etc. Default config draws
|
|
11
|
+
* a translucent blue overlay that's visible on screen + in screenshots.
|
|
12
|
+
*/
|
|
13
|
+
import { z } from 'zod';
|
|
14
|
+
export declare const highlightNodeTool: {
|
|
15
|
+
name: string;
|
|
16
|
+
description: string;
|
|
17
|
+
inputSchema: {
|
|
18
|
+
selector: z.ZodString;
|
|
19
|
+
showInfo: z.ZodOptional<z.ZodBoolean>;
|
|
20
|
+
};
|
|
21
|
+
handler: ({ selector, showInfo }: {
|
|
22
|
+
selector: string;
|
|
23
|
+
showInfo?: boolean;
|
|
24
|
+
}) => Promise<{
|
|
25
|
+
content: {
|
|
26
|
+
type: "text";
|
|
27
|
+
text: string;
|
|
28
|
+
}[];
|
|
29
|
+
}>;
|
|
30
|
+
};
|
|
31
|
+
export declare const hideHighlightTool: {
|
|
32
|
+
name: string;
|
|
33
|
+
description: string;
|
|
34
|
+
inputSchema: {};
|
|
35
|
+
handler: () => Promise<{
|
|
36
|
+
content: {
|
|
37
|
+
type: "text";
|
|
38
|
+
text: string;
|
|
39
|
+
}[];
|
|
40
|
+
}>;
|
|
41
|
+
};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `highlight_node` + `hide_highlight` (batch-13 Should) — visual feedback loop
|
|
3
|
+
* for AI agents picking DOM elements.
|
|
4
|
+
*
|
|
5
|
+
* Probe-confirmed (2026-05-17):
|
|
6
|
+
* - DOM.highlightNode ✅ (needs valid nodeId)
|
|
7
|
+
* - DOM.hideHighlight ✅
|
|
8
|
+
*
|
|
9
|
+
* The Apple WIP `DOM.highlightNode` schema differs from CDP — uses
|
|
10
|
+
* `highlightConfig: { contentColor: { r, g, b, a } }` etc. Default config draws
|
|
11
|
+
* a translucent blue overlay that's visible on screen + in screenshots.
|
|
12
|
+
*/
|
|
13
|
+
import { z } from 'zod';
|
|
14
|
+
import { PageSession } from '../page-session.js';
|
|
15
|
+
const DEFAULT_HIGHLIGHT = {
|
|
16
|
+
showInfo: true,
|
|
17
|
+
contentColor: { r: 111, g: 168, b: 220, a: 0.4 },
|
|
18
|
+
paddingColor: { r: 147, g: 196, b: 125, a: 0.4 },
|
|
19
|
+
borderColor: { r: 255, g: 229, b: 153, a: 0.4 },
|
|
20
|
+
marginColor: { r: 246, g: 178, b: 107, a: 0.4 },
|
|
21
|
+
};
|
|
22
|
+
export const highlightNodeTool = {
|
|
23
|
+
name: 'highlight_node',
|
|
24
|
+
description: [
|
|
25
|
+
'Visually highlight a DOM element on the inspected iOS WebView page via WIP `DOM.highlightNode` (✅ probe 2026-05-17).',
|
|
26
|
+
'Useful for "AI selected this element — let me confirm visually" loops. The highlight overlay is visible both on the device screen and in subsequent `take_screenshot` results.',
|
|
27
|
+
'Pair with `hide_highlight` to clear, or call again with a different selector to switch target. Highlight clears automatically when the page navigates.',
|
|
28
|
+
].join(' '),
|
|
29
|
+
inputSchema: {
|
|
30
|
+
selector: z.string().min(1).describe('CSS selector of the element to highlight (first match used).'),
|
|
31
|
+
showInfo: z
|
|
32
|
+
.boolean()
|
|
33
|
+
.optional()
|
|
34
|
+
.describe('Overlay show width × height + tag tooltip (default true).'),
|
|
35
|
+
},
|
|
36
|
+
handler: async ({ selector, showInfo = true }) => {
|
|
37
|
+
let ps;
|
|
38
|
+
try {
|
|
39
|
+
ps = await PageSession.get();
|
|
40
|
+
}
|
|
41
|
+
catch (e) {
|
|
42
|
+
return errorResult(`Unable to attach: ${describe(e)}`);
|
|
43
|
+
}
|
|
44
|
+
let rootId;
|
|
45
|
+
try {
|
|
46
|
+
const doc = await ps.session.send('DOM.getDocument', { depth: 0 }, 10_000);
|
|
47
|
+
rootId = doc.root.nodeId;
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
return errorResult(`DOM.getDocument failed: ${describe(e)}`);
|
|
51
|
+
}
|
|
52
|
+
let nodeId;
|
|
53
|
+
try {
|
|
54
|
+
const r = await ps.session.send('DOM.querySelector', { nodeId: rootId, selector }, 10_000);
|
|
55
|
+
if (r.nodeId === 0)
|
|
56
|
+
return textResult(`# highlight_node — 0 nodes match \`${selector}\``);
|
|
57
|
+
nodeId = r.nodeId;
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
return errorResult(`DOM.querySelector failed: ${describe(e)}`);
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
await ps.session.send('DOM.highlightNode', { highlightConfig: { ...DEFAULT_HIGHLIGHT, showInfo }, nodeId }, 5_000);
|
|
64
|
+
}
|
|
65
|
+
catch (e) {
|
|
66
|
+
return errorResult(`DOM.highlightNode failed: ${describe(e)}`);
|
|
67
|
+
}
|
|
68
|
+
return textResult(`# highlight_node ✓ \`${selector}\` (nodeId=${nodeId})\n\nOverlay visible on device + screenshots. Call \`hide_highlight\` to clear.`);
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
export const hideHighlightTool = {
|
|
72
|
+
name: 'hide_highlight',
|
|
73
|
+
description: 'Clear any active DOM highlight overlay via WIP `DOM.hideHighlight` (✅ probe 2026-05-17). Safe to call unconditionally — no-op if nothing was highlighted.',
|
|
74
|
+
inputSchema: {},
|
|
75
|
+
handler: async () => {
|
|
76
|
+
let ps;
|
|
77
|
+
try {
|
|
78
|
+
ps = await PageSession.get();
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
return errorResult(`Unable to attach: ${describe(e)}`);
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
await ps.session.send('DOM.hideHighlight', {}, 5_000);
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
return errorResult(`DOM.hideHighlight failed: ${describe(e)}`);
|
|
88
|
+
}
|
|
89
|
+
return textResult('# hide_highlight ✓');
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
function textResult(text) { return { content: [{ type: 'text', text }] }; }
|
|
93
|
+
function errorResult(text) { return { content: [{ type: 'text', text }], isError: true }; }
|
|
94
|
+
function describe(e) { return e instanceof Error ? e.message : String(e); }
|
|
95
|
+
//# sourceMappingURL=highlight_node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"highlight_node.js","sourceRoot":"","sources":["../../../src/tools/highlight_node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const hoverTool: {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
inputSchema: {
|
|
6
|
+
selector: z.ZodString;
|
|
7
|
+
nth: z.ZodOptional<z.ZodNumber>;
|
|
8
|
+
waitAfterMs: z.ZodOptional<z.ZodNumber>;
|
|
9
|
+
};
|
|
10
|
+
handler: ({ selector, nth, waitAfterMs, }: {
|
|
11
|
+
selector: string;
|
|
12
|
+
nth?: number;
|
|
13
|
+
waitAfterMs?: number;
|
|
14
|
+
}) => Promise<{
|
|
15
|
+
content: {
|
|
16
|
+
type: "text";
|
|
17
|
+
text: string;
|
|
18
|
+
}[];
|
|
19
|
+
}>;
|
|
20
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { PageSession } from '../page-session.js';
|
|
3
|
+
export const hoverTool = {
|
|
4
|
+
name: 'hover',
|
|
5
|
+
description: [
|
|
6
|
+
'Dispatch hover-style pointer events (`pointerover` + `mouseover` + `pointermove` + `mousemove` + `pointerenter` + `mouseenter`) on an element on the inspected iOS WebView page.',
|
|
7
|
+
'NOTE: WIP has no `Input.dispatchMouseEvent`. Synthetic Events via `Runtime.evaluate` — `isTrusted=false`, native :hover styles still apply because the events flow through normal DOM bubbling, but underlying iOS WebKit hit-testing is NOT involved (no real mouse cursor on touch devices).',
|
|
8
|
+
'Selector matches via `document.querySelectorAll(selector)[nth]`. Coordinates default to the element\'s center.',
|
|
9
|
+
].join(' '),
|
|
10
|
+
inputSchema: {
|
|
11
|
+
selector: z.string().min(1).describe('CSS selector for the element to hover.'),
|
|
12
|
+
nth: z.number().int().min(0).optional().describe('Pick the Nth match. Default 0.'),
|
|
13
|
+
waitAfterMs: z.number().int().min(0).max(10_000).optional().describe('Block after hover for this long (default 100).'),
|
|
14
|
+
},
|
|
15
|
+
handler: async ({ selector, nth = 0, waitAfterMs = 100, }) => {
|
|
16
|
+
let ps;
|
|
17
|
+
try {
|
|
18
|
+
ps = await PageSession.get();
|
|
19
|
+
}
|
|
20
|
+
catch (e) {
|
|
21
|
+
return errorResult(`Unable to attach to a WKWebView page: ${describe(e)}`);
|
|
22
|
+
}
|
|
23
|
+
const selJson = JSON.stringify(selector);
|
|
24
|
+
const expression = `
|
|
25
|
+
(() => {
|
|
26
|
+
const matches = document.querySelectorAll(${selJson});
|
|
27
|
+
if (matches.length === 0) return { ok: false, reason: 'no_match', selector: ${selJson} };
|
|
28
|
+
if (${nth} >= matches.length) return { ok: false, reason: 'nth_out_of_range', found: matches.length };
|
|
29
|
+
const el = matches[${nth}];
|
|
30
|
+
const rect = el.getBoundingClientRect();
|
|
31
|
+
const cx = rect.x + rect.width / 2;
|
|
32
|
+
const cy = rect.y + rect.height / 2;
|
|
33
|
+
const tag = el.tagName.toLowerCase();
|
|
34
|
+
const visible = rect.width > 0 && rect.height > 0;
|
|
35
|
+
const init = {
|
|
36
|
+
bubbles: true, cancelable: true,
|
|
37
|
+
clientX: cx, clientY: cy,
|
|
38
|
+
pointerType: 'mouse', pointerId: 1, isPrimary: true,
|
|
39
|
+
button: 0, buttons: 0,
|
|
40
|
+
};
|
|
41
|
+
try {
|
|
42
|
+
el.dispatchEvent(new PointerEvent('pointerover', init));
|
|
43
|
+
el.dispatchEvent(new MouseEvent('mouseover', init));
|
|
44
|
+
el.dispatchEvent(new PointerEvent('pointerenter', init));
|
|
45
|
+
el.dispatchEvent(new MouseEvent('mouseenter', init));
|
|
46
|
+
el.dispatchEvent(new PointerEvent('pointermove', init));
|
|
47
|
+
el.dispatchEvent(new MouseEvent('mousemove', init));
|
|
48
|
+
} catch (e) {
|
|
49
|
+
return { ok: false, reason: 'dispatch_threw', error: String(e), tag };
|
|
50
|
+
}
|
|
51
|
+
return { ok: true, tag, rect: { x: rect.x, y: rect.y, w: rect.width, h: rect.height }, visible, foundCount: matches.length };
|
|
52
|
+
})()
|
|
53
|
+
`;
|
|
54
|
+
let result;
|
|
55
|
+
try {
|
|
56
|
+
result = await ps.session.send('Runtime.evaluate', { expression, returnByValue: true, awaitPromise: false }, 10_000);
|
|
57
|
+
}
|
|
58
|
+
catch (e) {
|
|
59
|
+
return errorResult(`Runtime.evaluate (hover) failed: ${describe(e)}`);
|
|
60
|
+
}
|
|
61
|
+
if (waitAfterMs > 0) {
|
|
62
|
+
await new Promise(r => setTimeout(r, waitAfterMs));
|
|
63
|
+
}
|
|
64
|
+
const v = result?.result?.value;
|
|
65
|
+
if (!v || v.ok !== true) {
|
|
66
|
+
return errorResult(`# hover: failed\n\nselector: \`${selector}\`\n\n${JSON.stringify(v)}`);
|
|
67
|
+
}
|
|
68
|
+
return textResult([
|
|
69
|
+
`# hover ✓ ${selector}`,
|
|
70
|
+
'',
|
|
71
|
+
`tag: \`<${v.tag}>\``,
|
|
72
|
+
`match count: ${v.foundCount} (picked nth=${nth})`,
|
|
73
|
+
`visible: ${v.visible}`,
|
|
74
|
+
`rect: ${JSON.stringify(v.rect)}`,
|
|
75
|
+
'',
|
|
76
|
+
'⚠ Synthetic Pointer / Mouse events. iOS is a touch platform, so genuine `:hover` only persists until the next pointer cancel; long-lived hover UIs may rely on real mouse cursors and will not stick.',
|
|
77
|
+
].join('\n'));
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
function textResult(text) {
|
|
81
|
+
return { content: [{ type: 'text', text }] };
|
|
82
|
+
}
|
|
83
|
+
function errorResult(text) {
|
|
84
|
+
return { content: [{ type: 'text', text }], isError: true };
|
|
85
|
+
}
|
|
86
|
+
function describe(e) {
|
|
87
|
+
return e instanceof Error ? e.message : String(e);
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=hover.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hover.js","sourceRoot":"","sources":["../../../src/tools/hover.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* batch-14 intercept full state machine — fulfills the contract frozen in
|
|
3
|
+
* cross-review v2.0.1 R3 + spec §"intercept 完整状态机 (batch-14 契约)":
|
|
4
|
+
*
|
|
5
|
+
* - requestId 生命周期:`Network.requestIntercepted` → buffered in PageSession
|
|
6
|
+
* → consumed by `intercept_continue` / `intercept_respond` → drained.
|
|
7
|
+
* - 并发上限:16 pending. Overflow auto-continues the oldest.
|
|
8
|
+
* - 超时:30s without consumption → auto-continue (page does NOT hang).
|
|
9
|
+
* - 显式 abort 语义:`intercept_continue(reqId, abort=true)` blocks the
|
|
10
|
+
* request via `Network.interceptContinue` with `errorReason=BlockedByClient`.
|
|
11
|
+
*
|
|
12
|
+
* Apple WIP methods (probe-confirmed 2026-05-17):
|
|
13
|
+
* - Network.setInterceptionEnabled ✅ returns {}
|
|
14
|
+
* - Network.addInterception ✅ returns {} for any url pattern
|
|
15
|
+
* - Network.interceptContinue ✅ (probe shows method exists)
|
|
16
|
+
* - Network.interceptWithResponse ✅ (probe shows method exists)
|
|
17
|
+
*
|
|
18
|
+
* ⚠ **iOS WKWebView limitation (2026-05-17 实测)**: Even though the methods
|
|
19
|
+
* above all return `{}` success, `Network.requestIntercepted` event is NEVER
|
|
20
|
+
* fired for matching requests on iOS 26.5.0 + Apple system WKWebView. The
|
|
21
|
+
* underlying WebKit interception machinery is a no-op on iOS. iwdp-mcp's
|
|
22
|
+
* fulfill/continue flow validated against macOS Safari, where it works.
|
|
23
|
+
*
|
|
24
|
+
* Tools below are coded correctly per the WIP spec — they will Just Work the
|
|
25
|
+
* day Apple ships interception runtime in iOS WKWebView (or when this MCP is
|
|
26
|
+
* pointed at macOS Safari via a different transport). For now, calling
|
|
27
|
+
* `set_request_interception` does nothing observable; `list_intercepted_requests`
|
|
28
|
+
* always returns 0.
|
|
29
|
+
*/
|
|
30
|
+
import { z } from 'zod';
|
|
31
|
+
export declare const listInterceptedRequestsTool: {
|
|
32
|
+
name: string;
|
|
33
|
+
description: string;
|
|
34
|
+
inputSchema: {};
|
|
35
|
+
handler: () => Promise<{
|
|
36
|
+
content: {
|
|
37
|
+
type: "text";
|
|
38
|
+
text: string;
|
|
39
|
+
}[];
|
|
40
|
+
}>;
|
|
41
|
+
};
|
|
42
|
+
export declare const interceptContinueTool: {
|
|
43
|
+
name: string;
|
|
44
|
+
description: string;
|
|
45
|
+
inputSchema: {
|
|
46
|
+
requestId: z.ZodString;
|
|
47
|
+
abort: z.ZodOptional<z.ZodBoolean>;
|
|
48
|
+
};
|
|
49
|
+
handler: ({ requestId, abort }: {
|
|
50
|
+
requestId: string;
|
|
51
|
+
abort?: boolean;
|
|
52
|
+
}) => Promise<{
|
|
53
|
+
content: {
|
|
54
|
+
type: "text";
|
|
55
|
+
text: string;
|
|
56
|
+
}[];
|
|
57
|
+
}>;
|
|
58
|
+
};
|
|
59
|
+
export declare const interceptRespondTool: {
|
|
60
|
+
name: string;
|
|
61
|
+
description: string;
|
|
62
|
+
inputSchema: {
|
|
63
|
+
requestId: z.ZodString;
|
|
64
|
+
statusCode: z.ZodOptional<z.ZodNumber>;
|
|
65
|
+
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
66
|
+
body: z.ZodString;
|
|
67
|
+
base64: z.ZodOptional<z.ZodBoolean>;
|
|
68
|
+
};
|
|
69
|
+
handler: ({ requestId, statusCode, headers, body, base64 }: {
|
|
70
|
+
requestId: string;
|
|
71
|
+
statusCode?: number;
|
|
72
|
+
headers?: Record<string, string>;
|
|
73
|
+
body: string;
|
|
74
|
+
base64?: boolean;
|
|
75
|
+
}) => Promise<{
|
|
76
|
+
content: {
|
|
77
|
+
type: "text";
|
|
78
|
+
text: string;
|
|
79
|
+
}[];
|
|
80
|
+
}>;
|
|
81
|
+
};
|