cdp-skill 1.0.15 → 1.0.17

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 (103) hide show
  1. package/README.md +4 -4
  2. package/SKILL.md +276 -170
  3. package/package.json +9 -8
  4. package/{src → scripts}/aria/index.js +1 -1
  5. package/scripts/aria/role-query.js +295 -0
  6. package/{src → scripts}/aria.js +11 -5
  7. package/{src → scripts}/capture/console-capture.js +11 -9
  8. package/{src → scripts}/capture/screenshot-capture.js +8 -9
  9. package/{src → scripts}/cdp/connection.js +30 -6
  10. package/{src → scripts}/cdp-skill.js +7 -6
  11. package/{src → scripts}/diff.js +7 -6
  12. package/{src → scripts}/dom/LazyResolver.js +23 -12
  13. package/{src → scripts}/dom/actionability.js +39 -22
  14. package/{src → scripts}/dom/click-executor.js +90 -53
  15. package/{src → scripts}/dom/element-locator.js +4 -4
  16. package/{src → scripts}/dom/fill-executor.js +8 -4
  17. package/{src → scripts}/dom/input-emulator.js +47 -9
  18. package/{src → scripts}/dom/react-filler.js +11 -3
  19. package/{src → scripts}/dom/wait-executor.js +10 -2
  20. package/{src → scripts}/page/dialog-handler.js +7 -3
  21. package/{src → scripts}/page/dom-stability.js +17 -10
  22. package/{src → scripts}/page/page-controller.js +41 -34
  23. package/{src → scripts}/runner/context-helpers.js +7 -0
  24. package/{src → scripts}/runner/execute-browser.js +3 -118
  25. package/{src → scripts}/runner/execute-dynamic.js +46 -11
  26. package/{src → scripts}/runner/execute-form.js +6 -4
  27. package/{src → scripts}/runner/execute-input.js +127 -100
  28. package/{src → scripts}/runner/execute-interaction.js +31 -46
  29. package/{src → scripts}/runner/execute-navigation.js +14 -12
  30. package/{src → scripts}/runner/step-executors.js +28 -9
  31. package/{src → scripts}/runner/step-registry.js +57 -8
  32. package/{src → scripts}/runner/step-validator.js +13 -3
  33. package/{src → scripts}/tests/ExecuteInput.test.js +58 -188
  34. package/src/aria/role-query.js +0 -1229
  35. package/src/aria/snapshot.js +0 -459
  36. /package/{src → scripts}/aria/output-processor.js +0 -0
  37. /package/{src → scripts}/capture/debug-capture.js +0 -0
  38. /package/{src → scripts}/capture/error-aggregator.js +0 -0
  39. /package/{src → scripts}/capture/eval-serializer.js +0 -0
  40. /package/{src → scripts}/capture/index.js +0 -0
  41. /package/{src → scripts}/capture/network-capture.js +0 -0
  42. /package/{src → scripts}/capture/pdf-capture.js +0 -0
  43. /package/{src → scripts}/cdp/browser.js +0 -0
  44. /package/{src → scripts}/cdp/discovery.js +0 -0
  45. /package/{src → scripts}/cdp/index.js +0 -0
  46. /package/{src → scripts}/cdp/target-and-session.js +0 -0
  47. /package/{src → scripts}/constants.js +0 -0
  48. /package/{src → scripts}/dom/element-handle.js +0 -0
  49. /package/{src → scripts}/dom/element-validator.js +0 -0
  50. /package/{src → scripts}/dom/index.js +0 -0
  51. /package/{src → scripts}/dom/keyboard-executor.js +0 -0
  52. /package/{src → scripts}/dom/quad-helpers.js +0 -0
  53. /package/{src → scripts}/index.js +0 -0
  54. /package/{src → scripts}/page/cookie-manager.js +0 -0
  55. /package/{src → scripts}/page/index.js +0 -0
  56. /package/{src → scripts}/page/wait-utilities.js +0 -0
  57. /package/{src → scripts}/page/web-storage-manager.js +0 -0
  58. /package/{src → scripts}/runner/execute-query.js +0 -0
  59. /package/{src → scripts}/runner/index.js +0 -0
  60. /package/{src → scripts}/tests/Actionability.test.js +0 -0
  61. /package/{src → scripts}/tests/Aria.test.js +0 -0
  62. /package/{src → scripts}/tests/BrowserClient.test.js +0 -0
  63. /package/{src → scripts}/tests/CDPConnection.test.js +0 -0
  64. /package/{src → scripts}/tests/ChromeDiscovery.test.js +0 -0
  65. /package/{src → scripts}/tests/ClickExecutor.test.js +0 -0
  66. /package/{src → scripts}/tests/ConsoleCapture.test.js +0 -0
  67. /package/{src → scripts}/tests/ContextHelpers.test.js +0 -0
  68. /package/{src → scripts}/tests/CookieManager.test.js +0 -0
  69. /package/{src → scripts}/tests/DebugCapture.test.js +0 -0
  70. /package/{src → scripts}/tests/ElementHandle.test.js +0 -0
  71. /package/{src → scripts}/tests/ElementLocator.test.js +0 -0
  72. /package/{src → scripts}/tests/ErrorAggregator.test.js +0 -0
  73. /package/{src → scripts}/tests/EvalSerializer.test.js +0 -0
  74. /package/{src → scripts}/tests/ExecuteBrowser.test.js +0 -0
  75. /package/{src → scripts}/tests/ExecuteDynamic.test.js +0 -0
  76. /package/{src → scripts}/tests/ExecuteForm.test.js +0 -0
  77. /package/{src → scripts}/tests/ExecuteInteraction.test.js +0 -0
  78. /package/{src → scripts}/tests/ExecuteQuery.test.js +0 -0
  79. /package/{src → scripts}/tests/FillExecutor.test.js +0 -0
  80. /package/{src → scripts}/tests/InputEmulator.test.js +0 -0
  81. /package/{src → scripts}/tests/KeyboardExecutor.test.js +0 -0
  82. /package/{src → scripts}/tests/LazyResolver.test.js +0 -0
  83. /package/{src → scripts}/tests/NetworkErrorCapture.test.js +0 -0
  84. /package/{src → scripts}/tests/PageController.test.js +0 -0
  85. /package/{src → scripts}/tests/PdfCapture.test.js +0 -0
  86. /package/{src → scripts}/tests/ScreenshotCapture.test.js +0 -0
  87. /package/{src → scripts}/tests/SessionRegistry.test.js +0 -0
  88. /package/{src → scripts}/tests/StepValidator.test.js +0 -0
  89. /package/{src → scripts}/tests/TargetManager.test.js +0 -0
  90. /package/{src → scripts}/tests/TestRunner.test.js +0 -0
  91. /package/{src → scripts}/tests/WaitStrategy.test.js +0 -0
  92. /package/{src → scripts}/tests/WaitUtilities.test.js +0 -0
  93. /package/{src → scripts}/tests/WebStorageManager.test.js +0 -0
  94. /package/{src → scripts}/tests/integration.test.js +0 -0
  95. /package/{src → scripts}/types.js +0 -0
  96. /package/{src → scripts}/utils/backoff.js +0 -0
  97. /package/{src → scripts}/utils/cdp-helpers.js +0 -0
  98. /package/{src → scripts}/utils/devices.js +0 -0
  99. /package/{src → scripts}/utils/errors.js +0 -0
  100. /package/{src → scripts}/utils/index.js +0 -0
  101. /package/{src → scripts}/utils/temp.js +0 -0
  102. /package/{src → scripts}/utils/validators.js +0 -0
  103. /package/{src → scripts}/utils.js +0 -0
