@democratize-quality/mcp-server 1.0.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 (48) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +423 -0
  3. package/browserControl.js +113 -0
  4. package/cli.js +187 -0
  5. package/docs/api/tool-reference.md +317 -0
  6. package/docs/api_tools_usage.md +477 -0
  7. package/docs/development/adding-tools.md +274 -0
  8. package/docs/development/configuration.md +332 -0
  9. package/docs/examples/authentication.md +124 -0
  10. package/docs/examples/basic-automation.md +105 -0
  11. package/docs/getting-started.md +214 -0
  12. package/docs/index.md +61 -0
  13. package/mcpServer.js +280 -0
  14. package/package.json +83 -0
  15. package/run-server.js +140 -0
  16. package/src/config/environments/api-only.js +53 -0
  17. package/src/config/environments/development.js +54 -0
  18. package/src/config/environments/production.js +69 -0
  19. package/src/config/index.js +341 -0
  20. package/src/config/server.js +41 -0
  21. package/src/config/tools/api.js +67 -0
  22. package/src/config/tools/browser.js +90 -0
  23. package/src/config/tools/default.js +32 -0
  24. package/src/services/browserService.js +325 -0
  25. package/src/tools/api/api-request.js +641 -0
  26. package/src/tools/api/api-session-report.js +1262 -0
  27. package/src/tools/api/api-session-status.js +395 -0
  28. package/src/tools/base/ToolBase.js +230 -0
  29. package/src/tools/base/ToolRegistry.js +269 -0
  30. package/src/tools/browser/advanced/browser-console.js +384 -0
  31. package/src/tools/browser/advanced/browser-dialog.js +319 -0
  32. package/src/tools/browser/advanced/browser-evaluate.js +337 -0
  33. package/src/tools/browser/advanced/browser-file.js +480 -0
  34. package/src/tools/browser/advanced/browser-keyboard.js +343 -0
  35. package/src/tools/browser/advanced/browser-mouse.js +332 -0
  36. package/src/tools/browser/advanced/browser-network.js +421 -0
  37. package/src/tools/browser/advanced/browser-pdf.js +407 -0
  38. package/src/tools/browser/advanced/browser-tabs.js +497 -0
  39. package/src/tools/browser/advanced/browser-wait.js +378 -0
  40. package/src/tools/browser/click.js +168 -0
  41. package/src/tools/browser/close.js +60 -0
  42. package/src/tools/browser/dom.js +70 -0
  43. package/src/tools/browser/launch.js +67 -0
  44. package/src/tools/browser/navigate.js +270 -0
  45. package/src/tools/browser/screenshot.js +351 -0
  46. package/src/tools/browser/type.js +174 -0
  47. package/src/tools/index.js +95 -0
  48. package/src/utils/browserHelpers.js +83 -0
