octocode-cli 1.3.1 → 1.5.1
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 +193 -34
- package/out/chunks/chunk-7476PETK.js +309 -0
- package/out/chunks/chunk-CVNNNSMQ.js +26 -0
- package/out/chunks/chunk-OQBJTZWK.js +60 -0
- package/out/chunks/chunk-UCZCF3BQ.js +9 -0
- package/out/chunks/command-help-specs-JZXVSLZ5.js +8 -0
- package/out/chunks/commands-XBFPLHSQ.js +8 -0
- package/out/chunks/help-P7TCOYAJ.js +10 -0
- package/out/chunks/main-help-ULF5PAQY.js +10 -0
- package/out/chunks/prompts-5E6VKRX5.js +8 -0
- package/out/chunks/spinner-URV2OX6O.js +8 -0
- package/out/chunks/tool-command-M6VA7P2F.js +8 -0
- package/out/octocode-cli.js +1 -1
- package/package.json +5 -3
- package/skills/README.md +13 -1
- package/skills/agentic-flow-best-practices/SKILL.md +280 -0
- package/skills/agentic-flow-best-practices/references/agent-collaboration-patterns.md +75 -0
- package/skills/agentic-flow-best-practices/references/pr-review-agent-example.md +47 -0
- package/skills/agentic-flow-best-practices/references/resources.md +112 -0
- package/skills/octocode-chrome-devtools/README.md +541 -0
- package/skills/octocode-chrome-devtools/SKILL.md +197 -0
- package/skills/octocode-chrome-devtools/agents/openai.yaml +7 -0
- package/skills/octocode-chrome-devtools/references/CDP_AGENT_REFERENCE.md +401 -0
- package/skills/octocode-chrome-devtools/references/CHROME_FLAGS.md +234 -0
- package/skills/octocode-chrome-devtools/references/INTENTS.md +108 -0
- package/skills/octocode-chrome-devtools/references/INTENTS_AUTH.md +179 -0
- package/skills/octocode-chrome-devtools/references/INTENTS_AUTOMATION.md +214 -0
- package/skills/octocode-chrome-devtools/references/INTENTS_DEBUG.md +329 -0
- package/skills/octocode-chrome-devtools/references/INTENTS_ENVIRONMENT.md +237 -0
- package/skills/octocode-chrome-devtools/references/INTENTS_INSPECT.md +263 -0
- package/skills/octocode-chrome-devtools/references/INTENTS_STORAGE_CONSENT.md +214 -0
- package/skills/octocode-chrome-devtools/references/RECOVERY.md +39 -0
- package/skills/octocode-chrome-devtools/references/SCRIPT_PATTERNS.md +43 -0
- package/skills/octocode-chrome-devtools/references/SCRIPT_PATTERNS_ASYNC_WORKERS.md +345 -0
- package/skills/octocode-chrome-devtools/references/SCRIPT_PATTERNS_BROWSER.md +403 -0
- package/skills/octocode-chrome-devtools/references/SCRIPT_PATTERNS_OBSERVE.md +275 -0
- package/skills/octocode-chrome-devtools/references/SCRIPT_PATTERNS_SPECIAL.md +18 -0
- package/skills/octocode-chrome-devtools/scripts/cdp-runner.mjs +503 -0
- package/skills/octocode-chrome-devtools/scripts/cdp-sandbox.mjs +123 -0
- package/skills/octocode-chrome-devtools/scripts/cdp-template.mjs +81 -0
- package/skills/octocode-chrome-devtools/scripts/octocode-chrome-devtools.vpn.example.json +8 -0
- package/skills/octocode-chrome-devtools/scripts/open-browser.mjs +362 -0
- package/skills/octocode-chrome-devtools/scripts/sourcemap-resolver.mjs +193 -0
- package/skills/octocode-chrome-devtools/scripts/undercover.mjs +226 -0
- package/skills/octocode-design/README.md +2 -2
- package/skills/octocode-documentation-writer/README.md +1 -1
- package/skills/octocode-engineer/README.md +1 -1
- package/skills/octocode-engineer/SKILL.md +4 -2
- package/skills/octocode-engineer/references/output-files.md +3 -3
- package/skills/octocode-engineer/scripts/run.js +136 -136
- package/skills/octocode-engineer/src/reporting/summary-md.test.ts +48 -0
- package/skills/octocode-engineer/src/reporting/summary-md.ts +43 -2
- package/skills/octocode-install/SKILL.md +1 -1
- package/skills/octocode-pull-request-reviewer/README.md +5 -5
- package/skills/octocode-pull-request-reviewer/SKILL.md +1 -1
- package/skills/octocode-research/AGENTS.md +1 -1
- package/skills/octocode-research/README.md +2 -2
- package/skills/octocode-research/docs/ARCHITECTURE.md +1 -1
- package/skills/octocode-research/scripts/server.js +191 -246
- package/skills/octocode-research/src/routes/github.ts +4 -21
- package/skills/octocode-research/src/routes/local.ts +4 -21
- package/skills/octocode-research/src/utils/fileContentTransform.ts +44 -0
- package/skills/octocode-search-skill/SKILL.md +245 -229
- package/skills/octocode-search-skill/references/agent-skills-guide.md +177 -0
- package/skills/octocode-search-skill/references/discovery-surfaces.md +162 -0
- package/skills/octocode-search-skill/references/fetch-and-create-locally.md +57 -0
- package/skills/octocode-search-skill/references/install-reference.md +130 -0
- package/skills/octocode-search-skill/references/references-template.md +27 -0
- package/skills/octocode-search-skill/references/references.md +62 -0
- package/skills/octocode-slides/README.md +307 -0
- package/skills/octocode-slides/SKILL.md +410 -0
- package/skills/octocode-slides/references/01-brief.md +156 -0
- package/skills/octocode-slides/references/02-research.md +149 -0
- package/skills/octocode-slides/references/03-outline.md +172 -0
- package/skills/octocode-slides/references/04-design.md +301 -0
- package/skills/octocode-slides/references/05-implementation.md +213 -0
- package/skills/octocode-slides/references/06-review.md +258 -0
- package/skills/octocode-slides/references/animation.md +281 -0
- package/skills/octocode-slides/references/design-system.md +316 -0
- package/skills/octocode-slides/references/html-templates.md +673 -0
- package/skills/octocode-slides/references/image-generation.md +448 -0
- package/skills/octocode-slides/references/resources.md +840 -0
- package/skills/octocode-slides/references/slide-rules.md +541 -0
- package/skills/octocode-slides/references/wireframes.md +727 -0
- package/skills/octocode-slides/scripts/animation.js +182 -0
- package/skills/octocode-slides/scripts/base.css +353 -0
- package/skills/octocode-slides/scripts/base.html +655 -0
- package/skills/octocode-slides/scripts/generate_image.py +221 -0
- package/skills/octocode-slides/scripts/navbridge.js +79 -0
- package/skills/octocode-slides/scripts/presenter.js +316 -0
- package/skills/octocode-slides/scripts/slide.html +248 -0
- package/skills/octocode-stats/SKILL.md +73 -0
- package/skills/octocode-stats/assets/template.html +1332 -0
- package/skills/octocode-stats/scripts/build_dashboard.mjs +407 -0
- package/assets/example.png +0 -0
- package/out/chunks/chunk-QCY7Q7YW.js +0 -389
- package/out/chunks/command-help-specs-CQ3RBLP6.js +0 -8
- package/out/chunks/commands-OCTZP2TO.js +0 -51
- package/out/chunks/help-XPXP46ZT.js +0 -10
- package/out/chunks/main-help-35HX2UDQ.js +0 -10
- package/out/chunks/tool-command-HOSMVLNK.js +0 -8
- package/skills/octocode-search-skill/INSTALL_REFERENCE.md +0 -112
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# CDP Environment And Runtime Intent Details
|
|
2
|
+
|
|
3
|
+
## emulate
|
|
4
|
+
|
|
5
|
+
**Trigger phrases:** "mobile", "emulate device", "throttle network", "slow 3G", "offline", "fake location", "geolocation", "tablet", "responsive", "test on mobile", "iPhone", "Android", "viewport", "mobile UA"
|
|
6
|
+
|
|
7
|
+
**Purpose:** Override browser environment — device viewport, touch events, UA, network speed, geolocation — before running any other intent. All overrides are script-level and take effect before navigation.
|
|
8
|
+
|
|
9
|
+
**Domains:** No `enable` call needed for `Emulation.*`. For network throttling: `Network.enable` first. For geolocation: `Browser.grantPermissions` first.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Two-level emulation model
|
|
13
|
+
|
|
14
|
+
| Level | How | Controls |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| **Launch-level** | `open-browser.mjs --windowSize 390x844 --userAgent "<mobile-ua>"` | Window size, launch-time UA |
|
|
17
|
+
| **Script-level** | `Emulation.setDeviceMetricsOverride` + `setUserAgentOverride` + `setTouchEmulationEnabled` | Viewport, DPR, mobile mode, UA, touch, Sec-CH-UA hints |
|
|
18
|
+
|
|
19
|
+
**Prefer script-level emulation for accuracy** — it gives real mobile layout (media queries fire, `window.innerWidth` matches), real device pixel ratio, real touch events, and full UA hint spoofing. Launch-level `--windowSize` is useful for initial window dimensions before CDP attaches.
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Device presets
|
|
23
|
+
|
|
24
|
+
Replace `<current-version>` / `<current-major>` with the installed Chrome version before running a copied Android preset.
|
|
25
|
+
|
|
26
|
+
| Device | Width | Height | DPR | User-Agent |
|
|
27
|
+
|---|---|---|---|---|
|
|
28
|
+
| iPhone 15 Pro | 393 | 852 | 3 | `Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Mobile/15E148 Safari/604.1` |
|
|
29
|
+
| iPhone 13 | 390 | 844 | 3 | `Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1` |
|
|
30
|
+
| Pixel 7 (Android) | 412 | 915 | 2.625 | `Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/<current-version>.0.0.0 Mobile Safari/537.36` |
|
|
31
|
+
| Samsung Galaxy S23 | 360 | 780 | 3 | `Mozilla/5.0 (Linux; Android 13; SM-S911B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/<current-version>.0.0.0 Mobile Safari/537.36` |
|
|
32
|
+
| iPad Air (M2) | 820 | 1180 | 2 | `Mozilla/5.0 (iPad; CPU OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Mobile/15E148 Safari/604.1` |
|
|
33
|
+
| Desktop 1920 | 1920 | 1080 | 1 | _(use open-browser.mjs default — desktop UA)_ |
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
**Adaptive emulation recipe:**
|
|
38
|
+
|
|
39
|
+
Use launch flags for initial window shape only. Use script-level `Emulation.*` before navigation for viewport, DPR, touch, UA, and locale behavior. Replace UA/version hints with the installed browser version when spoofing Chrome.
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
const device = { width: 393, height: 852, dpr: 3, mobile: true };
|
|
43
|
+
await cdp.send('Page.enable', {});
|
|
44
|
+
await cdp.send('Network.enable', {});
|
|
45
|
+
await cdp.send('Emulation.setDeviceMetricsOverride', {
|
|
46
|
+
width: device.width, height: device.height,
|
|
47
|
+
deviceScaleFactor: device.dpr, mobile: device.mobile,
|
|
48
|
+
});
|
|
49
|
+
await cdp.send('Emulation.setTouchEmulationEnabled', { enabled: true, maxTouchPoints: 5 });
|
|
50
|
+
// Optional: setUserAgentOverride with current, realistic UA + hints.
|
|
51
|
+
await cdp.send('Page.navigate', { url: TARGET_URL });
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
After load, verify `window.innerWidth`, `devicePixelRatio`, and horizontal overflow. Feature-detect browser APIs inside the page when emulating capabilities, not just viewport.
|
|
55
|
+
|
|
56
|
+
**Network throttling snippets:**
|
|
57
|
+
```js
|
|
58
|
+
await cdp.send('Network.enable', {});
|
|
59
|
+
|
|
60
|
+
// Slow 3G
|
|
61
|
+
await cdp.send('Network.emulateNetworkConditions', {
|
|
62
|
+
offline: false, downloadThroughput: 50_000, uploadThroughput: 20_000, latency: 400,
|
|
63
|
+
});
|
|
64
|
+
console.log('[EMULATE] network: Slow 3G (50kb/s down, 400ms latency)');
|
|
65
|
+
|
|
66
|
+
// Fast 3G
|
|
67
|
+
await cdp.send('Network.emulateNetworkConditions', {
|
|
68
|
+
offline: false, downloadThroughput: 180_000, uploadThroughput: 84_000, latency: 100,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Offline
|
|
72
|
+
await cdp.send('Network.emulateNetworkConditions', {
|
|
73
|
+
offline: true, downloadThroughput: 0, uploadThroughput: 0, latency: 0,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Reset
|
|
77
|
+
await cdp.send('Network.emulateNetworkConditions', {
|
|
78
|
+
offline: false, downloadThroughput: -1, uploadThroughput: -1, latency: 0,
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**Geolocation:**
|
|
83
|
+
```js
|
|
84
|
+
await cdp.send('Browser.grantPermissions', { permissions: ['geolocation'] });
|
|
85
|
+
await cdp.send('Emulation.setGeolocationOverride', { latitude: 40.7128, longitude: -74.0060, accuracy: 100 });
|
|
86
|
+
console.log('[EMULATE] geolocation: New York City');
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Dark mode / media queries:**
|
|
90
|
+
```js
|
|
91
|
+
await cdp.send('Emulation.setEmulatedMedia', {
|
|
92
|
+
features: [{ name: 'prefers-color-scheme', value: 'dark' }],
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Output prefixes:** `[EMULATE]` `[METRIC]` `[FINDING]`
|
|
97
|
+
|
|
98
|
+
**`[FINDING]` conditions:**
|
|
99
|
+
- Layout breaks at mobile viewport → `[FINDING] LAYOUT_BREAK: horizontal scroll or overflow at ${width}px`
|
|
100
|
+
- Network throttle reveals slow resource → `[FINDING] SLOW_RESOURCE: ${url} took ${ms}ms on throttled connection`
|
|
101
|
+
- Offline triggers uncaught exception → `[FINDING] NO_OFFLINE_HANDLING: app crashes when offline`
|
|
102
|
+
|
|
103
|
+
**Ordering constraint:** `Emulation.*` overrides take effect on the next navigation. Call them before `Page.navigate` — media queries and viewport-dependent layout fire during the first parse, not on DOMContentLoaded.
|
|
104
|
+
|
|
105
|
+
**Combine with:** `screenshot` (capture mobile layout), `performance` (measure on throttled network), `debug` (find mobile-only errors), `scrape` (scrape mobile-rendered content).
|
|
106
|
+
|
|
107
|
+
## inject
|
|
108
|
+
|
|
109
|
+
**Trigger phrases:** "inject script", "patch before load", "hook function", "override", "monkey-patch", "add script to page", "intercept before", "modify behavior", "bypass CSP", "add tracking"
|
|
110
|
+
|
|
111
|
+
**Purpose:** Inject JavaScript into every new document before any page script runs. Use for hooking functions, overriding globals, adding instrumentation, or bypassing checks.
|
|
112
|
+
|
|
113
|
+
**Domains:** `Page.enable`
|
|
114
|
+
|
|
115
|
+
**Key methods:**
|
|
116
|
+
- `Page.addScriptToEvaluateOnNewDocument({source})` → runs before page JS on every navigation
|
|
117
|
+
- `Page.removeScriptToEvaluateOnNewDocument({identifier})` → remove when done
|
|
118
|
+
- `Page.setBypassCSP({enabled:true})` → bypass Content-Security-Policy (required before injection on CSP-protected pages)
|
|
119
|
+
|
|
120
|
+
**Injection patterns:**
|
|
121
|
+
```js
|
|
122
|
+
// Hook fetch to log all requests + bodies
|
|
123
|
+
const { identifier } = await cdp.send('Page.addScriptToEvaluateOnNewDocument', {
|
|
124
|
+
source: `
|
|
125
|
+
const _fetch = window.fetch;
|
|
126
|
+
window.fetch = async function(...args) {
|
|
127
|
+
const [url, init] = args;
|
|
128
|
+
console.log('[INJECTED:FETCH]', typeof url === 'string' ? url : url.url, init?.body?.slice?.(0,200));
|
|
129
|
+
return _fetch.apply(this, args);
|
|
130
|
+
};
|
|
131
|
+
`
|
|
132
|
+
});
|
|
133
|
+
console.log('[INJECT] fetch hook installed, identifier:', identifier);
|
|
134
|
+
|
|
135
|
+
// Hook XMLHttpRequest
|
|
136
|
+
await cdp.send('Page.addScriptToEvaluateOnNewDocument', {
|
|
137
|
+
source: `
|
|
138
|
+
const _open = XMLHttpRequest.prototype.open;
|
|
139
|
+
XMLHttpRequest.prototype.open = function(method, url) {
|
|
140
|
+
this._xhrUrl = url; this._xhrMethod = method;
|
|
141
|
+
console.log('[INJECTED:XHR]', method, url);
|
|
142
|
+
return _open.apply(this, arguments);
|
|
143
|
+
};
|
|
144
|
+
`
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// Expose a debug channel back to CDP
|
|
148
|
+
await cdp.send('Runtime.addBinding', { name: '__cdpLog' });
|
|
149
|
+
// then cdp.on('Runtime.bindingCalled', ({name, payload}) => ...) to receive messages from injected code
|
|
150
|
+
|
|
151
|
+
// Override a specific function (e.g. disable analytics)
|
|
152
|
+
await cdp.send('Page.addScriptToEvaluateOnNewDocument', {
|
|
153
|
+
source: `Object.defineProperty(window, 'analytics', { get: () => ({ track: () => {}, page: () => {} }) });`
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Bypass CSP before injection
|
|
157
|
+
await cdp.send('Page.setBypassCSP', { enabled: true });
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Cleanup:** Remove injected scripts after the session — they persist across navigations until removed.
|
|
161
|
+
```js
|
|
162
|
+
await cdp.send('Page.removeScriptToEvaluateOnNewDocument', { identifier });
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Output prefixes:** `[INJECT]` `[FINDING]`
|
|
166
|
+
|
|
167
|
+
**`[FINDING]` conditions:**
|
|
168
|
+
- Injected hook never fires → `[FINDING] INJECT_NO_CALLS: hook installed but never triggered`
|
|
169
|
+
- Page detects injection and shows error → `[FINDING] INJECTION_DETECTED: page responded to override`
|
|
170
|
+
|
|
171
|
+
**Combine with:** `debug` (observe injected logs), `network` (correlate injected fetch logs with CDP network events), `security` (hook crypto APIs to observe key usage).
|
|
172
|
+
|
|
173
|
+
## monitor
|
|
174
|
+
|
|
175
|
+
**Trigger phrases:** "monitor", "watch", "keep watching", "check every N seconds", "poll", "alert me when", "watch for changes", "continuous", "long-running"
|
|
176
|
+
|
|
177
|
+
**Purpose:** Long-running observation loop — poll a page or condition repeatedly and emit findings when state changes. Useful for catching intermittent errors, watching a live dashboard, or waiting for an event.
|
|
178
|
+
|
|
179
|
+
**Domains:** `Network.enable`, `Runtime.enable`, `Log.enable` (others as needed)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
**Adaptive monitor recipe:**
|
|
183
|
+
|
|
184
|
+
Monitor only the condition the user cares about. Use event listeners for async errors and one small `Runtime.evaluate` snapshot per interval.
|
|
185
|
+
|
|
186
|
+
```js
|
|
187
|
+
export async function run(cdp) {
|
|
188
|
+
const INTERVAL_MS = 5000;
|
|
189
|
+
const DURATION_MS = 60000;
|
|
190
|
+
await cdp.send('Runtime.enable', {});
|
|
191
|
+
await cdp.send('Network.enable', {});
|
|
192
|
+
await cdp.send('Log.enable', {});
|
|
193
|
+
|
|
194
|
+
const queue = [];
|
|
195
|
+
cdp.on('Runtime.exceptionThrown', ({ exceptionDetails }) => queue.push(exceptionDetails.text));
|
|
196
|
+
cdp.on('Network.responseReceived', ({ response }) => {
|
|
197
|
+
if (response.status >= 400) queue.push(`HTTP ${response.status} ${response.url}`);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
for (const end = Date.now() + DURATION_MS; Date.now() < end;) {
|
|
201
|
+
const { result } = await cdp.send('Runtime.evaluate', {
|
|
202
|
+
expression: `JSON.stringify({ url: location.href, title: document.title,
|
|
203
|
+
errorEls: document.querySelectorAll('.error,[aria-invalid="true"],[data-error]').length })`,
|
|
204
|
+
returnByValue: true,
|
|
205
|
+
});
|
|
206
|
+
console.log(`[MONITOR] ${result.value}`);
|
|
207
|
+
while (queue.length) console.log(`[FINDING] MONITOR_EVENT: ${queue.shift()}`);
|
|
208
|
+
await new Promise(r => setTimeout(r, INTERVAL_MS));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Write a JSON log to `cdp.outputDir` only if the user needs an artifact. Increase duration or add screenshot/network details only after the first run shows they are useful.
|
|
214
|
+
|
|
215
|
+
**Output prefixes:** `[MONITOR]` `[FINDING]` `[METRIC]` `[ACTION]`
|
|
216
|
+
|
|
217
|
+
**`[FINDING]` conditions:**
|
|
218
|
+
- Error count increases between iterations → `[FINDING] MONITOR_ERROR: new error at ${elapsed}s`
|
|
219
|
+
- URL changes unexpectedly → `[FINDING] MONITOR_REDIRECT: URL changed to ${url} at ${elapsed}s`
|
|
220
|
+
- DOM error indicators appear → `[FINDING] MONITOR_DOM_ERROR: error elements appeared at ${elapsed}s`
|
|
221
|
+
- Page becomes unresponsive (evaluate times out) → `[FINDING] MONITOR_HANG: page unresponsive at ${elapsed}s`
|
|
222
|
+
- Network 4xx/5xx mid-session → `[FINDING] MONITOR_NETWORK_ERROR: HTTP ${status} at ${elapsed}s`
|
|
223
|
+
|
|
224
|
+
**Agent loop usage:**
|
|
225
|
+
|
|
226
|
+
```
|
|
227
|
+
REASON → what condition am I watching for? how long?
|
|
228
|
+
MONITOR → set TARGET_URL + DURATION_MS, run script → read [MONITOR] + [FINDING] lines
|
|
229
|
+
OBSERVE → check === OBSERVE === block: changes, errors, redirects
|
|
230
|
+
ACT → follow [ACTION] lines: run debug on affected URL, inspect DOM errors
|
|
231
|
+
SAVE → change log auto-saved to cdp.outputDir/monitor-log.json
|
|
232
|
+
REPEAT → if condition not yet triggered, increase DURATION_MS and re-run
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Stop when: target condition observed OR `[ACTION] page is stable` with zero errors.
|
|
236
|
+
|
|
237
|
+
**Combine with:** `debug` (deep-dive on errors found), `screenshot` (capture state at each change), `network` (add network tracking).
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# CDP Inspection Surface Intent Details
|
|
2
|
+
|
|
3
|
+
## security
|
|
4
|
+
|
|
5
|
+
**Trigger phrases:** "security", "cookies", "tokens", "auth", "CSP", "headers", "data exfil", "what's being leaked", "credentials", "session", "localStorage", "is this safe"
|
|
6
|
+
|
|
7
|
+
**Domains:** `Network.enable`, `Runtime.enable`, `DOM.enable`, `Page.enable`
|
|
8
|
+
|
|
9
|
+
**Key events/methods:**
|
|
10
|
+
- `Network.getCookies({ urls: [TARGET_URL] })` → cookies scoped to the target URL only (use this — `getAllCookies` returns the entire browser jar and floods output with third-party ad cookies)
|
|
11
|
+
- `Network.requestWillBeSent` + `Network.getRequestPostData(requestId)` → actual POST bodies
|
|
12
|
+
- `Network.responseReceived` → inspect response headers: `Content-Security-Policy`, `Strict-Transport-Security`, `X-Frame-Options`, `X-Content-Type-Options`
|
|
13
|
+
- `Runtime.evaluate` → localStorage keys and sizes only; never emit stored values
|
|
14
|
+
- `Runtime.evaluate` → sessionStorage keys and sizes only; never emit stored values
|
|
15
|
+
- `DOMDebugger.getEventListeners({objectId})` → listeners on `document`, `window`, `input[type=password]` (get objectId via `Runtime.evaluate({expression:"document"})`)
|
|
16
|
+
- `Runtime.evaluate` → `Object.keys(Object.getPrototypeOf(Object.prototype))` → prototype pollution check
|
|
17
|
+
|
|
18
|
+
**Output prefixes:** `[SECURITY]` `[NETWORK]` `[FINDING]` `[METRIC]`
|
|
19
|
+
|
|
20
|
+
**`[FINDING]` conditions to emit:**
|
|
21
|
+
- Cookie missing `httpOnly` → `[FINDING] COOKIE_NO_HTTPONLY: ${name}`
|
|
22
|
+
- Cookie missing `secure` flag → `[FINDING] COOKIE_NO_SECURE: ${name}`
|
|
23
|
+
- Cookie `sameSite` is `None` without `secure` → `[FINDING] COOKIE_SAMESITE_NONE_INSECURE: ${name}`
|
|
24
|
+
- POST body contains `token`, `password`, `secret`, `apiKey`, `jwt` → `[FINDING] SENSITIVE_IN_POST: ${url}`
|
|
25
|
+
- Response missing `Content-Security-Policy` → `[FINDING] MISSING_CSP: ${url}`
|
|
26
|
+
- CSP contains `unsafe-eval` or `unsafe-inline` → `[FINDING] WEAK_CSP: ${directive} in ${url}`
|
|
27
|
+
- Response missing `Strict-Transport-Security` → `[FINDING] MISSING_HSTS: ${url}`
|
|
28
|
+
- Response missing `X-Frame-Options` → `[FINDING] MISSING_XFRAME: ${url}`
|
|
29
|
+
- `localStorage` key matches `token|auth|jwt|secret|key|password` → `[FINDING] SENSITIVE_IN_STORAGE: ${key}`
|
|
30
|
+
- `keydown`/`keyup` listener on `document` from third-party script URL → `[FINDING] POSSIBLE_KEYLOGGER: ${listenerUrl}`
|
|
31
|
+
- `copy`/`paste` listener on `document` → `[FINDING] CLIPBOARD_LISTENER: possible clipboard hijack`
|
|
32
|
+
- `Object.prototype` has unexpected own properties → `[FINDING] PROTOTYPE_POLLUTION: ${keys}`
|
|
33
|
+
- Request to unknown external domain → `[FINDING] DATA_EXFIL_SUSPECT: ${url}`
|
|
34
|
+
|
|
35
|
+
**Pattern:** See **Security Audit** in `SCRIPT_PATTERNS.md` and trim it to the specific security question.
|
|
36
|
+
|
|
37
|
+
## websocket
|
|
38
|
+
|
|
39
|
+
**Trigger phrases:** "websocket", "WS", "socket", "real-time", "socket frames", "what's sent over WS"
|
|
40
|
+
|
|
41
|
+
**Domains:** `Network.enable`
|
|
42
|
+
|
|
43
|
+
**Key events/methods:**
|
|
44
|
+
- `Network.webSocketCreated` → `{requestId, url, initiator}` — WS object constructed (`new WebSocket(url)`) — **this is the correct creation event, not `requestWillBeSent`**
|
|
45
|
+
- `Network.webSocketHandshakeResponseReceived` → headers, status
|
|
46
|
+
- `Network.webSocketFrameSent` → `{requestId, timestamp, response: {payloadData, opcode}}` — opcode 1 = text, 2 = binary
|
|
47
|
+
- `Network.webSocketFrameReceived` → `{requestId, timestamp, response: {payloadData, opcode}}`
|
|
48
|
+
- `Network.webSocketClosed` → `{requestId, timestamp}`
|
|
49
|
+
|
|
50
|
+
**Output prefixes:** `[NETWORK]` `[FINDING]` `[METRIC]`
|
|
51
|
+
|
|
52
|
+
**`[FINDING]` conditions to emit:**
|
|
53
|
+
- WS endpoint on unknown third-party domain → `[FINDING] WS_UNKNOWN_HOST: ${url}`
|
|
54
|
+
- Frame payload contains `token`, `password`, `key` → `[FINDING] SENSITIVE_IN_WS_FRAME: ${url}` (value redacted)
|
|
55
|
+
- Base64-encoded payload (matches `/^[A-Za-z0-9+/]{40,}={0,2}$/`) → `[FINDING] WS_BASE64_FRAME: possible encoded data`
|
|
56
|
+
- Frame size > 100KB → `[FINDING] LARGE_WS_FRAME: ${kb}KB sent` *(threshold is a default — adjust for your protocol)*
|
|
57
|
+
- opcode === 2 → binary frame; base64-decode payload for inspection
|
|
58
|
+
|
|
59
|
+
**WS inside Web Workers:** `Network.webSocketCreated` does **not** fire on the main session for WebSockets opened inside a Web Worker or Shared Worker. To capture those, enable the `workers` intent first (`Target.setAutoAttach`), then call `cdp.send('Network.enable', {}, sessionId)` on each attached worker session. See the **workers** intent and **WebSocket inside Workers** pattern in `SCRIPT_PATTERNS.md`.
|
|
60
|
+
|
|
61
|
+
## service-worker
|
|
62
|
+
|
|
63
|
+
**Trigger phrases:** "service worker", "SW lifecycle", "PWA worker", "offline cache", "push notifications", "background sync", "SW registered", "what service workers are running", "is there a service worker"
|
|
64
|
+
|
|
65
|
+
**Domains:** `ServiceWorker.enable` + `Target.setAutoAttach` (to receive the SW as an attachable target)
|
|
66
|
+
|
|
67
|
+
**Key events/methods:**
|
|
68
|
+
- `ServiceWorker.enable` — must call before navigation to receive SW events
|
|
69
|
+
- Event: `ServiceWorker.workerRegistrationUpdated` → `{registrations[]}` — fires when a SW registration is created or deleted; each `registration` has `{registrationId, scopeURL, isDeleted}`
|
|
70
|
+
- Event: `ServiceWorker.workerVersionUpdated` → `{versions[]}` — fires on **every state transition**; each `version` has `{registrationId, versionId, scriptURL, status, runningStatus}`:
|
|
71
|
+
- `status`: `"new"` → `"installing"` → `"installed"` → `"activating"` → `"activated"` → `"redundant"`
|
|
72
|
+
- `runningStatus`: `"stopped"` → `"starting"` → `"running"` → `"stopping"`
|
|
73
|
+
- `ServiceWorker.disable` — call at cleanup to stop receiving events
|
|
74
|
+
|
|
75
|
+
**SW methods (imperative):**
|
|
76
|
+
- `ServiceWorker.skipWaiting({scopeURL})` — force a waiting SW to activate immediately
|
|
77
|
+
- `ServiceWorker.updateRegistration({scopeURL})` — trigger a SW update check
|
|
78
|
+
- `ServiceWorker.unregister({scopeURL})` — unregister a SW
|
|
79
|
+
|
|
80
|
+
**Output prefixes:** `[SW]` `[FINDING]` `[MONITOR]`
|
|
81
|
+
|
|
82
|
+
**`[FINDING]` conditions to emit:**
|
|
83
|
+
- New registration for a non-extension scope → `[FINDING] SW_REGISTERED: ${scopeURL}`
|
|
84
|
+
- SW reaches `activated` state → `[FINDING] SW_ACTIVATED: ${scriptURL}`
|
|
85
|
+
- SW `isDeleted` = true (unregistered or evicted) → `[FINDING] SW_REMOVED: ${scopeURL}`
|
|
86
|
+
- SW script is on a third-party domain → `[FINDING] SW_THIRD_PARTY_SCRIPT: ${scriptURL}`
|
|
87
|
+
|
|
88
|
+
**Tip — navigator API for a point-in-time snapshot (after page load):**
|
|
89
|
+
```js
|
|
90
|
+
const regs = await cdp.send('Runtime.evaluate', {
|
|
91
|
+
expression: `navigator.serviceWorker.getRegistrations().then(r=>JSON.stringify(r.map(x=>({scope:x.scope,state:(x.active||x.installing||x.waiting)?.state,script:(x.active||x.installing||x.waiting)?.scriptURL}))))`,
|
|
92
|
+
awaitPromise: true, returnByValue: false,
|
|
93
|
+
});
|
|
94
|
+
const sws = JSON.parse(regs.result.value ?? '[]');
|
|
95
|
+
```
|
|
96
|
+
Use the CDP domain (`ServiceWorker.enable` + events) for **live lifecycle tracking**; use `navigator.serviceWorker.getRegistrations()` for a **snapshot** after load.
|
|
97
|
+
|
|
98
|
+
**Combine with:** `workers` (to attach to the SW target and capture its network traffic), `storage` (to read CacheStorage entries managed by the SW).
|
|
99
|
+
|
|
100
|
+
## workers
|
|
101
|
+
|
|
102
|
+
**Trigger phrases:** "web worker", "shared worker", "worker thread", "background worker", "blob worker", "WS inside worker", "what workers are running", "worker targets", "worker network traffic"
|
|
103
|
+
|
|
104
|
+
**Domains:** `Target.setAutoAttach` + `Target.setDiscoverTargets` — must be called **before navigation**
|
|
105
|
+
|
|
106
|
+
**Key events/methods:**
|
|
107
|
+
- `Target.setAutoAttach({autoAttach: true, waitForDebuggerOnStart: false, flatten: true})` — auto-attach to all child targets (workers, service workers) with flat session model
|
|
108
|
+
- `Target.setDiscoverTargets({discover: true})` — emit `Target.targetCreated` for worker targets that Chrome discovers
|
|
109
|
+
- Event: `Target.attachedToTarget` → `{targetInfo, sessionId}` — fires when a worker is attached; `targetInfo.type` is one of `"worker"` (Web Worker), `"shared_worker"`, `"service_worker"`
|
|
110
|
+
- Event: `Target.targetCreated` → `{targetInfo}` — fires when a worker target is created (type = worker/shared_worker/service_worker)
|
|
111
|
+
- `Target.getTargets({})` → `{targetInfos[]}` — list all known targets including workers (use after page settles)
|
|
112
|
+
- `Target.detachFromTarget({sessionId})` — detach from a worker when done
|
|
113
|
+
|
|
114
|
+
**Enabling Network on a worker (flat session model):**
|
|
115
|
+
|
|
116
|
+
When `flatten: true` is set in `setAutoAttach`, worker sessions share the same WebSocket connection. Pass `sessionId` as the **third argument** to `cdp.send()` to route commands to that session:
|
|
117
|
+
|
|
118
|
+
```js
|
|
119
|
+
cdp.on('Target.attachedToTarget', async ({ targetInfo, sessionId }) => {
|
|
120
|
+
if (!['worker', 'shared_worker', 'service_worker'].includes(targetInfo.type)) return;
|
|
121
|
+
console.log(`[WORKER] Attached: type=${targetInfo.type} url=${targetInfo.url || '(blob)'}`);
|
|
122
|
+
// Enable Network on this worker's session to capture its HTTP + WS traffic
|
|
123
|
+
await cdp.send('Network.enable', {}, sessionId);
|
|
124
|
+
// All Network events from this worker now carry { sessionId } in their metadata.
|
|
125
|
+
// Route them with a wrapper that filters by sessionId.
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Routing worker events (filter by sessionId):**
|
|
130
|
+
```js
|
|
131
|
+
// Generic: listen to an event on a specific worker session
|
|
132
|
+
function onWorkerEvent(event, workerSessionId, handler) {
|
|
133
|
+
cdp.on(event, (params, meta) => {
|
|
134
|
+
if (meta?.sessionId === workerSessionId) handler(params);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
// Then:
|
|
138
|
+
onWorkerEvent('Network.webSocketCreated', sessionId, ({ requestId, url }) => {
|
|
139
|
+
console.log(`[WORKER] WS inside worker: ${url}`);
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Output prefixes:** `[WORKER]` `[FINDING]` `[MONITOR]`
|
|
144
|
+
|
|
145
|
+
**`[FINDING]` conditions to emit:**
|
|
146
|
+
- Worker URL is a blob: URL → `[FINDING] WORKER_BLOB: ${url}` (expected but opaque; blob workers cannot be inspected via source)
|
|
147
|
+
- Shared Worker detected → `[FINDING] SHARED_WORKER: ${url}` (multi-tab communication channel)
|
|
148
|
+
- Service Worker detected → `[FINDING] SERVICE_WORKER_TARGET: ${url}` (complements service-worker intent)
|
|
149
|
+
- WS connection inside a worker → `[FINDING] WS_IN_WORKER: type=${type} url=${wsUrl}`
|
|
150
|
+
|
|
151
|
+
**Cleanup:**
|
|
152
|
+
```js
|
|
153
|
+
await cdp.send('Target.setAutoAttach', { autoAttach: false, waitForDebuggerOnStart: false, flatten: true });
|
|
154
|
+
await cdp.send('Target.setDiscoverTargets', { discover: false });
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Combine with:** `websocket` (capture WS frames inside workers), `service-worker` (track SW lifecycle alongside its target session), `network` (capture HTTP requests made by workers).
|
|
158
|
+
|
|
159
|
+
## intercept
|
|
160
|
+
|
|
161
|
+
**Trigger phrases:** "intercept", "mock", "block request", "fake response", "modify request", "inject headers", "replace API response"
|
|
162
|
+
|
|
163
|
+
**Domains:** `Fetch.enable` (call before navigation, no explicit `enable` method — call `Fetch.enable` with `patterns`)
|
|
164
|
+
|
|
165
|
+
**Key events/methods:**
|
|
166
|
+
- `Fetch.enable({patterns:[{urlPattern:"*", requestStage:"Request"}]})` → intercept all requests
|
|
167
|
+
- Event: `Fetch.requestPaused` → `{requestId, request, responseStatusCode, responseHeaders}`
|
|
168
|
+
- `Fetch.continueRequest({requestId})` → pass through unchanged
|
|
169
|
+
- `Fetch.fulfillRequest({requestId, responseCode:200, body: btoa(JSON.stringify(mockData))})` → return mock
|
|
170
|
+
- `Fetch.continueRequest({requestId, headers:[...]})` → modify headers before continuing
|
|
171
|
+
|
|
172
|
+
**Output prefixes:** `[NETWORK]` `[FINDING]`
|
|
173
|
+
|
|
174
|
+
**Note:** `Fetch.enable` must be called **before** navigation. `body` in `fulfillRequest` must be base64-encoded.
|
|
175
|
+
|
|
176
|
+
## screenshot
|
|
177
|
+
|
|
178
|
+
**Trigger phrases:** "screenshot", "capture", "take a photo", "visual", "PDF", "print page"
|
|
179
|
+
|
|
180
|
+
**Domains:** `Page.enable`
|
|
181
|
+
|
|
182
|
+
**Key events/methods:**
|
|
183
|
+
- `Page.captureScreenshot({format:"png"})` → `data` (base64) → write to `cdp.outputDir/screenshot-<slug>.png`
|
|
184
|
+
- `Page.printToPDF({printBackground:true})` → `data` (base64) → write to `cdp.outputDir/<slug>.pdf`
|
|
185
|
+
- Wait for `Page.loadEventFired` before capturing — capturing too early yields a blank or partial screenshot
|
|
186
|
+
|
|
187
|
+
**Output prefixes:** `[SCREENSHOT]`
|
|
188
|
+
|
|
189
|
+
**Write file pattern — use `cdp.outputDir` (sandbox-safe, cross-platform):**
|
|
190
|
+
```js
|
|
191
|
+
const { writeFileSync } = await import('fs');
|
|
192
|
+
const { join } = await import('path');
|
|
193
|
+
const screenshotPath = join(cdp.outputDir, 'screenshot.png');
|
|
194
|
+
writeFileSync(screenshotPath, Buffer.from(data, 'base64'));
|
|
195
|
+
console.log(`[SCREENSHOT] ${screenshotPath}`);
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## accessibility
|
|
199
|
+
|
|
200
|
+
**Trigger phrases:** "accessibility", "a11y", "aria", "screen reader", "wcag", "alt text", "labels"
|
|
201
|
+
|
|
202
|
+
**Domains:** `DOM.enable`, `Runtime.enable`, `Accessibility.enable`
|
|
203
|
+
|
|
204
|
+
**Key events/methods:**
|
|
205
|
+
- `Accessibility.getFullAXTree` → full semantic tree with `role`, `name`, `description`, `states`
|
|
206
|
+
- `Runtime.evaluate` → `img:not([alt])`, `input:not([aria-label])`, `button:empty`, `[role]` checks
|
|
207
|
+
|
|
208
|
+
**Output prefixes:** `[DOM]` `[FINDING]` `[METRIC]`
|
|
209
|
+
|
|
210
|
+
**`[FINDING]` conditions to emit:**
|
|
211
|
+
- AX node with `role:"img"` and empty `name` → `[FINDING] AX_MISSING_ALT: image has no accessible name`
|
|
212
|
+
- AX node with `role:"button"` and empty `name` → `[FINDING] AX_EMPTY_BUTTON`
|
|
213
|
+
- Input without label in AX tree → `[FINDING] AX_UNLABELED_INPUT`
|
|
214
|
+
- Page has no `role:"main"` landmark → `[FINDING] AX_NO_MAIN_LANDMARK`
|
|
215
|
+
- Heading order skips levels (h1 → h3) → `[FINDING] AX_HEADING_SKIP`
|
|
216
|
+
|
|
217
|
+
## supply-chain
|
|
218
|
+
|
|
219
|
+
**Trigger phrases:** "third-party scripts", "external scripts", "CDN", "supply chain", "what scripts load", "external JS"
|
|
220
|
+
|
|
221
|
+
**Domains:** `Network.enable`, `Runtime.enable`, `Page.enable`
|
|
222
|
+
|
|
223
|
+
**Key events/methods:**
|
|
224
|
+
- `Network.requestWillBeSent` → filter for `.js` requests where origin ≠ page hostname
|
|
225
|
+
- `Network.responseReceived` → check `Subresource-Integrity` / `integrity` attribute presence
|
|
226
|
+
- `Runtime.evaluate` → `[...document.querySelectorAll('script[src]')].map(s=>({src:s.src,integrity:s.integrity||null}))` → list all script tags
|
|
227
|
+
|
|
228
|
+
**Output prefixes:** `[NETWORK]` `[SECURITY]` `[FINDING]` `[METRIC]`
|
|
229
|
+
|
|
230
|
+
**`[FINDING]` conditions to emit:**
|
|
231
|
+
- Third-party JS loaded without SRI hash → `[FINDING] NO_SRI: ${url}`
|
|
232
|
+
- Script from unknown CDN (not from well-known CDNs) → `[FINDING] UNKNOWN_CDN: ${url}`
|
|
233
|
+
- More than 10 distinct third-party domains → `[FINDING] HIGH_THIRD_PARTY_COUNT: ${n} external domains` *(adjust for your expected vendor count)*
|
|
234
|
+
- Script loaded over HTTP (not HTTPS) → `[FINDING] INSECURE_SCRIPT_LOAD: ${url}`
|
|
235
|
+
- `window` gains new property after third-party script loads → `[FINDING] WINDOW_POLLUTION: ${key} added by ${url}`
|
|
236
|
+
|
|
237
|
+
## full-audit
|
|
238
|
+
|
|
239
|
+
**Trigger phrases:** "full audit", "everything", "all checks", "complete audit", "check it all"
|
|
240
|
+
|
|
241
|
+
**Domains:** Enable all: `Network.enable`, `Runtime.enable`, `Log.enable`, `Performance.enable`, `DOM.enable`, `CSS.enable`, `HeapProfiler.enable`, `Page.enable`
|
|
242
|
+
|
|
243
|
+
**Strategy:** Combine all intents above into one script. Run in this order:
|
|
244
|
+
1. Enable all domains
|
|
245
|
+
2. Attach all event listeners (network, console, exceptions, WS)
|
|
246
|
+
3. Navigate to target
|
|
247
|
+
4. Wait for `Page.loadEventFired`
|
|
248
|
+
5. Run synchronous checks: `Performance.getMetrics`, DOM queries, `Network.getCookies({ urls: [TARGET_URL] })` (not `getAllCookies` — that returns the entire browser jar), localStorage key/size inventory, `DOMDebugger.getEventListeners`
|
|
249
|
+
6. Wait 5–10s for async activity
|
|
250
|
+
7. Emit `[METRIC]` summary for each category
|
|
251
|
+
|
|
252
|
+
**Output prefixes:** All of the above.
|
|
253
|
+
|
|
254
|
+
**Emit a summary block at the end:**
|
|
255
|
+
```
|
|
256
|
+
[METRIC] === AUDIT SUMMARY ===
|
|
257
|
+
[METRIC] Network requests: N Errors: N
|
|
258
|
+
[METRIC] Console errors: N Exceptions: N
|
|
259
|
+
[METRIC] JS heap: NMB DOM nodes: N
|
|
260
|
+
[METRIC] Cookies: N Missing httpOnly: N
|
|
261
|
+
[METRIC] Missing CSP: yes/no Missing HSTS: yes/no
|
|
262
|
+
[METRIC] Third-party scripts: N Without SRI: N
|
|
263
|
+
```
|