cdp-skill 1.0.8 → 1.0.15

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 (51) hide show
  1. package/README.md +80 -35
  2. package/SKILL.md +157 -241
  3. package/install.js +1 -0
  4. package/package.json +1 -1
  5. package/src/aria/index.js +8 -0
  6. package/src/aria/output-processor.js +173 -0
  7. package/src/aria/role-query.js +1229 -0
  8. package/src/aria/snapshot.js +459 -0
  9. package/src/aria.js +251 -50
  10. package/src/cdp/browser.js +22 -4
  11. package/src/cdp-skill.js +246 -69
  12. package/src/dom/LazyResolver.js +634 -0
  13. package/src/dom/click-executor.js +366 -94
  14. package/src/dom/element-locator.js +34 -25
  15. package/src/dom/fill-executor.js +83 -50
  16. package/src/dom/index.js +3 -0
  17. package/src/page/dialog-handler.js +119 -0
  18. package/src/page/page-controller.js +236 -3
  19. package/src/runner/context-helpers.js +33 -55
  20. package/src/runner/execute-dynamic.js +8 -7
  21. package/src/runner/execute-form.js +11 -11
  22. package/src/runner/execute-input.js +2 -2
  23. package/src/runner/execute-interaction.js +105 -126
  24. package/src/runner/execute-navigation.js +14 -29
  25. package/src/runner/execute-query.js +17 -11
  26. package/src/runner/step-executors.js +225 -84
  27. package/src/runner/step-registry.js +1064 -0
  28. package/src/runner/step-validator.js +16 -754
  29. package/src/tests/Aria.test.js +1025 -0
  30. package/src/tests/ClickExecutor.test.js +170 -50
  31. package/src/tests/ContextHelpers.test.js +41 -30
  32. package/src/tests/ExecuteBrowser.test.js +572 -0
  33. package/src/tests/ExecuteDynamic.test.js +2 -457
  34. package/src/tests/ExecuteForm.test.js +700 -0
  35. package/src/tests/ExecuteInput.test.js +540 -0
  36. package/src/tests/ExecuteInteraction.test.js +319 -0
  37. package/src/tests/ExecuteQuery.test.js +820 -0
  38. package/src/tests/FillExecutor.test.js +89 -37
  39. package/src/tests/LazyResolver.test.js +383 -0
  40. package/src/tests/StepValidator.test.js +224 -78
  41. package/src/tests/TestRunner.test.js +38 -27
  42. package/src/tests/integration.test.js +2 -1
  43. package/src/types.js +9 -9
  44. package/src/utils/backoff.js +118 -0
  45. package/src/utils/cdp-helpers.js +130 -0
  46. package/src/utils/devices.js +140 -0
  47. package/src/utils/errors.js +242 -0
  48. package/src/utils/index.js +65 -0
  49. package/src/utils/temp.js +75 -0
  50. package/src/utils/validators.js +433 -0
  51. package/src/utils.js +14 -1142
package/SKILL.md CHANGED
@@ -19,7 +19,7 @@ Site profiles are per-domain cheatsheets stored at `~/.cdp-skill/sites/{domain}.
19
19
 
20
20
  ### How navigation uses profiles
21
21
 
22
- Every `goto` and `openTab` (with URL) checks for a profile. The result appears as a **top-level field** in the response:
22
+ Every `goto`, `newTab` (with URL), and `switchTab` checks for a profile. The result appears as a **top-level field** in the response:
23
23
 
24
24
  - **`siteProfile`** present → read it before doing anything else. It contains strategies, quirks, and recipes. Apply its `settledWhen`/`readyWhen` hooks, use its selectors, and respect its quirks.
25
25
  - **`actionRequired`** present → **STOP. Create the profile NOW** before continuing your task:
@@ -33,69 +33,61 @@ The profile only needs to capture what's useful: environment, quirks, stable sel
33
33
 
34
34
  After completing your goal, update the site profile with anything you learned. Discovered a quirk? Found a reliable selector? Worked out a multi-step flow? Call `writeSiteProfile` again with the improved content before closing your tab. If you didn't learn anything new, skip the update.
35
35
 
36
- ### readSiteProfile
37
-
38
- `"domain"` | `{domain}` — returns `{found, domain, content}` or `{found: false, domain}`
39
-
40
- Read a site profile without navigating. Use this to check or update a profile ad-hoc.
41
-
42
- ### writeSiteProfile
43
-
44
- `{domain, content}` — returns `{written, path, domain}`
45
-
46
36
  ### Profile format
47
37
 
48
38
  ```