@@ -0,0 +1,343 @@
1
+ const ToolBase = require('../../base/ToolBase');
2
+ const browserService = require('../../../services/browserService');
3
+
4
+ /**
5
+ * Enhanced Keyboard Tool - Provides comprehensive keyboard interactions
6
+ * Inspired by Playwright MCP keyboard capabilities
7
+ */
8
+ class BrowserKeyboardTool extends ToolBase {
9
+ static definition = {
10
+ name: "browser_keyboard",
11
+ description: "Perform keyboard actions including typing, key presses, and keyboard shortcuts. Supports special keys and modifier combinations.",
12
+ input_schema: {
13
+ type: "object",
14
+ properties: {
15
+ browserId: {
16
+ type: "string",
17
+ description: "The ID of the browser instance"
18
+ },
19
+ action: {
20
+ type: "string",
21
+ enum: ["type", "press", "down", "up", "shortcut"],
22
+ description: "The keyboard action to perform"
23
+ },
24
+ text: {
25
+ type: "string",
26
+ description: "Text to type (for 'type' action)"
27
+ },
28
+ key: {
29
+ type: "string",
30
+ description: "Key to press (for 'press', 'down', 'up' actions). Examples: 'Enter', 'Tab', 'ArrowLeft', 'F1'"
31
+ },
32
+ shortcut: {
33
+ type: "string",
34
+ description: "Keyboard shortcut (for 'shortcut' action). Examples: 'Ctrl+C', 'Cmd+V', 'Ctrl+Shift+I'"
35
+ },
36
+ target: {
37
+ type: "object",
38
+ properties: {
39
+ selector: {
40
+ type: "string",
41
+ description: "CSS selector of element to focus before typing (optional)"
42
+ }
43
+ },
44
+ description: "Target element to focus before keyboard action"
45
+ },
46
+ delay: {
47
+ type: "number",
48
+ default: 0,
49
+ description: "Delay between keystrokes in milliseconds"
50
+ },
51
+ modifiers: {
52
+ type: "array",
53
+ items: { type: "string", enum: ["ctrl", "shift", "alt", "meta"] },
54
+ description: "Modifier keys to hold during action"
55
+ }
56
+ },
57
+ required: ["browserId", "action"]
58
+ },
59
+ output_schema: {
60
+ type: "object",
61
+ properties: {
62
+ success: { type: "boolean", description: "Whether the keyboard action was successful" },
63
+ action: { type: "string", description: "The action that was performed" },
64
+ text: { type: "string", description: "Text that was typed" },
65
+ key: { type: "string", description: "Key that was pressed" },
66
+ target: { type: "string", description: "CSS selector of targeted element" },
67
+ browserId: { type: "string", description: "Browser instance ID" }
68
+ },
69
+ required: ["success", "action", "browserId"]
70
+ }
71
+ };
72
+
73
+ async execute(parameters) {
74
+ const { browserId, action, text, key, shortcut, target, delay = 0, modifiers = [] } = parameters;
75
+
76
+ const browser = browserService.getBrowserInstance(browserId);
77
+ if (!browser) {
78
+ throw new Error(`Browser instance '${browserId}' not found`);
79
+ }
80
+
81
+ const client = browser.client;
82
+
83
+ // Focus target element if specified
84
+ if (target?.selector) {
85
+ await this.focusElement(client, target.selector);
86
+ }
87
+
88
+ let result = {
89
+ success: true,
90
+ action: action,
91
+ browserId: browserId
92
+ };
93
+
94
+ // Convert modifiers to CDP format
95
+ const cdpModifiers = this.convertModifiers(modifiers);
96
+
97
+ switch (action) {
98
+ case 'type':
99
+ if (!text) {
100
+ throw new Error('Text is required for type action');
101
+ }
102
+ await this.typeText(client, text, delay);
103
+ result.text = text;
104
+ break;
105
+
106
+ case 'press':
107
+ if (!key) {
108
+ throw new Error('Key is required for press action');
109
+ }
110
+ await this.pressKey(client, key, cdpModifiers);
111
+ result.key = key;
112
+ break;
113
+
114
+ case 'down':
115
+ if (!key) {
116
+ throw new Error('Key is required for down action');
117
+ }
118
+ await this.keyDown(client, key, cdpModifiers);
119
+ result.key = key;
120
+ break;
121
+
122
+ case 'up':
123
+ if (!key) {
124
+ throw new Error('Key is required for up action');
125
+ }
126
+ await this.keyUp(client, key, cdpModifiers);
127
+ result.key = key;
128
+ break;
129
+
130
+ case 'shortcut':
131
+ if (!shortcut) {
132
+ throw new Error('Shortcut is required for shortcut action');
133
+ }
134
+ await this.executeShortcut(client, shortcut);
135
+ result.shortcut = shortcut;
136
+ break;
137
+
138
+ default:
139
+ throw new Error(`Unsupported keyboard action: ${action}`);
140
+ }
141
+
142
+ if (target?.selector) {
143
+ result.target = target.selector;
144
+ }
145
+
146
+ return result;
147
+ }
148
+
149
+ /**
150
+ * Focus an element by CSS selector
151
+ */
152
+ async focusElement(client, selector) {
153
+ const result = await client.Runtime.evaluate({
154
+ expression: `
155
+ (() => {
156
+ const element = document.querySelector('${selector}');
157
+ if (element) {
158
+ element.focus();
159
+ return true;
160
+ }
161
+ return false;
162
+ })()
163
+ `
164
+ });
165
+
166
+ if (!result.result.value) {
167
+ throw new Error(`Could not focus element with selector: ${selector}`);
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Type text with optional delay between keystrokes
173
+ */
174
+ async typeText(client, text, delay) {
175
+ for (const char of text) {
176
+ await client.Input.dispatchKeyEvent({
177
+ type: 'keyDown',
178
+ text: char
179
+ });
180
+
181
+ await client.Input.dispatchKeyEvent({
182
+ type: 'keyUp',
183
+ text: char
184
+ });
185
+
186
+ if (delay > 0) {
187
+ await new Promise(resolve => setTimeout(resolve, delay));
188
+ }
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Press a key (down and up)
194
+ */
195
+ async pressKey(client, key, modifiers = 0) {
196
+ const keyInfo = this.getKeyInfo(key);
197
+
198
+ await client.Input.dispatchKeyEvent({
199
+ type: 'keyDown',
200
+ key: keyInfo.key,
201
+ code: keyInfo.code,
202
+ modifiers: modifiers
203
+ });
204
+
205
+ await client.Input.dispatchKeyEvent({
206
+ type: 'keyUp',
207
+ key: keyInfo.key,
208
+ code: keyInfo.code,
209
+ modifiers: modifiers
210
+ });
211
+ }
212
+
213
+ /**
214
+ * Key down
215
+ */
216
+ async keyDown(client, key, modifiers = 0) {
217
+ const keyInfo = this.getKeyInfo(key);
218
+
219
+ await client.Input.dispatchKeyEvent({
220
+ type: 'keyDown',
221
+ key: keyInfo.key,
222
+ code: keyInfo.code,
223
+ modifiers: modifiers
224
+ });
225
+ }
226
+
227
+ /**
228
+ * Key up
229
+ */
230
+ async keyUp(client, key, modifiers = 0) {
231
+ const keyInfo = this.getKeyInfo(key);
232
+
233
+ await client.Input.dispatchKeyEvent({
234
+ type: 'keyUp',
235
+ key: keyInfo.key,
236
+ code: keyInfo.code,
237
+ modifiers: modifiers
238
+ });
239
+ }
240
+
241
+ /**
242
+ * Execute keyboard shortcut
243
+ */
244
+ async executeShortcut(client, shortcut) {
245
+ const keys = this.parseShortcut(shortcut);
246
+
247
+ // Press modifier keys
248
+ for (const modifier of keys.modifiers) {
249
+ await this.keyDown(client, modifier);
250
+ }
251
+
252
+ // Press main key
253
+ if (keys.key) {
254
+ await this.pressKey(client, keys.key, this.convertModifiers(keys.modifiers));
255
+ }
256
+
257
+ // Release modifier keys in reverse order
258
+ for (let i = keys.modifiers.length - 1; i >= 0; i--) {
259
+ await this.keyUp(client, keys.modifiers[i]);
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Parse keyboard shortcut string
265
+ */
266
+ parseShortcut(shortcut) {
267
+ const parts = shortcut.split('+');
268
+ const key = parts.pop();
269
+ const modifiers = parts.map(mod => mod.toLowerCase());
270
+
271
+ return { key, modifiers };
272
+ }
273
+
274
+ /**
275
+ * Get key information for CDP
276
+ */
277
+ getKeyInfo(key) {
278
+ const keyMap = {
279
+ 'Enter': { key: 'Enter', code: 'Enter' },
280
+ 'Tab': { key: 'Tab', code: 'Tab' },
281
+ 'Escape': { key: 'Escape', code: 'Escape' },
282
+ 'Space': { key: ' ', code: 'Space' },
283
+ 'ArrowLeft': { key: 'ArrowLeft', code: 'ArrowLeft' },
284
+ 'ArrowRight': { key: 'ArrowRight', code: 'ArrowRight' },
285
+ 'ArrowUp': { key: 'ArrowUp', code: 'ArrowUp' },
286
+ 'ArrowDown': { key: 'ArrowDown', code: 'ArrowDown' },
287
+ 'Backspace': { key: 'Backspace', code: 'Backspace' },
288
+ 'Delete': { key: 'Delete', code: 'Delete' },
289
+ 'Home': { key: 'Home', code: 'Home' },
290
+ 'End': { key: 'End', code: 'End' },
291
+ 'PageUp': { key: 'PageUp', code: 'PageUp' },
292
+ 'PageDown': { key: 'PageDown', code: 'PageDown' },
293
+ 'F1': { key: 'F1', code: 'F1' },
294
+ 'F2': { key: 'F2', code: 'F2' },
295
+ 'F3': { key: 'F3', code: 'F3' },
296
+ 'F4': { key: 'F4', code: 'F4' },
297
+ 'F5': { key: 'F5', code: 'F5' },
298
+ 'F6': { key: 'F6', code: 'F6' },
299
+ 'F7': { key: 'F7', code: 'F7' },
300
+ 'F8': { key: 'F8', code: 'F8' },
301
+ 'F9': { key: 'F9', code: 'F9' },
302
+ 'F10': { key: 'F10', code: 'F10' },
303
+ 'F11': { key: 'F11', code: 'F11' },
304
+ 'F12': { key: 'F12', code: 'F12' }
305
+ };
306
+
307
+ if (keyMap[key]) {
308
+ return keyMap[key];
309
+ }
310
+
311
+ // For single characters
312
+ return { key: key, code: `Key${key.toUpperCase()}` };
313
+ }
314
+
315
+ /**
316
+ * Convert modifier keys to CDP format
317
+ */
318
+ convertModifiers(modifiers) {
319
+ let cdpModifiers = 0;
320
+
321
+ for (const modifier of modifiers) {
322
+ switch (modifier.toLowerCase()) {
323
+ case 'ctrl':
324
+ cdpModifiers |= 2; // Ctrl
325
+ break;
326
+ case 'shift':
327
+ cdpModifiers |= 8; // Shift
328
+ break;
329
+ case 'alt':
330
+ cdpModifiers |= 1; // Alt
331
+ break;
332
+ case 'meta':
333
+ case 'cmd':
334
+ cdpModifiers |= 4; // Meta/Cmd
335
+ break;
336
+ }
337
+ }
338
+
339
+ return cdpModifiers;
340
+ }
341
+ }
342
+
343
+ module.exports = BrowserKeyboardTool;
@@ -0,0 +1,332 @@
1
+ const ToolBase = require('../../base/ToolBase');
2
+ const browserService = require('../../../services/browserService');
3
+
4
+ /**
5
+ * Enhanced Mouse Tool - Provides precise mouse interactions with coordinate support
6
+ * Inspired by Playwright MCP mouse capabilities
7
+ */
8
+ class BrowserMouseTool extends ToolBase {
9
+ static definition = {
10
+ name: "browser_mouse",
11
+ description: "Perform mouse actions including clicks, moves, and drags with precise coordinate control. Supports both CSS selectors and direct coordinates.",
12
+ input_schema: {
13
+ type: "object",
14
+ properties: {
15
+ browserId: {
16
+ type: "string",
17
+ description: "The ID of the browser instance"
18
+ },
19
+ action: {
20
+ type: "string",
21
+ enum: ["click", "move", "drag", "hover", "rightClick", "doubleClick"],
22
+ description: "The mouse action to perform"
23
+ },
24
+ target: {
25
+ type: "object",
26
+ oneOf: [
27
+ {
28
+ type: "object",
29
+ properties: {
30
+ type: { type: "string", enum: ["coordinates"] },
31
+ x: { type: "number", description: "X coordinate" },
32
+ y: { type: "number", description: "Y coordinate" }
33
+ },
34
+ required: ["type", "x", "y"]
35
+ },
36
+ {
37
+ type: "object",
38
+ properties: {
39
+ type: { type: "string", enum: ["selector"] },
40
+ selector: { type: "string", description: "CSS selector" },
41
+ offset: {
42
+ type: "object",
43
+ properties: {
44
+ x: { type: "number", description: "X offset from element center" },
45
+ y: { type: "number", description: "Y offset from element center" }
46
+ }
47
+ }
48
+ },
49
+ required: ["type", "selector"]
50
+ }
51
+ ],
52
+ description: "Target for the mouse action - either coordinates or CSS selector"
53
+ },
54
+ dragTo: {
55
+ type: "object",
56
+ properties: {
57
+ x: { type: "number", description: "End X coordinate for drag" },
58
+ y: { type: "number", description: "End Y coordinate for drag" }
59
+ },
60
+ description: "End coordinates for drag actions"
61
+ },
62
+ button: {
63
+ type: "string",
64
+ enum: ["left", "right", "middle"],
65
+ default: "left",
66
+ description: "Mouse button to use"
67
+ },
68
+ modifiers: {
69
+ type: "array",
70
+ items: { type: "string", enum: ["ctrl", "shift", "alt", "meta"] },
71
+ description: "Keyboard modifiers to hold during action"
72
+ }
73
+ },
74
+ required: ["browserId", "action", "target"]
75
+ },
76
+ output_schema: {
77
+ type: "object",
78
+ properties: {
79
+ success: { type: "boolean", description: "Whether the mouse action was successful" },
80
+ action: { type: "string", description: "The action that was performed" },
81
+ coordinates: {
82
+ type: "object",
83
+ properties: {
84
+ x: { type: "number" },
85
+ y: { type: "number" }
86
+ },
87
+ description: "Final coordinates of the mouse action"
88
+ },
89
+ element: { type: "string", description: "CSS selector if targeting an element" },
90
+ browserId: { type: "string", description: "Browser instance ID" }
91
+ },
92
+ required: ["success", "action", "browserId"]
93
+ }
94
+ };
95
+
96
+ async execute(parameters) {
97
+ const { browserId, action, target, dragTo, button = 'left', modifiers = [] } = parameters;
98
+
99
+ const browser = browserService.getBrowserInstance(browserId);
100
+ if (!browser) {
101
+ throw new Error(`Browser instance '${browserId}' not found`);
102
+ }
103
+
104
+ const client = browser.client;
105
+
106
+ // Calculate target coordinates
107
+ let targetX, targetY;
108
+ let elementInfo = null;
109
+
110
+ if (target.type === 'coordinates') {
111
+ targetX = target.x;
112
+ targetY = target.y;
113
+ } else if (target.type === 'selector') {
114
+ // Get element bounds using DOM API
115
+ const result = await client.Runtime.evaluate({
116
+ expression: `
117
+ (() => {
118
+ const element = document.querySelector('${target.selector}');
119
+ if (!element) return null;
120
+ const rect = element.getBoundingClientRect();
121
+ return {
122
+ x: rect.left + rect.width / 2,
123
+ y: rect.top + rect.height / 2,
124
+ width: rect.width,
125
+ height: rect.height,
126
+ selector: '${target.selector}'
127
+ };
128
+ })()
129
+ `
130
+ });
131
+
132
+ if (!result.result.value) {
133
+ throw new Error(`Element not found with selector: ${target.selector}`);
134
+ }
135
+
136
+ const elementData = result.result.value;
137
+ targetX = elementData.x + (target.offset?.x || 0);
138
+ targetY = elementData.y + (target.offset?.y || 0);
139
+ elementInfo = {
140
+ selector: target.selector,
141
+ bounds: elementData
142
+ };
143
+ }
144
+
145
+ // Convert modifiers to CDP format
146
+ const cdpModifiers = this.convertModifiers(modifiers);
147
+
148
+ // Perform the mouse action
149
+ let actionResult;
150
+
151
+ switch (action) {
152
+ case 'move':
153
+ await client.Input.dispatchMouseEvent({
154
+ type: 'mouseMoved',
155
+ x: targetX,
156
+ y: targetY,
157
+ modifiers: cdpModifiers
158
+ });
159
+ actionResult = { type: 'move', x: targetX, y: targetY };
160
+ break;
161
+
162
+ case 'click':
163
+ await client.Input.dispatchMouseEvent({
164
+ type: 'mousePressed',
165
+ x: targetX,
166
+ y: targetY,
167
+ button: button,
168
+ clickCount: 1,
169
+ modifiers: cdpModifiers
170
+ });
171
+ await client.Input.dispatchMouseEvent({
172
+ type: 'mouseReleased',
173
+ x: targetX,
174
+ y: targetY,
175
+ button: button,
176
+ clickCount: 1,
177
+ modifiers: cdpModifiers
178
+ });
179
+ actionResult = { type: 'click', x: targetX, y: targetY };
180
+ break;
181
+
182
+ case 'doubleClick':
183
+ // First click
184
+ await client.Input.dispatchMouseEvent({
185
+ type: 'mousePressed',
186
+ x: targetX,
187
+ y: targetY,
188
+ button: button,
189
+ clickCount: 1,
190
+ modifiers: cdpModifiers
191
+ });
192
+ await client.Input.dispatchMouseEvent({
193
+ type: 'mouseReleased',
194
+ x: targetX,
195
+ y: targetY,
196
+ button: button,
197
+ clickCount: 1,
198
+ modifiers: cdpModifiers
199
+ });
200
+
201
+ // Second click (double click)
202
+ await client.Input.dispatchMouseEvent({
203
+ type: 'mousePressed',
204
+ x: targetX,
205
+ y: targetY,
206
+ button: button,
207
+ clickCount: 2,
208
+ modifiers: cdpModifiers
209
+ });
210
+ await client.Input.dispatchMouseEvent({
211
+ type: 'mouseReleased',
212
+ x: targetX,
213
+ y: targetY,
214
+ button: button,
215
+ clickCount: 2,
216
+ modifiers: cdpModifiers
217
+ });
218
+ actionResult = { type: 'doubleClick', x: targetX, y: targetY };
219
+ break;
220
+
221
+ case 'rightClick':
222
+ await client.Input.dispatchMouseEvent({
223
+ type: 'mousePressed',
224
+ x: targetX,
225
+ y: targetY,
226
+ button: 'right',
227
+ clickCount: 1,
228
+ modifiers: cdpModifiers
229
+ });
230
+ await client.Input.dispatchMouseEvent({
231
+ type: 'mouseReleased',
232
+ x: targetX,
233
+ y: targetY,
234
+ button: 'right',
235
+ clickCount: 1,
236
+ modifiers: cdpModifiers
237
+ });
238
+ actionResult = { type: 'rightClick', x: targetX, y: targetY };
239
+ break;
240
+
241
+ case 'hover':
242
+ await client.Input.dispatchMouseEvent({
243
+ type: 'mouseMoved',
244
+ x: targetX,
245
+ y: targetY,
246
+ modifiers: cdpModifiers
247
+ });
248
+ actionResult = { type: 'hover', x: targetX, y: targetY };
249
+ break;
250
+
251
+ case 'drag':
252
+ if (!dragTo) {
253
+ throw new Error('dragTo coordinates required for drag action');
254
+ }
255
+
256
+ // Start drag
257
+ await client.Input.dispatchMouseEvent({
258
+ type: 'mousePressed',
259
+ x: targetX,
260
+ y: targetY,
261
+ button: button,
262
+ clickCount: 1,
263
+ modifiers: cdpModifiers
264
+ });
265
+
266
+ // Move to end position
267
+ await client.Input.dispatchMouseEvent({
268
+ type: 'mouseMoved',
269
+ x: dragTo.x,
270
+ y: dragTo.y,
271
+ modifiers: cdpModifiers
272
+ });
273
+
274
+ // Release
275
+ await client.Input.dispatchMouseEvent({
276
+ type: 'mouseReleased',
277
+ x: dragTo.x,
278
+ y: dragTo.y,
279
+ button: button,
280
+ clickCount: 1,
281
+ modifiers: cdpModifiers
282
+ });
283
+
284
+ actionResult = {
285
+ type: 'drag',
286
+ from: { x: targetX, y: targetY },
287
+ to: { x: dragTo.x, y: dragTo.y }
288
+ };
289
+ break;
290
+
291
+ default:
292
+ throw new Error(`Unsupported mouse action: ${action}`);
293
+ }
294
+
295
+ return {
296
+ success: true,
297
+ action: action,
298
+ coordinates: action === 'drag' ? actionResult : { x: targetX, y: targetY },
299
+ element: elementInfo?.selector,
300
+ browserId: browserId,
301
+ actionDetails: actionResult
302
+ };
303
+ }
304
+
305
+ /**
306
+ * Convert modifier keys to CDP format
307
+ */
308
+ convertModifiers(modifiers) {
309
+ let cdpModifiers = 0;
310
+
311
+ for (const modifier of modifiers) {
312
+ switch (modifier) {
313
+ case 'ctrl':
314
+ cdpModifiers |= 2; // Ctrl
315
+ break;
316
+ case 'shift':
317
+ cdpModifiers |= 8; // Shift
318
+ break;
319
+ case 'alt':
320
+ cdpModifiers |= 1; // Alt
321
+ break;
322
+ case 'meta':
323
+ cdpModifiers |= 4; // Meta/Cmd
324
+ break;
325
+ }
326
+ }
327
+
328
+ return cdpModifiers;
329
+ }
330
+ }
331
+
332
+ module.exports = BrowserMouseTool;