cdp-skill 1.0.8 → 1.0.14

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 (47) hide show
  1. package/README.md +80 -35
  2. package/SKILL.md +151 -239
  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 +237 -43
  10. package/src/cdp/browser.js +22 -4
  11. package/src/cdp-skill.js +245 -69
  12. package/src/dom/click-executor.js +240 -76
  13. package/src/dom/element-locator.js +34 -25
  14. package/src/dom/fill-executor.js +55 -27
  15. package/src/page/dialog-handler.js +119 -0
  16. package/src/page/page-controller.js +190 -3
  17. package/src/runner/context-helpers.js +33 -55
  18. package/src/runner/execute-dynamic.js +8 -7
  19. package/src/runner/execute-form.js +11 -11
  20. package/src/runner/execute-input.js +2 -2
  21. package/src/runner/execute-interaction.js +99 -120
  22. package/src/runner/execute-navigation.js +11 -26
  23. package/src/runner/execute-query.js +8 -5
  24. package/src/runner/step-executors.js +225 -84
  25. package/src/runner/step-registry.js +1064 -0
  26. package/src/runner/step-validator.js +16 -754
  27. package/src/tests/Aria.test.js +1025 -0
  28. package/src/tests/ContextHelpers.test.js +39 -28
  29. package/src/tests/ExecuteBrowser.test.js +572 -0
  30. package/src/tests/ExecuteDynamic.test.js +2 -457
  31. package/src/tests/ExecuteForm.test.js +700 -0
  32. package/src/tests/ExecuteInput.test.js +540 -0
  33. package/src/tests/ExecuteInteraction.test.js +319 -0
  34. package/src/tests/ExecuteQuery.test.js +820 -0
  35. package/src/tests/FillExecutor.test.js +2 -2
  36. package/src/tests/StepValidator.test.js +222 -76
  37. package/src/tests/TestRunner.test.js +36 -25
  38. package/src/tests/integration.test.js +2 -1
  39. package/src/types.js +9 -9
  40. package/src/utils/backoff.js +118 -0
  41. package/src/utils/cdp-helpers.js +130 -0
  42. package/src/utils/devices.js +140 -0
  43. package/src/utils/errors.js +242 -0
  44. package/src/utils/index.js +65 -0
  45. package/src/utils/temp.js +75 -0
  46. package/src/utils/validators.js +433 -0
  47. 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:**
@@ -129,7 +121,7 @@ Use refs with `click`, `fill`, `hover`. Each snapshot increments the ID. Refs fr
129
121
  | Action | Waits For |
130
122
  |--------|-----------|
131
123
  | `click` | visible, enabled, stable, not covered, pointer-events |
132
- | `fill`, `type` | visible, enabled, editable |
124
+ | `fill` | visible, enabled, editable |
133
125
  | `hover` | visible, stable |
134
126
 
135
127
  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 +136,112 @@ Optional parameters on action steps to customize the step lifecycle:
144
136
 
145
137
  Hooks can be combined on any action step. Applies to: click, fill, press, hover, drag, selectOption, scroll.