package/README.md CHANGED
@@ -15,13 +15,13 @@ A lightweight, zero-dependency browser automation library using Chrome DevTools
15
15
 
16
16
  ```bash
17
17
  # Open a tab (Chrome auto-launches if needed)
18
- node src/cdp-skill.js '{"steps":[{"newTab":"https://google.com"}]}'
18
+ node scripts/cdp-skill.js '{"steps":[{"newTab":"https://google.com"}]}'
19
19
 
20
20
  # Use the returned tab ID for subsequent calls
21
- node src/cdp-skill.js '{"tab":"t1","steps":[{"click":"#btn"}]}'
21
+ node scripts/cdp-skill.js '{"tab":"t1","steps":[{"click":"#btn"}]}'
22
22
 
23
23
  # Non-default Chrome (rare)
24
- node src/cdp-skill.js '{"steps":[{"newTab":{"url":"https://google.com","port":9333,"headless":true}}]}'
24
+ node scripts/cdp-skill.js '{"steps":[{"newTab":{"url":"https://google.com","port":9333,"headless":true}}]}'
25
25
  ```
26
26
 
27
27
  ## Features
@@ -125,7 +125,7 @@ node src/cdp-skill.js '{"steps":[{"newTab":{"url":"https://google.com","port":93
125
125
  ## Architecture
