@bubstack/moe-glass 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.
Files changed (62) hide show
  1. package/README.md +29 -0
  2. package/agents/browser-user.md +105 -0
  3. package/dist/LICENSE +25 -0
  4. package/dist/index.d.ts +9 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +22517 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/payload.d.ts +214 -0
  9. package/dist/payload.d.ts.map +1 -0
  10. package/dist/payload.js +325 -0
  11. package/dist/payload.js.map +1 -0
  12. package/package.json +59 -0
  13. package/skills/browsing/COMMANDLINE-USAGE.md +595 -0
  14. package/skills/browsing/EXAMPLES.md +717 -0
  15. package/skills/browsing/README.md +55 -0
  16. package/skills/browsing/SKILL.md +478 -0
  17. package/skills/browsing/chrome-ws +1021 -0
  18. package/skills/browsing/chrome-ws-lib.js +461 -0
  19. package/skills/browsing/host-override.js +98 -0
  20. package/skills/browsing/lib/browser-bridge.js +175 -0
  21. package/skills/browsing/lib/browser-session.js +137 -0
  22. package/skills/browsing/lib/capture.js +499 -0
  23. package/skills/browsing/lib/cdp-router.js +72 -0
  24. package/skills/browsing/lib/cdp-utils.js +18 -0
  25. package/skills/browsing/lib/chrome-launcher-helpers.js +374 -0
  26. package/skills/browsing/lib/chrome-process.js +464 -0
  27. package/skills/browsing/lib/console-logging.js +70 -0
  28. package/skills/browsing/lib/cookies.js +17 -0
  29. package/skills/browsing/lib/dialogs-render.js +154 -0
  30. package/skills/browsing/lib/dialogs-router.js +117 -0
  31. package/skills/browsing/lib/dialogs.js +254 -0
  32. package/skills/browsing/lib/element-selector.js +91 -0
  33. package/skills/browsing/lib/evaluation.js +85 -0
  34. package/skills/browsing/lib/extraction.js +55 -0
  35. package/skills/browsing/lib/file-upload.js +56 -0
  36. package/skills/browsing/lib/html-diff.js +122 -0
  37. package/skills/browsing/lib/key-definitions.js +149 -0
  38. package/skills/browsing/lib/keyboard-input.js +288 -0
  39. package/skills/browsing/lib/mouse.js +423 -0
  40. package/skills/browsing/lib/navigation.js +272 -0
  41. package/skills/browsing/lib/page-scripts/dom-summary.js +31 -0
  42. package/skills/browsing/lib/page-scripts/markdown.js +85 -0
  43. package/skills/browsing/lib/page-scripts/permission-shim.js +80 -0
  44. package/skills/browsing/lib/page-session.js +106 -0
  45. package/skills/browsing/lib/profile-lock.js +179 -0
  46. package/skills/browsing/lib/screenshot.js +171 -0
  47. package/skills/browsing/lib/select-option.js +99 -0
  48. package/skills/browsing/lib/session-state.js +66 -0
  49. package/skills/browsing/lib/tabs.js +144 -0
  50. package/skills/browsing/lib/viewport.js +103 -0
  51. package/skills/browsing/lib/websocket-client.js +162 -0
  52. package/skills/browsing/package.json +11 -0
  53. package/skills/browsing/test-chrome-args.js +81 -0
  54. package/skills/browsing/test-cookies.js +21 -0
  55. package/skills/browsing/test-e2e.sh +51 -0
  56. package/skills/browsing/test-extract.sh +17 -0
  57. package/skills/browsing/test-interact.sh +11 -0
  58. package/skills/browsing/test-navigate.sh +9 -0
  59. package/skills/browsing/test-raw.sh +8 -0
  60. package/skills/browsing/test-tabs.sh +15 -0
  61. package/skills/browsing/test-viewport.js +27 -0
  62. package/skills/browsing/test-wait.sh +9 -0
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@bubstack/moe-glass",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Direct Chrome DevTools Protocol access. Skill mode (17 CLI commands) plus MCP mode (single use_browser tool). Auto-starts Chrome.",
6
+ "license": "MIT",
7
+ "author": {
8
+ "name": "Zak Keown",
9
+ "email": "zak.keown@outlook.com"
10
+ },
11
+ "homepage": "https://gitlab.com/moe-ai/moe",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://gitlab.com/moe-ai/moe.git"
15
+ },
16
+ "keywords": [
17
+ "claude-code",
18
+ "codex",
19
+ "chrome",
20
+ "devtools-protocol",
21
+ "browser",
22
+ "automation",
23
+ "screenshots",
24
+ "mcp"
25
+ ],
26
+ "main": "dist/index.js",
27
+ "bin": {
28
+ "moe-glass": "./dist/index.js"
29
+ },
30
+ "publishConfig": {
31
+ "access": "public",
32
+ "registry": "https://registry.npmjs.org/"
33
+ },
34
+ "files": [
35
+ "dist",
36
+ "agents",
37
+ "skills"
38
+ ],
39
+ "engines": {
40
+ "node": ">=24"
41
+ },
42
+ "dependencies": {
43
+ "@modelcontextprotocol/sdk": "^1.6.1",
44
+ "zod": "^3.23.8"
45
+ },
46
+ "devDependencies": {
47
+ "@types/node": "^24.7.0",
48
+ "esbuild": "^0.25.11",
49
+ "jsdom": "^29.0.2",
50
+ "typescript": "^5.9.0"
51
+ },
52
+ "scripts": {
53
+ "build": "tsc -b && esbuild src/index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --external:fsevents && node ../../scripts/copy-license.mjs MIT dist/LICENSE",
54
+ "typecheck": "tsc -b --pretty",
55
+ "test": "vitest run --project unit",
56
+ "test:chrome": "vitest run --project chrome",
57
+ "lint": "biome check ."
58
+ }
59
+ }
@@ -0,0 +1,595 @@
1
+ # Command-Line Usage: chrome-ws Tool
2
+
3
+ Direct command-line access to Chrome DevTools Protocol via the `chrome-ws` bash tool.
4
+
5
+ **Note**: For use within Claude Code, the MCP `use_browser` tool is recommended. This document is for direct command-line usage or integration with other tools.
6
+
7
+ ## Setup
8
+
9
+ ```bash
10
+ cd ~/.claude/plugins/cache/using-chrome-directly/skills/using-chrome-directly
11
+ chmod +x chrome-ws
12
+ ./chrome-ws start # Auto-detects platform, launches Chrome
13
+ ./chrome-ws tabs # Verify running
14
+ ```
15
+
16
+ Chrome starts headed with `--remote-debugging-port=9222`, a separate profile in `/tmp/chrome-debug` (or `C:\temp\chrome-debug` on Windows), and the same automation flag set the MCP launch path uses.
17
+
18
+ ## Environment Variables
19
+
20
+ | Variable | Default | Description |
21
+ |----------|---------|-------------|
22
+ | `CHROME_WS_BROWSER` | (auto-detect) | Path to browser executable. Overrides auto-detection. |
23
+ | `CHROME_WS_HOST` | `127.0.0.1` | Debug host address |
24
+ | `CHROME_WS_PORT` | `9222` | Debug port number |
25
+ | `CHROME_EXTRA_ARGS` | (none) | Whitespace-separated extra Chrome flags appended at launch. Needed on headless Linux / as root, e.g. `--no-sandbox --headless=new --disable-dev-shm-usage`. |
26
+ | `CHROME_WS_PROFILE` | (auto) | Profile name. Default is `moe-glass`; if another live process holds that profile's lock, the CLI / MCP falls through to `moe-glass-2`, `-3`, etc. Set this to opt out — explicit names always claim the named profile (sharing with whoever else has it). |
27
+
28
+ **Examples:**
29
+
30
+ ```bash
31
+ # Force Chromium instead of Chrome
32
+ CHROME_WS_BROWSER=/usr/bin/chromium ./chrome-ws start
33
+
34
+ # Use custom port
35
+ CHROME_WS_PORT=9333 ./chrome-ws start
36
+
37
+ # Use Brave browser
38
+ CHROME_WS_BROWSER="/usr/bin/brave-browser" ./chrome-ws start
39
+ ```
40
+
41
+ ## Command Reference
42
+
43
+ **Lifecycle:**
44
+ ```bash
45
+ chrome-ws start [port] # Launch Chrome (auto-detects platform)
46
+ chrome-ws stop # Kill Chrome
47
+ chrome-ws pid # Print Chrome PID
48
+ chrome-ws info # Print Chrome info (JSON: pid, port, mode, profile, profileDir, running)
49
+ chrome-ws --help # Show usage
50
+ chrome-ws --version # Print version
51
+ ```
52
+
53
+ `chrome-ws --port=N <command>` overrides `CHROME_WS_PORT` for a single invocation.
54
+
55
+ Unknown commands now print `Unknown command: <name>` and point at `--help` instead of the raw-specific usage banner.
56
+
57
+ **Tab Management:**
58
+ ```bash
59
+ chrome-ws tabs # List tabs as TSV (id<TAB>url<TAB>title); use `info` for JSON
60
+ chrome-ws new <url> # Create tab
61
+ chrome-ws close <tab> # Close tab (accepts numeric index or ws-url)
62
+ ```
63
+
64
+ **Navigation:**
65
+ ```bash
66
+ chrome-ws navigate <tab> <url> # Navigate
67
+ chrome-ws wait-for <tab> <selector> # Wait for element
68
+ chrome-ws wait-text <tab> <text> # Wait for text
69
+ ```
70
+
71
+ **Interaction:**
72
+ ```bash
73
+ chrome-ws click <tab> <selector> # Click
74
+ chrome-ws fill <tab> <selector> <value> # Fill input
75
+ chrome-ws select <tab> <selector> <value> # Select dropdown
76
+ ```
77
+
78
+ **Extraction:**
79
+ ```bash
80
+ chrome-ws eval <tab> <js> # Execute JavaScript
81
+ chrome-ws extract <tab> <selector> # Get text content
82
+ chrome-ws attr <tab> <selector> <attr> # Get attribute
83
+ chrome-ws html <tab> [selector] # Get HTML
84
+ ```
85
+
86
+ **Export:**
87
+ ```bash
88
+ chrome-ws screenshot <tab> <file.png> # Capture screenshot
89
+ chrome-ws markdown <tab> <file.md> # Save as markdown
90
+ ```
91
+
92
+ **Raw Protocol:**
93
+ ```bash
94
+ chrome-ws raw <ws-url> <json-rpc> # Direct CDP access
95
+ ```
96
+
97
+ `<tab>` accepts either tab index (0, 1, 2) or full WebSocket URL.
98
+
99
+ ## Examples
100
+
101
+ ### Basic Operations
102
+
103
+ **Extract page content:**
104
+ ```bash
105
+ chrome-ws navigate 0 "https://example.com"
106
+ chrome-ws wait-for 0 "h1"
107
+
108
+ # Get page title
109
+ TITLE=$(chrome-ws eval 0 "document.title")
110
+
111
+ # Get main heading
112
+ HEADING=$(chrome-ws extract 0 "h1")
113
+
114
+ # Get first link URL
115
+ LINK=$(chrome-ws attr 0 "a" "href")
116
+ ```
117
+
118
+ **Get all links:**
119
+ ```bash
120
+ chrome-ws navigate 0 "https://example.com"
121
+ LINKS=$(chrome-ws eval 0 "Array.from(document.querySelectorAll('a')).map(a => ({
122
+ text: a.textContent.trim(),
123
+ href: a.href
124
+ }))")
125
+ echo "$LINKS"
126
+ ```
127
+
128
+ **Extract table data:**
129
+ ```bash
130
+ chrome-ws navigate 0 "https://example.com/data"
131
+ chrome-ws wait-for 0 "table"
132
+
133
+ # Convert table to JSON array
134
+ TABLE=$(chrome-ws eval 0 "
135
+ Array.from(document.querySelectorAll('table tr')).map(row =>
136
+ Array.from(row.cells).map(cell => cell.textContent.trim())
137
+ )
138
+ ")
139
+ ```
140
+
141
+ ### Form Automation
142
+
143
+ **Simple login:**
144
+ ```bash
145
+ chrome-ws navigate 0 "https://app.example.com/login"
146
+ chrome-ws wait-for 0 "input[name=email]"
147
+
148
+ # Fill credentials
149
+ chrome-ws fill 0 "input[name=email]" "user@example.com"
150
+ chrome-ws fill 0 "input[name=password]" "securepass123"
151
+
152
+ # Submit and wait for dashboard
153
+ chrome-ws click 0 "button[type=submit]"
154
+ chrome-ws wait-text 0 "Dashboard"
155
+ ```
156
+
157
+ **Multi-step form:**
158
+ ```bash
159
+ chrome-ws navigate 0 "https://example.com/register"
160
+
161
+ # Step 1: Personal information
162
+ chrome-ws fill 0 "input[name=firstName]" "John"
163
+ chrome-ws fill 0 "input[name=lastName]" "Doe"
164
+ chrome-ws fill 0 "input[name=email]" "john@example.com"
165
+ chrome-ws click 0 "button.next"
166
+
167
+ # Wait for step 2 to load
168
+ chrome-ws wait-for 0 "input[name=address]"
169
+
170
+ # Step 2: Address
171
+ chrome-ws fill 0 "input[name=address]" "123 Main St"
172
+ chrome-ws select 0 "select[name=state]" "IL"
173
+ chrome-ws fill 0 "input[name=zip]" "62701"
174
+ chrome-ws click 0 "button.submit"
175
+
176
+ chrome-ws wait-text 0 "Registration complete"
177
+ ```
178
+
179
+ **Search with filters:**
180
+ ```bash
181
+ chrome-ws navigate 0 "https://library.example.com/search"
182
+ chrome-ws wait-for 0 "form"
183
+
184
+ # Select category dropdown
185
+ chrome-ws select 0 "select[name=category]" "books"
186
+
187
+ # Fill search term
188
+ chrome-ws fill 0 "input[name=query]" "chrome devtools"
189
+
190
+ # Submit search
191
+ chrome-ws click 0 "button[type=submit]"
192
+ chrome-ws wait-for 0 ".results"
193
+
194
+ # Count results
195
+ RESULTS=$(chrome-ws eval 0 "document.querySelectorAll('.result').length")
196
+ echo "Found $RESULTS results"
197
+ ```
198
+
199
+ ### Web Scraping
200
+
201
+ **Article content:**
202
+ ```bash
203
+ chrome-ws navigate 0 "https://blog.example.com/article"
204
+ chrome-ws wait-for 0 "article"
205
+
206
+ # Extract metadata
207
+ TITLE=$(chrome-ws extract 0 "article h1")
208
+ AUTHOR=$(chrome-ws extract 0 ".author-name")
209
+ DATE=$(chrome-ws extract 0 "time")
210
+ CONTENT=$(chrome-ws extract 0 "article .content")
211
+
212
+ # Save to file
213
+ cat > article.txt <<EOF
214
+ Title: $TITLE
215
+ Author: $AUTHOR
216
+ Date: $DATE
217
+
218
+ $CONTENT
219
+ EOF
220
+ ```
221
+
222
+ **Product information:**
223
+ ```bash
224
+ chrome-ws navigate 0 "https://shop.example.com/product/123"
225
+ chrome-ws wait-for 0 ".product-details"
226
+
227
+ NAME=$(chrome-ws extract 0 "h1.product-name")
228
+ PRICE=$(chrome-ws extract 0 ".price")
229
+ IMAGE=$(chrome-ws attr 0 ".product-image img" "src")
230
+ STOCK=$(chrome-ws extract 0 ".stock-status")
231
+
232
+ # Output as JSON
233
+ cat <<EOF
234
+ {
235
+ "name": "$NAME",
236
+ "price": "$PRICE",
237
+ "image": "$IMAGE",
238
+ "in_stock": "$STOCK"
239
+ }
240
+ EOF
241
+ ```
242
+
243
+ **Batch process URLs:**
244
+ ```bash
245
+ URLS=("page1" "page2" "page3")
246
+
247
+ for URL in "${URLS[@]}"; do
248
+ chrome-ws navigate 0 "https://example.com/$URL"
249
+ chrome-ws wait-for 0 "h1"
250
+ TITLE=$(chrome-ws extract 0 "h1")
251
+ echo "$URL: $TITLE" >> results.txt
252
+ done
253
+ ```
254
+
255
+ ### Multi-Tab Workflows
256
+
257
+ **Email extraction:**
258
+ ```bash
259
+ # List all tabs
260
+ chrome-ws tabs
261
+
262
+ # Use the email tab index from output (e.g., tab 2)
263
+ EMAIL_TAB=2
264
+
265
+ # Click specific email
266
+ chrome-ws click $EMAIL_TAB "a[title*='Organization receipt']"
267
+
268
+ # Wait for email to load
269
+ chrome-ws wait-for $EMAIL_TAB ".email-body"
270
+
271
+ # Extract donation amount
272
+ AMOUNT=$(chrome-ws extract $EMAIL_TAB ".donation-amount")
273
+ echo "Donation: $AMOUNT"
274
+ ```
275
+
276
+ **Price comparison:**
277
+ ```bash
278
+ chrome-ws navigate 0 "https://store1.com/product"
279
+ chrome-ws new "https://store2.com/product"
280
+ chrome-ws new "https://store3.com/product"
281
+ sleep 3 # Let pages load
282
+
283
+ PRICE1=$(chrome-ws extract 0 ".price")
284
+ PRICE2=$(chrome-ws extract 1 ".price")
285
+ PRICE3=$(chrome-ws extract 2 ".price")
286
+
287
+ echo "Store 1: $PRICE1"
288
+ echo "Store 2: $PRICE2"
289
+ echo "Store 3: $PRICE3"
290
+ ```
291
+
292
+ **Cross-reference between sites:**
293
+ ```bash
294
+ # Get phone number from company site
295
+ chrome-ws navigate 0 "https://company.com/contact"
296
+ chrome-ws wait-for 0 ".phone"
297
+ PHONE=$(chrome-ws extract 0 ".phone")
298
+
299
+ # Look up phone number in verification site
300
+ chrome-ws new "https://lookup.com"
301
+ chrome-ws fill 1 "input[name=phone]" "$PHONE"
302
+ chrome-ws click 1 "button.search"
303
+ chrome-ws wait-for 1 ".results"
304
+ chrome-ws extract 1 ".verification-status"
305
+ ```
306
+
307
+ ### Dynamic Content
308
+
309
+ **Wait for AJAX to complete:**
310
+ ```bash
311
+ chrome-ws navigate 0 "https://app.com/dashboard"
312
+
313
+ # Wait for spinner to disappear
314
+ chrome-ws eval 0 "new Promise(resolve => {
315
+ const check = () => {
316
+ if (!document.querySelector('.spinner')) {
317
+ resolve(true);
318
+ } else {
319
+ setTimeout(check, 100);
320
+ }
321
+ };
322
+ check();
323
+ })"
324
+
325
+ # Now safe to extract
326
+ chrome-ws extract 0 ".dashboard-data"
327
+ ```
328
+
329
+ **Infinite scroll:**
330
+ ```bash
331
+ chrome-ws navigate 0 "https://example.com/feed"
332
+ chrome-ws wait-for 0 ".feed-item"
333
+
334
+ # Scroll 5 times
335
+ for i in {1..5}; do
336
+ chrome-ws eval 0 "window.scrollTo(0, document.body.scrollHeight)"
337
+ sleep 2
338
+ done
339
+
340
+ # Count loaded items
341
+ chrome-ws eval 0 "document.querySelectorAll('.feed-item').length"
342
+ ```
343
+
344
+ **Monitor for changes:**
345
+ ```bash
346
+ chrome-ws navigate 0 "https://example.com/status"
347
+ END=$(($(date +%s) + 300))
348
+
349
+ while [ $(date +%s) -lt $END ]; do
350
+ STATUS=$(chrome-ws extract 0 ".status")
351
+ echo "[$(date +%H:%M:%S)] $STATUS"
352
+
353
+ if [[ "$STATUS" == *"ERROR"* ]]; then
354
+ echo "ALERT: Error detected"
355
+ break
356
+ fi
357
+
358
+ sleep 10
359
+ done
360
+ ```
361
+
362
+ ### Advanced Patterns
363
+
364
+ **Multi-step workflow:**
365
+ ```bash
366
+ chrome-ws navigate 0 "https://booking.example.com"
367
+
368
+ # Search
369
+ chrome-ws fill 0 "input[name=destination]" "San Francisco"
370
+ chrome-ws fill 0 "input[name=checkin]" "2025-12-01"
371
+ chrome-ws click 0 "button.search"
372
+
373
+ # Select hotel
374
+ chrome-ws wait-for 0 ".hotel-results"
375
+ chrome-ws click 0 ".hotel-card:first-child .select"
376
+
377
+ # Choose room
378
+ chrome-ws wait-for 0 ".room-options"
379
+ chrome-ws click 0 ".room[data-type=deluxe] .book"
380
+
381
+ # Fill guest info
382
+ chrome-ws wait-for 0 "form.guest-info"
383
+ chrome-ws fill 0 "input[name=firstName]" "Jane"
384
+ chrome-ws fill 0 "input[name=lastName]" "Smith"
385
+ chrome-ws fill 0 "input[name=email]" "jane@example.com"
386
+
387
+ # Review
388
+ chrome-ws click 0 "button.review"
389
+ chrome-ws wait-for 0 ".summary"
390
+
391
+ # Extract confirmation
392
+ HOTEL=$(chrome-ws extract 0 ".hotel-name")
393
+ TOTAL=$(chrome-ws extract 0 ".total-price")
394
+ echo "$HOTEL: $TOTAL"
395
+ ```
396
+
397
+ **Cookies and localStorage:**
398
+ ```bash
399
+ # Get cookies
400
+ chrome-ws eval 0 "document.cookie"
401
+
402
+ # Set cookie
403
+ chrome-ws eval 0 "document.cookie = 'theme=dark; path=/'"
404
+
405
+ # Get localStorage
406
+ chrome-ws eval 0 "JSON.stringify(localStorage)"
407
+
408
+ # Set localStorage
409
+ chrome-ws eval 0 "localStorage.setItem('lastVisit', new Date().toISOString())"
410
+ ```
411
+
412
+ **Handle modals:**
413
+ ```bash
414
+ chrome-ws click 0 "button.open-modal"
415
+ chrome-ws wait-for 0 ".modal.visible"
416
+
417
+ # Fill modal form
418
+ chrome-ws fill 0 ".modal input[name=username]" "testuser"
419
+ chrome-ws click 0 ".modal button.submit"
420
+
421
+ # Wait for modal to close
422
+ chrome-ws eval 0 "new Promise(resolve => {
423
+ const check = () => {
424
+ if (!document.querySelector('.modal.visible')) {
425
+ resolve(true);
426
+ } else {
427
+ setTimeout(check, 100);
428
+ }
429
+ };
430
+ check();
431
+ })"
432
+ ```
433
+
434
+ **Network monitoring with raw CDP:**
435
+ ```bash
436
+ # Enable network monitoring
437
+ chrome-ws raw 0 '{"id":1,"method":"Network.enable","params":{}}'
438
+
439
+ # Navigate and capture traffic
440
+ chrome-ws navigate 0 "https://api.example.com"
441
+
442
+ # Get performance metrics
443
+ chrome-ws raw 0 '{"id":2,"method":"Performance.getMetrics","params":{}}'
444
+ ```
445
+
446
+ **Screenshots and PDF:**
447
+ ```bash
448
+ # Capture screenshot
449
+ chrome-ws screenshot 0 "page.png"
450
+
451
+ # Or use raw CDP for more control
452
+ SCREENSHOT=$(chrome-ws raw 0 '{
453
+ "id":1,
454
+ "method":"Page.captureScreenshot",
455
+ "params":{"format":"png","quality":80}
456
+ }')
457
+
458
+ # Extract base64 and save
459
+ echo "$SCREENSHOT" | node -pe "JSON.parse(require('fs').readFileSync(0)).result.data" | base64 -d > screenshot.png
460
+ ```
461
+
462
+ ## Error Handling
463
+
464
+ **Check element exists:**
465
+ ```bash
466
+ # Verify button exists
467
+ EXISTS=$(chrome-ws eval 0 "!!document.querySelector('.important-button')")
468
+
469
+ if [ "$EXISTS" = "true" ]; then
470
+ chrome-ws click 0 ".important-button"
471
+ else
472
+ echo "Button not found on page"
473
+ fi
474
+ ```
475
+
476
+ **Verify command success:**
477
+ ```bash
478
+ if ! chrome-ws navigate 0 "https://example.com"; then
479
+ echo "Navigation failed - Chrome not running?"
480
+ exit 1
481
+ fi
482
+ ```
483
+
484
+ **Retry pattern:**
485
+ ```bash
486
+ for attempt in {1..3}; do
487
+ if chrome-ws click 0 ".submit-button"; then
488
+ echo "Click succeeded"
489
+ break
490
+ fi
491
+ echo "Attempt $attempt failed, retrying..."
492
+ sleep 2
493
+ done
494
+ ```
495
+
496
+ ## Best Practices
497
+
498
+ **Always wait before interaction:**
499
+ ```bash
500
+ # BAD - might fail if page slow to load
501
+ chrome-ws navigate 0 "https://example.com"
502
+ chrome-ws click 0 "button" # May fail!
503
+
504
+ # GOOD - wait for element first
505
+ chrome-ws navigate 0 "https://example.com"
506
+ chrome-ws wait-for 0 "button"
507
+ chrome-ws click 0 "button"
508
+ ```
509
+
510
+ **Use specific selectors:**
511
+ ```bash
512
+ # BAD - matches first button on page
513
+ chrome-ws click 0 "button"
514
+
515
+ # GOOD - specific selector
516
+ chrome-ws click 0 "button[type=submit]"
517
+ chrome-ws click 0 "button.login-button"
518
+ chrome-ws click 0 "#submit-form"
519
+ ```
520
+
521
+ **Test selectors with html command:**
522
+ ```bash
523
+ # Check page structure
524
+ chrome-ws html 0 | grep "submit"
525
+
526
+ # Check specific element exists
527
+ chrome-ws html 0 "form"
528
+ ```
529
+
530
+ **Escape special characters:**
531
+ ```bash
532
+ # Use double quotes for variables
533
+ chrome-ws fill 0 "input[name=search]" "$SEARCH_TERM"
534
+
535
+ # Use single quotes for literal strings with special chars
536
+ chrome-ws eval 0 'document.querySelector(".item").textContent'
537
+ ```
538
+
539
+ ## Common Pitfalls
540
+
541
+ **Don't cache tab indices** - they change when tabs close:
542
+ ```bash
543
+ # BAD - index might be stale
544
+ TAB=2
545
+ # ... much later ...
546
+ chrome-ws click $TAB "button" # Tab 2 might not exist anymore
547
+
548
+ # GOOD - fetch fresh before use
549
+ chrome-ws tabs
550
+ chrome-ws click 2 "button"
551
+ ```
552
+
553
+ **Don't forget to wait for dynamic content:**
554
+ ```bash
555
+ # BAD - tries to extract before content loads
556
+ chrome-ws navigate 0 "https://app.com"
557
+ chrome-ws extract 0 ".user-name" # Might be empty!
558
+
559
+ # GOOD - wait for content
560
+ chrome-ws navigate 0 "https://app.com"
561
+ chrome-ws wait-for 0 ".user-name"
562
+ chrome-ws extract 0 ".user-name"
563
+ ```
564
+
565
+ **Handle element state:**
566
+ ```bash
567
+ # Check if button is disabled
568
+ DISABLED=$(chrome-ws eval 0 "document.querySelector('button.submit').disabled")
569
+
570
+ if [ "$DISABLED" = "false" ]; then
571
+ chrome-ws click 0 "button.submit"
572
+ else
573
+ echo "Button is disabled"
574
+ fi
575
+ ```
576
+
577
+ ## Troubleshooting
578
+
579
+ **Connection refused:** Verify Chrome running with `curl http://127.0.0.1:9222/json`
580
+
581
+ **Element not found:** Check page structure with `chrome-ws html 0`
582
+
583
+ **Timeout:** Use `wait-for` before interaction. Chrome has 30s timeout.
584
+
585
+ **Tab index out of range:** Run `chrome-ws tabs` to get current indices.
586
+
587
+ ## Protocol Reference
588
+
589
+ Full CDP documentation: https://chromedevtools.github.io/devtools-protocol/
590
+
591
+ Common methods via `raw` command:
592
+ - `Page.navigate`
593
+ - `Runtime.evaluate`
594
+ - `Network.enable`
595
+ - `Performance.getMetrics`