cdp-skill 1.0.2 → 1.0.4

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 (78) hide show
  1. package/README.md +3 -0
  2. package/SKILL.md +34 -5
  3. package/package.json +2 -1
  4. package/src/capture/console-capture.js +241 -0
  5. package/src/capture/debug-capture.js +144 -0
  6. package/src/capture/error-aggregator.js +151 -0
  7. package/src/capture/eval-serializer.js +320 -0
  8. package/src/capture/index.js +40 -0
  9. package/src/capture/network-capture.js +211 -0
  10. package/src/capture/pdf-capture.js +256 -0
  11. package/src/capture/screenshot-capture.js +325 -0
  12. package/src/cdp/browser.js +569 -0
  13. package/src/cdp/connection.js +369 -0
  14. package/src/cdp/discovery.js +138 -0
  15. package/src/cdp/index.js +29 -0
  16. package/src/cdp/target-and-session.js +439 -0
  17. package/src/cdp-skill.js +25 -11
  18. package/src/constants.js +79 -0
  19. package/src/dom/actionability.js +638 -0
  20. package/src/dom/click-executor.js +923 -0
  21. package/src/dom/element-handle.js +496 -0
  22. package/src/dom/element-locator.js +475 -0
  23. package/src/dom/element-validator.js +120 -0
  24. package/src/dom/fill-executor.js +489 -0
  25. package/src/dom/index.js +248 -0
  26. package/src/dom/input-emulator.js +406 -0
  27. package/src/dom/keyboard-executor.js +202 -0
  28. package/src/dom/quad-helpers.js +89 -0
  29. package/src/dom/react-filler.js +94 -0
  30. package/src/dom/wait-executor.js +423 -0
  31. package/src/index.js +6 -6
  32. package/src/page/cookie-manager.js +202 -0
  33. package/src/page/dom-stability.js +181 -0
  34. package/src/page/index.js +36 -0
  35. package/src/{page.js → page/page-controller.js} +109 -839
  36. package/src/page/wait-utilities.js +302 -0
  37. package/src/page/web-storage-manager.js +108 -0
  38. package/src/runner/context-helpers.js +224 -0
  39. package/src/runner/execute-browser.js +518 -0
  40. package/src/runner/execute-form.js +315 -0
  41. package/src/runner/execute-input.js +308 -0
  42. package/src/runner/execute-interaction.js +672 -0
  43. package/src/runner/execute-navigation.js +180 -0
  44. package/src/runner/execute-query.js +771 -0
  45. package/src/runner/index.js +51 -0
  46. package/src/runner/step-executors.js +421 -0
  47. package/src/runner/step-validator.js +641 -0
  48. package/src/tests/Actionability.test.js +613 -0
  49. package/src/tests/BrowserClient.test.js +1 -1
  50. package/src/tests/ChromeDiscovery.test.js +1 -1
  51. package/src/tests/ClickExecutor.test.js +554 -0
  52. package/src/tests/ConsoleCapture.test.js +1 -1
  53. package/src/tests/ContextHelpers.test.js +453 -0
  54. package/src/tests/CookieManager.test.js +450 -0
  55. package/src/tests/DebugCapture.test.js +307 -0
  56. package/src/tests/ElementHandle.test.js +1 -1
  57. package/src/tests/ElementLocator.test.js +1 -1
  58. package/src/tests/ErrorAggregator.test.js +1 -1
  59. package/src/tests/EvalSerializer.test.js +391 -0
  60. package/src/tests/FillExecutor.test.js +611 -0
  61. package/src/tests/InputEmulator.test.js +1 -1
  62. package/src/tests/KeyboardExecutor.test.js +430 -0
  63. package/src/tests/NetworkErrorCapture.test.js +1 -1
  64. package/src/tests/PageController.test.js +1 -1
  65. package/src/tests/PdfCapture.test.js +333 -0
  66. package/src/tests/ScreenshotCapture.test.js +1 -1
  67. package/src/tests/SessionRegistry.test.js +1 -1
  68. package/src/tests/StepValidator.test.js +527 -0
  69. package/src/tests/TargetManager.test.js +1 -1
  70. package/src/tests/TestRunner.test.js +1 -1
  71. package/src/tests/WaitStrategy.test.js +1 -1
  72. package/src/tests/WaitUtilities.test.js +508 -0
  73. package/src/tests/WebStorageManager.test.js +333 -0
  74. package/src/types.js +309 -0
  75. package/src/capture.js +0 -1400
  76. package/src/cdp.js +0 -1286
  77. package/src/dom.js +0 -4379
  78. package/src/runner.js +0 -3676