126
126
 
127
127
  ```
128
- src/
128
+ scripts/
129
129
  ├── cdp-skill.js # CLI entry point, JSON parsing, response assembly
130
130
  ├── aria.js # Accessibility snapshots, role queries
131
131
  ├── diff.js # Snapshot diffing, viewport change detection
package/SKILL.md CHANGED
@@ -10,78 +10,18 @@ compatibility: Requires Chrome/Chromium (auto-launched if not running) and Node.
10
10
  Automate Chrome browser interactions via JSON step definitions passed to a Node.js CLI.
11
11
 
12
12
  > **See EXAMPLES.md** for full JSON examples, response shapes, and worked patterns for every step type.
13
-
14
- ## Site Profiles
15
-
16
- Site profiles are per-domain cheatsheets stored at `~/.cdp-skill/sites/{domain}.md`. They record what the agent learned about a site: framework, quirks, stable selectors, and recipes for common flows.
17
-
18
- > **IMPORTANT — `actionRequired` responses are MANDATORY.** When any response contains `actionRequired`, you **MUST** complete it before doing anything else. Do not skip it. Do not proceed to your main task first. Handle `actionRequired` immediately.
19
-
20
- ### How navigation uses profiles
21
-
22
- Every `goto`, `newTab` (with URL), and `switchTab` checks for a profile. The result appears as a **top-level field** in the response:
23
-
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
- - **`actionRequired`** present → **STOP. Create the profile NOW** before continuing your task:
26
- 1. `snapshot` — map page structure and landmarks
27
- 2. `pageFunction` — detect framework (e.g. `() => { return { react: !!window.__REACT, next: !!window.__NEXT_DATA__, vue: !!window.__VUE__ } }`)
28
- 3. `writeSiteProfile` — save domain and markdown content
29
-
30
- The profile only needs to capture what's useful: environment, quirks, stable selectors, strategies for fill/click/wait, and recipes for common flows. A minimal profile is fine — even just the environment and one quirk is valuable.
31
-
32
- ### Updating profiles after your task
33
-
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
-
36
- ### Profile format
37
-
38
- ```
39
- # domain.com
40
- Updated: YYYY-MM-DD | Fingerprint: <tech-stack>
41
-
42
- ## Environment / Quirks / Strategies / Regions / Recipes
43
- ```
44
-
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
50
-
51
- Sections are optional — include what's useful. See EXAMPLES.md for a full profile template.
52
-
53
- ### readSiteProfile
54
-
55
- `"domain"` | `{domain}` — returns `{found, domain, content}` or `{found: false, domain}`
56
-
57
- ### writeSiteProfile
58
-
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..."}}
62
- ```
13
+ >
14
+ > **For implementation details**, read the source in `cdp-skill/scripts/`. The step registry (`runner/step-registry.js`) defines all step types and their validation. The executor files (`runner/execute-*.js`) contain the implementation for each step.
63
15
 
64
16
  ## Quick Start
65
17
 
66
18
  ```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
19
+ echo '{"steps":[{"newTab":"https://google.com"}]}' | node scripts/cdp-skill.js
20
+ echo '{"tab":"t1","steps":[{"click":"#btn"}]}' | node scripts/cdp-skill.js
21
+ echo '{"tab":"t1","steps":[{"snapshot":true}]}' | node scripts/cdp-skill.js
70
22
  ```
71
23
 
72
- Tab IDs (t1, t2, ...) persist across CLI invocations. Chrome auto-launches if not running.
73
-
74
- ## Reliability (v1.0.10-1.0.11)
75
-
76
- Recent improvements to stability and correctness:
77
-
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.
24
+ Tab IDs (t1, t2, ...) persist across CLI invocations. Chrome auto-launches if not running. Steps execute sequentially — each step completes before the next begins.
85
25
 
86
26
  ## Input / Output Schema
87
27
 
@@ -93,7 +33,7 @@ The skill now passes 1261/1263 unit tests (99.8%) and maintains SHS 99/100 on th
93
33
  **Output fields:**
94
34
  - `status`: "ok" or "error"
95
35
  - `tab`: short tab ID (e.g. "t1")
96
- - `siteProfile`: full markdown content of existing profile (after goto/openTab to known site)
36
+ - `siteProfile`: full markdown content of existing profile (after goto/newTab to known site)
97
37
  - `actionRequired`: `{action, domain, message}` — **MUST be handled immediately** before continuing (see Site Profiles)
