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.
- package/README.md +4 -4
- package/SKILL.md +276 -170
- package/package.json +9 -8
- package/{src → scripts}/aria/index.js +1 -1
- package/scripts/aria/role-query.js +295 -0
- package/{src → scripts}/aria.js +11 -5
- package/{src → scripts}/capture/console-capture.js +11 -9
- package/{src → scripts}/capture/screenshot-capture.js +8 -9
- package/{src → scripts}/cdp/connection.js +30 -6
- package/{src → scripts}/cdp-skill.js +7 -6
- package/{src → scripts}/diff.js +7 -6
- package/{src → scripts}/dom/LazyResolver.js +23 -12
- package/{src → scripts}/dom/actionability.js +39 -22
- package/{src → scripts}/dom/click-executor.js +90 -53
- package/{src → scripts}/dom/element-locator.js +4 -4
- package/{src → scripts}/dom/fill-executor.js +8 -4
- package/{src → scripts}/dom/input-emulator.js +47 -9
- package/{src → scripts}/dom/react-filler.js +11 -3
- package/{src → scripts}/dom/wait-executor.js +10 -2
- package/{src → scripts}/page/dialog-handler.js +7 -3
- package/{src → scripts}/page/dom-stability.js +17 -10
- package/{src → scripts}/page/page-controller.js +41 -34
- package/{src → scripts}/runner/context-helpers.js +7 -0
- package/{src → scripts}/runner/execute-browser.js +3 -118
- package/{src → scripts}/runner/execute-dynamic.js +46 -11
- package/{src → scripts}/runner/execute-form.js +6 -4
- package/{src → scripts}/runner/execute-input.js +127 -100
- package/{src → scripts}/runner/execute-interaction.js +31 -46
- package/{src → scripts}/runner/execute-navigation.js +14 -12
- package/{src → scripts}/runner/step-executors.js +28 -9
- package/{src → scripts}/runner/step-registry.js +57 -8
- package/{src → scripts}/runner/step-validator.js +13 -3
- package/{src → scripts}/tests/ExecuteInput.test.js +58 -188
- package/src/aria/role-query.js +0 -1229
- package/src/aria/snapshot.js +0 -459
- /package/{src → scripts}/aria/output-processor.js +0 -0
- /package/{src → scripts}/capture/debug-capture.js +0 -0
- /package/{src → scripts}/capture/error-aggregator.js +0 -0
- /package/{src → scripts}/capture/eval-serializer.js +0 -0
- /package/{src → scripts}/capture/index.js +0 -0
- /package/{src → scripts}/capture/network-capture.js +0 -0
- /package/{src → scripts}/capture/pdf-capture.js +0 -0
- /package/{src → scripts}/cdp/browser.js +0 -0
- /package/{src → scripts}/cdp/discovery.js +0 -0
- /package/{src → scripts}/cdp/index.js +0 -0
- /package/{src → scripts}/cdp/target-and-session.js +0 -0
- /package/{src → scripts}/constants.js +0 -0
- /package/{src → scripts}/dom/element-handle.js +0 -0
- /package/{src → scripts}/dom/element-validator.js +0 -0
- /package/{src → scripts}/dom/index.js +0 -0
- /package/{src → scripts}/dom/keyboard-executor.js +0 -0
- /package/{src → scripts}/dom/quad-helpers.js +0 -0
- /package/{src → scripts}/index.js +0 -0
- /package/{src → scripts}/page/cookie-manager.js +0 -0
- /package/{src → scripts}/page/index.js +0 -0
- /package/{src → scripts}/page/wait-utilities.js +0 -0
- /package/{src → scripts}/page/web-storage-manager.js +0 -0
- /package/{src → scripts}/runner/execute-query.js +0 -0
- /package/{src → scripts}/runner/index.js +0 -0
- /package/{src → scripts}/tests/Actionability.test.js +0 -0
- /package/{src → scripts}/tests/Aria.test.js +0 -0
- /package/{src → scripts}/tests/BrowserClient.test.js +0 -0
- /package/{src → scripts}/tests/CDPConnection.test.js +0 -0
- /package/{src → scripts}/tests/ChromeDiscovery.test.js +0 -0
- /package/{src → scripts}/tests/ClickExecutor.test.js +0 -0
- /package/{src → scripts}/tests/ConsoleCapture.test.js +0 -0
- /package/{src → scripts}/tests/ContextHelpers.test.js +0 -0
- /package/{src → scripts}/tests/CookieManager.test.js +0 -0
- /package/{src → scripts}/tests/DebugCapture.test.js +0 -0
- /package/{src → scripts}/tests/ElementHandle.test.js +0 -0
- /package/{src → scripts}/tests/ElementLocator.test.js +0 -0
- /package/{src → scripts}/tests/ErrorAggregator.test.js +0 -0
- /package/{src → scripts}/tests/EvalSerializer.test.js +0 -0
- /package/{src → scripts}/tests/ExecuteBrowser.test.js +0 -0
- /package/{src → scripts}/tests/ExecuteDynamic.test.js +0 -0
- /package/{src → scripts}/tests/ExecuteForm.test.js +0 -0
- /package/{src → scripts}/tests/ExecuteInteraction.test.js +0 -0
- /package/{src → scripts}/tests/ExecuteQuery.test.js +0 -0
- /package/{src → scripts}/tests/FillExecutor.test.js +0 -0
- /package/{src → scripts}/tests/InputEmulator.test.js +0 -0
- /package/{src → scripts}/tests/KeyboardExecutor.test.js +0 -0
- /package/{src → scripts}/tests/LazyResolver.test.js +0 -0
- /package/{src → scripts}/tests/NetworkErrorCapture.test.js +0 -0
- /package/{src → scripts}/tests/PageController.test.js +0 -0
- /package/{src → scripts}/tests/PdfCapture.test.js +0 -0
- /package/{src → scripts}/tests/ScreenshotCapture.test.js +0 -0
- /package/{src → scripts}/tests/SessionRegistry.test.js +0 -0
- /package/{src → scripts}/tests/StepValidator.test.js +0 -0
- /package/{src → scripts}/tests/TargetManager.test.js +0 -0
- /package/{src → scripts}/tests/TestRunner.test.js +0 -0
- /package/{src → scripts}/tests/WaitStrategy.test.js +0 -0
- /package/{src → scripts}/tests/WaitUtilities.test.js +0 -0
- /package/{src → scripts}/tests/WebStorageManager.test.js +0 -0
- /package/{src → scripts}/tests/integration.test.js +0 -0
- /package/{src → scripts}/types.js +0 -0
- /package/{src → scripts}/utils/backoff.js +0 -0
- /package/{src → scripts}/utils/cdp-helpers.js +0 -0
- /package/{src → scripts}/utils/devices.js +0 -0
- /package/{src → scripts}/utils/errors.js +0 -0
- /package/{src → scripts}/utils/index.js +0 -0
- /package/{src → scripts}/utils/temp.js +0 -0
- /package/{src → scripts}/utils/validators.js +0 -0
- /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
|
|
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
|
|
21
|
+
node scripts/cdp-skill.js '{"tab":"t1","steps":[{"click":"#btn"}]}'
|
|
22
22
|
|
|
23
23
|
# Non-default Chrome (rare)
|
|
24
|
-
node
|
|
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
|
-
|
|
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
|
-
|
|
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":[{"
|
|
68
|
-
echo '{"tab":"t1","steps":[{"click":"#btn"}]}' | node
|
|
69
|
-
echo '{"tab":"t1","steps":[{"snapshot":true}]}' | node
|
|
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/
|
|
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
|
|
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
|
-
##
|
|
83
|
+
## Optional Steps
|
|
144
84
|
|
|
145
|
-
|
|
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
|
-
|
|
87
|
+
---
|
|
149
88
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
-
|
|
157
|
-
`
|
|
158
|
-
Response includes top-level `siteProfile` or `actionRequired` (see Site Profiles).
|
|
236
|
+
#### getTitle
|
|
237
|
+
`true` — returns `{title}`
|
|
159
238
|
|
|
160
|
-
###
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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
|
-
|
|
167
|
-
`{
|
|
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
|
-
|
|
173
|
-
`"Enter"` | `"Control+a"` | `"Meta+Shift+Enter"` — keyboard shortcuts and key presses.
|
|
333
|
+
**How navigation uses profiles:**
|
|
174
334
|
|
|
175
|
-
|
|
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
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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
|
-
|
|
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
|
-
|
|
192
|
-
|
|
345
|
+
**Profile format:**
|
|
346
|
+
```
|
|
347
|
+
# domain.com
|
|
348
|
+
Updated: YYYY-MM-DD | Fingerprint: <tech-stack>
|
|
193
349
|
|
|
194
|
-
|
|
195
|
-
|
|
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
|
-
###
|
|
199
|
-
`true` — returns `{url}`
|
|
353
|
+
### Legacy Steps
|
|
200
354
|
|
|
201
|
-
|
|
202
|
-
`
|
|
355
|
+
- `getDom` — use `get` with `mode: "html"` instead
|
|
356
|
+
- `getBox` — use `get` with `mode: "box"` instead
|
|
203
357
|
|
|
204
|
-
|
|
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"`
|
|
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.
|
|
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": "
|
|
6
|
+
"main": "scripts/index.js",
|
|
7
7
|
"bin": {
|
|
8
|
-
"cdp-skill": "
|
|
8
|
+
"cdp-skill": "scripts/cdp-skill.js"
|
|
9
9
|
},
|
|
10
10
|
"exports": {
|
|
11
|
-
".": "./
|
|
12
|
-
"./utils": "./
|
|
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
|
|
21
|
-
"test:run": "node --test --test-force-exit --test-reporter spec
|
|
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
|
-
"
|
|
27
|
+
"scripts/",
|
|
28
|
+
"scripts/"
|
|
28
29
|
],
|
|
29
30
|
"repository": {
|
|
30
31
|
"type": "git",
|