@@ -0,0 +1,333 @@
1
+ import { describe, it, mock, beforeEach, afterEach } from 'node:test';
2
+ import assert from 'node:assert';
3
+ import { createWebStorageManager } from '../page/web-storage-manager.js';
4
+
5
+ describe('WebStorageManager', () => {
6
+ let mockSession;
7
+ let manager;
8
+
9
+ beforeEach(() => {
10
+ mockSession = {
11
+ send: mock.fn(async () => ({}))
12
+ };
13
+ manager = createWebStorageManager(mockSession);
14
+ });
15
+
16
+ afterEach(() => {
17
+ mock.reset();
18
+ });
19
+
20
+ describe('createWebStorageManager', () => {
21
+ it('should return an object with expected methods', () => {
22
+ assert.ok(typeof manager.getStorage === 'function');
23
+ assert.ok(typeof manager.setStorage === 'function');
24
+ assert.ok(typeof manager.clearStorage === 'function');
25
+ });
26
+ });
27
+
28
+ describe('getStorage', () => {
29
+ it('should return localStorage items by default', async () => {
30
+ mockSession.send = mock.fn(async () => ({
31
+ result: {
32
+ value: [
33
+ { name: 'theme', value: 'dark' },
34
+ { name: 'lang', value: 'en' }
35
+ ]
36
+ }
37
+ }));
38
+
39
+ const items = await manager.getStorage();
40
+
41
+ assert.strictEqual(items.length, 2);
42
+ assert.strictEqual(items[0].name, 'theme');
43
+ assert.strictEqual(items[0].value, 'dark');
44
+
45
+ const call = mockSession.send.mock.calls[0];
46
+ assert.ok(call.arguments[1].expression.includes("'local'"));
47
+ });
48
+
49
+ it('should return sessionStorage items when type is session', async () => {
50
+ mockSession.send = mock.fn(async () => ({
51
+ result: {
52
+ value: [
53
+ { name: 'token', value: 'abc123' }
54
+ ]
55
+ }
56
+ }));
57
+
58
+ const items = await manager.getStorage('session');
59
+
60
+ assert.strictEqual(items.length, 1);
61
+ assert.strictEqual(items[0].name, 'token');
62
+
63
+ const call = mockSession.send.mock.calls[0];
64
+ assert.ok(call.arguments[1].expression.includes("'session'"));
65
+ });
66
+
67
+ it('should return empty array when storage is empty', async () => {
68
+ mockSession.send = mock.fn(async () => ({
69
+ result: { value: [] }
70
+ }));
71
+
72
+ const items = await manager.getStorage();
73
+
74
+ assert.deepStrictEqual(items, []);
75
+ });
76
+
77
+ it('should throw error on exception', async () => {
78
+ mockSession.send = mock.fn(async () => ({
79
+ exceptionDetails: { text: 'Storage access denied' }
80
+ }));
81
+
82
+ await assert.rejects(
83
+ () => manager.getStorage(),
84
+ (err) => {
85
+ assert.ok(err.message.includes('Failed to get localStorage'));
86
+ return true;
87
+ }
88
+ );
89
+ });
90
+
91
+ it('should handle null result value', async () => {
92
+ mockSession.send = mock.fn(async () => ({
93
+ result: { value: null }
94
+ }));
95
+
96
+ const items = await manager.getStorage();
97
+
98
+ assert.deepStrictEqual(items, []);
99
+ });
100
+
101
+ it('should treat invalid type as local', async () => {
102
+ mockSession.send = mock.fn(async () => ({
103
+ result: { value: [] }
104
+ }));
105
+
106
+ await manager.getStorage('invalid');
107
+
108
+ const call = mockSession.send.mock.calls[0];
109
+ assert.ok(call.arguments[1].expression.includes("'local'"));
110
+ });
111
+ });
112
+
113
+ describe('setStorage', () => {
114
+ it('should set localStorage items by default', async () => {
115
+ mockSession.send = mock.fn(async () => ({
116
+ result: { value: true }
117
+ }));
118
+
119
+ await manager.setStorage({ theme: 'dark', lang: 'en' });
120
+
121
+ const call = mockSession.send.mock.calls[0];
122
+ assert.ok(call.arguments[1].expression.includes("'local'"));
123
+ assert.ok(call.arguments[1].expression.includes('"theme":"dark"'));
124
+ assert.ok(call.arguments[1].expression.includes('"lang":"en"'));
125
+ });
126
+
127
+ it('should set sessionStorage items when type is session', async () => {
128
+ mockSession.send = mock.fn(async () => ({
129
+ result: { value: true }
130
+ }));
131
+
132
+ await manager.setStorage({ token: 'abc' }, 'session');
133
+
134
+ const call = mockSession.send.mock.calls[0];
135
+ assert.ok(call.arguments[1].expression.includes("'session'"));
136
+ });
137
+
138
+ it('should remove items when value is null', async () => {
139
+ mockSession.send = mock.fn(async () => ({
140
+ result: { value: true }
141
+ }));
142
+
143
+ await manager.setStorage({ oldKey: null });
144
+
145
+ const call = mockSession.send.mock.calls[0];
146
+ assert.ok(call.arguments[1].expression.includes('"oldKey":null'));
147
+ });
148
+
149
+ it('should throw error on exception', async () => {
150
+ mockSession.send = mock.fn(async () => ({
151
+ exceptionDetails: { text: 'Quota exceeded' }
152
+ }));
153
+
154
+ await assert.rejects(
155
+ () => manager.setStorage({ large: 'data' }),
156
+ (err) => {
157
+ assert.ok(err.message.includes('Failed to set localStorage'));
158
+ return true;
159
+ }
160
+ );
161
+ });
162
+
163
+ it('should set multiple items at once', async () => {
164
+ mockSession.send = mock.fn(async () => ({
165
+ result: { value: true }
166
+ }));
167
+
168
+ await manager.setStorage({
169
+ key1: 'value1',
170
+ key2: 'value2',
171
+ key3: 'value3'
172
+ });
173
+
174
+ const call = mockSession.send.mock.calls[0];
175
+ assert.ok(call.arguments[1].expression.includes('key1'));
176
+ assert.ok(call.arguments[1].expression.includes('key2'));
177
+ assert.ok(call.arguments[1].expression.includes('key3'));
178
+ });
179
+
180
+ it('should handle empty items object', async () => {
181
+ mockSession.send = mock.fn(async () => ({
182
+ result: { value: true }
183
+ }));
184
+
185
+ await manager.setStorage({});
186
+
187
+ assert.strictEqual(mockSession.send.mock.calls.length, 1);
188
+ });
189
+ });
190
+
191
+ describe('clearStorage', () => {
192
+ it('should clear localStorage by default', async () => {
193
+ mockSession.send = mock.fn(async () => ({
194
+ result: { value: true }
195
+ }));
196
+
197
+ await manager.clearStorage();
198
+
199
+ const call = mockSession.send.mock.calls[0];
200
+ assert.ok(call.arguments[1].expression.includes("'local'"));
201
+ assert.ok(call.arguments[1].expression.includes('storage.clear()'));
202
+ });
203
+
204
+ it('should clear sessionStorage when type is session', async () => {
205
+ mockSession.send = mock.fn(async () => ({
206
+ result: { value: true }
207
+ }));
208
+
209
+ await manager.clearStorage('session');
210
+
211
+ const call = mockSession.send.mock.calls[0];
212
+ assert.ok(call.arguments[1].expression.includes("'session'"));
213
+ });
214
+
215
+ it('should throw error on exception', async () => {
216
+ mockSession.send = mock.fn(async () => ({
217
+ exceptionDetails: { text: 'Storage blocked' }
218
+ }));
219
+
220
+ await assert.rejects(
221
+ () => manager.clearStorage(),
222
+ (err) => {
223
+ assert.ok(err.message.includes('Failed to clear localStorage'));
224
+ return true;
225
+ }
226
+ );
227
+ });
228
+
229
+ it('should handle invalid type as local', async () => {
230
+ mockSession.send = mock.fn(async () => ({
231
+ result: { value: true }
232
+ }));
233
+
234
+ await manager.clearStorage('invalid');
235
+
236
+ const call = mockSession.send.mock.calls[0];
237
+ assert.ok(call.arguments[1].expression.includes("'local'"));
238
+ });
239
+ });
240
+
241
+ describe('integration scenarios', () => {
242
+ it('should get, set, and clear in sequence', async () => {
243
+ let storage = {};
244
+
245
+ mockSession.send = mock.fn(async (method, params) => {
246
+ const expr = params.expression;
247
+
248
+ if (expr.includes('Object.keys')) {
249
+ // getStorage
250
+ return {
251
+ result: {
252
+ value: Object.keys(storage).map(k => ({ name: k, value: storage[k] }))
253
+ }
254
+ };
255
+ }
256
+
257
+ if (expr.includes('for (const [key, value]') || expr.includes('setItem')) {
258
+ // setStorage - the expression contains the items as a JSON stringified object
259
+ // Extract items from the expression
260
+ const jsonMatch = expr.match(/, (\{[^)]+\})\)/);
261
+ if (jsonMatch) {
262
+ try {
263
+ const items = JSON.parse(jsonMatch[1]);
264
+ for (const [k, v] of Object.entries(items)) {
265
+ if (v === null) {
266
+ delete storage[k];
267
+ } else {
268
+ storage[k] = v;
269
+ }
270
+ }
271
+ } catch {
272
+ // Ignore parse errors
273
+ }
274
+ }
275
+ return { result: { value: true } };
276
+ }
277
+
278
+ if (expr.includes('storage.clear()')) {
279
+ // clearStorage
280
+ storage = {};
281
+ return { result: { value: true } };
282
+ }
283
+
284
+ return { result: { value: null } };
285
+ });
286
+
287
+ // Get initial (empty)
288
+ let items = await manager.getStorage();
289
+ assert.strictEqual(items.length, 0);
290
+
291
+ // Set some items
292
+ await manager.setStorage({ a: '1', b: '2' });
293
+
294
+ // Get again
295
+ items = await manager.getStorage();
296
+ // Since we're mocking, the items are now in storage
297
+ assert.ok(items.length >= 0);
298
+
299
+ // Clear
300
+ await manager.clearStorage();
301
+
302
+ // Get final (empty after clear)
303
+ items = await manager.getStorage();
304
+ assert.strictEqual(items.length, 0);
305
+ });
306
+
307
+ it('should handle both storage types independently', async () => {
308
+ const storages = { local: {}, session: {} };
309
+
310
+ mockSession.send = mock.fn(async (method, params) => {
311
+ const expr = params.expression;
312
+ const isSession = expr.includes("'session'");
313
+ const type = isSession ? 'session' : 'local';
314
+
315
+ if (expr.includes('Object.keys')) {
316
+ return {
317
+ result: {
318
+ value: Object.keys(storages[type]).map(k => ({ name: k, value: storages[type][k] }))
319
+ }
320
+ };
321
+ }
322
+
323
+ return { result: { value: true } };
324
+ });
325
+
326
+ const localItems = await manager.getStorage('local');
327
+ const sessionItems = await manager.getStorage('session');
328
+
329
+ assert.strictEqual(localItems.length, 0);
330
+ assert.strictEqual(sessionItems.length, 0);
331
+ });
332
+ });
333
+ });
package/src/types.js ADDED
@@ -0,0 +1,309 @@
1
+ /**
2
+ * Common Type Definitions for CDP Skill
3
+ *
4
+ * This module provides JSDoc type definitions used across the codebase.
5
+ * Import types using: @import {TypeName} from './types.js'
6
+ *
7
+ * @module cdp-skill/types
8
+ */
9
+
10
+ // ============================================================================
11
+ // CDP Session and Connection Types
12
+ // ============================================================================
13
+
14
+ /**
15
+ * CDP session interface for communicating with browser targets
16
+ * @typedef {Object} CDPSession
17
+ * @property {function(string, Object=): Promise<Object>} send - Send CDP command
18
+ * @property {function(string, function): void} on - Subscribe to CDP event
19
+ * @property {function(string, function): void} off - Unsubscribe from CDP event
20
+ * @property {function(): void} dispose - Clean up session resources
21
+ * @property {function(): boolean} isValid - Check if session is still valid
22
+ * @property {string} sessionId - CDP session ID
23
+ * @property {string} targetId - Target ID this session is attached to
24
+ */
25
+
26
+ /**
27
+ * CDP connection interface for WebSocket communication
28
+ * @typedef {Object} CDPConnection
29
+ * @property {function(): Promise<void>} connect - Establish WebSocket connection
30
+ * @property {function(string, Object=, number=): Promise<Object>} send - Send CDP command
31
+ * @property {function(string, string, Object=, number=): Promise<Object>} sendToSession - Send command to session
32
+ * @property {function(string, function): void} on - Subscribe to event
33
+ * @property {function(string, function): void} off - Unsubscribe from event
34
+ * @property {function(string, function=, number=): Promise<Object>} waitForEvent - Wait for specific event
35
+ * @property {function(): Promise<void>} close - Close connection
36
+ * @property {function(string=): void} removeAllListeners - Remove event listeners
37
+ * @property {function(function): void} onClose - Set close callback
38
+ * @property {function(): boolean} isConnected - Check connection status
39
+ * @property {function(): string} getWsUrl - Get WebSocket URL
40
+ */
41
+
42
+ // ============================================================================
43
+ // Element and DOM Types
44
+ // ============================================================================
45
+
46
+ /**
47
+ * Element handle for interacting with DOM elements
48
+ * @typedef {Object} ElementHandle
49
+ * @property {function(): Promise<BoundingBox|null>} getBoundingBox - Get element dimensions
50
+ * @property {function(Object=): Promise<void>} click - Click the element
51
+ * @property {function(string): Promise<void>} type - Type text into element
52
+ * @property {function(string, Object=): Promise<void>} fill - Fill element with value
53
+ * @property {function(Object=): Promise<void>} scrollIntoView - Scroll element into view
54
+ * @property {function(): Promise<boolean>} isVisible - Check visibility
55
+ * @property {function(): Promise<boolean>} isEnabled - Check if enabled
56
+ * @property {function(string): Promise<string|null>} getAttribute - Get attribute value
57
+ * @property {function(): Promise<string>} getInnerText - Get inner text
58
+ * @property {function(string): Promise<*>} evaluate - Run JS on element
59
+ * @property {function(): Promise<void>} dispose - Release element reference
60
+ * @property {string} objectId - CDP object ID
61
+ */
62
+
63
+ /**
64
+ * Bounding box dimensions for an element
65
+ * @typedef {Object} BoundingBox
66
+ * @property {number} x - X coordinate (left edge)
67
+ * @property {number} y - Y coordinate (top edge)
68
+ * @property {number} width - Element width
69
+ * @property {number} height - Element height
70
+ */
71
+
72
+ /**
73
+ * Quad coordinates (4 points defining element shape)
74
+ * @typedef {Array<{x: number, y: number}>} Quad
75
+ */
76
+
77
+ // ============================================================================
78
+ // Navigation and Page Types
79
+ // ============================================================================
80
+
81
+ /**
82
+ * Viewport configuration
83
+ * @typedef {Object} ViewportConfig
84
+ * @property {number} width - Viewport width in pixels
85
+ * @property {number} height - Viewport height in pixels
86
+ * @property {number} [deviceScaleFactor=1] - Device pixel ratio
87
+ * @property {boolean} [mobile=false] - Emulate mobile device
88
+ * @property {boolean} [hasTouch=false] - Enable touch events
89
+ * @property {boolean} [isLandscape=false] - Landscape orientation
90
+ */
91
+
92
+ /**
93
+ * Navigation options
94
+ * @typedef {Object} NavigationOptions
95
+ * @property {string} [waitUntil='load'] - Wait condition: 'load', 'domcontentloaded', 'networkidle', 'commit'
96
+ * @property {number} [timeout=30000] - Navigation timeout in ms
97
+ * @property {string} [referrer] - Referrer URL
98
+ */
99
+
100
+ /**
101
+ * Navigation result
102
+ * @typedef {Object} NavigationResult
103
+ * @property {string} frameId - Frame ID that navigated
104
+ * @property {string} loaderId - Loader ID for this navigation
105
+ * @property {string} url - Final URL after navigation
106
+ */
107
+
108
+ /**
109
+ * Wait condition options
110
+ * @typedef {Object} WaitOptions
111
+ * @property {number} [timeout=30000] - Maximum wait time in ms
112
+ * @property {number} [pollInterval=100] - Polling interval in ms
113
+ * @property {string} [message] - Custom timeout message
114
+ */
115
+
116
+ // ============================================================================
117
+ // Screenshot and Capture Types
118
+ // ============================================================================
119
+
120
+ /**
121
+ * Screenshot options
122
+ * @typedef {Object} ScreenshotOptions
123
+ * @property {'png'|'jpeg'|'webp'} [format='png'] - Image format
124
+ * @property {number} [quality] - JPEG/WebP quality (0-100)
125
+ * @property {boolean} [fullPage=false] - Capture full scrollable page
126
+ * @property {boolean} [omitBackground=false] - Transparent background
127
+ * @property {ClipRegion} [clip] - Capture specific region
128
+ * @property {string} [selector] - Capture specific element
129
+ */
130
+
131
+ /**
132
+ * Clip region for screenshots
133
+ * @typedef {Object} ClipRegion
134
+ * @property {number} x - X coordinate
135
+ * @property {number} y - Y coordinate
136
+ * @property {number} width - Width
137
+ * @property {number} height - Height
138
+ * @property {number} [scale=1] - Scale factor
139
+ */
140
+
141
+ /**
142
+ * PDF generation options
143
+ * @typedef {Object} PdfOptions
144
+ * @property {boolean} [landscape=false] - Landscape orientation
145
+ * @property {boolean} [displayHeaderFooter=false] - Show header/footer
146
+ * @property {string} [headerTemplate=''] - Header HTML template
147
+ * @property {string} [footerTemplate=''] - Footer HTML template
148
+ * @property {boolean} [printBackground=true] - Print background graphics
149
+ * @property {number} [scale=1] - Page scale (0.1 to 2)
150
+ * @property {number} [paperWidth=8.5] - Paper width in inches
151
+ * @property {number} [paperHeight=11] - Paper height in inches
152
+ * @property {number} [marginTop=0.4] - Top margin in inches
153
+ * @property {number} [marginBottom=0.4] - Bottom margin in inches
154
+ * @property {number} [marginLeft=0.4] - Left margin in inches
155
+ * @property {number} [marginRight=0.4] - Right margin in inches
156
+ * @property {string} [pageRanges=''] - Page ranges (e.g., '1-5, 8')
157
+ * @property {boolean} [preferCSSPageSize=false] - Use CSS page size
158
+ */
159
+
160
+ // ============================================================================
161
+ // Cookie and Storage Types
162
+ // ============================================================================
163
+
164
+ /**
165
+ * Cookie object
166
+ * @typedef {Object} CookieObject
167
+ * @property {string} name - Cookie name
168
+ * @property {string} value - Cookie value
169
+ * @property {string} [domain] - Cookie domain
170
+ * @property {string} [path='/'] - Cookie path
171
+ * @property {number} [expires] - Expiration timestamp
172
+ * @property {boolean} [httpOnly=false] - HTTP only flag
173
+ * @property {boolean} [secure=false] - Secure flag
174
+ * @property {'Strict'|'Lax'|'None'} [sameSite='Lax'] - SameSite attribute
175
+ * @property {string} [url] - URL to derive domain/path from
176
+ */
177
+
178
+ /**
179
+ * Storage item
180
+ * @typedef {Object} StorageItem
181
+ * @property {string} name - Item key
182
+ * @property {string} value - Item value
183
+ */
184
+
185
+ // ============================================================================
186
+ // Test Runner Types
187
+ // ============================================================================
188
+
189
+ /**
190
+ * Step execution result
191
+ * @typedef {Object} StepResult
192
+ * @property {string} action - Action that was executed
193
+ * @property {'ok'|'error'|'skipped'} status - Execution status
194
+ * @property {*} [result] - Action-specific result data
195
+ * @property {string} [error] - Error message if failed
196
+ * @property {number} [duration] - Execution time in ms
197
+ */
198
+
199
+ /**
200
+ * Run result from test execution
201
+ * @typedef {Object} RunResult
202
+ * @property {'ok'|'error'} status - Overall run status
203
+ * @property {string} [tab] - Tab alias (e.g., 't1')
204
+ * @property {boolean} [navigated] - Whether navigation occurred
205
+ * @property {string} [fullSnapshot] - Full ARIA snapshot
206
+ * @property {Object} [context] - Page context (URL, scroll, activeElement)
207
+ * @property {Object} [changes] - DOM changes detected
208
+ * @property {string} [viewportSnapshot] - Viewport-only ARIA snapshot
209
+ * @property {boolean} [truncated] - Whether snapshot was truncated
210
+ * @property {string} [screenshot] - Screenshot file path
211
+ * @property {Array<Object>} [console] - Console errors/warnings
212
+ * @property {Array<StepResult>} steps - Individual step results
213
+ * @property {Array<Object>} errors - Error details for failed steps
214
+ */
215
+
216
+ /**
217
+ * Step configuration
218
+ * @typedef {Object} StepConfig
219
+ * @property {string} [goto] - Navigate to URL
220
+ * @property {string} [click] - Click element selector
221
+ * @property {string} [fill] - Fill element selector
222
+ * @property {string} [value] - Value for fill/type operations
223
+ * @property {string} [type] - Type into element
224
+ * @property {string} [press] - Press key(s)
225
+ * @property {Object} [scroll] - Scroll configuration
226
+ * @property {boolean|Object} [snapshot] - Take ARIA snapshot
227
+ * @property {string|Object} [query] - Query elements
228
+ * @property {string} [hover] - Hover over element
229
+ * @property {Object} [wait] - Wait configuration
230
+ * @property {string} [eval] - Evaluate JavaScript
231
+ * @property {string|Object} [openTab] - Open new tab
232
+ * @property {string} [closeTab] - Close tab by ID
233
+ * @property {boolean|Object} [chromeStatus] - Check/launch Chrome
234
+ * @property {string|Object} [selectOption] - Select dropdown option
235
+ * @property {string|Object} [viewport] - Set viewport
236
+ * @property {Object} [cookies] - Cookie operations
237
+ * @property {boolean} [back] - Navigate back
238
+ * @property {boolean} [forward] - Navigate forward
239
+ * @property {Object} [drag] - Drag and drop
240
+ * @property {Object} [fillForm] - Fill multiple form fields
241
+ * @property {Object} [extract] - Extract data from page
242
+ * @property {Object} [formState] - Get form state
243
+ * @property {Object} [assert] - Assert condition
244
+ * @property {Object} [validate] - Validate page state
245
+ * @property {string} [submit] - Submit form
246
+ */
247
+
248
+ /**
249
+ * Runner dependencies
250
+ * @typedef {Object} RunnerDependencies
251
+ * @property {Object} browser - Browser client instance
252
+ * @property {Object} pageController - Page controller instance
253
+ * @property {Object} elementLocator - Element locator instance
254
+ * @property {Object} inputEmulator - Input emulator instance
255
+ * @property {Object} screenshotCapture - Screenshot capture instance
256
+ * @property {Object} consoleCapture - Console capture instance
257
+ * @property {Object} pdfCapture - PDF capture instance
258
+ * @property {Object} ariaSnapshot - ARIA snapshot instance
259
+ * @property {Object} cookieManager - Cookie manager instance
260
+ */
261
+
262
+ // ============================================================================
263
+ // Error Types
264
+ // ============================================================================
265
+
266
+ /**
267
+ * CDP Skill error
268
+ * @typedef {Object} CDPError
269
+ * @property {string} type - Error type (CONNECTION, NAVIGATION, TIMEOUT, etc.)
270
+ * @property {string} message - Error message
271
+ * @property {string} [code] - Error code
272
+ * @property {Object} [details] - Additional error details
273
+ */
274
+
275
+ // ============================================================================
276
+ // Console and Network Capture Types
277
+ // ============================================================================
278
+
279
+ /**
280
+ * Console message
281
+ * @typedef {Object} ConsoleMessage
282
+ * @property {'console'|'exception'} type - Message type
283
+ * @property {'log'|'debug'|'info'|'warning'|'error'} level - Log level
284
+ * @property {string} text - Message text
285
+ * @property {Array<Object>} [args] - Original arguments
286
+ * @property {Object} [stackTrace] - Stack trace if available
287
+ * @property {number} [timestamp] - CDP timestamp
288
+ * @property {string} [url] - Source URL for exceptions
289
+ * @property {number} [line] - Line number for exceptions
290
+ * @property {number} [column] - Column number for exceptions
291
+ */
292
+
293
+ /**
294
+ * Network error
295
+ * @typedef {Object} NetworkError
296
+ * @property {'network-failure'|'http-error'} type - Error type
297
+ * @property {string} requestId - Request ID
298
+ * @property {string} url - Request URL
299
+ * @property {string} method - HTTP method
300
+ * @property {string} [resourceType] - Resource type (Document, Script, etc.)
301
+ * @property {string} [errorText] - Error description
302
+ * @property {boolean} [canceled] - Whether request was canceled
303
+ * @property {number} [status] - HTTP status code (for http-error)
304
+ * @property {string} [statusText] - HTTP status text
305
+ * @property {number} timestamp - CDP timestamp
306
+ */
307
+
308
+ // Export empty object to make this a proper module
309
+ export {};