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,331 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WaitForHandler = void 0;
4
+ class WaitForHandler {
5
+ constructor() {
6
+ this.name = 'wait_for';
7
+ }
8
+ async execute(client, args) {
9
+ const waitArgs = args;
10
+ if (!waitArgs.selector) {
11
+ return {
12
+ success: false,
13
+ error: 'CSS selector is required for wait_for command'
14
+ };
15
+ }
16
+ if (typeof waitArgs.selector !== 'string') {
17
+ return {
18
+ success: false,
19
+ error: 'CSS selector must be a string'
20
+ };
21
+ }
22
+ try {
23
+ await client.send('Runtime.enable');
24
+ const timeout = waitArgs.timeout || 10000;
25
+ const condition = waitArgs.condition || (waitArgs.visible ? 'visible' : 'exists');
26
+ const pollInterval = waitArgs.pollInterval || 100;
27
+ const result = await this.waitForCondition(client, waitArgs.selector, condition, timeout, pollInterval);
28
+ return result;
29
+ }
30
+ catch (error) {
31
+ return {
32
+ success: false,
33
+ error: error instanceof Error ? error.message : String(error)
34
+ };
35
+ }
36
+ }
37
+ async waitForCondition(client, selector, condition, timeout, pollInterval) {
38
+ const startTime = Date.now();
39
+ let lastError = null;
40
+ while (Date.now() - startTime < timeout) {
41
+ try {
42
+ const checkResult = await this.checkCondition(client, selector, condition);
43
+ if (checkResult.success && checkResult.data) {
44
+ return {
45
+ success: true,
46
+ data: {
47
+ selector: selector,
48
+ condition: condition,
49
+ waitTime: Date.now() - startTime,
50
+ element: checkResult.data
51
+ }
52
+ };
53
+ }
54
+ lastError = checkResult.error || 'Condition not met';
55
+ await new Promise(resolve => setTimeout(resolve, pollInterval));
56
+ }
57
+ catch (error) {
58
+ lastError = error instanceof Error ? error.message : String(error);
59
+ await new Promise(resolve => setTimeout(resolve, pollInterval));
60
+ }
61
+ }
62
+ return {
63
+ success: false,
64
+ error: `Timeout after ${timeout}ms waiting for element "${selector}" to be ${condition}. Last error: ${lastError}`
65
+ };
66
+ }
67
+ async checkCondition(client, selector, condition) {
68
+ try {
69
+ const escapedSelector = selector.replace(/'/g, "\\'").replace(/"/g, '\\"');
70
+ let expression;
71
+ switch (condition) {
72
+ case 'exists':
73
+ expression = `
74
+ (function() {
75
+ const element = document.querySelector('${escapedSelector}');
76
+ if (element) {
77
+ return {
78
+ success: true,
79
+ tagName: element.tagName,
80
+ id: element.id,
81
+ className: element.className,
82
+ condition: 'exists'
83
+ };
84
+ }
85
+ return { success: false, error: 'Element does not exist' };
86
+ })()
87
+ `;
88
+ break;
89
+ case 'visible':
90
+ expression = `
91
+ (function() {
92
+ const element = document.querySelector('${escapedSelector}');
93
+ if (!element) {
94
+ return { success: false, error: 'Element does not exist' };
95
+ }
96
+
97
+ // Check if element is visible
98
+ const rect = element.getBoundingClientRect();
99
+ const style = window.getComputedStyle(element);
100
+
101
+ const isVisible = (
102
+ rect.width > 0 &&
103
+ rect.height > 0 &&
104
+ style.visibility !== 'hidden' &&
105
+ style.display !== 'none' &&
106
+ style.opacity !== '0'
107
+ );
108
+
109
+ if (isVisible) {
110
+ return {
111
+ success: true,
112
+ tagName: element.tagName,
113
+ id: element.id,
114
+ className: element.className,
115
+ condition: 'visible',
116
+ rect: { width: rect.width, height: rect.height, x: rect.x, y: rect.y }
117
+ };
118
+ }
119
+
120
+ return { success: false, error: 'Element exists but is not visible' };
121
+ })()
122
+ `;
123
+ break;
124
+ case 'hidden':
125
+ expression = `
126
+ (function() {
127
+ const element = document.querySelector('${escapedSelector}');
128
+ if (!element) {
129
+ return {
130
+ success: true,
131
+ condition: 'hidden',
132
+ reason: 'Element does not exist (considered hidden)'
133
+ };
134
+ }
135
+
136
+ // Check if element is hidden
137
+ const rect = element.getBoundingClientRect();
138
+ const style = window.getComputedStyle(element);
139
+
140
+ const isHidden = (
141
+ rect.width === 0 ||
142
+ rect.height === 0 ||
143
+ style.visibility === 'hidden' ||
144
+ style.display === 'none' ||
145
+ style.opacity === '0'
146
+ );
147
+
148
+ if (isHidden) {
149
+ return {
150
+ success: true,
151
+ tagName: element.tagName,
152
+ id: element.id,
153
+ className: element.className,
154
+ condition: 'hidden'
155
+ };
156
+ }
157
+
158
+ return { success: false, error: 'Element exists and is visible' };
159
+ })()
160
+ `;
161
+ break;
162
+ case 'enabled':
163
+ expression = `
164
+ (function() {
165
+ const element = document.querySelector('${escapedSelector}');
166
+ if (!element) {
167
+ return { success: false, error: 'Element does not exist' };
168
+ }
169
+
170
+ // Check if element is enabled (not disabled)
171
+ const isEnabled = !element.disabled && !element.hasAttribute('disabled');
172
+
173
+ if (isEnabled) {
174
+ return {
175
+ success: true,
176
+ tagName: element.tagName,
177
+ id: element.id,
178
+ className: element.className,
179
+ condition: 'enabled',
180
+ disabled: element.disabled
181
+ };
182
+ }
183
+
184
+ return { success: false, error: 'Element exists but is disabled' };
185
+ })()
186
+ `;
187
+ break;
188
+ case 'disabled':
189
+ expression = `
190
+ (function() {
191
+ const element = document.querySelector('${escapedSelector}');
192
+ if (!element) {
193
+ return { success: false, error: 'Element does not exist' };
194
+ }
195
+
196
+ // Check if element is disabled
197
+ const isDisabled = element.disabled || element.hasAttribute('disabled');
198
+
199
+ if (isDisabled) {
200
+ return {
201
+ success: true,
202
+ tagName: element.tagName,
203
+ id: element.id,
204
+ className: element.className,
205
+ condition: 'disabled',
206
+ disabled: element.disabled
207
+ };
208
+ }
209
+
210
+ return { success: false, error: 'Element exists but is enabled' };
211
+ })()
212
+ `;
213
+ break;
214
+ default:
215
+ return {
216
+ success: false,
217
+ error: `Unknown condition: ${condition}. Supported conditions: exists, visible, hidden, enabled, disabled`
218
+ };
219
+ }
220
+ const response = await client.send('Runtime.evaluate', {
221
+ expression: expression,
222
+ returnByValue: true
223
+ });
224
+ if (response.exceptionDetails) {
225
+ return {
226
+ success: false,
227
+ error: response.exceptionDetails.exception?.description || response.exceptionDetails.text
228
+ };
229
+ }
230
+ const result = response.result.value;
231
+ if (result.success) {
232
+ return {
233
+ success: true,
234
+ data: result
235
+ };
236
+ }
237
+ else {
238
+ return {
239
+ success: false,
240
+ error: result.error || 'Condition check failed'
241
+ };
242
+ }
243
+ }
244
+ catch (error) {
245
+ return {
246
+ success: false,
247
+ error: `Condition check failed: ${error instanceof Error ? error.message : String(error)}`
248
+ };
249
+ }
250
+ }
251
+ validateArgs(args) {
252
+ if (typeof args !== 'object' || args === null) {
253
+ return false;
254
+ }
255
+ const waitArgs = args;
256
+ if (!waitArgs.selector || typeof waitArgs.selector !== 'string') {
257
+ return false;
258
+ }
259
+ if (waitArgs.timeout !== undefined && typeof waitArgs.timeout !== 'number') {
260
+ return false;
261
+ }
262
+ if (waitArgs.visible !== undefined && typeof waitArgs.visible !== 'boolean') {
263
+ return false;
264
+ }
265
+ if (waitArgs.condition !== undefined &&
266
+ !['exists', 'visible', 'hidden', 'enabled', 'disabled'].includes(waitArgs.condition)) {
267
+ return false;
268
+ }
269
+ if (waitArgs.pollInterval !== undefined && typeof waitArgs.pollInterval !== 'number') {
270
+ return false;
271
+ }
272
+ return true;
273
+ }
274
+ getHelp() {
275
+ return `
276
+ wait_for - Wait for an element to appear or meet specific conditions
277
+
278
+ Usage:
279
+ wait_for <selector>
280
+ wait_for <selector> --condition visible
281
+ wait_for <selector> --timeout 15000
282
+ wait_for "#button" --condition enabled --timeout 5000
283
+
284
+ Arguments:
285
+ <selector> CSS selector for the element to wait for
286
+
287
+ Options:
288
+ --timeout <ms> Maximum time to wait (default: 10000ms)
289
+ --condition <type> Condition to wait for (default: exists)
290
+ --visible Wait for element to be visible (same as --condition visible)
291
+ --poll-interval <ms> Polling interval (default: 100ms)
292
+
293
+ Conditions:
294
+ exists Element exists in DOM (default)
295
+ visible Element exists and is visible (not hidden, has dimensions)
296
+ hidden Element is hidden or does not exist
297
+ enabled Element exists and is not disabled
298
+ disabled Element exists and is disabled
299
+
300
+ Examples:
301
+ # Wait for element to exist
302
+ wait_for "#loading-spinner"
303
+
304
+ # Wait for element to be visible
305
+ wait_for "#modal" --condition visible
306
+
307
+ # Wait for button to be enabled
308
+ wait_for "#submit-btn" --condition enabled
309
+
310
+ # Wait for element to be hidden
311
+ wait_for "#loading" --condition hidden
312
+
313
+ # Wait with custom timeout
314
+ wait_for ".slow-element" --timeout 30000
315
+
316
+ # Wait with custom polling interval
317
+ wait_for "#element" --poll-interval 500
318
+
319
+ # Legacy syntax (still supported)
320
+ wait_for "#modal" --visible
321
+
322
+ Note:
323
+ - Polls the condition at regular intervals until met or timeout
324
+ - Returns immediately when condition is satisfied
325
+ - For 'visible' condition, checks dimensions, display, visibility, and opacity
326
+ - For 'enabled/disabled' conditions, works with form elements
327
+ - Useful for waiting for dynamic content, AJAX responses, or animations
328
+ `;
329
+ }
330
+ }
331
+ exports.WaitForHandler = WaitForHandler;
@@ -23,3 +23,12 @@ __exportStar(require("./GetNetworkRequestHandler"), exports);
23
23
  __exportStar(require("./ListNetworkRequestsHandler"), exports);