98
38
  - `context`: `{url, title, scroll: {y, percent}, viewport: {width, height}, activeElement?, modal?}`
99
39
  - `screenshot`: path to after-screenshot (auto-captured on every visual action)
@@ -116,7 +56,7 @@ Snapshots return versioned refs like `[ref=f0s1e4]` — format: `f{frameId}s{sna
116
56
  - `f1`, `f2`, ... = iframe by index
117
57
  - `f[name]` = iframe by name (e.g., `f[frame-top]`)
118
58
 
119
- Each frame maintains its own snapshot counter. Use refs with `click`, `fill`, `hover`. Refs remain valid while the element is in DOM.
59
+ Each frame maintains its own snapshot counter. Use refs with `click`, `fill`, `hover`, `scroll`, `drag`, `upload`, `get`. Refs remain valid while the element is in DOM.
120
60
 
121
61
  **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.
122
62
 
@@ -138,133 +78,300 @@ Optional parameters on action steps to customize the step lifecycle:
138
78
  - **settledWhen**: `"() => condition"` — polled until truthy **after** the action completes
139
79
  - **observe**: `"() => data"` — runs after settlement, return value appears in `result.observation`
140
80
 
141
- Hooks can be combined on any action step. Applies to: click, fill, press, hover, drag, selectOption, scroll.
81
+ Hooks can be combined on any visual action step: click, fill, press, hover, drag, selectOption, scroll, goto, reload, newTab, switchTab, snapshot, snapshotSearch, query, queryAll, inspect, get, submit, assert, wait, upload, pageFunction, selectText.
142
82
 
143
- ## Core Steps
83
+ ## Optional Steps
144
84
 
145
- ### chromeStatus (optional diagnostic)
146
- `true` | `{host, port, headless, autoLaunch}` — returns `{running, launched, version, port, tabs[]}`
85
+ Add `"optional": true` to any step to continue on failure (status becomes "skipped"). Useful for dismissing optional modals or clicking elements that may not exist.
147
86
 
148
- > **Note**: You rarely need this — `newTab` auto-launches Chrome. Use `chromeStatus` only for diagnostics or non-default ports.
87
+ ---
149
88
 
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}}]}`
89
+ ## Steps
90
+
91
+ ### Navigation
92
+
93
+ #### goto
94
+ `"url"` | `{url, waitUntil}`
95
+ - **waitUntil**: `"commit"` | `"domcontentloaded"` | `"load"` | `"networkidle"`
96
+ - **Returns**: navigation result with snapshot
97
+ - Response includes top-level `siteProfile` or `actionRequired` (see Site Profiles)
98
+
99
+ #### newTab
100
+ `true` | `"url"` | `{url, host, port, headless, timeout}`
101
+ - Opens a new browser tab. **Required as first step** when no tab exists. Chrome auto-launches if not running.
102
+ - **Returns**: `{opened, tab, url, navigated, viewportSnapshot, fullSnapshot, context}`
103
+ - Response includes top-level `siteProfile` or `actionRequired` when URL provided
104
+
105
+ #### switchTab
106
+ `"alias"` | `{targetId}` | `{url: "regex"}` | `{host, port}`
107
+ - Connects to an existing tab by alias (`"t2"`), targetId, or URL regex pattern.
108
+ - **Returns**: tab context with snapshot
109
+
110
+ #### back
111
+ `true` | `{timeout}`
112
+ - **Returns**: `{url, title}` or `{noHistory: true}`
113
+
114
+ #### forward
115
+ `true` | `{timeout}`
116
+ - **Returns**: `{url, title}` or `{noHistory: true}`
117
+
118
+ #### reload
119
+ `true` | `{waitUntil}`
120
+ - **waitUntil**: `"commit"` | `"domcontentloaded"` | `"load"` | `"networkidle"`
121
+
122
+ #### waitForNavigation
123
+ `true` | `{timeout, waitUntil}`
124
+ - Waits for an in-progress navigation to reach the specified readyState.
125
+ - **waitUntil**: `"commit"` | `"domcontentloaded"` | `"load"` (default) | `"networkidle"`
126
+
127
+ ### Interaction
128
+
129
+ #### click
130
+ `"selector"` | `{ref, selector, text, selectors[], x/y}`
131
+ - **Options**: `force`, `timeout` (default 10000), `button` (left/middle/right), `clickCount`
132
+ - **Hooks**: readyWhen, settledWhen, observe
133
+ - **Returns**: `{clicked, method: "cdp"|"jsClick-auto", navigated?, newUrl?, newTabs?}`
134
+ - `newTabs` reports any tabs opened by the click (e.g., `target="_blank"` links)
135
+
136
+ #### fill
137
+ Multiple shapes for flexibility:
138
+ - **Focused**: `"text"` or `{value, clear}` — types into the currently focused element
139
+ - **Targeted**: `{selector|ref|label, value}` — targets a specific field
140
+ - **Batch**: `{"#a":"x", "#b":"y"}` — fills multiple fields by selector
141
+ - **Batch with options**: `{fields: {"#a":"x"}, react: true, clear: true}`
142
+
143
+ **Options**: `clear` (default true), `react`, `force`, `exact`, `timeout`
144
+ **Returns**: `{filled, navigated?, newUrl?}` (targeted) or `{total, filled, failed, results[], mode: "batch"}` (batch)
145
+
146
+ #### press
147
+ `"Enter"` | `"Control+a"` | `"Meta+Shift+Enter"`
148
+ - Keyboard shortcuts and key presses. Key names follow the [KeyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values) spec.
149
+
150
+ #### hover
151
+ `"selector"` | `{selector, ref, text, x/y}`
152
+ - **Options**: `duration` (ms), `force`, `timeout`, `captureResult`
153
+ - **Returns**: `{hovered}` — with `captureResult: true`: adds `capturedResult: {visibleElements[]}`
154
+
155
+ #### drag
156
+ `{source, target}` — drag and drop between elements or coordinates.
157
+ - **source/target**: `"selector"` | `{ref}` | `{x, y}` | `{ref, offsetX, offsetY}`
158
+ - **Options**: `steps` (default 10), `delay` (default 0), `method: "auto"|"mouse"|"html5"`
159
+ - **Returns**: `{dragged, method, source: {x,y}, target: {x,y}}`
160
+
161
+ #### selectOption
162
+ `{selector, value|label|index|values}`
163
+ - Select from `<select>` dropdown by value, label text, or index. `values` for multi-select.
164
+ - **Returns**: `{selected: [string], multiple: boolean}`
165
+
166
+ #### selectText
167
+ `"selector"` | `{selector, start, end}`
168
+ - Selects text in an input/textarea. Omit start/end to select all.
169
+ - **Returns**: `{selected: true, selector}`
170
+
171
+ #### upload
172
+ `"/path/to/file"` | `["/a.txt", "/b.png"]` | `{selector|ref, file|files}`
173
+ - Auto-finds `input[type="file"]` if no selector given.
174
+ - **Returns**: `{uploaded, files[], accept, multiple, target}`
175
+
176
+ #### submit
177
+ `"selector"` | `{selector, reportValidity}`
178
+ - Submits a form. With `reportValidity: true`, triggers HTML5 validation.
179
+ - **Returns**: `{submitted, valid, errors[]}`
180
+
181
+ ### Query & Extraction
182
+
183
+ #### snapshot
184
+ `true` | `{detail, mode, root, maxDepth, maxElements, maxNameLength, includeText, includeFrames, pierceShadow, viewportOnly, inlineLimit, preserveRefs, since}`
185
+ - **detail**: `"summary"` | `"interactive"` | `"full"` (default) — controls output verbosity
186
+ - **mode**: `"ai"` (default) | `"full"` — ai mode filters to relevant content
187
+ - **root**: CSS selector or `"role=main"` to scope the snapshot
188
+ - **maxNameLength**: truncate accessible names to N chars (default 150, 0 to disable)
189
+ - **inlineLimit**: bytes before saving to file (default 9000)
190
+ - **since**: `"f0s1"` — returns `{unchanged: true}` if page hasn't changed since that snapshot
191
+ - **Returns**: YAML with role, "name", states, `[ref=f{F}s{N}e{M}]`, snapshotId
192
+ - Snapshots over `inlineLimit` bytes are saved to a file (path in `artifacts.snapshot`)
193
+
194
+ #### snapshotSearch
195
+ `{text, pattern, role, exact, limit, context, near: {x, y, radius}}`
196
+ - Search the snapshot for matching elements by text, regex pattern, or ARIA role.
197
+ - **limit**: max results (default 10)
198
+ - **context**: lines of surrounding context to include
199
+ - **near**: spatial filter — only elements within `radius` px of `{x, y}`
200
+ - **Returns**: `{matches[], matchCount, searchedElements}`
201
+
202
+ #### query
203
+ `"selector"` | `{selector, role, name, nameExact, nameRegex, level, limit, output, count}`
204
+ - Query by CSS selector or ARIA role with optional name/level filters.
205
+ - **output**: `"text"` (default) | `"html"` | `"value"` | `"attributes"` | `["attr1", "attr2"]` | `{attribute: "data-id"}`
206
+ - **limit**: max results (default 10)
207
+ - **count**: `true` for count-only mode
208
+ - **Returns**: `{selector, total, showing, results[]}`
209
+
210
+ #### queryAll
211
+ `{"label": "selector", ...}` — batch multiple queries in one step.
212
+ - Each key is a friendly name, each value is a selector string or `{role, name}` object.
213
+ - **Returns**: `{queries: {label: result, ...}}`
214
+
215
+ #### get
216
+ `"selector"` | `{selector|ref, mode}`
217
+ - **Modes**: `"text"` (default), `"html"`, `"value"`, `"box"`, `"attributes"`
218
+ - Auto-detects tables and lists when mode is `"text"`, returning structured data
219
+ - `"value"` mode on a form returns all field values, validation state, and labels
220
+ - See EXAMPLES.md for response shapes per mode
221
+
222
+ #### inspect
223
+ `true` | `{selectors[], limit}`
224
+ - Page overview: returns element counts by tag type (a, button, input, form, etc.)
225
+ - **selectors**: additional CSS selectors to count; **limit**: sample values per selector
226
+ - **Returns**: `{title, url, elements: {a, button, input, ...}, custom?: {}}`
227
+
228
+ #### elementsAt
229
+ `{x, y}` | `[{x,y}, ...]` | `{x, y, radius, limit}`
230
+ - Find elements at specific coordinates or within a radius.
231
+ - **Returns**: element info with ref, tag, selector, clickable, box, distance (for radius)
232
+
233
+ #### getUrl
234
+ `true` — returns `{url}`
155
235
 
