@cgaspard/webappmcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +213 -0
- package/dist/browser.d.ts +3 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +20 -0
- package/dist/browser.js.map +1 -0
- package/dist/browser.min.js +2 -0
- package/dist/browser.min.js.map +1 -0
- package/dist/client/devtools.d.ts +28 -0
- package/dist/client/devtools.d.ts.map +1 -0
- package/dist/client/devtools.js +347 -0
- package/dist/client/devtools.js.map +1 -0
- package/dist/client/index.d.ts +47 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +655 -0
- package/dist/client/index.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.esm.js +15 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/index.d.ts +30 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +323 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/mcp-server.d.ts +19 -0
- package/dist/middleware/mcp-server.d.ts.map +1 -0
- package/dist/middleware/mcp-server.js +149 -0
- package/dist/middleware/mcp-server.js.map +1 -0
- package/dist/middleware/mcp-socket-server.d.ts +29 -0
- package/dist/middleware/mcp-socket-server.d.ts.map +1 -0
- package/dist/middleware/mcp-socket-server.js +315 -0
- package/dist/middleware/mcp-socket-server.js.map +1 -0
- package/dist/middleware/mcp-sse-server.d.ts +26 -0
- package/dist/middleware/mcp-sse-server.d.ts.map +1 -0
- package/dist/middleware/mcp-sse-server.js +258 -0
- package/dist/middleware/mcp-sse-server.js.map +1 -0
- package/dist/middleware/tools/capture.d.ts +3 -0
- package/dist/middleware/tools/capture.d.ts.map +1 -0
- package/dist/middleware/tools/capture.js +54 -0
- package/dist/middleware/tools/capture.js.map +1 -0
- package/dist/middleware/tools/diagnostic.d.ts +3 -0
- package/dist/middleware/tools/diagnostic.d.ts.map +1 -0
- package/dist/middleware/tools/diagnostic.js +62 -0
- package/dist/middleware/tools/diagnostic.js.map +1 -0
- package/dist/middleware/tools/dom.d.ts +3 -0
- package/dist/middleware/tools/dom.d.ts.map +1 -0
- package/dist/middleware/tools/dom.js +84 -0
- package/dist/middleware/tools/dom.js.map +1 -0
- package/dist/middleware/tools/execute.d.ts +3 -0
- package/dist/middleware/tools/execute.d.ts.map +1 -0
- package/dist/middleware/tools/execute.js +30 -0
- package/dist/middleware/tools/execute.js.map +1 -0
- package/dist/middleware/tools/index.d.ts +3 -0
- package/dist/middleware/tools/index.d.ts.map +1 -0
- package/dist/middleware/tools/index.js +20 -0
- package/dist/middleware/tools/index.js.map +1 -0
- package/dist/middleware/tools/interaction.d.ts +3 -0
- package/dist/middleware/tools/interaction.d.ts.map +1 -0
- package/dist/middleware/tools/interaction.js +87 -0
- package/dist/middleware/tools/interaction.js.map +1 -0
- package/dist/middleware/tools/state.d.ts +3 -0
- package/dist/middleware/tools/state.d.ts.map +1 -0
- package/dist/middleware/tools/state.js +71 -0
- package/dist/middleware/tools/state.js.map +1 -0
- package/dist/middleware/types.d.ts +32 -0
- package/dist/middleware/types.d.ts.map +1 -0
- package/dist/middleware/types.js +3 -0
- package/dist/middleware/types.js.map +1 -0
- package/dist/server/cli.d.ts +3 -0
- package/dist/server/cli.d.ts.map +1 -0
- package/dist/server/cli.js +32 -0
- package/dist/server/cli.js.map +1 -0
- package/dist/server/index.d.ts +17 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +136 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/tools/capture.d.ts +3 -0
- package/dist/server/tools/capture.d.ts.map +1 -0
- package/dist/server/tools/capture.js +46 -0
- package/dist/server/tools/capture.js.map +1 -0
- package/dist/server/tools/dom.d.ts +3 -0
- package/dist/server/tools/dom.d.ts.map +1 -0
- package/dist/server/tools/dom.js +84 -0
- package/dist/server/tools/dom.js.map +1 -0
- package/dist/server/tools/index.d.ts +3 -0
- package/dist/server/tools/index.d.ts.map +1 -0
- package/dist/server/tools/index.js +16 -0
- package/dist/server/tools/index.js.map +1 -0
- package/dist/server/tools/interaction.d.ts +3 -0
- package/dist/server/tools/interaction.d.ts.map +1 -0
- package/dist/server/tools/interaction.js +87 -0
- package/dist/server/tools/interaction.js.map +1 -0
- package/dist/server/tools/state.d.ts +3 -0
- package/dist/server/tools/state.d.ts.map +1 -0
- package/dist/server/tools/state.js +63 -0
- package/dist/server/tools/state.js.map +1 -0
- package/dist/server/types.d.ts +32 -0
- package/dist/server/types.d.ts.map +1 -0
- package/dist/server/types.js +3 -0
- package/dist/server/types.js.map +1 -0
- package/dist/server/websocket-manager.d.ts +17 -0
- package/dist/server/websocket-manager.d.ts.map +1 -0
- package/dist/server/websocket-manager.js +67 -0
- package/dist/server/websocket-manager.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,655 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WebAppMCPClient = void 0;
|
|
4
|
+
const devtools_1 = require("./devtools");
|
|
5
|
+
class WebAppMCPClient {
|
|
6
|
+
get isConnected() {
|
|
7
|
+
return this._isConnected && this.ws?.readyState === WebSocket.OPEN;
|
|
8
|
+
}
|
|
9
|
+
constructor(config) {
|
|
10
|
+
this.ws = null;
|
|
11
|
+
this.reconnectAttempts = 0;
|
|
12
|
+
this.messageHandlers = new Map();
|
|
13
|
+
this.consoleLogs = [];
|
|
14
|
+
this._isConnected = false;
|
|
15
|
+
this.devTools = null;
|
|
16
|
+
this.config = {
|
|
17
|
+
reconnectInterval: 5000,
|
|
18
|
+
maxReconnectAttempts: 10,
|
|
19
|
+
enableDevTools: true,
|
|
20
|
+
...config,
|
|
21
|
+
};
|
|
22
|
+
this.setupConsoleInterception();
|
|
23
|
+
if (this.config.enableDevTools) {
|
|
24
|
+
this.devTools = new devtools_1.MCPDevTools();
|
|
25
|
+
this.devTools.setConnectionStatus('disconnected');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
connect() {
|
|
29
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
this.devTools?.setConnectionStatus('connecting');
|
|
33
|
+
this.devTools?.logWebSocketEvent('Attempting to connect', { url: this.config.serverUrl });
|
|
34
|
+
try {
|
|
35
|
+
const url = new URL(this.config.serverUrl);
|
|
36
|
+
const headers = {};
|
|
37
|
+
if (this.config.authToken) {
|
|
38
|
+
headers['Authorization'] = `Bearer ${this.config.authToken}`;
|
|
39
|
+
}
|
|
40
|
+
// WebSocket in browser doesn't support custom headers directly
|
|
41
|
+
// So we'll pass the token in the URL
|
|
42
|
+
if (this.config.authToken) {
|
|
43
|
+
url.searchParams.set('token', this.config.authToken);
|
|
44
|
+
}
|
|
45
|
+
this.ws = new WebSocket(url.toString());
|
|
46
|
+
this.setupWebSocketHandlers();
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
console.error('Failed to connect to WebApp MCP server:', error);
|
|
50
|
+
this.devTools?.logError('websocket', 'Failed to connect', error);
|
|
51
|
+
this.scheduleReconnect();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
disconnect() {
|
|
55
|
+
this._isConnected = false;
|
|
56
|
+
if (this.reconnectTimer) {
|
|
57
|
+
clearTimeout(this.reconnectTimer);
|
|
58
|
+
this.reconnectTimer = null;
|
|
59
|
+
}
|
|
60
|
+
if (this.ws) {
|
|
61
|
+
this.ws.close();
|
|
62
|
+
this.ws = null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
setupWebSocketHandlers() {
|
|
66
|
+
if (!this.ws)
|
|
67
|
+
return;
|
|
68
|
+
this.ws.onopen = () => {
|
|
69
|
+
console.log('Connected to WebApp MCP server');
|
|
70
|
+
this._isConnected = true;
|
|
71
|
+
this.reconnectAttempts = 0;
|
|
72
|
+
this.devTools?.setConnectionStatus('connected');
|
|
73
|
+
this.devTools?.logWebSocketEvent('Connected to WebApp MCP server');
|
|
74
|
+
this.sendMessage({
|
|
75
|
+
type: 'init',
|
|
76
|
+
url: window.location.href,
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
this.ws.onmessage = (event) => {
|
|
80
|
+
try {
|
|
81
|
+
const message = JSON.parse(event.data);
|
|
82
|
+
this.devTools?.logWebSocketEvent('Message received', message);
|
|
83
|
+
this.handleMessage(message);
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
console.error('Failed to parse WebSocket message:', error);
|
|
87
|
+
this.devTools?.logError('websocket', 'Failed to parse message', error);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
this.ws.onerror = (error) => {
|
|
91
|
+
console.error('WebSocket error:', error);
|
|
92
|
+
this.devTools?.logError('websocket', 'WebSocket error', error);
|
|
93
|
+
};
|
|
94
|
+
this.ws.onclose = () => {
|
|
95
|
+
console.log('Disconnected from WebApp MCP server');
|
|
96
|
+
this._isConnected = false;
|
|
97
|
+
this.devTools?.setConnectionStatus('disconnected');
|
|
98
|
+
this.devTools?.logWebSocketEvent('Disconnected from WebApp MCP server');
|
|
99
|
+
this.scheduleReconnect();
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
scheduleReconnect() {
|
|
103
|
+
if (this.reconnectAttempts >= (this.config.maxReconnectAttempts || 10) ||
|
|
104
|
+
this.reconnectTimer) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
this.reconnectAttempts++;
|
|
108
|
+
console.log(`Scheduling reconnect attempt ${this.reconnectAttempts}/${this.config.maxReconnectAttempts}`);
|
|
109
|
+
this.reconnectTimer = setTimeout(() => {
|
|
110
|
+
this.reconnectTimer = null;
|
|
111
|
+
this.connect();
|
|
112
|
+
}, this.config.reconnectInterval);
|
|
113
|
+
}
|
|
114
|
+
sendMessage(message) {
|
|
115
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
116
|
+
this.ws.send(JSON.stringify(message));
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
console.error('WebSocket is not connected');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
handleMessage(message) {
|
|
123
|
+
const { type, requestId, tool, args } = message;
|
|
124
|
+
console.log('[WebApp Client] Received message:', JSON.stringify(message));
|
|
125
|
+
if (type === 'connected') {
|
|
126
|
+
console.log('WebApp MCP client registered:', message.clientId);
|
|
127
|
+
this.devTools?.logMCPEvent('Client registered', { clientId: message.clientId });
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
if (type === 'execute_tool') {
|
|
131
|
+
console.log(`[WebApp Client] Executing tool: ${tool} with requestId: ${requestId}`);
|
|
132
|
+
this.devTools?.logMCPEvent(`Executing tool: ${tool}`, { requestId, args });
|
|
133
|
+
this.executeToolHandler(requestId, tool, args);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const handler = this.messageHandlers.get(type);
|
|
137
|
+
if (handler) {
|
|
138
|
+
handler(message);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async executeToolHandler(requestId, toolName, args) {
|
|
142
|
+
console.log(`[WebApp Client] Executing tool handler for ${toolName}`);
|
|
143
|
+
console.log(`[WebApp Client] Tool args:`, JSON.stringify(args));
|
|
144
|
+
try {
|
|
145
|
+
let result;
|
|
146
|
+
switch (toolName) {
|
|
147
|
+
case 'dom_query':
|
|
148
|
+
result = await this.domQuery(args);
|
|
149
|
+
break;
|
|
150
|
+
case 'dom_get_properties':
|
|
151
|
+
result = await this.domGetProperties(args);
|
|
152
|
+
break;
|
|
153
|
+
case 'dom_get_text':
|
|
154
|
+
result = await this.domGetText(args);
|
|
155
|
+
break;
|
|
156
|
+
case 'dom_get_html':
|
|
157
|
+
result = await this.domGetHTML(args);
|
|
158
|
+
break;
|
|
159
|
+
case 'interaction_click':
|
|
160
|
+
result = await this.interactionClick(args);
|
|
161
|
+
break;
|
|
162
|
+
case 'interaction_type':
|
|
163
|
+
result = await this.interactionType(args);
|
|
164
|
+
break;
|
|
165
|
+
case 'interaction_scroll':
|
|
166
|
+
result = await this.interactionScroll(args);
|
|
167
|
+
break;
|
|
168
|
+
case 'interaction_hover':
|
|
169
|
+
result = await this.interactionHover(args);
|
|
170
|
+
break;
|
|
171
|
+
case 'capture_screenshot':
|
|
172
|
+
result = await this.captureScreenshot(args);
|
|
173
|
+
break;
|
|
174
|
+
case 'capture_element_screenshot':
|
|
175
|
+
result = await this.captureElementScreenshot(args);
|
|
176
|
+
break;
|
|
177
|
+
case 'state_get_variable':
|
|
178
|
+
result = await this.stateGetVariable(args);
|
|
179
|
+
break;
|
|
180
|
+
case 'state_local_storage':
|
|
181
|
+
result = await this.stateLocalStorage(args);
|
|
182
|
+
break;
|
|
183
|
+
case 'console_get_logs':
|
|
184
|
+
result = await this.consoleGetLogs(args);
|
|
185
|
+
break;
|
|
186
|
+
case 'dom_manipulate':
|
|
187
|
+
result = await this.domManipulate(args);
|
|
188
|
+
break;
|
|
189
|
+
case 'javascript_inject':
|
|
190
|
+
result = await this.javascriptInject(args);
|
|
191
|
+
break;
|
|
192
|
+
case 'webapp_list_clients':
|
|
193
|
+
result = await this.webappListClients(args);
|
|
194
|
+
break;
|
|
195
|
+
case 'execute_javascript':
|
|
196
|
+
result = await this.executeJavascript(args);
|
|
197
|
+
break;
|
|
198
|
+
default:
|
|
199
|
+
throw new Error(`Unknown tool: ${toolName}`);
|
|
200
|
+
}
|
|
201
|
+
console.log(`[WebApp Client] Tool execution successful, sending response`);
|
|
202
|
+
this.devTools?.logMCPEvent(`Tool ${toolName} executed successfully`, { requestId, result });
|
|
203
|
+
this.sendMessage({
|
|
204
|
+
type: 'tool_response',
|
|
205
|
+
requestId,
|
|
206
|
+
result,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
console.error(`[WebApp Client] Tool execution failed:`, error);
|
|
211
|
+
this.devTools?.logError('mcp', `Tool ${toolName} execution failed`, error);
|
|
212
|
+
this.sendMessage({
|
|
213
|
+
type: 'tool_response',
|
|
214
|
+
requestId,
|
|
215
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
setupConsoleInterception() {
|
|
220
|
+
const originalConsole = {
|
|
221
|
+
log: console.log,
|
|
222
|
+
info: console.info,
|
|
223
|
+
warn: console.warn,
|
|
224
|
+
error: console.error,
|
|
225
|
+
};
|
|
226
|
+
const interceptor = (level) => {
|
|
227
|
+
return (...args) => {
|
|
228
|
+
this.consoleLogs.push({
|
|
229
|
+
level,
|
|
230
|
+
timestamp: new Date().toISOString(),
|
|
231
|
+
args: args.map((arg) => {
|
|
232
|
+
try {
|
|
233
|
+
return typeof arg === 'object' ? JSON.stringify(arg) : String(arg);
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
return String(arg);
|
|
237
|
+
}
|
|
238
|
+
}),
|
|
239
|
+
});
|
|
240
|
+
if (this.consoleLogs.length > 1000) {
|
|
241
|
+
this.consoleLogs.shift();
|
|
242
|
+
}
|
|
243
|
+
originalConsole[level](...args);
|
|
244
|
+
};
|
|
245
|
+
};
|
|
246
|
+
console.log = interceptor('log');
|
|
247
|
+
console.info = interceptor('info');
|
|
248
|
+
console.warn = interceptor('warn');
|
|
249
|
+
console.error = interceptor('error');
|
|
250
|
+
}
|
|
251
|
+
async domQuery(args) {
|
|
252
|
+
const { selector, limit = 10 } = args;
|
|
253
|
+
const elements = Array.from(document.querySelectorAll(selector)).slice(0, limit);
|
|
254
|
+
return {
|
|
255
|
+
elements: elements.map((el) => ({
|
|
256
|
+
selector,
|
|
257
|
+
tagName: el.tagName.toLowerCase(),
|
|
258
|
+
id: el.id || undefined,
|
|
259
|
+
className: el.className || undefined,
|
|
260
|
+
text: el.textContent?.trim().substring(0, 100),
|
|
261
|
+
attributes: (() => {
|
|
262
|
+
const attrs = {};
|
|
263
|
+
for (let i = 0; i < el.attributes.length; i++) {
|
|
264
|
+
const attr = el.attributes[i];
|
|
265
|
+
attrs[attr.name] = attr.value;
|
|
266
|
+
}
|
|
267
|
+
return attrs;
|
|
268
|
+
})(),
|
|
269
|
+
})),
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
async domGetProperties(args) {
|
|
273
|
+
const { selector, properties = [] } = args;
|
|
274
|
+
const element = document.querySelector(selector);
|
|
275
|
+
if (!element) {
|
|
276
|
+
throw new Error(`Element not found: ${selector}`);
|
|
277
|
+
}
|
|
278
|
+
const result = {};
|
|
279
|
+
for (const prop of properties) {
|
|
280
|
+
try {
|
|
281
|
+
result[prop] = element[prop];
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
result[prop] = undefined;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return result;
|
|
288
|
+
}
|
|
289
|
+
async domGetText(args) {
|
|
290
|
+
const { selector, includeHidden = false } = args;
|
|
291
|
+
const elements = document.querySelectorAll(selector);
|
|
292
|
+
const texts = [];
|
|
293
|
+
elements.forEach((el) => {
|
|
294
|
+
if (includeHidden || el.offsetParent !== null) {
|
|
295
|
+
const text = el.textContent?.trim();
|
|
296
|
+
if (text)
|
|
297
|
+
texts.push(text);
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
return { texts };
|
|
301
|
+
}
|
|
302
|
+
async domGetHTML(args) {
|
|
303
|
+
const { selector, outerHTML = false } = args;
|
|
304
|
+
const element = document.querySelector(selector);
|
|
305
|
+
if (!element) {
|
|
306
|
+
throw new Error(`Element not found: ${selector}`);
|
|
307
|
+
}
|
|
308
|
+
return {
|
|
309
|
+
html: outerHTML ? element.outerHTML : element.innerHTML,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
async interactionClick(args) {
|
|
313
|
+
const { selector, button = 'left' } = args;
|
|
314
|
+
const element = document.querySelector(selector);
|
|
315
|
+
if (!element) {
|
|
316
|
+
throw new Error(`Element not found: ${selector}`);
|
|
317
|
+
}
|
|
318
|
+
const event = new MouseEvent('click', {
|
|
319
|
+
view: window,
|
|
320
|
+
bubbles: true,
|
|
321
|
+
cancelable: true,
|
|
322
|
+
button: button === 'right' ? 2 : button === 'middle' ? 1 : 0,
|
|
323
|
+
});
|
|
324
|
+
element.dispatchEvent(event);
|
|
325
|
+
return { success: true };
|
|
326
|
+
}
|
|
327
|
+
async interactionType(args) {
|
|
328
|
+
const { selector, text, clear = false } = args;
|
|
329
|
+
const element = document.querySelector(selector);
|
|
330
|
+
if (!element) {
|
|
331
|
+
throw new Error(`Element not found: ${selector}`);
|
|
332
|
+
}
|
|
333
|
+
if (clear) {
|
|
334
|
+
element.value = '';
|
|
335
|
+
}
|
|
336
|
+
element.focus();
|
|
337
|
+
element.value += text;
|
|
338
|
+
element.dispatchEvent(new Event('input', { bubbles: true }));
|
|
339
|
+
element.dispatchEvent(new Event('change', { bubbles: true }));
|
|
340
|
+
return { success: true };
|
|
341
|
+
}
|
|
342
|
+
async interactionScroll(args) {
|
|
343
|
+
const { selector, direction, amount = 100 } = args;
|
|
344
|
+
const element = selector ? document.querySelector(selector) : window;
|
|
345
|
+
if (!element && selector) {
|
|
346
|
+
throw new Error(`Element not found: ${selector}`);
|
|
347
|
+
}
|
|
348
|
+
const scrollOptions = {
|
|
349
|
+
behavior: 'smooth',
|
|
350
|
+
};
|
|
351
|
+
if (direction === 'up' || direction === 'down') {
|
|
352
|
+
scrollOptions.top = direction === 'down' ? amount : -amount;
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
scrollOptions.left = direction === 'right' ? amount : -amount;
|
|
356
|
+
}
|
|
357
|
+
if (element === window) {
|
|
358
|
+
window.scrollBy(scrollOptions);
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
element.scrollBy(scrollOptions);
|
|
362
|
+
}
|
|
363
|
+
return { success: true };
|
|
364
|
+
}
|
|
365
|
+
async interactionHover(args) {
|
|
366
|
+
const { selector } = args;
|
|
367
|
+
const element = document.querySelector(selector);
|
|
368
|
+
if (!element) {
|
|
369
|
+
throw new Error(`Element not found: ${selector}`);
|
|
370
|
+
}
|
|
371
|
+
element.dispatchEvent(new MouseEvent('mouseenter', {
|
|
372
|
+
view: window,
|
|
373
|
+
bubbles: true,
|
|
374
|
+
cancelable: true,
|
|
375
|
+
}));
|
|
376
|
+
element.dispatchEvent(new MouseEvent('mouseover', {
|
|
377
|
+
view: window,
|
|
378
|
+
bubbles: true,
|
|
379
|
+
cancelable: true,
|
|
380
|
+
}));
|
|
381
|
+
return { success: true };
|
|
382
|
+
}
|
|
383
|
+
async captureScreenshot(args) {
|
|
384
|
+
const { fullPage = true, format = 'png' } = args;
|
|
385
|
+
try {
|
|
386
|
+
// For now, we'll use a simple approach that captures the visible viewport
|
|
387
|
+
// In a production environment, you'd want to use html2canvas or similar
|
|
388
|
+
const canvas = document.createElement('canvas');
|
|
389
|
+
const ctx = canvas.getContext('2d');
|
|
390
|
+
if (!ctx) {
|
|
391
|
+
throw new Error('Failed to create canvas context');
|
|
392
|
+
}
|
|
393
|
+
// Get dimensions
|
|
394
|
+
const width = fullPage ? document.documentElement.scrollWidth : window.innerWidth;
|
|
395
|
+
const height = fullPage ? document.documentElement.scrollHeight : window.innerHeight;
|
|
396
|
+
canvas.width = width;
|
|
397
|
+
canvas.height = height;
|
|
398
|
+
// Draw a placeholder for now
|
|
399
|
+
// In production, you'd use html2canvas or similar library
|
|
400
|
+
ctx.fillStyle = '#f0f0f0';
|
|
401
|
+
ctx.fillRect(0, 0, width, height);
|
|
402
|
+
ctx.fillStyle = '#333';
|
|
403
|
+
ctx.font = '20px Arial';
|
|
404
|
+
ctx.textAlign = 'center';
|
|
405
|
+
ctx.fillText('Screenshot placeholder - implement with html2canvas', width / 2, height / 2);
|
|
406
|
+
// Convert to data URL
|
|
407
|
+
const dataUrl = canvas.toDataURL(`image/${format}`);
|
|
408
|
+
return {
|
|
409
|
+
success: true,
|
|
410
|
+
dataUrl,
|
|
411
|
+
width,
|
|
412
|
+
height,
|
|
413
|
+
message: 'Screenshot captured (placeholder - integrate html2canvas for full implementation)'
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
catch (error) {
|
|
417
|
+
throw new Error(`Failed to capture screenshot: ${error}`);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
async captureElementScreenshot(args) {
|
|
421
|
+
const { selector, format = 'png' } = args;
|
|
422
|
+
if (!selector) {
|
|
423
|
+
throw new Error('Selector is required for element screenshot');
|
|
424
|
+
}
|
|
425
|
+
const element = document.querySelector(selector);
|
|
426
|
+
if (!element) {
|
|
427
|
+
throw new Error(`Element not found: ${selector}`);
|
|
428
|
+
}
|
|
429
|
+
try {
|
|
430
|
+
const rect = element.getBoundingClientRect();
|
|
431
|
+
const canvas = document.createElement('canvas');
|
|
432
|
+
const ctx = canvas.getContext('2d');
|
|
433
|
+
if (!ctx) {
|
|
434
|
+
throw new Error('Failed to create canvas context');
|
|
435
|
+
}
|
|
436
|
+
canvas.width = rect.width;
|
|
437
|
+
canvas.height = rect.height;
|
|
438
|
+
// Draw a placeholder
|
|
439
|
+
ctx.fillStyle = '#e0e0e0';
|
|
440
|
+
ctx.fillRect(0, 0, rect.width, rect.height);
|
|
441
|
+
ctx.fillStyle = '#666';
|
|
442
|
+
ctx.font = '16px Arial';
|
|
443
|
+
ctx.textAlign = 'center';
|
|
444
|
+
ctx.fillText(`Element: ${selector}`, rect.width / 2, rect.height / 2);
|
|
445
|
+
const dataUrl = canvas.toDataURL(`image/${format}`);
|
|
446
|
+
return {
|
|
447
|
+
success: true,
|
|
448
|
+
dataUrl,
|
|
449
|
+
width: rect.width,
|
|
450
|
+
height: rect.height,
|
|
451
|
+
selector,
|
|
452
|
+
message: 'Element screenshot captured (placeholder - integrate html2canvas for full implementation)'
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
catch (error) {
|
|
456
|
+
throw new Error(`Failed to capture element screenshot: ${error}`);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
async stateGetVariable(args) {
|
|
460
|
+
const { path } = args;
|
|
461
|
+
const parts = path.split('.');
|
|
462
|
+
let current = window;
|
|
463
|
+
for (const part of parts) {
|
|
464
|
+
if (current && typeof current === 'object' && part in current) {
|
|
465
|
+
current = current[part];
|
|
466
|
+
}
|
|
467
|
+
else {
|
|
468
|
+
throw new Error(`Variable not found: ${path}`);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
return { value: current };
|
|
472
|
+
}
|
|
473
|
+
async stateLocalStorage(args) {
|
|
474
|
+
const { operation, key, value } = args;
|
|
475
|
+
switch (operation) {
|
|
476
|
+
case 'get':
|
|
477
|
+
return { value: localStorage.getItem(key) };
|
|
478
|
+
case 'set':
|
|
479
|
+
localStorage.setItem(key, value);
|
|
480
|
+
return { success: true };
|
|
481
|
+
case 'remove':
|
|
482
|
+
localStorage.removeItem(key);
|
|
483
|
+
return { success: true };
|
|
484
|
+
case 'clear':
|
|
485
|
+
localStorage.clear();
|
|
486
|
+
return { success: true };
|
|
487
|
+
case 'getAll':
|
|
488
|
+
const items = {};
|
|
489
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
490
|
+
const k = localStorage.key(i);
|
|
491
|
+
if (k)
|
|
492
|
+
items[k] = localStorage.getItem(k) || '';
|
|
493
|
+
}
|
|
494
|
+
return { items };
|
|
495
|
+
default:
|
|
496
|
+
throw new Error(`Unknown localStorage operation: ${operation}`);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
async consoleGetLogs(args) {
|
|
500
|
+
const { level = 'all', limit = 100 } = args;
|
|
501
|
+
let logs = this.consoleLogs;
|
|
502
|
+
if (level !== 'all') {
|
|
503
|
+
logs = logs.filter((log) => log.level === level);
|
|
504
|
+
}
|
|
505
|
+
return { logs: logs.slice(-limit) };
|
|
506
|
+
}
|
|
507
|
+
async domManipulate(args) {
|
|
508
|
+
const { action, selector, value, attribute, property } = args;
|
|
509
|
+
if (!selector) {
|
|
510
|
+
throw new Error('Selector is required for DOM manipulation');
|
|
511
|
+
}
|
|
512
|
+
const element = document.querySelector(selector);
|
|
513
|
+
if (!element) {
|
|
514
|
+
throw new Error(`Element not found: ${selector}`);
|
|
515
|
+
}
|
|
516
|
+
switch (action) {
|
|
517
|
+
case 'setAttribute':
|
|
518
|
+
if (!attribute || value === undefined) {
|
|
519
|
+
throw new Error('Attribute name and value are required for setAttribute');
|
|
520
|
+
}
|
|
521
|
+
element.setAttribute(attribute, value);
|
|
522
|
+
return { success: true, message: `Set attribute ${attribute}="${value}" on ${selector}` };
|
|
523
|
+
case 'removeAttribute':
|
|
524
|
+
if (!attribute) {
|
|
525
|
+
throw new Error('Attribute name is required for removeAttribute');
|
|
526
|
+
}
|
|
527
|
+
element.removeAttribute(attribute);
|
|
528
|
+
return { success: true, message: `Removed attribute ${attribute} from ${selector}` };
|
|
529
|
+
case 'setProperty':
|
|
530
|
+
if (!property || value === undefined) {
|
|
531
|
+
throw new Error('Property name and value are required for setProperty');
|
|
532
|
+
}
|
|
533
|
+
element[property] = value;
|
|
534
|
+
return { success: true, message: `Set property ${property}=${value} on ${selector}` };
|
|
535
|
+
case 'addClass':
|
|
536
|
+
if (!value) {
|
|
537
|
+
throw new Error('Class name is required for addClass');
|
|
538
|
+
}
|
|
539
|
+
element.classList.add(value);
|
|
540
|
+
return { success: true, message: `Added class "${value}" to ${selector}` };
|
|
541
|
+
case 'removeClass':
|
|
542
|
+
if (!value) {
|
|
543
|
+
throw new Error('Class name is required for removeClass');
|
|
544
|
+
}
|
|
545
|
+
element.classList.remove(value);
|
|
546
|
+
return { success: true, message: `Removed class "${value}" from ${selector}` };
|
|
547
|
+
case 'setInnerHTML':
|
|
548
|
+
if (value === undefined) {
|
|
549
|
+
throw new Error('HTML content is required for setInnerHTML');
|
|
550
|
+
}
|
|
551
|
+
element.innerHTML = value;
|
|
552
|
+
return { success: true, message: `Set innerHTML on ${selector}` };
|
|
553
|
+
case 'setTextContent':
|
|
554
|
+
if (value === undefined) {
|
|
555
|
+
throw new Error('Text content is required for setTextContent');
|
|
556
|
+
}
|
|
557
|
+
element.textContent = value;
|
|
558
|
+
return { success: true, message: `Set textContent on ${selector}` };
|
|
559
|
+
case 'setStyle':
|
|
560
|
+
if (!property || value === undefined) {
|
|
561
|
+
throw new Error('Style property and value are required for setStyle');
|
|
562
|
+
}
|
|
563
|
+
element.style[property] = value;
|
|
564
|
+
return { success: true, message: `Set style ${property}=${value} on ${selector}` };
|
|
565
|
+
case 'remove':
|
|
566
|
+
element.remove();
|
|
567
|
+
return { success: true, message: `Removed element ${selector}` };
|
|
568
|
+
default:
|
|
569
|
+
throw new Error(`Unknown DOM manipulation action: ${action}`);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
async javascriptInject(args) {
|
|
573
|
+
const { code, returnValue = false } = args;
|
|
574
|
+
if (!code) {
|
|
575
|
+
throw new Error('JavaScript code is required');
|
|
576
|
+
}
|
|
577
|
+
try {
|
|
578
|
+
let result;
|
|
579
|
+
if (returnValue) {
|
|
580
|
+
// Use eval to return a value
|
|
581
|
+
result = eval(code);
|
|
582
|
+
}
|
|
583
|
+
else {
|
|
584
|
+
// Use Function constructor for execution without return
|
|
585
|
+
const func = new Function(code);
|
|
586
|
+
result = func();
|
|
587
|
+
}
|
|
588
|
+
return {
|
|
589
|
+
success: true,
|
|
590
|
+
result: result !== undefined ? result : null,
|
|
591
|
+
message: 'JavaScript executed successfully'
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
catch (error) {
|
|
595
|
+
throw new Error(`JavaScript execution failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
async webappListClients(args) {
|
|
599
|
+
// This returns information about the current client
|
|
600
|
+
return {
|
|
601
|
+
clients: [{
|
|
602
|
+
id: 'browser-client',
|
|
603
|
+
type: 'browser',
|
|
604
|
+
url: window.location.href,
|
|
605
|
+
userAgent: navigator.userAgent,
|
|
606
|
+
connected: this.isConnected,
|
|
607
|
+
timestamp: new Date().toISOString()
|
|
608
|
+
}]
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
async executeJavascript(args) {
|
|
612
|
+
const { code, returnValue = false, async = false } = args;
|
|
613
|
+
if (!code) {
|
|
614
|
+
throw new Error('JavaScript code is required');
|
|
615
|
+
}
|
|
616
|
+
try {
|
|
617
|
+
let result;
|
|
618
|
+
if (async) {
|
|
619
|
+
// Execute asynchronously
|
|
620
|
+
if (returnValue) {
|
|
621
|
+
result = await eval(`(async () => { return ${code}; })()`);
|
|
622
|
+
}
|
|
623
|
+
else {
|
|
624
|
+
result = await eval(`(async () => { ${code}; })()`);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
else {
|
|
628
|
+
// Execute synchronously
|
|
629
|
+
if (returnValue) {
|
|
630
|
+
result = eval(`(() => { return ${code}; })()`);
|
|
631
|
+
}
|
|
632
|
+
else {
|
|
633
|
+
eval(code);
|
|
634
|
+
result = undefined;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
return {
|
|
638
|
+
success: true,
|
|
639
|
+
result: result !== undefined ? result : null,
|
|
640
|
+
message: 'JavaScript executed successfully',
|
|
641
|
+
executionTime: new Date().toISOString()
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
catch (error) {
|
|
645
|
+
throw new Error(`JavaScript execution failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
exports.WebAppMCPClient = WebAppMCPClient;
|
|
650
|
+
// Default export
|
|
651
|
+
exports.default = WebAppMCPClient;
|
|
652
|
+
if (typeof window !== 'undefined') {
|
|
653
|
+
window.WebAppMCP = { WebAppMCPClient };
|
|
654
|
+
}
|
|
655
|
+
//# sourceMappingURL=index.js.map
|