24
24
  __exportStar(require("./InstallCursorCommandHandler"), exports);
25
25
  __exportStar(require("./InstallClaudeSkillHandler"), exports);
26
+ __exportStar(require("./ClickHandler"), exports);
27
+ __exportStar(require("./HoverHandler"), exports);
28
+ __exportStar(require("./FillHandler"), exports);
29
+ __exportStar(require("./FillFormHandler"), exports);
30
+ __exportStar(require("./DragHandler"), exports);
31
+ __exportStar(require("./PressKeyHandler"), exports);
32
+ __exportStar(require("./UploadFileHandler"), exports);
33
+ __exportStar(require("./WaitForHandler"), exports);
34
+ __exportStar(require("./HandleDialogHandler"), exports);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "chrome-cdp-cli",
3
- "version": "1.2.2",
4
- "description": "Command-line tool for controlling Chrome browser via Chrome DevTools Protocol. Features: JavaScript execution, screenshots, DOM snapshots, console/network monitoring, IDE integration (Cursor commands & Claude skills). Install IDE tools with --force option for directory validation bypass.",
3
+ "version": "1.3.0",
4
+ "description": "LLM-first browser automation CLI via Chrome DevTools Protocol. Eval-first design optimized for AI assistants - LLMs write JavaScript scripts for rapid validation. Features: JavaScript execution, element interaction, screenshots, DOM snapshots, console/network monitoring. Built-in IDE integration for Cursor and Claude.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "bin": {