156
- ### goto
157
- `"url"` | `{url, waitUntil}` — waitUntil: commit | domcontentloaded | load | networkidle
158
- Response includes top-level `siteProfile` or `actionRequired` (see Site Profiles).
236
+ #### getTitle
237
+ `true` returns `{title}`
159
238
 
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}]}`
239
+ ### Waiting & Polling
240
+
241
+ #### wait
242
+ `"selector"` | `{selector, hidden, minCount}` | `{text, caseSensitive}` | `{textRegex}` | `{urlContains}`
243
+ - Waits for element presence/absence, text appearance, or URL change.
244
+ - **hidden**: `true` to wait for element to disappear
245
+ - **minCount**: wait until at least N elements match (default 1)
246
+ - **caseSensitive**: `true` (default) for text matching
247
+ - **timeout**: ms to wait (default 30000)
248
+
249
+ #### sleep
250
+ `number` (ms, 0–60000) — fixed time delay.
251
+
252
+ #### poll
253
+ `"() => expr"` | `{fn, interval, timeout}`
254
+ - Polls a function in the browser until it returns truthy.
255
+ - **interval**: poll frequency in ms (default 100)
256
+ - **timeout**: max wait in ms (default 30000)
257
+ - **Returns**: `{resolved: true, value, elapsed}` or `{resolved: false, elapsed, lastValue}`
258
+
259
+ ### Scripting
260
+
261
+ #### pageFunction
262
+ `"() => expr"` | `"document.title"` | `{fn, expression, refs, timeout}`
263
+ - Runs custom JavaScript in the browser in the current frame context.
264
+ - Bare expressions auto-wrapped. Return values auto-serialized (Dates, Maps, Sets, Elements, NodeLists).
265
+ - **refs**: `true` to pass `window.__ariaRefs` as first argument
266
+ - **timeout**: execution timeout in ms
267
+ - **Returns**: `{type, value}` — see EXAMPLES.md for typed return values
268
+
269
+ #### assert
270
+ `{url: {contains|equals|startsWith|endsWith|matches}}` | `{text}` | `{selector, text, caseSensitive}`
271
+ - Assert URL conditions or text presence. Throws on failure.
272
+ - **Returns**: `{passed, assertions[]}`
273
+
274
+ ### Page Control
275
+
276
+ #### scroll
277
+ `"top"` | `"bottom"` | `"up"` | `"down"` | `"selector"` | `"ref"` | `{deltaY}` | `{x, y}`
278
+ - Scroll page or scroll an element into view. Direction strings (`"up"`, `"down"`) scroll by 300px.
279
+ - **Returns**: `{scrollX, scrollY}`
280
+
281
+ #### frame
282
+ `"selector"` | `0` | `"top"` | `{name: "frameName"}` | `{list: true}`
283
+ - Switch to an iframe by selector, index, or name. `"top"` returns to main frame.
284
+ - `{list: true}` returns the frame tree without switching.
285
+
286
+ #### viewport
287
+ `"iphone-14"` | `{width, height, mobile, hasTouch, isLandscape, deviceScaleFactor}`
288
+ - Set viewport size. Accepts device preset strings (e.g., `"pixel-7"`, `"ipad-pro-11"`, `"macbook-pro-14"`, `"desktop-hd"`) or explicit dimensions.
289
+ - **Returns**: `{width, height, deviceScaleFactor}`
290
+
291
+ ### Browser & Tabs
292
+
293
+ #### chromeStatus
294
+ `true` | `{host, port, headless, autoLaunch}`
295
+ - Diagnostics step — checks if Chrome is running and reachable.
296
+ - **Returns**: `{running, launched, version, port, tabs[]}`
297
+ - You rarely need this — `newTab` auto-launches Chrome.
298
+
299
+ #### listTabs
300
+ `true` — returns `{count, tabs[]}` with targetId, url, title, alias per tab.
301
+
302
+ #### closeTab
303
+ `"tabId"` — closes the specified tab. Use your tab alias (e.g., `"t1"`).
304
+
305
+ #### cookies
306
+ - `{get: true}` | `{get: ["url"], name: "session_id"}` — get cookies for current page or specific URLs
307
+ - `{set: [{name, value, domain, path, expires, httpOnly, secure, sameSite}]}` — set cookies (`expires` accepts `"1h"`, `"7d"`, `"30m"`, `"1w"`, `"1y"` or Unix timestamp)
308
+ - `{delete: "name", domain}` — delete specific cookie(s)
309
+ - `{clear: true, domain}` — clear all cookies, optionally filtered by domain
310
+
311
+ #### console
312
+ `true` | `{level, type, since, limit, clear, stackTrace}`
313
+ - **level**: filter by `"error"`, `"warning"`, `"log"`, etc.
314
+ - **type**: `"console"` or `"exception"`
315
+ - **limit**: max messages (default 50)
316
+ - **clear**: clear buffer after returning
317
+ - **stackTrace**: include call stacks
318
+ - **Returns**: `{total, showing, messages[]}`
319
+
320
+ #### pdf
321
+ `"filename"` | `{path, landscape, printBackground, scale, pageRanges, selector}`
322
+ - Generate PDF. Relative paths resolve to platform temp directory.
323
+ - **selector**: capture a specific element instead of full page
324
+
325
+ ### Site Profiles
326
+
327
+ #### readSiteProfile
328
+ `"domain"` | `{domain}` — returns `{found, domain, content}` or `{found: false, domain}`
165
329
 
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?}`
330
+ #### writeSiteProfile
331
+ `{domain, content}` — returns `{written, path, domain}`
171
332
 