146
138
 
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.)
139
+ ## Core Steps
140
+
141
+ ### chromeStatus (optional diagnostic)
142
+ `true` | `{host, port, headless, autoLaunch}` — returns `{running, launched, version, port, tabs[]}`
143
+
144
+ > **Note**: You rarely need this — `newTab` auto-launches Chrome. Use `chromeStatus` only for diagnostics or non-default ports.
145
+
146
+ ### newTab
147
+ `true` | `"url"` | `{url, host, port, headless}` — returns `{opened, tab, url, navigated, viewportSnapshot, fullSnapshot, context}`
148
+ Response includes top-level `siteProfile` or `actionRequired` when URL provided (see Site Profiles).
149
+ **REQUIRED as first step** when no tab specified. Chrome auto-launches if not running.
150
+ Non-default Chrome: `{"steps":[{"newTab":{"url":"https://example.com","port":9333,"headless":true}}]}`
151
+
152
+ ### goto
153
+ `"url"` | `{url, waitUntil}` — waitUntil: commit | domcontentloaded | load | networkidle
154
+ Response includes top-level `siteProfile` or `actionRequired` (see Site Profiles).
155
+
156
+ ### click
157
+ `"selector"` | `"ref"` | `{ref, selector, text, x/y, selectors[]}`
158
+ Options: force, jsClick, nativeOnly, doubleClick, scrollUntilVisible, searchFrames, exact, timeout, waitAfter
159
+ Hooks: readyWhen, settledWhen, observe
160
+ Returns: `{clicked, method: "cdp"|"jsClick"|"jsClick-auto", navigated?, newUrl?, newTabs?: [{targetId, url, title}]}`
161
+
162
+ ### fill
163
+ `{selector|ref|label, value}` — the primary way to input text.
164
+ Options: clear(true), react, force, exact, timeout
165
+ Hooks: readyWhen, settledWhen, observe
166
+ Returns: `{filled, navigated?, newUrl?}`
167
+
168
+ ### press
169
+ `"Enter"` | `"Control+a"` | `"Meta+Shift+Enter"` — keyboard shortcuts and key presses.
170
+
171
+ ### scroll
172
+ `"top"` | `"bottom"` | `"selector"` | `{deltaY}` | `{x, y}`
173
+ Returns: `{scrollX, scrollY}`
174
+
175
+ ### snapshot
176
+ `true` | `{root, detail, mode, maxDepth, maxElements, includeText, includeFrames, pierceShadow, viewportOnly, inlineLimit, since}`
177
+ Detail: summary | interactive | full(default)
178
+ Since: `"s1"` returns `{unchanged: true}` if page hasn't changed
179
+ Returns: YAML with role, "name", states, `[ref=s{N}e{M}]`, snapshotId
180
+ Notes: snapshots over 9KB saved to file (configurable via inlineLimit)
181
+
182
+ ### snapshotSearch
183
+ `{text, pattern, role, exact, limit(10), context, near: {x, y, radius}}`
184
+ Returns: `{matches[], matchCount, searchedElements}`
185
+ Notes: refs from snapshotSearch persist across subsequent commands.
186
+
187
+ ### wait
188
+ `"selector"` | `{selector, hidden, minCount}` | `{text}` | `{textRegex}` | `{urlContains}`
189
+
190
+ ### get
191
+ `"selector"` (text) | `{selector, mode: "text"|"html"|"value"|"box"|"attributes"}` unified content extraction.
192
+ Returns: text → `{text}`, html → `{html, tagName, length}`, value → `{fields[], valid, fieldCount}`, box → `{x, y, width, height}`, attributes → `{attributes}`
193
+
194
+ ### getUrl
195
+ `true` — returns `{url}`
196
+
197
+ ### getTitle
198
+ `true` returns `{title}`
199
+
200
+ ### pageFunction
201
+ `"() => expr"` | `{fn, refs(bool), timeout}` — run custom JavaScript in the browser.
202
+ Notes: auto-wrapped as IIFE, refs passes `window.__ariaRefs`, return value auto-serialized, runs in current frame context.
203
+
204
+ ### closeTab
205
+ `"tabId"` returns `{closed}`
206
+
207
+ ### listTabs
208
+ `true` — returns `{count, tabs[]}`
209
+
210
+ ## Also Available
211
+
212
+ These steps are fully functional see EXAMPLES.md for usage details.
213
+
214
+ | Step | Description |
215
+ |------|-------------|
216
+ | `fill` | Unified fill (replaces `type`): `"text"` (focused), `{selector, value}` (single), `{"#a":"x","#b":"y"}` (batch), `{fields:{...}, react}` (batch+options), `{value, clear}` (focused+options) |
217
+ | `sleep` | Time delay: `2000` (ms, 0–60000) |
218
+ | `pageFunction` | JS execution: `"() => expr"` (function) or `"document.title"` (bare expression) or `{fn, expression, refs, timeout}` |
219
+ | `poll` | Poll predicate until truthy: `"() => expr"` or `{fn, interval, timeout}` |
220
+ | `query` | CSS/ARIA query: `"selector"` or `{role, name}` → `{total, results[]}` |
221
+ | `queryAll` | Batch queries: `{"label": "selector", ...}` |
222
+ | `inspect` | Page overview: `true``{title, url, counts}` |
223
+ | `get` | Unified content extraction (replaces `extract`, `getDom`, `getBox`, `formState`): `"selector"` (text), `{selector, mode: "text"\|"html"\|"value"\|"box"\|"attributes"}` |
224
+ | `getUrl` | Get current URL: `true` → `{url}` |
225
+ | `getTitle` | Get page title: `true` `{title}` |
226
+ | `hover` | Hover element: `"selector"` or `{selector, ref, text, x/y, duration}` |
227
+ | `drag` | Drag and drop: `{source, target, steps, delay, method}` — method: `auto`(default)\|`mouse`\|`html5` |
228
+ | `selectText` | Text selection (renamed from `select`): `"selector"` or `{selector, start, end}` |
229
+ | `selectOption` | Dropdown: `{selector, value\|label\|index\|values}` |
230
+ | `submit` | Submit form: `"selector"` → `{submitted, valid, errors[]}` |
231
+ | `assert` | Assert conditions: `{url: {contains\|equals\|matches}}` or `{text}` or `{selector, text}` |
232
+ | `elementsAt` | Coordinate lookup: `{x,y}` (point), `[{x,y},...]` (batch), `{x,y,radius}` (nearby) |
233
+ | `frame` | Frame ops: `"selector"` (switch), `0` (by index), `"top"` (main frame), `{name}`, `{list:true}` |
234
+ | `viewport` | Set viewport: `"iphone-14"` or `{width, height, mobile}` |
235
+ | `cookies` | Get/set/delete: `{get: true}`, `{set: [...]}`, `{delete: "name"}`, `{clear: true}` |
236
+ | `console` | Browser console: `true` or `{level, limit, clear}` → `{messages[]}` |
237
+ | `pdf` | Generate PDF: `"filename"` or `{path, landscape, scale, pageRanges}` |
238
+ | `back` / `forward` | History navigation: `true` → `{url, title}` or `{noHistory: true}` |
239
+ | `reload` | Reload page: `true` or `{waitUntil}` |
240
+ | `newTab` | Create new tab (renamed from `openTab`): `"url"` or `{url, wait}` `{tab, url}` |
241
+ | `switchTab` | Switch to existing tab (renamed from `connectTab`): `"t1"` or `{targetId}` or `{url: "regex"}` |
242
+ | `closeTab` | Close tab: `"t1"` or `true` (current) |
243
+ | `listTabs` | List all tabs: `true` `{tabs[]}` |
244
+ | `waitForNavigation` | Wait for nav: `true` or `{timeout, waitUntil}` |
336
245
 
