chrome-cdp-cli 1.2.2 → 1.3.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.
@@ -0,0 +1,197 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HandleDialogHandler = void 0;
4
+ class HandleDialogHandler {
5
+ constructor() {
6
+ this.name = 'handle_dialog';
7
+ this.pendingDialog = null;
8
+ this.dialogPromise = null;
9
+ this.dialogResolve = null;
10
+ }
11
+ async execute(client, args) {
12
+ const dialogArgs = args;
13
+ if (!dialogArgs.action) {
14
+ return {
15
+ success: false,
16
+ error: 'Action is required for handle_dialog command (accept or dismiss)'
17
+ };
18
+ }
19
+ if (!['accept', 'dismiss'].includes(dialogArgs.action)) {
20
+ return {
21
+ success: false,
22
+ error: 'Action must be either "accept" or "dismiss"'
23
+ };
24
+ }
25
+ try {
26
+ await client.send('Page.enable');
27
+ await client.send('Runtime.enable');
28
+ const timeout = dialogArgs.timeout || 5000;
29
+ const waitForDialog = dialogArgs.waitForDialog !== false;
30
+ this.setupDialogListener(client);
31
+ let dialog;
32
+ if (waitForDialog) {
33
+ dialog = await this.waitForDialog(timeout);
34
+ }
35
+ else {
36
+ if (!this.pendingDialog) {
37
+ return {
38
+ success: false,
39
+ error: 'No dialog is currently open'
40
+ };
41
+ }
42
+ dialog = this.pendingDialog;
43
+ }
44
+ const result = await this.handleDialog(client, dialog, dialogArgs.action, dialogArgs.text);
45
+ this.pendingDialog = null;
46
+ return result;
47
+ }
48
+ catch (error) {
49
+ return {
50
+ success: false,
51
+ error: error instanceof Error ? error.message : String(error)
52
+ };
53
+ }
54
+ }
55
+ setupDialogListener(client) {
56
+ client.off('Page.javascriptDialogOpening', this.onDialogOpening.bind(this));
57
+ client.on('Page.javascriptDialogOpening', this.onDialogOpening.bind(this));
58
+ }
59
+ onDialogOpening(params) {
60
+ const dialogParams = params;
61
+ const dialog = {
62
+ type: dialogParams.type,
63
+ message: dialogParams.message,
64
+ defaultPrompt: dialogParams.defaultPrompt
65
+ };
66
+ this.pendingDialog = dialog;
67
+ if (this.dialogResolve) {
68
+ this.dialogResolve(dialog);
69
+ this.dialogResolve = null;
70
+ this.dialogPromise = null;
71
+ }
72
+ }
73
+ async waitForDialog(timeout) {
74
+ if (this.pendingDialog) {
75
+ return this.pendingDialog;
76
+ }
77
+ this.dialogPromise = new Promise((resolve, reject) => {
78
+ this.dialogResolve = resolve;
79
+ setTimeout(() => {
80
+ if (this.dialogResolve === resolve) {
81
+ this.dialogResolve = null;
82
+ this.dialogPromise = null;
83
+ reject(new Error(`Timeout after ${timeout}ms waiting for dialog to appear`));
84
+ }
85
+ }, timeout);
86
+ });
87
+ return this.dialogPromise;
88
+ }
89
+ async handleDialog(client, dialog, action, text) {
90
+ try {
91
+ const accept = action === 'accept';
92
+ let promptText;
93
+ if (dialog.type === 'prompt' && accept) {
94
+ promptText = text !== undefined ? text : dialog.defaultPrompt || '';
95
+ }
96
+ await client.send('Page.handleJavaScriptDialog', {
97
+ accept: accept,
98
+ promptText: promptText
99
+ });
100
+ return {
101
+ success: true,
102
+ data: {
103
+ action: action,
104
+ dialog: {
105
+ type: dialog.type,
106
+ message: dialog.message,
107
+ defaultPrompt: dialog.defaultPrompt
108
+ },
109
+ promptText: promptText
110
+ }
111
+ };
112
+ }
113
+ catch (error) {
114
+ return {
115
+ success: false,
116
+ error: `Failed to handle dialog: ${error instanceof Error ? error.message : String(error)}`
117
+ };
118
+ }
119
+ }
120
+ validateArgs(args) {
121
+ if (typeof args !== 'object' || args === null) {
122
+ return false;
123
+ }
124
+ const dialogArgs = args;
125
+ if (!dialogArgs.action || !['accept', 'dismiss'].includes(dialogArgs.action)) {
126
+ return false;
127
+ }
128
+ if (dialogArgs.text !== undefined && typeof dialogArgs.text !== 'string') {
129
+ return false;
130
+ }
131
+ if (dialogArgs.waitForDialog !== undefined && typeof dialogArgs.waitForDialog !== 'boolean') {
132
+ return false;
133
+ }
134
+ if (dialogArgs.timeout !== undefined && typeof dialogArgs.timeout !== 'number') {
135
+ return false;
136
+ }
137
+ return true;
138
+ }
139
+ getHelp() {
140
+ return `
141
+ handle_dialog - Handle browser dialogs (alert, confirm, prompt)
142
+
143
+ Usage:
144
+ handle_dialog <action>
145
+ handle_dialog accept
146
+ handle_dialog dismiss
147
+ handle_dialog accept --text "user input"
148
+ handle_dialog accept --timeout 10000
149
+
150
+ Arguments:
151
+ <action> Action to take: "accept" or "dismiss"
152
+
153
+ Options:
154
+ --text <string> Text to enter for prompt dialogs (when accepting)
155
+ --wait-for-dialog Wait for dialog to appear (default: true)
156
+ --no-wait Don't wait for dialog (handle immediately if present)
157
+ --timeout <ms> Timeout for waiting for dialog (default: 5000ms)
158
+
159
+ Examples:
160
+ # Accept an alert dialog
161
+ handle_dialog accept
162
+
163
+ # Dismiss a confirm dialog
164
+ handle_dialog dismiss
165
+
166
+ # Accept a prompt dialog with text input
167
+ handle_dialog accept --text "John Doe"
168
+
169
+ # Accept a prompt dialog with empty text
170
+ handle_dialog accept --text ""
171
+
172
+ # Handle dialog with custom timeout
173
+ handle_dialog accept --timeout 10000
174
+
175
+ # Handle dialog without waiting (if already present)
176
+ handle_dialog accept --no-wait
177
+
178
+ Dialog Types:
179
+ alert Simple message dialog (only "OK" button)
180
+ confirm Yes/No dialog ("OK" and "Cancel" buttons)
181
+ prompt Input dialog with text field
182
+ beforeunload Page unload confirmation dialog
183
+
184
+ Actions:
185
+ accept Click "OK" or "Yes" button
186
+ dismiss Click "Cancel" or "No" button
187
+
188
+ Note:
189
+ - Uses CDP Page.javascriptDialogOpening event to detect dialogs
190
+ - Automatically waits for dialogs to appear unless --no-wait is specified
191
+ - For prompt dialogs, use --text to provide input (defaults to empty string)
192
+ - Dialog must be triggered by JavaScript (alert(), confirm(), prompt())
193
+ - Command will timeout if no dialog appears within the specified time
194
+ `;
195
+ }
196
+ }
197
+ exports.HandleDialogHandler = HandleDialogHandler;
@@ -0,0 +1,268 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HoverHandler = void 0;
4
+ class HoverHandler {
5
+ constructor() {
6
+ this.name = 'hover';
7
+ }
8
+ async execute(client, args) {
9
+ const hoverArgs = args;
10
+ if (!hoverArgs.selector) {
11
+ return {
12
+ success: false,
13
+ error: 'CSS selector is required for hover command'
14
+ };
15
+ }
16
+ if (typeof hoverArgs.selector !== 'string') {
17
+ return {
18
+ success: false,
19
+ error: 'CSS selector must be a string'
20
+ };
21
+ }
22
+ try {
23
+ await client.send('DOM.enable');
24
+ await client.send('Runtime.enable');
25
+ const timeout = hoverArgs.timeout || 5000;
26
+ const waitForElement = hoverArgs.waitForElement !== false;
27
+ if (waitForElement) {
28
+ const elementFound = await this.waitForElement(client, hoverArgs.selector, timeout);
29
+ if (!elementFound) {
30
+ return {
31
+ success: false,
32
+ error: `Element with selector "${hoverArgs.selector}" not found within ${timeout}ms`
33
+ };
34
+ }
35
+ }
36
+ const cdpResult = await this.hoverViaCDP(client, hoverArgs.selector);
37
+ if (cdpResult.success) {
38
+ return cdpResult;
39
+ }
40
+ const evalResult = await this.hoverViaEval(client, hoverArgs.selector);
41
+ return evalResult;
42
+ }
43
+ catch (error) {
44
+ return {
45
+ success: false,
46
+ error: error instanceof Error ? error.message : String(error)
47
+ };
48
+ }
49
+ }
50
+ async waitForElement(client, selector, timeout) {
51
+ const startTime = Date.now();
52
+ while (Date.now() - startTime < timeout) {
53
+ try {
54
+ const result = await client.send('Runtime.evaluate', {
55
+ expression: `document.querySelector('${selector.replace(/'/g, "\\'")}') !== null`,
56
+ returnByValue: true
57
+ });
58
+ if (result.result.value) {
59
+ return true;
60
+ }
61
+ await new Promise(resolve => setTimeout(resolve, 100));
62
+ }
63
+ catch (error) {
64
+ await new Promise(resolve => setTimeout(resolve, 100));
65
+ }
66
+ }
67
+ return false;
68
+ }
69
+ async hoverViaCDP(client, selector) {
70
+ try {
71
+ const documentResponse = await client.send('DOM.getDocument');
72
+ const rootNodeId = documentResponse.root.nodeId;
73
+ const queryResponse = await client.send('DOM.querySelector', {
74
+ nodeId: rootNodeId,
75
+ selector: selector
76
+ });
77
+ if (!queryResponse.nodeId || queryResponse.nodeId === 0) {
78
+ return {
79
+ success: false,
80
+ error: `Element with selector "${selector}" not found`
81
+ };
82
+ }
83
+ const objectResponse = await client.send('DOM.resolveNode', {
84
+ nodeId: queryResponse.nodeId
85
+ });
86
+ if (!objectResponse.object?.objectId) {
87
+ return {
88
+ success: false,
89
+ error: 'Failed to resolve element to remote object'
90
+ };
91
+ }
92
+ const hoverResponse = await client.send('Runtime.callFunctionOn', {
93
+ objectId: objectResponse.object.objectId,
94
+ functionDeclaration: `
95
+ function() {
96
+ // Scroll element into view if needed
97
+ this.scrollIntoView({ behavior: 'instant', block: 'center' });
98
+
99
+ // Create and dispatch mouseover event
100
+ const mouseOverEvent = new MouseEvent('mouseover', {
101
+ bubbles: true,
102
+ cancelable: true,
103
+ view: window
104
+ });
105
+ this.dispatchEvent(mouseOverEvent);
106
+
107
+ // Create and dispatch mouseenter event
108
+ const mouseEnterEvent = new MouseEvent('mouseenter', {
109
+ bubbles: false,
110
+ cancelable: false,
111
+ view: window
112
+ });
113
+ this.dispatchEvent(mouseEnterEvent);
114
+
115
+ return {
116
+ success: true,
117
+ tagName: this.tagName,
118
+ id: this.id,
119
+ className: this.className
120
+ };
121
+ }
122
+ `,
123
+ returnByValue: true,
124
+ userGesture: true
125
+ });
126
+ if (hoverResponse.exceptionDetails) {
127
+ return {
128
+ success: false,
129
+ error: `Hover execution failed: ${hoverResponse.exceptionDetails.exception?.description || hoverResponse.exceptionDetails.text}`
130
+ };
131
+ }
132
+ return {
133
+ success: true,
134
+ data: {
135
+ selector: selector,
136
+ element: hoverResponse.result.value,
137
+ method: 'CDP'
138
+ }
139
+ };
140
+ }
141
+ catch (error) {
142
+ return {
143
+ success: false,
144
+ error: `CDP hover failed: ${error instanceof Error ? error.message : String(error)}`
145
+ };
146
+ }
147
+ }
148
+ async hoverViaEval(client, selector) {
149
+ try {
150
+ const escapedSelector = selector.replace(/'/g, "\\'").replace(/"/g, '\\"');
151
+ const expression = `
152
+ (function() {
153
+ const element = document.querySelector('${escapedSelector}');
154
+ if (!element) {
155
+ throw new Error('Element with selector "${escapedSelector}" not found');
156
+ }
157
+
158
+ // Scroll element into view if needed
159
+ element.scrollIntoView({ behavior: 'instant', block: 'center' });
160
+
161
+ // Create and dispatch mouseover event
162
+ const mouseOverEvent = new MouseEvent('mouseover', {
163
+ bubbles: true,
164
+ cancelable: true,
165
+ view: window
166
+ });
167
+ element.dispatchEvent(mouseOverEvent);
168
+
169
+ // Create and dispatch mouseenter event
170
+ const mouseEnterEvent = new MouseEvent('mouseenter', {
171
+ bubbles: false,
172
+ cancelable: false,
173
+ view: window
174
+ });
175
+ element.dispatchEvent(mouseEnterEvent);
176
+
177
+ return {
178
+ success: true,
179
+ tagName: element.tagName,
180
+ id: element.id,
181
+ className: element.className
182
+ };
183
+ })()
184
+ `;
185
+ const response = await client.send('Runtime.evaluate', {
186
+ expression: expression,
187
+ returnByValue: true,
188
+ userGesture: true
189
+ });
190
+ if (response.exceptionDetails) {
191
+ return {
192
+ success: false,
193
+ error: response.exceptionDetails.exception?.description || response.exceptionDetails.text
194
+ };
195
+ }
196
+ return {
197
+ success: true,
198
+ data: {
199
+ selector: selector,
200
+ element: response.result.value,
201
+ method: 'eval'
202
+ }
203
+ };
204
+ }
205
+ catch (error) {
206
+ return {
207
+ success: false,
208
+ error: `Eval hover failed: ${error instanceof Error ? error.message : String(error)}`
209
+ };
210
+ }
211
+ }
212
+ validateArgs(args) {
213
+ if (typeof args !== 'object' || args === null) {
214
+ return false;
215
+ }
216
+ const hoverArgs = args;
217
+ if (!hoverArgs.selector || typeof hoverArgs.selector !== 'string') {
218
+ return false;
219
+ }
220
+ if (hoverArgs.waitForElement !== undefined && typeof hoverArgs.waitForElement !== 'boolean') {
221
+ return false;
222
+ }
223
+ if (hoverArgs.timeout !== undefined && typeof hoverArgs.timeout !== 'number') {
224
+ return false;
225
+ }
226
+ return true;
227
+ }
228
+ getHelp() {
229
+ return `
230
+ hover - Hover over an element using CSS selector
231
+
232
+ Usage:
233
+ hover <selector>
234
+ hover "#menu-item"
235
+ hover ".dropdown-trigger" --timeout 10000
236
+ hover "button[data-tooltip]" --no-wait
237
+
238
+ Arguments:
239
+ <selector> CSS selector for the element to hover over
240
+
241
+ Options:
242
+ --wait-for-element Wait for element to be available (default: true)
243
+ --no-wait Don't wait for element (same as --wait-for-element=false)
244
+ --timeout <ms> Timeout for waiting for element (default: 5000ms)
245
+
246
+ Examples:
247
+ # Hover over a menu item by ID
248
+ hover "#menu-item"
249
+
250
+ # Hover over a dropdown trigger
251
+ hover ".dropdown-trigger"
252
+
253
+ # Hover with custom timeout
254
+ hover ".slow-loading-element" --timeout 10000
255
+
256
+ # Hover without waiting (fail immediately if not found)
257
+ hover "#optional-tooltip" --no-wait
258
+
259
+ Note:
260
+ - Uses CDP DOM.querySelector and Runtime.callFunctionOn for precise control
261
+ - Falls back to JavaScript eval if CDP approach fails
262
+ - Automatically scrolls element into view before hovering
263
+ - Dispatches both 'mouseover' and 'mouseenter' events
264
+ - Works with CSS :hover selectors and JavaScript event listeners
265
+ `;
266
+ }
267
+ }
268
+ exports.HoverHandler = HoverHandler;