49
- # example.com
50
- Updated: 2026-02-03 | Fingerprint: react18-next-spa
39
+ # domain.com
40
+ Updated: YYYY-MM-DD | Fingerprint: <tech-stack>
41
+
42
+ ## Environment / Quirks / Strategies / Regions / Recipes
43
+ ```
51
44
 
52
- ## Environment
53
- - React 18.x, Next.js (SSR)
54
- - SPA with pushState navigation
45
+ - **Environment**: tech stack, SPA behavior, main element selectors
46
+ - **Quirks**: pitfalls that cause failures without foreknowledge
47
+ - **Strategies**: how to fill, click, or wait on this specific site (include `settledWhen`/`readyWhen` hooks)
48
+ - **Regions**: stable landmark selectors
49
+ - **Recipes**: pre-built step sequences for common flows
55
50
 
56
- ## Quirks
57
- - Turbo intercepts link clicks — use settledWhen with URL check
58
- - Search results load async — poll for .results-count before extracting
51
+ Sections are optional — include what's useful. See EXAMPLES.md for a full profile template.
59
52
 
60
- ## Strategies
61
- ### fill (React controlled inputs)
62
- Use nativeSetter.call(el, value) then dispatch input event with bubbles.
53
+ ### readSiteProfile
63
54
 
64
- ### Navigation readiness
65
- settledWhen: () => !document.querySelector('.loading-bar')
55
+ `"domain"` | `{domain}` — returns `{found, domain, content}` or `{found: false, domain}`
66
56
 
67
- ## Regions
68
- - mainContent: main, [role="main"]
69
- - navigation: .nav-bar
70
- - searchResults: #search-results
57
+ ### writeSiteProfile
71
58
 
72
- ## Recipes
73
- ### Login
74
- pipeline: find #username fill {{user}}, find #password fill {{pass}}, find #login click, waitFor () => location.pathname !== '/login'
59
+ `{domain, content}` — returns `{written, path, domain}`
60
+ ```json
61
+ {"writeSiteProfile": {"domain": "example.com", "content": "# example.com\nUpdated: 2025-01-15\n\n## Environment\n- React SPA\n..."}}
75
62
  ```
76
63
 
77
- Sections are optional — include what's useful. The key sections agents rely on:
78
- - **Quirks**: pitfalls that cause failures without foreknowledge
79
- - **Strategies**: how to fill, click, or wait on this specific site
80
- - **Recipes**: pre-built step sequences for common flows
81
-
82
64
  ## Quick Start
83
65
 
84
- **1. Check Chrome status** (auto-launches if needed):
85
- `node src/cdp-skill.js '{"steps":[{"chromeStatus":true}]}'`
66
+ ```bash
67
+ echo '{"steps":[{"openTab":"https://google.com"}]}' | node src/cdp-skill.js
68
+ echo '{"tab":"t1","steps":[{"click":"#btn"}]}' | node src/cdp-skill.js
69
+ echo '{"tab":"t1","steps":[{"snapshot":true}]}' | node src/cdp-skill.js
70
+ ```
71
+
72
+ Tab IDs (t1, t2, ...) persist across CLI invocations. Chrome auto-launches if not running.
86
73
 
87
- **2. Open a tab and navigate:**
88
- `node src/cdp-skill.js '{"steps":[{"openTab":"https://google.com"}]}'`
74
+ ## Reliability (v1.0.10-1.0.11)
89
75
 
90
- **3. Use the returned tab ID for subsequent calls:**
91
- `node src/cdp-skill.js '{"config":{"tab":"t1"},"steps":[{"click":"#btn"}]}'`
76
+ Recent improvements to stability and correctness:
92
77
 
93
- Tab IDs (t1, t2, ...) persist across CLI invocations. Stdin pipe also works: `echo '{"steps":[...]}' | node src/cdp-skill.js`
78
+ - **Validation robustness** Fixed null pointer crashes in step validation for edge cases with missing or malformed parameters
79
+ - **Race condition fixes** — Resolved timing issues in browser connection initialization, file lock contention, and scroll-wait coordination
80
+ - **Resource cleanup** — Fixed HTTP connection leaks, event listener cleanup, and stderr stream handling
81
+ - **Frame context** — Corrected iframe element location and interaction to respect frame boundaries
82
+ - **Step simplification** — Consolidated 47 steps into 41 unified operations (fill, frame, elementsAt, pageFunction, sleep) for clearer API
83
+
84
+ The skill now passes 1261/1263 unit tests (99.8%) and maintains SHS 99/100 on the cdp-bench evaluation suite.
94
85
 
95
86
  ## Input / Output Schema
96
87
 
97
88
  **Input fields:**
98
- - `config`: `{host, port, tab, timeout, headless}` tab is required after first call
89
+ - `tab`: tab alias (e.g. "t1") — required after first call
90
+ - `timeout`: step timeout in ms (default 30000)
99
91
  - `steps`: array of step objects (one action per step)
100
92
 
101
93
  **Output fields:**
@@ -119,8 +111,12 @@ Tab IDs (t1, t2, ...) persist across CLI invocations. Stdin pipe also works: `ec
119
111
 
