safari-devtools-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +268 -0
  3. package/build/src/SafariDriver.d.ts +56 -0
  4. package/build/src/SafariDriver.d.ts.map +1 -0
  5. package/build/src/SafariDriver.js +475 -0
  6. package/build/src/SafariDriver.js.map +1 -0
  7. package/build/src/bin/safari-devtools-mcp.d.ts +9 -0
  8. package/build/src/bin/safari-devtools-mcp.d.ts.map +1 -0
  9. package/build/src/bin/safari-devtools-mcp.js +42 -0
  10. package/build/src/bin/safari-devtools-mcp.js.map +1 -0
  11. package/build/src/formatters/ConsoleFormatter.d.ts +7 -0
  12. package/build/src/formatters/ConsoleFormatter.d.ts.map +1 -0
  13. package/build/src/formatters/ConsoleFormatter.js +47 -0
  14. package/build/src/formatters/ConsoleFormatter.js.map +1 -0
  15. package/build/src/formatters/NetworkFormatter.d.ts +7 -0
  16. package/build/src/formatters/NetworkFormatter.d.ts.map +1 -0
  17. package/build/src/formatters/NetworkFormatter.js +70 -0
  18. package/build/src/formatters/NetworkFormatter.js.map +1 -0
  19. package/build/src/formatters/SnapshotFormatter.d.ts +6 -0
  20. package/build/src/formatters/SnapshotFormatter.d.ts.map +1 -0
  21. package/build/src/formatters/SnapshotFormatter.js +34 -0
  22. package/build/src/formatters/SnapshotFormatter.js.map +1 -0
  23. package/build/src/index.d.ts +13 -0
  24. package/build/src/index.d.ts.map +1 -0
  25. package/build/src/index.js +81 -0
  26. package/build/src/index.js.map +1 -0
  27. package/build/src/injected/console.d.ts +9 -0
  28. package/build/src/injected/console.d.ts.map +1 -0
  29. package/build/src/injected/console.js +119 -0
  30. package/build/src/injected/console.js.map +1 -0
  31. package/build/src/injected/network.d.ts +14 -0
  32. package/build/src/injected/network.d.ts.map +1 -0
  33. package/build/src/injected/network.js +273 -0
  34. package/build/src/injected/network.js.map +1 -0
  35. package/build/src/injected/snapshot.d.ts +9 -0
  36. package/build/src/injected/snapshot.d.ts.map +1 -0
  37. package/build/src/injected/snapshot.js +217 -0
  38. package/build/src/injected/snapshot.js.map +1 -0
  39. package/build/src/tools/console.d.ts +17 -0
  40. package/build/src/tools/console.d.ts.map +1 -0
  41. package/build/src/tools/console.js +76 -0
  42. package/build/src/tools/console.js.map +1 -0
  43. package/build/src/tools/input.d.ts +59 -0
  44. package/build/src/tools/input.d.ts.map +1 -0
  45. package/build/src/tools/input.js +220 -0
  46. package/build/src/tools/input.js.map +1 -0
  47. package/build/src/tools/network.d.ts +17 -0
  48. package/build/src/tools/network.d.ts.map +1 -0
  49. package/build/src/tools/network.js +74 -0
  50. package/build/src/tools/network.js.map +1 -0
  51. package/build/src/tools/pages.d.ts +36 -0
  52. package/build/src/tools/pages.d.ts.map +1 -0
  53. package/build/src/tools/pages.js +236 -0
  54. package/build/src/tools/pages.js.map +1 -0
  55. package/build/src/tools/screenshot.d.ts +15 -0
  56. package/build/src/tools/screenshot.d.ts.map +1 -0
  57. package/build/src/tools/screenshot.js +70 -0
  58. package/build/src/tools/screenshot.js.map +1 -0
  59. package/build/src/tools/script.d.ts +12 -0
  60. package/build/src/tools/script.d.ts.map +1 -0
  61. package/build/src/tools/script.js +48 -0
  62. package/build/src/tools/script.js.map +1 -0
  63. package/build/src/tools/snapshot.d.ts +17 -0
  64. package/build/src/tools/snapshot.d.ts.map +1 -0
  65. package/build/src/tools/snapshot.js +75 -0
  66. package/build/src/tools/snapshot.js.map +1 -0
  67. package/build/src/tools/types.d.ts +17 -0
  68. package/build/src/tools/types.d.ts.map +1 -0
  69. package/build/src/tools/types.js +5 -0
  70. package/build/src/tools/types.js.map +1 -0
  71. package/build/src/types.d.ts +74 -0
  72. package/build/src/types.d.ts.map +1 -0
  73. package/build/src/types.js +11 -0
  74. package/build/src/types.js.map +1 -0
  75. package/package.json +66 -0
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Formats network log entries for MCP response output.
3
+ */
4
+ export function formatNetworkRequests(logs, total) {
5
+ if (logs.length === 0) {
6
+ return 'No network requests captured.';
7
+ }
8
+ const lines = [];
9
+ lines.push(`Network requests (${logs.length} of ${total} total):`);
10
+ lines.push('');
11
+ for (const log of logs) {
12
+ const status = log.status || '---';
13
+ const duration = log.duration ? `${Math.round(log.duration)}ms` : '?';
14
+ const size = formatBytes(log.transferSize || log.decodedBodySize);
15
+ const historical = log.historical ? ' [historical]' : '';
16
+ lines.push(` reqid=${log.reqid} ${log.method} ${status} ${truncateUrl(log.url)} ${duration} ${size}${historical}`);
17
+ }
18
+ return lines.join('\n');
19
+ }
20
+ export function formatNetworkRequest(log) {
21
+ const lines = [];
22
+ lines.push(`Network Request (reqid=${log.reqid})`);
23
+ lines.push(` URL: ${log.url}`);
24
+ lines.push(` Method: ${log.method}`);
25
+ lines.push(` Status: ${log.status} ${log.statusText}`);
26
+ lines.push(` Resource Type: ${log.resourceType}`);
27
+ lines.push(` Duration: ${Math.round(log.duration)}ms`);
28
+ lines.push(` MIME Type: ${log.mimeType || 'unknown'}`);
29
+ if (log.historical) {
30
+ lines.push(' Note: Historical entry (captured from Performance API before injection). Headers and status may be unavailable.');
31
+ }
32
+ lines.push(` Transfer Size: ${formatBytes(log.transferSize)}`);
33
+ lines.push(` Encoded Body Size: ${formatBytes(log.encodedBodySize)}`);
34
+ lines.push(` Decoded Body Size: ${formatBytes(log.decodedBodySize)}`);
35
+ if (log.requestHeaders && Object.keys(log.requestHeaders).length > 0) {
36
+ lines.push(' Request Headers:');
37
+ for (const [key, value] of Object.entries(log.requestHeaders)) {
38
+ lines.push(` ${key}: ${value}`);
39
+ }
40
+ }
41
+ if (log.responseHeaders && Object.keys(log.responseHeaders).length > 0) {
42
+ lines.push(' Response Headers:');
43
+ for (const [key, value] of Object.entries(log.responseHeaders)) {
44
+ lines.push(` ${key}: ${value}`);
45
+ }
46
+ }
47
+ if (log.requestBody) {
48
+ lines.push(' Request Body:');
49
+ lines.push(` ${log.requestBody}`);
50
+ }
51
+ if (log.error) {
52
+ lines.push(` Error: ${log.error}`);
53
+ }
54
+ return lines.join('\n');
55
+ }
56
+ function formatBytes(bytes) {
57
+ if (!bytes || bytes === 0)
58
+ return '0 B';
59
+ if (bytes < 1024)
60
+ return `${bytes} B`;
61
+ if (bytes < 1024 * 1024)
62
+ return `${(bytes / 1024).toFixed(1)} KB`;
63
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
64
+ }
65
+ function truncateUrl(url, maxLen = 80) {
66
+ if (url.length <= maxLen)
67
+ return url;
68
+ return url.substring(0, maxLen - 3) + '...';
69
+ }
70
+ //# sourceMappingURL=NetworkFormatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NetworkFormatter.js","sourceRoot":"","sources":["../../../src/formatters/NetworkFormatter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Formats DOM snapshot trees for MCP response output.
3
+ */
4
+ import type { SnapshotNode } from '../types.js';
5
+ export declare function formatSnapshot(node: SnapshotNode, verbose?: boolean): string;
6
+ //# sourceMappingURL=SnapshotFormatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SnapshotFormatter.d.ts","sourceRoot":"","sources":["../../../src/formatters/SnapshotFormatter.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAE9C,wBAAgB,cAAc,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAI5E"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Formats DOM snapshot trees for MCP response output.
3
+ */
4
+ export function formatSnapshot(node, verbose) {
5
+ const lines = [];
6
+ formatNode(node, 0, lines, verbose);
7
+ return lines.join('\n');
8
+ }
9
+ function formatNode(node, depth, lines, verbose) {
10
+ const indent = ' '.repeat(depth);
11
+ let line = `${indent}[${node.uid}] ${node.role}`;
12
+ if (node.name) {
13
+ line += ` "${node.name}"`;
14
+ }
15
+ if (node.value !== undefined) {
16
+ line += ` value="${node.value}"`;
17
+ }
18
+ if (verbose && node.tagName) {
19
+ line += ` <${node.tagName}>`;
20
+ }
21
+ if (verbose && node.attributes) {
22
+ const attrs = Object.entries(node.attributes)
23
+ .map(([k, v]) => `${k}="${v}"`)
24
+ .join(' ');
25
+ if (attrs) {
26
+ line += ` [${attrs}]`;
27
+ }
28
+ }
29
+ lines.push(line);
30
+ for (const child of node.children) {
31
+ formatNode(child, depth + 1, lines, verbose);
32
+ }
33
+ }
34
+ //# sourceMappingURL=SnapshotFormatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SnapshotFormatter.js","sourceRoot":"","sources":["../../../src/formatters/SnapshotFormatter.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,MAAM,UAAU,cAAc,CAAC,IAAkB,EAAE,OAAiB;IAClE,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,UAAU,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;IACpC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,UAAU,CACjB,IAAkB,EAClB,KAAa,EACb,KAAe,EACf,OAAiB;IAEjB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAClC,IAAI,IAAI,GAAG,GAAG,MAAM,IAAI,IAAI,CAAC,GAAG,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC;IAEjD,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,IAAI,IAAI,KAAK,IAAI,CAAC,IAAI,GAAG,CAAC;IAC5B,CAAC;IAED,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAC7B,IAAI,IAAI,WAAW,IAAI,CAAC,KAAK,GAAG,CAAC;IACnC,CAAC;IAED,IAAI,OAAO,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QAC5B,IAAI,IAAI,KAAK,IAAI,CAAC,OAAO,GAAG,CAAC;IAC/B,CAAC;IAED,IAAI,OAAO,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC;aAC1C,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC;aAC9B,IAAI,CAAC,GAAG,CAAC,CAAC;QACb,IAAI,KAAK,EAAE,CAAC;YACV,IAAI,IAAI,KAAK,KAAK,GAAG,CAAC;QACxB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEjB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClC,UAAU,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;IAC/C,CAAC;AACH,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Safari DevTools MCP Server.
3
+ *
4
+ * Provides browser debugging and automation tools for AI coding agents
5
+ * via the Model Context Protocol, targeting Safari on macOS.
6
+ */
7
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
8
+ import { SafariDriver } from './SafariDriver.js';
9
+ export declare function createSafariMcpServer(): {
10
+ server: McpServer;
11
+ driver: SafariDriver;
12
+ };
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAC,SAAS,EAAC,MAAM,yCAAyC,CAAC;AAElE,OAAO,EAAC,YAAY,EAAC,MAAM,mBAAmB,CAAC;AA4D/C,wBAAgB,qBAAqB,IAAI;IACvC,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,YAAY,CAAC;CACtB,CA2NA"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Safari DevTools MCP Server.
3
+ *
4
+ * Provides browser debugging and automation tools for AI coding agents
5
+ * via the Model Context Protocol, targeting Safari on macOS.
6
+ */
7
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
8
+ import { SafariDriver } from './SafariDriver.js';
9
+ // Tool handlers
10
+ import { listConsoleMessages, listConsoleMessagesSchema, getConsoleMessage, getConsoleMessageSchema, } from './tools/console.js';
11
+ import { listNetworkRequests, listNetworkRequestsSchema, getNetworkRequest, getNetworkRequestSchema, } from './tools/network.js';
12
+ import { evaluateScript, evaluateScriptSchema } from './tools/script.js';
13
+ import { takeScreenshot, takeScreenshotSchema } from './tools/screenshot.js';
14
+ import { takeSnapshot, takeSnapshotSchema, waitFor, waitForSchema, } from './tools/snapshot.js';
15
+ import { listPages, listPagesSchema, selectPage, selectPageSchema, closePage, closePageSchema, newPage, newPageSchema, navigatePage, navigatePageSchema, resizePage, resizePageSchema, handleDialog, handleDialogSchema, } from './tools/pages.js';
16
+ import { click, clickSchema, clickAt, clickAtSchema, hover, hoverSchema, fill, fillSchema, fillForm, fillFormSchema, typeText, typeTextSchema, drag, dragSchema, pressKey, pressKeySchema, uploadFile, uploadFileSchema, } from './tools/input.js';
17
+ export function createSafariMcpServer() {
18
+ const server = new McpServer({
19
+ name: 'safari-devtools-mcp',
20
+ version: '0.1.0',
21
+ }, {
22
+ capabilities: {
23
+ tools: {},
24
+ },
25
+ });
26
+ const driver = new SafariDriver();
27
+ // Helper to register a tool
28
+ function registerTool(name, description, schema, handler) {
29
+ server.tool(name, description, schema, async (params) => {
30
+ try {
31
+ return await handler(params, driver);
32
+ }
33
+ catch (error) {
34
+ return {
35
+ content: [
36
+ {
37
+ type: 'text',
38
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`,
39
+ },
40
+ ],
41
+ isError: true,
42
+ };
43
+ }
44
+ });
45
+ }
46
+ // =====================
47
+ // DEBUGGING TOOLS (highest priority)
48
+ // =====================
49
+ registerTool('list_console_messages', 'List all console messages for the currently selected page since the last navigation.', listConsoleMessagesSchema, listConsoleMessages);
50
+ registerTool('get_console_message', 'Gets a console message by its ID. You can get all messages by calling list_console_messages.', getConsoleMessageSchema, getConsoleMessage);
51
+ registerTool('list_network_requests', 'List all network requests for the currently selected page since the last navigation. Includes historical requests made before monitoring started (with limited detail).', listNetworkRequestsSchema, listNetworkRequests);
52
+ registerTool('get_network_request', 'Gets a network request by its reqid from the listed requests.', getNetworkRequestSchema, getNetworkRequest);
53
+ registerTool('evaluate_script', 'Evaluate a JavaScript function inside the currently selected page. Returns the response as JSON, so returned values have to be JSON-serializable.', evaluateScriptSchema, evaluateScript);
54
+ registerTool('take_screenshot', 'Take a screenshot of the page or element.', takeScreenshotSchema, takeScreenshot);
55
+ registerTool('take_snapshot', 'Take a text snapshot of the currently selected page based on the DOM/a11y tree. The snapshot lists page elements along with a unique identifier (uid). Always use the latest snapshot. Prefer taking a snapshot over taking a screenshot.', takeSnapshotSchema, takeSnapshot);
56
+ // =====================
57
+ // NAVIGATION TOOLS
58
+ // =====================
59
+ registerTool('list_pages', 'Get a list of pages (tabs) open in Safari.', listPagesSchema, listPages);
60
+ registerTool('select_page', 'Select a page as context for future tool calls.', selectPageSchema, selectPage);
61
+ registerTool('close_page', 'Closes a page by its ID. The last open page cannot be closed.', closePageSchema, closePage);
62
+ registerTool('new_page', 'Open a new tab and load a URL.', newPageSchema, newPage);
63
+ registerTool('navigate_page', 'Go to a URL, or back, forward, or reload.', navigatePageSchema, navigatePage);
64
+ registerTool('wait_for', 'Wait for the specified text to appear on the selected page.', waitForSchema, waitFor);
65
+ registerTool('resize_page', "Resizes the selected page's window so that the page has specified dimensions.", resizePageSchema, resizePage);
66
+ registerTool('handle_dialog', 'If a browser dialog was opened, use this command to handle it.', handleDialogSchema, handleDialog);
67
+ // =====================
68
+ // INPUT TOOLS
69
+ // =====================
70
+ registerTool('click', 'Clicks on the provided element.', clickSchema, click);
71
+ registerTool('click_at', 'Clicks at the provided coordinates.', clickAtSchema, clickAt);
72
+ registerTool('hover', 'Hover over the provided element.', hoverSchema, hover);
73
+ registerTool('fill', 'Type text into an input, text area or select an option from a <select> element.', fillSchema, fill);
74
+ registerTool('fill_form', 'Fill out multiple form elements at once.', fillFormSchema, fillForm);
75
+ registerTool('type_text', 'Type text using keyboard into a previously focused input.', typeTextSchema, typeText);
76
+ registerTool('drag', 'Drag an element onto another element.', dragSchema, drag);
77
+ registerTool('press_key', 'Press a key or key combination. Use this when other input methods like fill() cannot be used.', pressKeySchema, pressKey);
78
+ registerTool('upload_file', 'Upload a file through a provided file input element.', uploadFileSchema, uploadFile);
79
+ return { server, driver };
80
+ }
81
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Injectable script for capturing browser console messages.
3
+ *
4
+ * This script wraps all console methods to capture log messages with
5
+ * structured metadata (level, timestamp, stack traces for errors).
6
+ * Messages are stored in window.__safariDevToolsConsoleLogs.
7
+ */
8
+ export declare const CONSOLE_CAPTURE_SCRIPT = "\n(function() {\n if (window.__safariDevToolsConsoleInitialized) return;\n window.__safariDevToolsConsoleInitialized = true;\n window.__safariDevToolsConsoleLogs = [];\n window.__safariDevToolsConsoleMsgId = 0;\n\n const originalConsole = {};\n const methods = [\n 'log', 'debug', 'info', 'error', 'warn', 'trace',\n 'assert', 'dir', 'table', 'clear', 'count', 'timeEnd'\n ];\n\n methods.forEach(function(method) {\n originalConsole[method] = console[method];\n\n console[method] = function() {\n var args = Array.from(arguments);\n var entry = {\n msgid: window.__safariDevToolsConsoleMsgId++,\n level: method,\n message: args.map(function(arg) {\n try {\n if (typeof arg === 'object') {\n var s = JSON.stringify(arg, null, 2);\n return s && s.length > 1000 ? s.substring(0, 1000) + '... [truncated]' : s;\n }\n var str = String(arg);\n return str.length > 1000 ? str.substring(0, 1000) + '... [truncated]' : str;\n } catch(e) {\n return String(arg).substring(0, 1000);\n }\n }).join(' '),\n timestamp: Date.now(),\n args: args.map(function(arg) {\n try {\n var s = typeof arg === 'object' ? JSON.stringify(arg) : String(arg);\n return s && s.length > 2000 ? s.substring(0, 2000) + '... [truncated]' : s;\n } catch(e) {\n return String(arg).substring(0, 2000);\n }\n })\n };\n\n // Capture stack trace for errors and traces\n if (method === 'error' || method === 'warn' || method === 'trace' || method === 'assert') {\n try {\n var err = new Error();\n // Remove the first two lines (Error + this wrapper)\n var stack = err.stack || '';\n var lines = stack.split('\\n');\n entry.stackTrace = lines.slice(2).join('\\n');\n } catch(e) {}\n }\n\n // Special handling for assert - only log if assertion fails\n if (method === 'assert') {\n if (args[0]) return originalConsole[method].apply(console, args);\n entry.message = 'Assertion failed: ' + args.slice(1).map(function(a) {\n try {\n var s = typeof a === 'object' ? JSON.stringify(a) : String(a);\n return s && s.length > 1000 ? s.substring(0, 1000) + '... [truncated]' : s;\n } catch(e) { return String(a).substring(0, 1000); }\n }).join(' ');\n }\n\n // Special handling for clear\n if (method === 'clear') {\n entry.message = 'Console was cleared';\n }\n\n window.__safariDevToolsConsoleLogs.push(entry);\n\n // Call original method\n if (originalConsole[method]) {\n originalConsole[method].apply(console, args);\n }\n };\n });\n\n // Capture uncaught errors\n window.addEventListener('error', function(event) {\n window.__safariDevToolsConsoleLogs.push({\n msgid: window.__safariDevToolsConsoleMsgId++,\n level: 'error',\n message: event.message || String(event),\n timestamp: Date.now(),\n source: event.filename ? event.filename + ':' + event.lineno + ':' + event.colno : undefined,\n stackTrace: event.error ? event.error.stack : undefined\n });\n });\n\n // Capture unhandled promise rejections\n window.addEventListener('unhandledrejection', function(event) {\n var message = 'Unhandled Promise Rejection: ';\n try {\n var r = event.reason instanceof Error ? event.reason.message : JSON.stringify(event.reason);\n message += r && r.length > 1000 ? r.substring(0, 1000) + '... [truncated]' : r;\n } catch(e) {\n message += String(event.reason);\n }\n window.__safariDevToolsConsoleLogs.push({\n msgid: window.__safariDevToolsConsoleMsgId++,\n level: 'error',\n message: message,\n timestamp: Date.now(),\n stackTrace: event.reason instanceof Error ? event.reason.stack : undefined\n });\n });\n})();\n";
9
+ //# sourceMappingURL=console.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../../../src/injected/console.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,eAAO,MAAM,sBAAsB,s7HA8GlC,CAAC"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Injectable script for capturing browser console messages.
3
+ *
4
+ * This script wraps all console methods to capture log messages with
5
+ * structured metadata (level, timestamp, stack traces for errors).
6
+ * Messages are stored in window.__safariDevToolsConsoleLogs.
7
+ */
8
+ export const CONSOLE_CAPTURE_SCRIPT = `
9
+ (function() {
10
+ if (window.__safariDevToolsConsoleInitialized) return;
11
+ window.__safariDevToolsConsoleInitialized = true;
12
+ window.__safariDevToolsConsoleLogs = [];
13
+ window.__safariDevToolsConsoleMsgId = 0;
14
+
15
+ const originalConsole = {};
16
+ const methods = [
17
+ 'log', 'debug', 'info', 'error', 'warn', 'trace',
18
+ 'assert', 'dir', 'table', 'clear', 'count', 'timeEnd'
19
+ ];
20
+
21
+ methods.forEach(function(method) {
22
+ originalConsole[method] = console[method];
23
+
24
+ console[method] = function() {
25
+ var args = Array.from(arguments);
26
+ var entry = {
27
+ msgid: window.__safariDevToolsConsoleMsgId++,
28
+ level: method,
29
+ message: args.map(function(arg) {
30
+ try {
31
+ if (typeof arg === 'object') {
32
+ var s = JSON.stringify(arg, null, 2);
33
+ return s && s.length > 1000 ? s.substring(0, 1000) + '... [truncated]' : s;
34
+ }
35
+ var str = String(arg);
36
+ return str.length > 1000 ? str.substring(0, 1000) + '... [truncated]' : str;
37
+ } catch(e) {
38
+ return String(arg).substring(0, 1000);
39
+ }
40
+ }).join(' '),
41
+ timestamp: Date.now(),
42
+ args: args.map(function(arg) {
43
+ try {
44
+ var s = typeof arg === 'object' ? JSON.stringify(arg) : String(arg);
45
+ return s && s.length > 2000 ? s.substring(0, 2000) + '... [truncated]' : s;
46
+ } catch(e) {
47
+ return String(arg).substring(0, 2000);
48
+ }
49
+ })
50
+ };
51
+
52
+ // Capture stack trace for errors and traces
53
+ if (method === 'error' || method === 'warn' || method === 'trace' || method === 'assert') {
54
+ try {
55
+ var err = new Error();
56
+ // Remove the first two lines (Error + this wrapper)
57
+ var stack = err.stack || '';
58
+ var lines = stack.split('\\n');
59
+ entry.stackTrace = lines.slice(2).join('\\n');
60
+ } catch(e) {}
61
+ }
62
+
63
+ // Special handling for assert - only log if assertion fails
64
+ if (method === 'assert') {
65
+ if (args[0]) return originalConsole[method].apply(console, args);
66
+ entry.message = 'Assertion failed: ' + args.slice(1).map(function(a) {
67
+ try {
68
+ var s = typeof a === 'object' ? JSON.stringify(a) : String(a);
69
+ return s && s.length > 1000 ? s.substring(0, 1000) + '... [truncated]' : s;
70
+ } catch(e) { return String(a).substring(0, 1000); }
71
+ }).join(' ');
72
+ }
73
+
74
+ // Special handling for clear
75
+ if (method === 'clear') {
76
+ entry.message = 'Console was cleared';
77
+ }
78
+
79
+ window.__safariDevToolsConsoleLogs.push(entry);
80
+
81
+ // Call original method
82
+ if (originalConsole[method]) {
83
+ originalConsole[method].apply(console, args);
84
+ }
85
+ };
86
+ });
87
+
88
+ // Capture uncaught errors
89
+ window.addEventListener('error', function(event) {
90
+ window.__safariDevToolsConsoleLogs.push({
91
+ msgid: window.__safariDevToolsConsoleMsgId++,
92
+ level: 'error',
93
+ message: event.message || String(event),
94
+ timestamp: Date.now(),
95
+ source: event.filename ? event.filename + ':' + event.lineno + ':' + event.colno : undefined,
96
+ stackTrace: event.error ? event.error.stack : undefined
97
+ });
98
+ });
99
+
100
+ // Capture unhandled promise rejections
101
+ window.addEventListener('unhandledrejection', function(event) {
102
+ var message = 'Unhandled Promise Rejection: ';
103
+ try {
104
+ var r = event.reason instanceof Error ? event.reason.message : JSON.stringify(event.reason);
105
+ message += r && r.length > 1000 ? r.substring(0, 1000) + '... [truncated]' : r;
106
+ } catch(e) {
107
+ message += String(event.reason);
108
+ }
109
+ window.__safariDevToolsConsoleLogs.push({
110
+ msgid: window.__safariDevToolsConsoleMsgId++,
111
+ level: 'error',
112
+ message: message,
113
+ timestamp: Date.now(),
114
+ stackTrace: event.reason instanceof Error ? event.reason.stack : undefined
115
+ });
116
+ });
117
+ })();
118
+ `;
119
+ //# sourceMappingURL=console.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console.js","sourceRoot":"","sources":["../../../src/injected/console.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,CAAC,MAAM,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8GrC,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Injectable script for capturing network requests.
3
+ *
4
+ * Hybrid approach:
5
+ * 1. Performance API backfill — captures historical requests made before injection
6
+ * (timing, size, URL available; no headers/status)
7
+ * 2. Fetch interception — captures ongoing fetch() requests with full details
8
+ * 3. XMLHttpRequest interception — captures ongoing XHR requests with full details
9
+ * 4. PerformanceObserver — catches resource loads the interceptors miss
10
+ *
11
+ * Historical entries are flagged so the AI knows they have limited detail.
12
+ */
13
+ export declare const NETWORK_CAPTURE_SCRIPT = "\n(function() {\n if (window.__safariDevToolsNetworkInitialized) return;\n window.__safariDevToolsNetworkInitialized = true;\n window.__safariDevToolsNetworkLogs = [];\n window.__safariDevToolsNetworkReqId = 0;\n\n // Helper to guess resource type from initiatorType or URL\n function guessResourceType(initiatorType, url) {\n if (initiatorType === 'xmlhttprequest') return 'xhr';\n if (initiatorType === 'fetch') return 'fetch';\n if (initiatorType === 'script') return 'script';\n if (initiatorType === 'link' || initiatorType === 'css') return 'stylesheet';\n if (initiatorType === 'img') return 'image';\n if (initiatorType === 'video' || initiatorType === 'audio') return 'media';\n\n // Guess from URL extension\n if (url) {\n var ext = url.split('?')[0].split('#')[0].split('.').pop().toLowerCase();\n var typeMap = {\n js: 'script', mjs: 'script',\n css: 'stylesheet',\n png: 'image', jpg: 'image', jpeg: 'image', gif: 'image', svg: 'image', webp: 'image', ico: 'image',\n woff: 'font', woff2: 'font', ttf: 'font', otf: 'font', eot: 'font',\n mp4: 'media', webm: 'media', mp3: 'media', ogg: 'media',\n html: 'document', htm: 'document'\n };\n if (typeMap[ext]) return typeMap[ext];\n }\n return 'other';\n }\n\n // Step 1: Backfill historical requests from Performance API\n try {\n var entries = performance.getEntriesByType('resource');\n entries.forEach(function(entry) {\n window.__safariDevToolsNetworkLogs.push({\n reqid: window.__safariDevToolsNetworkReqId++,\n url: entry.name,\n method: 'GET',\n status: 0,\n statusText: '',\n resourceType: guessResourceType(entry.initiatorType, entry.name),\n startTime: entry.startTime,\n duration: entry.duration,\n transferSize: entry.transferSize || 0,\n encodedBodySize: entry.encodedBodySize || 0,\n decodedBodySize: entry.decodedBodySize || 0,\n requestHeaders: {},\n responseHeaders: {},\n historical: true,\n mimeType: ''\n });\n });\n } catch(e) {}\n\n // Step 2: PerformanceObserver for new resource loads\n try {\n var observer = new PerformanceObserver(function(list) {\n list.getEntries().forEach(function(entry) {\n // Skip if we already have this URL from fetch/XHR interception\n var isDuplicate = window.__safariDevToolsNetworkLogs.some(function(log) {\n return !log.historical && log.url === entry.name && Math.abs(log.startTime - entry.startTime) < 100;\n });\n if (isDuplicate) return;\n\n // Only add if not already captured by interceptors\n var hasIntercepted = window.__safariDevToolsNetworkLogs.some(function(log) {\n return !log.historical && log.url === entry.name;\n });\n if (!hasIntercepted) {\n window.__safariDevToolsNetworkLogs.push({\n reqid: window.__safariDevToolsNetworkReqId++,\n url: entry.name,\n method: 'GET',\n status: 0,\n statusText: '',\n resourceType: guessResourceType(entry.initiatorType, entry.name),\n startTime: entry.startTime,\n duration: entry.duration,\n transferSize: entry.transferSize || 0,\n encodedBodySize: entry.encodedBodySize || 0,\n decodedBodySize: entry.decodedBodySize || 0,\n requestHeaders: {},\n responseHeaders: {},\n historical: false,\n mimeType: ''\n });\n }\n });\n });\n observer.observe({ entryTypes: ['resource'] });\n } catch(e) {}\n\n // Step 3: Intercept fetch()\n var originalFetch = window.fetch;\n window.fetch = function() {\n var args = arguments;\n var request;\n try {\n request = new Request(args[0], args[1]);\n } catch(e) {\n return originalFetch.apply(this, args);\n }\n\n var entry = {\n reqid: window.__safariDevToolsNetworkReqId++,\n url: request.url,\n method: request.method,\n status: 0,\n statusText: '',\n resourceType: 'fetch',\n startTime: performance.now(),\n duration: 0,\n transferSize: 0,\n encodedBodySize: 0,\n decodedBodySize: 0,\n requestHeaders: {},\n responseHeaders: {},\n historical: false,\n mimeType: ''\n };\n\n // Capture request headers\n try {\n request.headers.forEach(function(value, key) {\n entry.requestHeaders[key] = value;\n });\n } catch(e) {}\n\n // Capture request body\n try {\n if (args[1] && args[1].body) {\n if (typeof args[1].body === 'string') {\n entry.requestBody = args[1].body;\n } else {\n entry.requestBody = '[Binary or FormData body]';\n }\n }\n } catch(e) {}\n\n window.__safariDevToolsNetworkLogs.push(entry);\n\n return originalFetch.apply(this, args).then(function(response) {\n entry.status = response.status;\n entry.statusText = response.statusText;\n entry.duration = performance.now() - entry.startTime;\n entry.mimeType = response.headers.get('content-type') || '';\n\n // Capture response headers\n try {\n response.headers.forEach(function(value, key) {\n entry.responseHeaders[key] = value;\n });\n } catch(e) {}\n\n return response;\n }).catch(function(error) {\n entry.error = error.message || String(error);\n entry.duration = performance.now() - entry.startTime;\n throw error;\n });\n };\n\n // Step 4: Intercept XMLHttpRequest\n var originalXHROpen = XMLHttpRequest.prototype.open;\n var originalXHRSend = XMLHttpRequest.prototype.send;\n var originalXHRSetHeader = XMLHttpRequest.prototype.setRequestHeader;\n\n XMLHttpRequest.prototype.open = function(method, url) {\n this.__safariDevToolsMeta = {\n method: method,\n url: String(url),\n requestHeaders: {}\n };\n return originalXHROpen.apply(this, arguments);\n };\n\n XMLHttpRequest.prototype.setRequestHeader = function(name, value) {\n if (this.__safariDevToolsMeta) {\n this.__safariDevToolsMeta.requestHeaders[name] = value;\n }\n return originalXHRSetHeader.apply(this, arguments);\n };\n\n XMLHttpRequest.prototype.send = function(body) {\n var xhr = this;\n var meta = xhr.__safariDevToolsMeta;\n\n if (meta) {\n var entry = {\n reqid: window.__safariDevToolsNetworkReqId++,\n url: meta.url,\n method: meta.method,\n status: 0,\n statusText: '',\n resourceType: 'xhr',\n startTime: performance.now(),\n duration: 0,\n transferSize: 0,\n encodedBodySize: 0,\n decodedBodySize: 0,\n requestHeaders: meta.requestHeaders,\n responseHeaders: {},\n historical: false,\n mimeType: ''\n };\n\n if (body) {\n try {\n entry.requestBody = typeof body === 'string' ? body : '[Binary body]';\n } catch(e) {}\n }\n\n window.__safariDevToolsNetworkLogs.push(entry);\n\n xhr.addEventListener('load', function() {\n entry.status = xhr.status;\n entry.statusText = xhr.statusText;\n entry.duration = performance.now() - entry.startTime;\n entry.mimeType = xhr.getResponseHeader('content-type') || '';\n\n // Parse response headers\n try {\n var headerStr = xhr.getAllResponseHeaders();\n if (headerStr) {\n headerStr.split('\\r\\n').forEach(function(line) {\n var parts = line.split(': ');\n if (parts.length >= 2) {\n entry.responseHeaders[parts[0]] = parts.slice(1).join(': ');\n }\n });\n }\n } catch(e) {}\n\n // Estimate body size\n try {\n var responseText = xhr.responseText;\n if (responseText) {\n entry.decodedBodySize = responseText.length;\n entry.encodedBodySize = responseText.length;\n }\n } catch(e) {}\n });\n\n xhr.addEventListener('error', function() {\n entry.error = 'Network error';\n entry.duration = performance.now() - entry.startTime;\n });\n\n xhr.addEventListener('timeout', function() {\n entry.error = 'Request timeout';\n entry.duration = performance.now() - entry.startTime;\n });\n }\n\n return originalXHRSend.apply(this, arguments);\n };\n})();\n";
14
+ //# sourceMappingURL=network.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../../../src/injected/network.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,eAAO,MAAM,sBAAsB,61QAmQlC,CAAC"}