172
- ### press
173
- `"Enter"` | `"Control+a"` | `"Meta+Shift+Enter"` — keyboard shortcuts and key presses.
333
+ **How navigation uses profiles:**
174
334
 
175
- ### scroll
176
- `"top"` | `"bottom"` | `"selector"` | `{deltaY}` | `{x, y}`
177
- Returns: `{scrollX, scrollY}`
335
+ Every `goto`, `newTab` (with URL), and `switchTab` checks for a profile. The result appears as a **top-level field** in the response:
178
336
 
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)
337
+ - **`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.
338
+ - **`actionRequired`** present **STOP. Create the profile NOW** before continuing your task:
339
+ 1. `snapshot` map page structure and landmarks
340
+ 2. `pageFunction` — detect framework (e.g. `() => { return { react: !!window.__REACT, next: !!window.__NEXT_DATA__, vue: !!window.__VUE__ } }`)
341
+ 3. `writeSiteProfile` save domain and markdown content
185
342
 
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.
343
+ After completing your goal, update the site profile with anything you learned. Call `writeSiteProfile` again with the improved content before closing your tab.
190
344
 
191
- ### wait
192
- `"selector"` | `{selector, hidden, minCount}` | `{text}` | `{textRegex}` | `{urlContains}`
345
+ **Profile format:**
346
+ ```
347
+ # domain.com
348
+ Updated: YYYY-MM-DD | Fingerprint: <tech-stack>
193
349
 
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}`
350
+ ## Environment / Quirks / Strategies / Regions / Recipes
351
+ ```
197
352
 
