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,113 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { fetchProxyDevices, fetchDevicePages } from '../wip-client/iwdp-http.js';
|
|
3
|
+
const DEFAULT_PROXY_BASE = 'http://127.0.0.1:9221';
|
|
4
|
+
export const listPagesTool = {
|
|
5
|
+
name: 'list_pages',
|
|
6
|
+
description: [
|
|
7
|
+
'List iOS WebView pages currently inspectable via ios_webkit_debug_proxy.',
|
|
8
|
+
'Requires the proxy to be running with an iPhone connected and Web Inspector enabled.',
|
|
9
|
+
'Returns one row per page (id, type, title, url) plus the raw WIP webSocketDebuggerUrl.',
|
|
10
|
+
'NOTE: webSocketDebuggerUrl path looks chromium-style but frame content is Apple WIP, not CDP.',
|
|
11
|
+
].join(' '),
|
|
12
|
+
inputSchema: {
|
|
13
|
+
proxyDeviceListUrl: z
|
|
14
|
+
.string()
|
|
15
|
+
.url()
|
|
16
|
+
.optional()
|
|
17
|
+
.describe(`Base URL of ios_webkit_debug_proxy's device-list endpoint (default ${DEFAULT_PROXY_BASE})`),
|
|
18
|
+
},
|
|
19
|
+
handler: async ({ proxyDeviceListUrl, }) => {
|
|
20
|
+
const base = proxyDeviceListUrl ?? DEFAULT_PROXY_BASE;
|
|
21
|
+
let devices;
|
|
22
|
+
try {
|
|
23
|
+
devices = await fetchProxyDevices(base);
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
return errorResult(`Failed to reach ios_webkit_debug_proxy at ${base}: ${describeError(e)}\n\nIs the proxy running? Try: pgrep -fl ios_webkit_debug_proxy`);
|
|
27
|
+
}
|
|
28
|
+
if (devices.length === 0) {
|
|
29
|
+
return textResult([
|
|
30
|
+
'# No iOS devices found',
|
|
31
|
+
'',
|
|
32
|
+
'Possible causes:',
|
|
33
|
+
'- iPhone not connected via USB',
|
|
34
|
+
'- iPhone has not trusted this Mac (check `idevice_id -l`)',
|
|
35
|
+
'- Settings > Safari > Advanced > Web Inspector is OFF',
|
|
36
|
+
'- ios_webkit_debug_proxy is not running',
|
|
37
|
+
].join('\n'));
|
|
38
|
+
}
|
|
39
|
+
const allPages = [];
|
|
40
|
+
const deviceErrors = [];
|
|
41
|
+
for (const dev of devices) {
|
|
42
|
+
try {
|
|
43
|
+
const pages = await fetchDevicePages(dev.url);
|
|
44
|
+
pages.forEach((p, idx) => {
|
|
45
|
+
allPages.push({
|
|
46
|
+
deviceId: dev.deviceId,
|
|
47
|
+
idx,
|
|
48
|
+
id: p.id,
|
|
49
|
+
title: p.title,
|
|
50
|
+
url: p.url,
|
|
51
|
+
type: p.type ?? 'page',
|
|
52
|
+
webSocketDebuggerUrl: p.webSocketDebuggerUrl,
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
catch (e) {
|
|
57
|
+
deviceErrors.push({ deviceId: dev.deviceId, error: describeError(e) });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (allPages.length === 0) {
|
|
61
|
+
const lines = [
|
|
62
|
+
`# No inspectable pages on ${devices.length} device(s)`,
|
|
63
|
+
'',
|
|
64
|
+
`Devices found: ${devices.map(d => d.deviceId).join(', ')}`,
|
|
65
|
+
'',
|
|
66
|
+
'Make sure the target app has a WebView/Safari page open AND the WKWebView has `isInspectable = true` (iOS 16.4+ requirement).',
|
|
67
|
+
];
|
|
68
|
+
if (deviceErrors.length > 0) {
|
|
69
|
+
lines.push('', '## Errors fetching device pages');
|
|
70
|
+
for (const e of deviceErrors) {
|
|
71
|
+
lines.push(`- ${e.deviceId}: ${e.error}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return textResult(lines.join('\n'));
|
|
75
|
+
}
|
|
76
|
+
const pageCount = allPages.filter(p => p.url && !p.title.startsWith('jsi_')).length;
|
|
77
|
+
const workerCount = allPages.length - pageCount;
|
|
78
|
+
const md = [
|
|
79
|
+
`# ${allPages.length} inspectable target(s) on ${devices.length} device(s) — ${pageCount} WKWebView page(s), ${workerCount} JSCore worker(s)`,
|
|
80
|
+
'',
|
|
81
|
+
'| # | kind | device | id | title | url |',
|
|
82
|
+
'|---|------|--------|----|-------|-----|',
|
|
83
|
+
];
|
|
84
|
+
allPages.forEach((p, i) => {
|
|
85
|
+
const kind = p.url && !p.title.startsWith('jsi_') ? 'page' : 'worker';
|
|
86
|
+
md.push(`| ${i} | ${kind} | \`${p.deviceId.slice(0, 12)}${p.deviceId.length > 12 ? '…' : ''}\` | ${p.id} | ${escapeCell(p.title)} | ${escapeCell(p.url)} |`);
|
|
87
|
+
});
|
|
88
|
+
md.push('', '## webSocketDebuggerUrl (raw WIP frames, not CDP)', '');
|
|
89
|
+
allPages.forEach((p, i) => md.push(`- [${i}] \`${p.webSocketDebuggerUrl}\``));
|
|
90
|
+
if (deviceErrors.length > 0) {
|
|
91
|
+
md.push('', '## Devices with errors', '');
|
|
92
|
+
for (const e of deviceErrors) {
|
|
93
|
+
md.push(`- ${e.deviceId}: ${e.error}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return textResult(md.join('\n'));
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
function textResult(text) {
|
|
100
|
+
return { content: [{ type: 'text', text }] };
|
|
101
|
+
}
|
|
102
|
+
function errorResult(text) {
|
|
103
|
+
return { content: [{ type: 'text', text }], isError: true };
|
|
104
|
+
}
|
|
105
|
+
function escapeCell(s) {
|
|
106
|
+
return s.replace(/\|/g, '\\|').replace(/\n/g, ' ');
|
|
107
|
+
}
|
|
108
|
+
function describeError(e) {
|
|
109
|
+
if (e instanceof Error)
|
|
110
|
+
return e.message;
|
|
111
|
+
return String(e);
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=list_pages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list_pages.js","sourceRoot":"","sources":["../../../src/tools/list_pages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAEjF,MAAM,kBAAkB,GAAG,uBAAuB,CAAC;AAEnD,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,IAAI,EAAE,YAAY;IAClB,WAAW,EAAE;QACX,0EAA0E;QAC1E,sFAAsF;QACtF,wFAAwF;QACxF,+FAA+F;KAChG,CAAC,IAAI,CAAC,GAAG,CAAC;IAEX,WAAW,EAAE;QACX,kBAAkB,EAAE,CAAC;aAClB,MAAM,EAAE;aACR,GAAG,EAAE;aACL,QAAQ,EAAE;aACV,QAAQ,CACP,sEAAsE,kBAAkB,GAAG,CAC5F;KACJ;IAED,OAAO,EAAE,KAAK,EAAE,EACd,kBAAkB,GAGnB,EAGE,EAAE;QACH,MAAM,IAAI,GAAG,kBAAkB,IAAI,kBAAkB,CAAC;QAEtD,IAAI,OAAO,CAAC;QACZ,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,iBAAiB,CAAC,IAAI,CAAC,CAAC;QAC1C,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,WAAW,CAAC,6CAA6C,IAAI,KAAK,aAAa,CAAC,CAAC,CAAC,iEAAiE,CAAC,CAAC;QAC9J,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,UAAU,CACf;gBACE,wBAAwB;gBACxB,EAAE;gBACF,kBAAkB;gBAClB,gCAAgC;gBAChC,2DAA2D;gBAC3D,uDAAuD;gBACvD,yCAAyC;aAC1C,CAAC,IAAI,CAAC,IAAI,CAAC,CACb,CAAC;QACJ,CAAC;QAED,MAAM,QAAQ,GAQT,EAAE,CAAC;QAER,MAAM,YAAY,GAA+C,EAAE,CAAC;QAEpE,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;YAC1B,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,MAAM,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBAC9C,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE;oBACvB,QAAQ,CAAC,IAAI,CAAC;wBACZ,QAAQ,EAAE,GAAG,CAAC,QAAQ;wBACtB,GAAG;wBACH,EAAE,EAAE,CAAC,CAAC,EAAE;wBACR,KAAK,EAAE,CAAC,CAAC,KAAK;wBACd,GAAG,EAAE,CAAC,CAAC,GAAG;wBACV,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,MAAM;wBACtB,oBAAoB,EAAE,CAAC,CAAC,oBAAoB;qBAC7C,CAAC,CAAC;gBACL,CAAC,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,YAAY,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACzE,CAAC;QACH,CAAC;QAED,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,KAAK,GAAG;gBACZ,6BAA6B,OAAO,CAAC,MAAM,YAAY;gBACvD,EAAE;gBACF,kBAAkB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;gBAC3D,EAAE;gBACF,+HAA+H;aAChI,CAAC;YACF,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC5B,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,iCAAiC,CAAC,CAAC;gBAClD,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;oBAC7B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;gBAC5C,CAAC;YACH,CAAC;YACD,OAAO,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACtC,CAAC;QAED,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;QACpF,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC;QAEhD,MAAM,EAAE,GAAa;YACnB,KAAK,QAAQ,CAAC,MAAM,6BAA6B,OAAO,CAAC,MAAM,gBAAgB,SAAS,uBAAuB,WAAW,mBAAmB;YAC7I,EAAE;YACF,0CAA0C;YAC1C,0CAA0C;SAC3C,CAAC;QACF,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YACxB,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;YACtE,EAAE,CAAC,IAAI,CACL,KAAK,CAAC,MAAM,IAAI,QAAQ,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CACpJ,CAAC;QACJ,CAAC,CAAC,CAAC;QACH,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,mDAAmD,EAAE,EAAE,CAAC,CAAC;QACrE,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,oBAAoB,IAAI,CAAC,CAAC,CAAC;QAE9E,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5B,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,wBAAwB,EAAE,EAAE,CAAC,CAAC;YAC1C,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;gBAC7B,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;YACzC,CAAC;QACH,CAAC;QAED,OAAO,UAAU,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACnC,CAAC;CACF,CAAC;AAEF,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;AACxD,CAAC;AAED,SAAS,WAAW,CAAC,IAAY;IAC/B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AACvE,CAAC;AAED,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AACrD,CAAC;AAED,SAAS,aAAa,CAAC,CAAU;IAC/B,IAAI,CAAC,YAAY,KAAK;QAAE,OAAO,CAAC,CAAC,OAAO,CAAC;IACzC,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const navigatePageTool: {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
inputSchema: {
|
|
6
|
+
url: z.ZodString;
|
|
7
|
+
waitMs: z.ZodOptional<z.ZodNumber>;
|
|
8
|
+
};
|
|
9
|
+
handler: ({ url, waitMs }: {
|
|
10
|
+
url: string;
|
|
11
|
+
waitMs?: number;
|
|
12
|
+
}) => Promise<{
|
|
13
|
+
content: {
|
|
14
|
+
type: "text";
|
|
15
|
+
text: string;
|
|
16
|
+
}[];
|
|
17
|
+
}>;
|
|
18
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { PageSession } from '../page-session.js';
|
|
3
|
+
export const navigatePageTool = {
|
|
4
|
+
name: 'navigate_page',
|
|
5
|
+
description: [
|
|
6
|
+
'Navigate the inspected iOS WebView page to a new URL.',
|
|
7
|
+
'NOTE: WIP `Page.navigate` is NOT implemented on iOS (-32601). This tool falls back to `Runtime.evaluate` setting `location.href` — semantics match a regular client-side navigation, but you cannot abort/intercept the way Page.navigate would allow.',
|
|
8
|
+
'After issuing, call `list_pages` to confirm the new page id (it may change if the navigation crosses an origin boundary).',
|
|
9
|
+
].join(' '),
|
|
10
|
+
inputSchema: {
|
|
11
|
+
url: z.string().url().describe('Target URL (must be absolute http(s) or about: URL).'),
|
|
12
|
+
waitMs: z
|
|
13
|
+
.number()
|
|
14
|
+
.int()
|
|
15
|
+
.min(0)
|
|
16
|
+
.max(60_000)
|
|
17
|
+
.optional()
|
|
18
|
+
.describe('Block this long after issuing navigation to let load events settle (default 1500).'),
|
|
19
|
+
},
|
|
20
|
+
handler: async ({ url, waitMs = 1500 }) => {
|
|
21
|
+
let ps;
|
|
22
|
+
try {
|
|
23
|
+
ps = await PageSession.get();
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
return errorResult(`Unable to attach to a WKWebView page: ${describe(e)}`);
|
|
27
|
+
}
|
|
28
|
+
const safe = JSON.stringify(url);
|
|
29
|
+
try {
|
|
30
|
+
await ps.session.send('Runtime.evaluate', {
|
|
31
|
+
expression: `location.href = ${safe}`,
|
|
32
|
+
returnByValue: true,
|
|
33
|
+
awaitPromise: false,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
return errorResult(`Runtime.evaluate (navigation) failed: ${describe(e)}`);
|
|
38
|
+
}
|
|
39
|
+
if (waitMs > 0) {
|
|
40
|
+
await new Promise(r => setTimeout(r, waitMs));
|
|
41
|
+
}
|
|
42
|
+
// Verify by reading location.href back
|
|
43
|
+
let after = '(unread)';
|
|
44
|
+
try {
|
|
45
|
+
const r = await ps.session.send('Runtime.evaluate', {
|
|
46
|
+
expression: 'location.href',
|
|
47
|
+
returnByValue: true,
|
|
48
|
+
});
|
|
49
|
+
after = String(r?.result?.value ?? '(unread)');
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// ignore — page may have detached
|
|
53
|
+
}
|
|
54
|
+
return textResult([
|
|
55
|
+
`# navigate_page`,
|
|
56
|
+
'',
|
|
57
|
+
`Requested: ${url}`,
|
|
58
|
+
`After ${waitMs}ms: ${after}`,
|
|
59
|
+
'',
|
|
60
|
+
'⚠ Page.navigate is not implemented on iOS WIP; this used `location.href = …` instead. If a redirect happened, the URL above is the final landing.',
|
|
61
|
+
].join('\n'));
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
function textResult(text) {
|
|
65
|
+
return { content: [{ type: 'text', text }] };
|
|
66
|
+
}
|
|
67
|
+
function errorResult(text) {
|
|
68
|
+
return { content: [{ type: 'text', text }], isError: true };
|
|
69
|
+
}
|
|
70
|
+
function describe(e) {
|
|
71
|
+
return e instanceof Error ? e.message : String(e);
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=navigate_page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"navigate_page.js","sourceRoot":"","sources":["../../../src/tools/navigate_page.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* batch-13 Should network runtime controls.
|
|
3
|
+
*
|
|
4
|
+
* - `set_resource_caching_disabled` — `Network.setResourceCachingDisabled`
|
|
5
|
+
* - `set_request_interception` — `Network.setInterceptionEnabled` (enable-only switch)
|
|
6
|
+
*
|
|
7
|
+
* `Network.{interceptContinue, interceptWithResponse}` are deferred to batch-14
|
|
8
|
+
* with a complete fulfill/continue state machine — exposing only the on/off
|
|
9
|
+
* switch here avoids the "能力幻觉" pitfall raised in cross-review v2.0.1 R3
|
|
10
|
+
* (LLM sees `intercept_request` and assumes it can fulfill).
|
|
11
|
+
*
|
|
12
|
+
* Probe-confirmed (2026-05-17):
|
|
13
|
+
* - Network.setInterceptionEnabled ✅
|
|
14
|
+
* - Network.setResourceCachingDisabled ✅
|
|
15
|
+
* - Network.addInterception ✅ (not exposed yet — needed for fulfill)
|
|
16
|
+
*/
|
|
17
|
+
import { z } from 'zod';
|
|
18
|
+
export declare const setResourceCachingDisabledTool: {
|
|
19
|
+
name: string;
|
|
20
|
+
description: string;
|
|
21
|
+
inputSchema: {
|
|
22
|
+
disabled: z.ZodBoolean;
|
|
23
|
+
};
|
|
24
|
+
handler: ({ disabled }: {
|
|
25
|
+
disabled: boolean;
|
|
26
|
+
}) => Promise<{
|
|
27
|
+
content: {
|
|
28
|
+
type: "text";
|
|
29
|
+
text: string;
|
|
30
|
+
}[];
|
|
31
|
+
}>;
|
|
32
|
+
};
|
|
33
|
+
export declare const setRequestInterceptionTool: {
|
|
34
|
+
name: string;
|
|
35
|
+
description: string;
|
|
36
|
+
inputSchema: {
|
|
37
|
+
enabled: z.ZodBoolean;
|
|
38
|
+
urlPattern: z.ZodOptional<z.ZodString>;
|
|
39
|
+
stage: z.ZodOptional<z.ZodEnum<["request", "response"]>>;
|
|
40
|
+
};
|
|
41
|
+
handler: ({ enabled, urlPattern, stage }: {
|
|
42
|
+
enabled: boolean;
|
|
43
|
+
urlPattern?: string;
|
|
44
|
+
stage?: "request" | "response";
|
|
45
|
+
}) => Promise<{
|
|
46
|
+
content: {
|
|
47
|
+
type: "text";
|
|
48
|
+
text: string;
|
|
49
|
+
}[];
|
|
50
|
+
}>;
|
|
51
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* batch-13 Should network runtime controls.
|
|
3
|
+
*
|
|
4
|
+
* - `set_resource_caching_disabled` — `Network.setResourceCachingDisabled`
|
|
5
|
+
* - `set_request_interception` — `Network.setInterceptionEnabled` (enable-only switch)
|
|
6
|
+
*
|
|
7
|
+
* `Network.{interceptContinue, interceptWithResponse}` are deferred to batch-14
|
|
8
|
+
* with a complete fulfill/continue state machine — exposing only the on/off
|
|
9
|
+
* switch here avoids the "能力幻觉" pitfall raised in cross-review v2.0.1 R3
|
|
10
|
+
* (LLM sees `intercept_request` and assumes it can fulfill).
|
|
11
|
+
*
|
|
12
|
+
* Probe-confirmed (2026-05-17):
|
|
13
|
+
* - Network.setInterceptionEnabled ✅
|
|
14
|
+
* - Network.setResourceCachingDisabled ✅
|
|
15
|
+
* - Network.addInterception ✅ (not exposed yet — needed for fulfill)
|
|
16
|
+
*/
|
|
17
|
+
import { z } from 'zod';
|
|
18
|
+
import { PageSession } from '../page-session.js';
|
|
19
|
+
export const setResourceCachingDisabledTool = {
|
|
20
|
+
name: 'set_resource_caching_disabled',
|
|
21
|
+
description: [
|
|
22
|
+
'Enable or disable resource caching for the current page session via WIP `Network.setResourceCachingDisabled` (✅ probe 2026-05-17).',
|
|
23
|
+
'Useful for hot-debugging — flip on to force every reload to refetch CSS/JS/images even when ETag would normally serve from cache.',
|
|
24
|
+
'State persists for the life of the WIP session; auto-cleared when the MCP server restarts. Reload the page after toggling for the new state to take effect.',
|
|
25
|
+
].join(' '),
|
|
26
|
+
inputSchema: {
|
|
27
|
+
disabled: z.boolean().describe('`true` disables cache (force network fetch); `false` re-enables normal caching.'),
|
|
28
|
+
},
|
|
29
|
+
handler: async ({ disabled }) => {
|
|
30
|
+
let ps;
|
|
31
|
+
try {
|
|
32
|
+
ps = await PageSession.get();
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
return errorResult(`Unable to attach: ${describe(e)}`);
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
await ps.session.send('Network.setResourceCachingDisabled', { disabled }, 5_000);
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
return errorResult(`Network.setResourceCachingDisabled failed: ${describe(e)}`);
|
|
42
|
+
}
|
|
43
|
+
return textResult(`# set_resource_caching_disabled ✓ \`${disabled}\`\n\n` +
|
|
44
|
+
(disabled
|
|
45
|
+
? 'Cache disabled. Subsequent navigations will force re-fetch all resources. Reload the page to see effect.'
|
|
46
|
+
: 'Cache re-enabled. Normal HTTP cache semantics apply.'));
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
export const setRequestInterceptionTool = {
|
|
50
|
+
name: 'set_request_interception',
|
|
51
|
+
description: [
|
|
52
|
+
'Enable or disable global request interception via WIP `Network.setInterceptionEnabled` + `Network.addInterception` (✅ probe 2026-05-17).',
|
|
53
|
+
'Apple WIP requires BOTH the master switch AND at least one URL pattern in `addInterception` to actually intercept requests. This tool wires them together: enable=true with `urlPattern` adds the pattern; enable=false clears.',
|
|
54
|
+
'Pair with `list_intercepted_requests` + `intercept_continue` + `intercept_respond` for fulfill/continue. PageSession buffers pending intercepted requests (cap 16, 30s auto-continue safety net) so the page never hangs.',
|
|
55
|
+
'⚠ **iOS WKWebView limitation (实测 2026-05-17)**: methods all return `{}` but `Network.requestIntercepted` event is NOT fired on iOS 26.5 — interception runtime is a no-op. Works on macOS Safari per iwdp-mcp. Tool kept for future iOS builds + macOS contexts.',
|
|
56
|
+
].join(' '),
|
|
57
|
+
inputSchema: {
|
|
58
|
+
enabled: z.boolean().describe('`true` enables interception with the given `urlPattern`; `false` disables and removes all patterns.'),
|
|
59
|
+
urlPattern: z
|
|
60
|
+
.string()
|
|
61
|
+
.optional()
|
|
62
|
+
.describe('URL substring/glob pattern to intercept when enabled (default `*` matches all). Apple WIP wildcard semantics — `*` matches any prefix/suffix.'),
|
|
63
|
+
stage: z
|
|
64
|
+
.enum(['request', 'response'])
|
|
65
|
+
.optional()
|
|
66
|
+
.describe('Intercept at request issue (`request`, default) or response receipt (`response`).'),
|
|
67
|
+
},
|
|
68
|
+
handler: async ({ enabled, urlPattern = '*', stage = 'request' }) => {
|
|
69
|
+
let ps;
|
|
70
|
+
try {
|
|
71
|
+
ps = await PageSession.get();
|
|
72
|
+
}
|
|
73
|
+
catch (e) {
|
|
74
|
+
return errorResult(`Unable to attach: ${describe(e)}`);
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
if (enabled) {
|
|
78
|
+
await ps.session.send('Network.setInterceptionEnabled', { enabled: true }, 5_000);
|
|
79
|
+
await ps.session.send('Network.addInterception', { url: urlPattern, stage }, 5_000);
|
|
80
|
+
return textResult('# set_request_interception ✓ `enabled`\n\n' +
|
|
81
|
+
`URL pattern: \`${urlPattern}\` (stage=${stage})\n\n` +
|
|
82
|
+
'Matching requests now suspend awaiting a decision. PageSession buffers pending (cap 16, 30s auto-continue safety net). ' +
|
|
83
|
+
'Use `list_intercepted_requests` to inspect, `intercept_continue` to release/abort, or `intercept_respond` to mock a response.');
|
|
84
|
+
}
|
|
85
|
+
// Disable path: remove the pattern, then disable master switch.
|
|
86
|
+
try {
|
|
87
|
+
await ps.session.send('Network.removeInterception', { url: urlPattern, stage }, 5_000);
|
|
88
|
+
}
|
|
89
|
+
catch { /* may have been added with diff pattern */ }
|
|
90
|
+
await ps.session.send('Network.setInterceptionEnabled', { enabled: false }, 5_000);
|
|
91
|
+
return textResult('# set_request_interception ✓ `disabled`\n\nInterception cleared. Pending intercepted requests release.');
|
|
92
|
+
}
|
|
93
|
+
catch (e) {
|
|
94
|
+
return errorResult(`set_request_interception failed: ${describe(e)}`);
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
function textResult(text) { return { content: [{ type: 'text', text }] }; }
|
|
99
|
+
function errorResult(text) { return { content: [{ type: 'text', text }], isError: true }; }
|
|
100
|
+
function describe(e) { return e instanceof Error ? e.message : String(e); }
|
|
101
|
+
//# sourceMappingURL=network_runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network_runtime.js","sourceRoot":"","sources":["../../../src/tools/network_runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const pressKeyTool: {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
inputSchema: {
|
|
6
|
+
key: z.ZodString;
|
|
7
|
+
code: z.ZodOptional<z.ZodString>;
|
|
8
|
+
selector: z.ZodOptional<z.ZodString>;
|
|
9
|
+
nth: z.ZodOptional<z.ZodNumber>;
|
|
10
|
+
shift: z.ZodOptional<z.ZodBoolean>;
|
|
11
|
+
ctrl: z.ZodOptional<z.ZodBoolean>;
|
|
12
|
+
alt: z.ZodOptional<z.ZodBoolean>;
|
|
13
|
+
meta: z.ZodOptional<z.ZodBoolean>;
|
|
14
|
+
waitAfterMs: z.ZodOptional<z.ZodNumber>;
|
|
15
|
+
};
|
|
16
|
+
handler: ({ key, code, selector, nth, shift, ctrl, alt, meta, waitAfterMs, }: {
|
|
17
|
+
key: string;
|
|
18
|
+
code?: string;
|
|
19
|
+
selector?: string;
|
|
20
|
+
nth?: number;
|
|
21
|
+
shift?: boolean;
|
|
22
|
+
ctrl?: boolean;
|
|
23
|
+
alt?: boolean;
|
|
24
|
+
meta?: boolean;
|
|
25
|
+
waitAfterMs?: number;
|
|
26
|
+
}) => Promise<{
|
|
27
|
+
content: {
|
|
28
|
+
type: "text";
|
|
29
|
+
text: string;
|
|
30
|
+
}[];
|
|
31
|
+
}>;
|
|
32
|
+
};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { PageSession } from '../page-session.js';
|
|
3
|
+
export const pressKeyTool = {
|
|
4
|
+
name: 'press_key',
|
|
5
|
+
description: [
|
|
6
|
+
'Dispatch a single key press (`keydown` + `keyup`, plus `keypress` for printable keys) on the inspected iOS WebView page.',
|
|
7
|
+
'NOTE: WIP has no `Input.dispatchKeyEvent`. This is a synthetic `KeyboardEvent` dispatched via `Runtime.evaluate` — listeners see `isTrusted=false`, and browser default actions (form submit on Enter, scrolling on PageDown) will NOT fire unless explicitly triggered by JS handlers.',
|
|
8
|
+
'Target is `document.activeElement` by default. Pass `selector` to focus an element first.',
|
|
9
|
+
].join(' '),
|
|
10
|
+
inputSchema: {
|
|
11
|
+
key: z.string().min(1).describe('KeyboardEvent.key value, e.g. "Enter", "ArrowDown", "a", "Escape".'),
|
|
12
|
+
code: z.string().optional().describe('KeyboardEvent.code, e.g. "Enter", "ArrowDown", "KeyA". Defaults to a guess from key.'),
|
|
13
|
+
selector: z.string().optional().describe('Optional CSS selector to focus before dispatching.'),
|
|
14
|
+
nth: z.number().int().min(0).optional().describe('Pick the Nth selector match. Default 0.'),
|
|
15
|
+
shift: z.boolean().optional().describe('Hold Shift modifier. Default false.'),
|
|
16
|
+
ctrl: z.boolean().optional().describe('Hold Control modifier. Default false.'),
|
|
17
|
+
alt: z.boolean().optional().describe('Hold Alt modifier. Default false.'),
|
|
18
|
+
meta: z.boolean().optional().describe('Hold Meta (⌘) modifier. Default false.'),
|
|
19
|
+
waitAfterMs: z.number().int().min(0).max(10_000).optional().describe('Block after press for this long (default 100).'),
|
|
20
|
+
},
|
|
21
|
+
handler: async ({ key, code, selector, nth = 0, shift = false, ctrl = false, alt = false, meta = false, waitAfterMs = 100, }) => {
|
|
22
|
+
let ps;
|
|
23
|
+
try {
|
|
24
|
+
ps = await PageSession.get();
|
|
25
|
+
}
|
|
26
|
+
catch (e) {
|
|
27
|
+
return errorResult(`Unable to attach to a WKWebView page: ${describe(e)}`);
|
|
28
|
+
}
|
|
29
|
+
const selJson = selector !== undefined ? JSON.stringify(selector) : 'null';
|
|
30
|
+
const keyJson = JSON.stringify(key);
|
|
31
|
+
const codeJson = JSON.stringify(code ?? guessCode(key));
|
|
32
|
+
const expression = `
|
|
33
|
+
(() => {
|
|
34
|
+
let el = null;
|
|
35
|
+
if (${selJson} !== null) {
|
|
36
|
+
const matches = document.querySelectorAll(${selJson});
|
|
37
|
+
if (matches.length === 0) return { ok: false, reason: 'no_match', selector: ${selJson} };
|
|
38
|
+
if (${nth} >= matches.length) return { ok: false, reason: 'nth_out_of_range', found: matches.length };
|
|
39
|
+
el = matches[${nth}];
|
|
40
|
+
try { el.focus(); } catch (e) {}
|
|
41
|
+
} else {
|
|
42
|
+
el = document.activeElement || document.body;
|
|
43
|
+
}
|
|
44
|
+
const init = {
|
|
45
|
+
bubbles: true,
|
|
46
|
+
cancelable: true,
|
|
47
|
+
key: ${keyJson},
|
|
48
|
+
code: ${codeJson},
|
|
49
|
+
shiftKey: ${shift},
|
|
50
|
+
ctrlKey: ${ctrl},
|
|
51
|
+
altKey: ${alt},
|
|
52
|
+
metaKey: ${meta},
|
|
53
|
+
};
|
|
54
|
+
const tag = el.tagName ? el.tagName.toLowerCase() : '#';
|
|
55
|
+
try {
|
|
56
|
+
el.dispatchEvent(new KeyboardEvent('keydown', init));
|
|
57
|
+
if (${keyJson}.length === 1) el.dispatchEvent(new KeyboardEvent('keypress', init));
|
|
58
|
+
el.dispatchEvent(new KeyboardEvent('keyup', init));
|
|
59
|
+
} catch (e) {
|
|
60
|
+
return { ok: false, reason: 'dispatch_threw', error: String(e), tag };
|
|
61
|
+
}
|
|
62
|
+
return { ok: true, tag, target: ${selJson} === null ? 'activeElement' : ${selJson} };
|
|
63
|
+
})()
|
|
64
|
+
`;
|
|
65
|
+
let result;
|
|
66
|
+
try {
|
|
67
|
+
result = await ps.session.send('Runtime.evaluate', { expression, returnByValue: true, awaitPromise: false }, 10_000);
|
|
68
|
+
}
|
|
69
|
+
catch (e) {
|
|
70
|
+
return errorResult(`Runtime.evaluate (press_key) failed: ${describe(e)}`);
|
|
71
|
+
}
|
|
72
|
+
if (waitAfterMs > 0) {
|
|
73
|
+
await new Promise(r => setTimeout(r, waitAfterMs));
|
|
74
|
+
}
|
|
75
|
+
const v = result?.result?.value;
|
|
76
|
+
if (!v || v.ok !== true) {
|
|
77
|
+
return errorResult(`# press_key: failed\n\n${JSON.stringify(v)}`);
|
|
78
|
+
}
|
|
79
|
+
const mods = [shift && 'Shift', ctrl && 'Ctrl', alt && 'Alt', meta && 'Meta'].filter(Boolean).join('+');
|
|
80
|
+
return textResult([
|
|
81
|
+
`# press_key ✓ ${mods ? mods + '+' : ''}${key}`,
|
|
82
|
+
'',
|
|
83
|
+
`target: ${v.target}`,
|
|
84
|
+
`tag: \`<${v.tag}>\``,
|
|
85
|
+
'',
|
|
86
|
+
'⚠ Synthetic KeyboardEvent — `isTrusted=false`, browser default actions (form submit, scroll) suppressed unless app JS handlers invoke them.',
|
|
87
|
+
].join('\n'));
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
function guessCode(key) {
|
|
91
|
+
if (key.length === 1) {
|
|
92
|
+
if (/[a-zA-Z]/.test(key))
|
|
93
|
+
return 'Key' + key.toUpperCase();
|
|
94
|
+
if (/[0-9]/.test(key))
|
|
95
|
+
return 'Digit' + key;
|
|
96
|
+
}
|
|
97
|
+
return key;
|
|
98
|
+
}
|
|
99
|
+
function textResult(text) {
|
|
100
|
+
return { content: [{ type: 'text', text }] };
|
|
101
|
+
}
|
|
102
|
+
function errorResult(text) {
|
|
103
|
+
return { content: [{ type: 'text', text }], isError: true };
|
|
104
|
+
}
|
|
105
|
+
function describe(e) {
|
|
106
|
+
return e instanceof Error ? e.message : String(e);
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=press_key.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"press_key.js","sourceRoot":"","sources":["../../../src/tools/press_key.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const queryDomNodeTool: {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
inputSchema: {
|
|
6
|
+
selector: z.ZodString;
|
|
7
|
+
mode: z.ZodOptional<z.ZodEnum<["first", "all"]>>;
|
|
8
|
+
maxMatches: z.ZodOptional<z.ZodNumber>;
|
|
9
|
+
perNodeMaxLen: z.ZodOptional<z.ZodNumber>;
|
|
10
|
+
};
|
|
11
|
+
handler: ({ selector, mode, maxMatches, perNodeMaxLen, }: {
|
|
12
|
+
selector: string;
|
|
13
|
+
mode?: "first" | "all";
|
|
14
|
+
maxMatches?: number;
|
|
15
|
+
perNodeMaxLen?: number;
|
|
16
|
+
}) => Promise<{
|
|
17
|
+
content: {
|
|
18
|
+
type: "text";
|
|
19
|
+
text: string;
|
|
20
|
+
}[];
|
|
21
|
+
}>;
|
|
22
|
+
};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { PageSession } from '../page-session.js';
|
|
3
|
+
export const queryDomNodeTool = {
|
|
4
|
+
name: 'query_dom_node',
|
|
5
|
+
description: [
|
|
6
|
+
'Query and serialize matching DOM nodes on the inspected iOS WebView page via WIP `DOM.querySelector(All)` + `DOM.getOuterHTML`.',
|
|
7
|
+
'Targeted alternative to `take_snapshot` (which dumps whole document). Useful when the AI needs only a specific element\'s HTML — saves context and lets you scope inspection.',
|
|
8
|
+
'Probe-confirmed chain (2026-05-16): `DOM.getDocument` → `DOM.querySelector` / `DOM.querySelectorAll` → `DOM.getOuterHTML`. `DOM.enable` is NOT required.',
|
|
9
|
+
].join(' '),
|
|
10
|
+
inputSchema: {
|
|
11
|
+
selector: z.string().min(1).describe('CSS selector to query.'),
|
|
12
|
+
mode: z
|
|
13
|
+
.enum(['first', 'all'])
|
|
14
|
+
.optional()
|
|
15
|
+
.describe('`first` (default) = querySelector single match; `all` = querySelectorAll multiple matches.'),
|
|
16
|
+
maxMatches: z
|
|
17
|
+
.number()
|
|
18
|
+
.int()
|
|
19
|
+
.min(1)
|
|
20
|
+
.max(200)
|
|
21
|
+
.optional()
|
|
22
|
+
.describe('When mode=all, cap returned matches (default 20).'),
|
|
23
|
+
perNodeMaxLen: z
|
|
24
|
+
.number()
|
|
25
|
+
.int()
|
|
26
|
+
.min(100)
|
|
27
|
+
.max(100_000)
|
|
28
|
+
.optional()
|
|
29
|
+
.describe('Per-node outerHTML truncation cap (default 4000).'),
|
|
30
|
+
},
|
|
31
|
+
handler: async ({ selector, mode = 'first', maxMatches = 20, perNodeMaxLen = 4_000, }) => {
|
|
32
|
+
let ps;
|
|
33
|
+
try {
|
|
34
|
+
ps = await PageSession.get();
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
return errorResult(`Unable to attach to a WKWebView page: ${describe(e)}`);
|
|
38
|
+
}
|
|
39
|
+
let rootId;
|
|
40
|
+
try {
|
|
41
|
+
const doc = await ps.session.send('DOM.getDocument', { depth: 0 }, 10_000);
|
|
42
|
+
rootId = doc.root.nodeId;
|
|
43
|
+
}
|
|
44
|
+
catch (e) {
|
|
45
|
+
return errorResult(`DOM.getDocument failed: ${describe(e)}`);
|
|
46
|
+
}
|
|
47
|
+
let nodeIds = [];
|
|
48
|
+
try {
|
|
49
|
+
if (mode === 'first') {
|
|
50
|
+
const r = await ps.session.send('DOM.querySelector', { nodeId: rootId, selector }, 10_000);
|
|
51
|
+
if (r.nodeId === 0) {
|
|
52
|
+
return textResult(`# query_dom_node — 0 matches for \`${selector}\``);
|
|
53
|
+
}
|
|
54
|
+
nodeIds = [r.nodeId];
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
const r = await ps.session.send('DOM.querySelectorAll', { nodeId: rootId, selector }, 10_000);
|
|
58
|
+
nodeIds = (r.nodeIds || []).slice(0, maxMatches);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch (e) {
|
|
62
|
+
return errorResult(`DOM.querySelector${mode === 'all' ? 'All' : ''} failed: ${describe(e)}`);
|
|
63
|
+
}
|
|
64
|
+
const lines = [
|
|
65
|
+
`# query_dom_node — \`${selector}\` (mode=${mode})`,
|
|
66
|
+
'',
|
|
67
|
+
`Matches: ${nodeIds.length}${mode === 'all' && nodeIds.length === maxMatches ? ` (capped at ${maxMatches})` : ''}`,
|
|
68
|
+
'',
|
|
69
|
+
];
|
|
70
|
+
for (let i = 0; i < nodeIds.length; i++) {
|
|
71
|
+
const nodeId = nodeIds[i];
|
|
72
|
+
let html;
|
|
73
|
+
try {
|
|
74
|
+
const r = await ps.session.send('DOM.getOuterHTML', { nodeId }, 10_000);
|
|
75
|
+
html = r.outerHTML ?? '';
|
|
76
|
+
}
|
|
77
|
+
catch (e) {
|
|
78
|
+
lines.push(`## [${i + 1}] nodeId=${nodeId} — getOuterHTML failed: ${describe(e)}`);
|
|
79
|
+
lines.push('');
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const truncated = html.length > perNodeMaxLen
|
|
83
|
+
? html.slice(0, perNodeMaxLen) + `\n… (truncated, full length ${html.length})`
|
|
84
|
+
: html;
|
|
85
|
+
lines.push(`## [${i + 1}] nodeId=${nodeId} · ${html.length} bytes`);
|
|
86
|
+
lines.push('```html');
|
|
87
|
+
lines.push(truncated);
|
|
88
|
+
lines.push('```');
|
|
89
|
+
lines.push('');
|
|
90
|
+
}
|
|
91
|
+
return textResult(lines.join('\n'));
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
function textResult(text) {
|
|
95
|
+
return { content: [{ type: 'text', text }] };
|
|
96
|
+
}
|
|
97
|
+
function errorResult(text) {
|
|
98
|
+
return { content: [{ type: 'text', text }], isError: true };
|
|
99
|
+
}
|
|
100
|
+
function describe(e) {
|
|
101
|
+
return e instanceof Error ? e.message : String(e);
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=query_dom_node.js.map
|