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.
- package/README.md +430 -45
- package/dist/cli/CLIApplication.js +9 -0
- package/dist/cli/CommandRouter.js +12 -1
- package/dist/handlers/ClickHandler.js +241 -0
- package/dist/handlers/DragHandler.js +267 -0
- package/dist/handlers/FillFormHandler.js +245 -0
- package/dist/handlers/FillHandler.js +354 -0
- package/dist/handlers/HandleDialogHandler.js +197 -0
- package/dist/handlers/HoverHandler.js +268 -0
- package/dist/handlers/InstallClaudeSkillHandler.js +706 -58
- package/dist/handlers/InstallCursorCommandHandler.js +208 -74
- package/dist/handlers/PressKeyHandler.js +337 -0
- package/dist/handlers/TakeSnapshotHandler.js +104 -32
- package/dist/handlers/UploadFileHandler.js +325 -0
- package/dist/handlers/WaitForHandler.js +331 -0
- package/dist/handlers/index.js +9 -0
- package/package.json +2 -2
|
@@ -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;
|