198
- ### getUrl
199
- `true` — returns `{url}`
353
+ ### Legacy Steps
200
354
 
201
- ### getTitle
202
- `true` — returns `{title}`
355
+ - `getDom` — use `get` with `mode: "html"` instead
356
+ - `getBox` — use `get` with `mode: "box"` instead
203
357
 
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}` |
249
-
250
- ### Optional Steps
251
-
252
- Add `"optional": true` to any step to continue on failure (status becomes "skipped").
358
+ ---
253
359
 
254
360
  ## Troubleshooting
255
361
 
256
362
  | Issue | Solution |
257
363
  |-------|----------|
258
- | Tabs accumulating | Include `tab` at top level |
364
+ | Tabs accumulating | Include `tab` at top level to reuse existing tabs |
259
365
  | CONNECTION error | Check Chrome is reachable; use `chromeStatus` to diagnose |
260
366
  | Chrome not found | Set `CHROME_PATH` env var |
261
- | Element not found | Add `wait` step first |
262
- | Clicks not working | Scroll into view first, or `force: true` |
367
+ | Element not found | Add `wait` step first, or check snapshot for correct ref |
368
+ | Clicks not working | Scroll into view first, or use `force: true` |
263
369
  | `back` returns noHistory | New tabs start at about:blank; navigate first |
264
- | Select dropdown not working | Use click+click or press arrow keys |
370
+ | Select dropdown not working | Use click+click pattern or press arrow keys |
265
371
  | 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 |
372
+ | Elements missing from snapshot | Custom widgets may lack ARIA roles; use `pageFunction` or `get` with `mode: "html"` |
267
373
  | macOS: Chrome running but no CDP | `chromeStatus` launches new instance with CDP enabled |
374
+ | Shell escaping issues | Use heredoc or pipe from file (see EXAMPLES.md) |
268
375
 
269
376
  ## Best Practices
270
377
 
@@ -278,5 +385,4 @@ Add `"optional": true` to any step to continue on failure (status becomes "skipp
278
385
  - **Use website navigation** — click links and submit forms; don't guess URLs
279
386
  - **Prefer refs** over CSS selectors — use `snapshot` + refs for resilient targeting
280
387
  - **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"}`)