337
246
  ### Optional Steps
338
247
 
@@ -342,25 +251,28 @@ Add `"optional": true` to any step to continue on failure (status becomes "skipp
342
251
 
343
252
  | Issue | Solution |
344
253
  |-------|----------|
345
- | Tabs accumulating | Include `tab` in config |
346
- | CONNECTION error | Use `chromeStatus` first |
254
+ | Tabs accumulating | Include `tab` at top level |
255
+ | CONNECTION error | Check Chrome is reachable; use `chromeStatus` to diagnose |
347
256
  | Chrome not found | Set `CHROME_PATH` env var |
348
257
  | Element not found | Add `wait` step first |
349
258
  | Clicks not working | Scroll into view first, or `force: true` |
350
259
  | `back` returns noHistory | New tabs start at about:blank; navigate first |
351
260
  | Select dropdown not working | Use click+click or press arrow keys |
352
261
  | Type not appearing | Click input first to focus, then type |
262
+ | Elements missing from snapshot | Custom widgets may lack ARIA roles; use `pageFunction` or `get` with `mode: "html"` as fallback |
353
263
  | macOS: Chrome running but no CDP | `chromeStatus` launches new instance with CDP enabled |
354
264
 
355
265
  ## Best Practices
356
266
 
357
267
  - **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
268
+ - **Never launch Chrome directly** — `newTab` handles it automatically
269
+ - **Use newTab** as your first step to create a tab; use the returned tab ID for all subsequent calls
360
270
  - **Reuse only your own tabs** — other agents may share the browser
361
271
  - **Update the site profile before closing** — add any quirks, selectors, or recipes you discovered
362
272
  - **Close your tab when done** — `closeTab` with your tab ID
363
- - **Discover before interacting** — use `snapshot` and `inspect` to understand the page
273
+ - **Discover before interacting** — use `snapshot` to understand the page structure
364
274
  - **Use website navigation** — click links and submit forms; don't guess URLs
365
275
  - **Prefer refs** over CSS selectors — use `snapshot` + refs for resilient targeting
276
+ - **Check `newTabs` after click** — clicks on `target="_blank"` links report new tabs; use `switchTab` to switch
277
+ - **Use `switchTab` for popups** — connect by alias (`"t2"`), targetId, or URL regex (`{url: "pattern"}`)
366
278
  - **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.14",
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';