120
112
  ## Element References
121
113
 
122
- Snapshots return versioned refs like `[ref=s1e4]` — format: `s{snapshotId}e{elementNumber}`.
123
- Use refs with `click`, `fill`, `hover`. Each snapshot increments the ID. Refs from earlier snapshots remain valid while the element is in DOM.
114
+ Snapshots return versioned refs like `[ref=f0s1e4]` — format: `f{frameId}s{snapshotId}e{elementNumber}`.
115
+ - `f0` = main frame (default)
116
+ - `f1`, `f2`, ... = iframe by index
117
+ - `f[name]` = iframe by name (e.g., `f[frame-top]`)
118
+
119
+ Each frame maintains its own snapshot counter. Use refs with `click`, `fill`, `hover`. Refs remain valid while the element is in DOM.
124
120
 
125
121
  **Auto re-resolution**: when a ref's element leaves the DOM (React re-render, lazy-load), the system tries to re-find it by stored selector + role + name. Response includes `reResolved: true` on success.
126
122
 
@@ -129,7 +125,7 @@ Use refs with `click`, `fill`, `hover`. Each snapshot increments the ID. Refs fr
129
125
  | Action | Waits For |
130
126
  |--------|-----------|
131
127
  | `click` | visible, enabled, stable, not covered, pointer-events |
132
- | `fill`, `type` | visible, enabled, editable |
128
+ | `fill` | visible, enabled, editable |
133
129
  | `hover` | visible, stable |
134
130
 
135
131
  Use `force: true` to bypass all checks. **Auto-force**: when actionability times out but element exists, automatically retries with force (outputs `autoForced: true`).
@@ -144,195 +140,112 @@ Optional parameters on action steps to customize the step lifecycle:
144
140
 
145
141
  Hooks can be combined on any action step. Applies to: click, fill, press, hover, drag, selectOption, scroll.
146
142
 