282
388
  - **Be persistent** — try alternative selectors, add waits, scroll first
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "cdp-skill",
3
- "version": "1.0.15",
3
+ "version": "1.0.17",
4
4
  "description": "Browser automation skill using Chrome DevTools Protocol for Claude Code and AI agents",
5
5
  "type": "module",
6
- "main": "src/index.js",
6
+ "main": "scripts/index.js",
7
7
  "bin": {
8
- "cdp-skill": "src/cdp-skill.js"
8
+ "cdp-skill": "scripts/cdp-skill.js"
9
9
  },
10
10
  "exports": {
11
- ".": "./src/index.js",
12
- "./utils": "./src/utils.js"
11
+ ".": "./scripts/index.js",
12
+ "./utils": "./scripts/utils.js"
13
13
  },
14
14
  "engines": {
15
15
  "node": ">=22.0.0"
@@ -17,14 +17,15 @@
17
17
  "scripts": {
18
18
  "postinstall": "node install.js",
19
19
  "preuninstall": "node uninstall.js",
20
- "test": "node --test --test-force-exit src/tests/*.test.js",
21
- "test:run": "node --test --test-force-exit --test-reporter spec src/tests/*.test.js"
20
+ "test": "node --test --test-force-exit scripts/tests/*.test.js",
21
+ "test:run": "node --test --test-force-exit --test-reporter spec scripts/tests/*.test.js"
22
22
  },
23
23
  "files": [
24
24
  "install.js",
25
25
  "uninstall.js",
26
26
  "SKILL.md",
27
- "src/"
27
+ "scripts/",
28
+ "scripts/"
28
29
  ],
29
30
  "repository": {
30
31
  "type": "git",
@@ -5,4 +5,4 @@
5
5
 
6
6
  export { createQueryOutputProcessor } from './output-processor.js';
7
7
  export { createRoleQueryExecutor } from './role-query.js';
8
- export { createAriaSnapshot } from './snapshot.js';
8
+ export { createAriaSnapshot } from '../aria.js';