147
- ## Step Reference
148
-
149
- ### Chrome Management
150
-
151
- > **IMPORTANT**: Never launch Chrome manually via shell commands. Always use `chromeStatus`.
152
-
153
- **chromeStatus**: true | {autoLaunch, headless}
154
- returns: {running, launched, version, port, tabs[]}
155
-
156
- ### Navigation
157
-
158
- **goto**: "url" | {url, waitUntil}
159
- waitUntil: commit | domcontentloaded | load | networkidle
160
- response: top-level `siteProfile` or `actionRequired` (see Site Profiles)
161
-
162
- **reload**: true | {waitUntil}
163
-
164
- **back** / **forward**: true
165
- returns: {url, title} or {noHistory: true}
166
-
167
- **waitForNavigation**: true | {timeout, waitUntil}
168
-
169
- ### Frames
170
-
171
- **listFrames**: true
172
- returns: {mainFrameId, currentFrameId, frames[]}
173
-
174
- **switchToFrame**: "selector" | index | {selector, index, name, frameId}
175
- returns: {frameId, url, name}
176
-
177
- **switchToMainFrame**: true
178
-
179
- ### Waiting
180
-
181
- **wait**: "selector" | number(ms) | {selector, hidden, minCount} | {text} | {textRegex} | {urlContains}
182
-
183
- ### Interaction
184
-
185
- **click**: "selector" | "ref" | {ref, selector, text, x/y, selectors[]}
186
- options: force, jsClick, nativeOnly, doubleClick, scrollUntilVisible, searchFrames, exact, timeout, waitAfter
187
- hooks: readyWhen, settledWhen, observe
188
- returns: {clicked, method: "cdp"|"jsClick"|"jsClick-auto", navigated?, newUrl?}
189
-
190
- **fill**: {selector|ref|label, value}
191
- options: clear(true), react, force, exact, timeout
192
- hooks: readyWhen, settledWhen, observe
193
- returns: {filled, navigated?, newUrl?}
194
-
195
- **fillForm**: {"#sel": "value", ...}
196
- returns: {total, filled, failed, results[]}
197
-
198
- **fillActive**: "value" | {value, clear}
199
- returns: {filled, tag, type, selector, valueBefore, valueAfter}
200
-
201
- **type**: {selector, text, delay}
202
- returns: {selector, typed, length}
203
-
204
- **press**: "Enter" | "Control+a" | "Meta+Shift+Enter"
205
-
206
- **select**: "selector" | {selector, start, end}
207
- returns: {selector, start, end, selectedText, totalLength}
208
-
209
- **hover**: "selector" | {selector, ref, duration, captureResult}
210
- hooks: readyWhen, settledWhen, observe
211
-
212
- **drag**: {source, target}
213
- source/target: "selector" | "ref" | {ref, offsetX, offsetY} | {x, y}
214
- options: steps(10), delay(0)
215
- hooks: readyWhen, settledWhen, observe
216
- returns: {dragged, method: "html5-dnd"|"range-input"|"mouse-events", source, target}
217
-
218
- **selectOption**: {selector, value|label|index|values}
219
- returns: {selected[], multiple}
220
- notes: uses JS to set option.selected (native dropdowns can't be clicked via CDP)
221
-
222
- ### Scrolling
223
-
224
- **scroll**: "top" | "bottom" | "selector" | {deltaY} | {x, y}
225
- returns: {scrollX, scrollY}
226
-
227
- ### Data Extraction
228
-
229
- **extract**: "selector" | {selector, type: auto|table|list|text, includeHeaders}
230
- returns: table {type, headers, rows, rowCount, columnCount}; list → {type, items, itemCount}
231
-
232
- **getDom**: true | "selector" | {selector, outer(true)}
233
- returns: {html, tagName, selector, length}
234
-
235
- **getBox**: "ref" | ["ref1", "ref2"] | {refs: [...]}
236
- returns: {x, y, width, height, center} per ref
237
-
238
- **refAt**: {x, y}
239
- returns: {ref, existing, tag, selector, clickable, role, name, box}
240
-
241
- **elementsAt**: [{x, y}, ...]
242
- returns: {count, elements[]}
243
-
244
- **elementsNear**: {x, y, radius(50), limit(20)}
245
- returns: {center, radius, count, elements[]}
246
-
247
- **formState**: "selector" | {selector, includeHidden}
248
- returns: {selector, action, method, fields[], valid, fieldCount}
249
-
250
- **query**: "selector" | {selector, limit(10), output} | {role, name, nameExact, nameRegex, checked, disabled, level, countOnly, refs}
251
- output: text | html | href | value | tag | {attribute: "data-id"}
252
- returns: {selector, total, showing, results[]}
253
-
254
- **queryAll**: {"label": "selector", ...}
255
-
256
- **inspect**: true | {selectors[], limit}
257
- returns: {title, url, counts, custom}
258
-
259
- **console**: true | {level, type, since, limit, clear, stackTrace}
260
- returns: {total, showing, messages[]}
261
- notes: logs don't persist across CLI invocations
262
-
263
- ### Accessibility Snapshot
264
-
265
- **snapshot**: true | {root, detail, mode, maxDepth, maxElements, includeText, includeFrames, pierceShadow, viewportOnly, inlineLimit, since}
266
- detail: summary | interactive | full(default)
267
- since: "s1" — returns {unchanged: true} if page hasn't changed
268
- returns: YAML with role, "name", states, [ref=s{N}e{M}], snapshotId
269
- notes: snapshots over 9KB saved to file (configurable via inlineLimit)
270
-
271
- **snapshotSearch**: {text, pattern, role, exact, limit(10), context, near: {x, y, radius}}
272
- returns: {matches[], matchCount, searchedElements}
273
- notes: refs from snapshotSearch persist across subsequent commands
274
-
275
- ### Screenshots & PDF
276
-
277
- Screenshots auto-captured on every visual action to `/tmp/cdp-skill/<tab>.before.png` and `.after.png`.
278
-
279
- **pdf**: "filename" | {path, selector, landscape, printBackground, scale, paperWidth, paperHeight, margins, pageRanges, validate}
280
- returns: {path, fileSize, fileSizeFormatted, pageCount, dimensions}
281
-
282
- ### Dynamic Browser Execution
283
-
284
- **pageFunction**: "() => expr" | {fn, refs(bool), timeout}
285
- notes: auto-wrapped as IIFE, refs passes window.__ariaRefs, return value auto-serialized, runs in current frame context
286
-
287
- **poll**: "() => predicate" | {fn, interval(100), timeout(30000)}
288
- returns: {resolved, value|lastValue, elapsed}
289
-
290
- **pipeline**: [{find+fill|click|type|check|select}, {waitFor}, {sleep}, {return}] | {steps[], timeout(30000)}
291
- returns: {completed, steps, results[]} or {completed: false, failedAt, error, results[]}
292
- notes: compiles to single async JS function, zero roundtrips between micro-ops
293
-
294
- ### Form Validation
295
-
296
- **validate**: "selector"
297
- returns: {valid, message, validity}
298
-
299
- **submit**: "selector" | {selector, reportValidity}
300
- returns: {submitted, valid, errors[]}
301
-
302
- ### Assertions
303
-
304
- **assert**: {url: {contains|equals|startsWith|endsWith|matches}} | {text} | {selector, text, caseSensitive}
305
-
306
- ### Tab Management
307
-
308
- **listTabs**: true
309
- returns: {count, tabs[]}
310
-
311
- **closeTab**: "tabId"
312
- returns: {closed}
313
-
314
- **openTab**: true | "url" | {url}
315
- returns: {opened, tab, url, navigated, viewportSnapshot, fullSnapshot, context}
316
- response: top-level `siteProfile` or `actionRequired` when URL provided (see Site Profiles)
317
- notes: REQUIRED as first step when no tab specified
318
-
319
- ### Viewport
320
-
321
- **viewport**: "preset" | {width, height, deviceScaleFactor, mobile, hasTouch, isLandscape}
322
- presets: iphone-se, iphone-14, iphone-15-pro, ipad, ipad-pro-11, pixel-7, samsung-galaxy-s23, desktop, desktop-hd, macbook-pro-14
323
- returns: {width, height, deviceScaleFactor, mobile, hasTouch}
324
-
325
- ### Cookies
326
-
327
- **cookies**: {get: true|[urls], name?} | {set: [{name, value, domain, expires}]} | {delete: "name", domain?} | {clear: true, domain?}
328
- expires formats: 30m, 1h, 7d, 1w, 1y, or Unix timestamp
329
- returns: get → {cookies[]}; set → {action, count}; delete/clear → {action, count}
330
- notes: get without URLs returns only current tab's domain cookies
331
-
332
- ### JavaScript Execution
333
-
334
- **eval**: "expression" | {expression, await, timeout, serialize}
335
- returns: typed results (Number, Date, Map, Set, Element, NodeList, etc.)
143
+ ## Core Steps
144
+
145
+ ### chromeStatus (optional diagnostic)
146
+ `true` | `{host, port, headless, autoLaunch}` — returns `{running, launched, version, port, tabs[]}`
147
+
148
+ > **Note**: You rarely need this — `newTab` auto-launches Chrome. Use `chromeStatus` only for diagnostics or non-default ports.
149
+
150
+ ### newTab
151
+ `true` | `"url"` | `{url, host, port, headless}` — returns `{opened, tab, url, navigated, viewportSnapshot, fullSnapshot, context}`
152
+ Response includes top-level `siteProfile` or `actionRequired` when URL provided (see Site Profiles).
153
+ **REQUIRED as first step** when no tab specified. Chrome auto-launches if not running.
154
+ Non-default Chrome: `{"steps":[{"newTab":{"url":"https://example.com","port":9333,"headless":true}}]}`
155
+
156
+ ### goto
157
+ `"url"` | `{url, waitUntil}` — waitUntil: commit | domcontentloaded | load | networkidle
158
+ Response includes top-level `siteProfile` or `actionRequired` (see Site Profiles).
159
+
160
+ ### click
161
+ `"selector"` | `"ref"` | `{ref, selector, text, x/y, selectors[]}`
162
+ Options: force, jsClick, nativeOnly, doubleClick, scrollUntilVisible, searchFrames, exact, timeout, waitAfter
163
+ Hooks: readyWhen, settledWhen, observe
164
+ Returns: `{clicked, method: "cdp"|"jsClick"|"jsClick-auto", navigated?, newUrl?, newTabs?: [{targetId, url, title}]}`
165
+
166
+ ### fill
167
+ `{selector|ref|label, value}` — the primary way to input text.
168
+ Options: clear(true), react, force, exact, timeout
169
+ Hooks: readyWhen, settledWhen, observe
170
+ Returns: `{filled, navigated?, newUrl?}`
171
+
172
+ ### press
173
+ `"Enter"` | `"Control+a"` | `"Meta+Shift+Enter"` — keyboard shortcuts and key presses.
174
+
175
+ ### scroll
176
+ `"top"` | `"bottom"` | `"selector"` | `{deltaY}` | `{x, y}`
177
+ Returns: `{scrollX, scrollY}`
178
+
179
+ ### snapshot
180
+ `true` | `{root, detail, mode, maxDepth, maxElements, includeText, includeFrames, pierceShadow, viewportOnly, inlineLimit, since}`
181
+ Detail: summary | interactive | full(default)
182
+ Since: `"f0s1"` returns `{unchanged: true}` if page hasn't changed
183
+ Returns: YAML with role, "name", states, `[ref=f{F}s{N}e{M}]`, snapshotId (`f0s1`, `f1s1`, etc.)
184
+ Notes: snapshots over 9KB saved to file (configurable via inlineLimit)
185
+
186
+ ### snapshotSearch
187
+ `{text, pattern, role, exact, limit(10), context, near: {x, y, radius}}`
188
+ Returns: `{matches[], matchCount, searchedElements}`
189
+ Notes: refs from snapshotSearch persist across subsequent commands.
190
+
191
+ ### wait
192
+ `"selector"` | `{selector, hidden, minCount}` | `{text}` | `{textRegex}` | `{urlContains}`
193
+
194
+ ### get
195
+ `"selector"` (text) | `{selector, mode: "text"|"html"|"value"|"box"|"attributes"}` unified content extraction.
196
+ Returns: text → `{text}`, html → `{html, tagName, length}`, value → `{fields[], valid, fieldCount}`, box → `{x, y, width, height}`, attributes → `{attributes}`
197
+
198
+ ### getUrl
199
+ `true` — returns `{url}`
200
+
201
+ ### getTitle
202
+ `true` returns `{title}`
203
+
204
+ ### pageFunction
205
+ `"() => expr"` | `{fn, refs(bool), timeout}` — run custom JavaScript in the browser.
206
+ Notes: auto-wrapped as IIFE, refs passes `window.__ariaRefs`, return value auto-serialized, runs in current frame context.
207
+
208
+ ### closeTab
209
+ `"tabId"` returns `{closed}`
210
+
211
+ ### listTabs
212
+ `true` — returns `{count, tabs[]}`
213
+
214
+ ## Also Available
215
+
216
+ These steps are fully functional see EXAMPLES.md for usage details.
217
+
218
+ | Step | Description |
219
+ |------|-------------|
220
+ | `fill` | Unified fill (replaces `type`): `"text"` (focused), `{selector, value}` (single), `{"#a":"x","#b":"y"}` (batch), `{fields:{...}, react}` (batch+options), `{value, clear}` (focused+options) |
221
+ | `sleep` | Time delay: `2000` (ms, 0–60000) |
222
+ | `pageFunction` | JS execution: `"() => expr"` (function) or `"document.title"` (bare expression) or `{fn, expression, refs, timeout}` |
223
+ | `poll` | Poll predicate until truthy: `"() => expr"` or `{fn, interval, timeout}` |
224
+ | `query` | CSS/ARIA query: `"selector"` or `{role, name}` → `{total, results[]}` |
225
+ | `queryAll` | Batch queries: `{"label": "selector", ...}` |
226
+ | `inspect` | Page overview: `true``{title, url, counts}` |
227
+ | `get` | Unified content extraction (replaces `extract`, `getDom`, `getBox`, `formState`): `"selector"` (text), `{selector, mode: "text"\|"html"\|"value"\|"box"\|"attributes"}` |
228
+ | `getUrl` | Get current URL: `true` → `{url}` |
229
+ | `getTitle` | Get page title: `true` `{title}` |
230
+ | `hover` | Hover element: `"selector"` or `{selector, ref, text, x/y, duration}` |
231
+ | `drag` | Drag and drop: `{source, target, steps, delay, method}` — method: `auto`(default)\|`mouse`\|`html5` |
232
+ | `selectText` | Text selection (renamed from `select`): `"selector"` or `{selector, start, end}` |
233
+ | `selectOption` | Dropdown: `{selector, value\|label\|index\|values}` |
234
+ | `submit` | Submit form: `"selector"` → `{submitted, valid, errors[]}` |
235
+ | `assert` | Assert conditions: `{url: {contains\|equals\|matches}}` or `{text}` or `{selector, text}` |
236
+ | `elementsAt` | Coordinate lookup: `{x,y}` (point), `[{x,y},...]` (batch), `{x,y,radius}` (nearby) |
237
+ | `frame` | Frame ops: `"selector"` (switch), `0` (by index), `"top"` (main frame), `{name}`, `{list:true}` |
238
+ | `viewport` | Set viewport: `"iphone-14"` or `{width, height, mobile}` |
239
+ | `cookies` | Get/set/delete: `{get: true}`, `{set: [...]}`, `{delete: "name"}`, `{clear: true}` |
240
+ | `console` | Browser console: `true` or `{level, limit, clear}` → `{messages[]}` |
241
+ | `pdf` | Generate PDF: `"filename"` or `{path, landscape, scale, pageRanges}` |
242
+ | `back` / `forward` | History navigation: `true` → `{url, title}` or `{noHistory: true}` |
243
+ | `reload` | Reload page: `true` or `{waitUntil}` |
244
+ | `newTab` | Create new tab (renamed from `openTab`): `"url"` or `{url, wait}` `{tab, url}` |
245
+ | `switchTab` | Switch to existing tab (renamed from `connectTab`): `"t1"` or `{targetId}` or `{url: "regex"}` |
246
+ | `closeTab` | Close tab: `"t1"` or `true` (current) |
247
+ | `listTabs` | List all tabs: `true` `{tabs[]}` |
248
+ | `waitForNavigation` | Wait for nav: `true` or `{timeout, waitUntil}` |
336
249
 
337
250
  ### Optional Steps
338
251
 
@@ -342,25 +255,28 @@ Add `"optional": true` to any step to continue on failure (status becomes "skipp
342
255
 
343
256
  | Issue | Solution |
344
257
  |-------|----------|
345
- | Tabs accumulating | Include `tab` in config |
346
- | CONNECTION error | Use `chromeStatus` first |
258
+ | Tabs accumulating | Include `tab` at top level |
259
+ | CONNECTION error | Check Chrome is reachable; use `chromeStatus` to diagnose |
347
260
  | Chrome not found | Set `CHROME_PATH` env var |
348
261
  | Element not found | Add `wait` step first |
349
262
  | Clicks not working | Scroll into view first, or `force: true` |
350
263
  | `back` returns noHistory | New tabs start at about:blank; navigate first |
351
264
  | Select dropdown not working | Use click+click or press arrow keys |
352
265
  | Type not appearing | Click input first to focus, then type |
266
+ | Elements missing from snapshot | Custom widgets may lack ARIA roles; use `pageFunction` or `get` with `mode: "html"` as fallback |
353
267
  | macOS: Chrome running but no CDP | `chromeStatus` launches new instance with CDP enabled |
354
268
 
355
269
  ## Best Practices
356
270
 
357
271
  - **Handle `actionRequired` immediately** — when a response contains this field, complete it before doing anything else
358
- - **Never launch Chrome directly** — always use `chromeStatus`
359
- - **Use openTab** as your first step to create a tab; use the returned tab ID for all subsequent calls
272
+ - **Never launch Chrome directly** — `newTab` handles it automatically
273
+ - **Use newTab** as your first step to create a tab; use the returned tab ID for all subsequent calls
360
274
  - **Reuse only your own tabs** — other agents may share the browser
361
275
  - **Update the site profile before closing** — add any quirks, selectors, or recipes you discovered
362
276
  - **Close your tab when done** — `closeTab` with your tab ID
363
- - **Discover before interacting** — use `snapshot` and `inspect` to understand the page
277
+ - **Discover before interacting** — use `snapshot` to understand the page structure
364
278
  - **Use website navigation** — click links and submit forms; don't guess URLs
365
279
  - **Prefer refs** over CSS selectors — use `snapshot` + refs for resilient targeting
280
+ - **Check `newTabs` after click** — clicks on `target="_blank"` links report new tabs; use `switchTab` to switch
281
+ - **Use `switchTab` for popups** — connect by alias (`"t2"`), targetId, or URL regex (`{url: "pattern"}`)
366
282
  - **Be persistent** — try alternative selectors, add waits, scroll first
package/install.js CHANGED
@@ -19,6 +19,7 @@ const targets = [
19
19
 
20
20
  const filesToCopy = [
21
21
  'SKILL.md',
22
+ 'EXAMPLES.md',
22
23
  'src',
23
24
  ];
24
25
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cdp-skill",
3
- "version": "1.0.8",
3
+ "version": "1.0.15",
4
4
  "description": "Browser automation skill using Chrome DevTools Protocol for Claude Code and AI agents",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -0,0 +1,8 @@
1
+ /**
2
+ * ARIA Module
3
+ * Re-exports accessibility tree and role-based query functionality
4
+ */
5
+
6
+ export { createQueryOutputProcessor } from './output-processor.js';
7
+ export { createRoleQueryExecutor } from './role-query.js';
8
+ export { createAriaSnapshot } from './snapshot.js';