browser-debugger-cli 0.6.4 → 0.6.6

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 (154) hide show
  1. package/dist/commands/__tests__/machineReadableHelp.contract.test.d.ts +12 -0
  2. package/dist/commands/__tests__/machineReadableHelp.contract.test.d.ts.map +1 -0
  3. package/dist/commands/__tests__/machineReadableHelp.contract.test.js +102 -0
  4. package/dist/commands/__tests__/machineReadableHelp.contract.test.js.map +1 -0
  5. package/dist/commands/cdp.js +8 -4
  6. package/dist/commands/cdp.js.map +1 -1
  7. package/dist/commands/console.d.ts.map +1 -1
  8. package/dist/commands/console.js +22 -2
  9. package/dist/commands/console.js.map +1 -1
  10. package/dist/commands/dom/a11y.d.ts.map +1 -1
  11. package/dist/commands/dom/a11y.js +83 -19
  12. package/dist/commands/dom/a11y.js.map +1 -1
  13. package/dist/commands/dom/evalHelpers.d.ts +1 -1
  14. package/dist/commands/dom/evalHelpers.d.ts.map +1 -1
  15. package/dist/commands/dom/evalHelpers.js +3 -2
  16. package/dist/commands/dom/evalHelpers.js.map +1 -1
  17. package/dist/commands/dom/formFillHelpers.d.ts +68 -2
  18. package/dist/commands/dom/formFillHelpers.d.ts.map +1 -1
  19. package/dist/commands/dom/formFillHelpers.js +240 -66
  20. package/dist/commands/dom/formFillHelpers.js.map +1 -1
  21. package/dist/commands/dom/formInteraction.d.ts.map +1 -1
  22. package/dist/commands/dom/formInteraction.js +128 -15
  23. package/dist/commands/dom/formInteraction.js.map +1 -1
  24. package/dist/commands/dom/helpers.d.ts +61 -2
  25. package/dist/commands/dom/helpers.d.ts.map +1 -1
  26. package/dist/commands/dom/helpers.js +103 -0
  27. package/dist/commands/dom/helpers.js.map +1 -1
  28. package/dist/commands/dom/index.d.ts.map +1 -1
  29. package/dist/commands/dom/index.js +135 -16
  30. package/dist/commands/dom/index.js.map +1 -1
  31. package/dist/commands/dom/keyMapping.d.ts +62 -0
  32. package/dist/commands/dom/keyMapping.d.ts.map +1 -0
  33. package/dist/commands/dom/keyMapping.js +118 -0
  34. package/dist/commands/dom/keyMapping.js.map +1 -0
  35. package/dist/commands/dom/reactEventHelpers.d.ts +10 -5
  36. package/dist/commands/dom/reactEventHelpers.d.ts.map +1 -1
  37. package/dist/commands/dom/reactEventHelpers.js +55 -13
  38. package/dist/commands/dom/reactEventHelpers.js.map +1 -1
  39. package/dist/commands/machineReadableHelp.d.ts +48 -0
  40. package/dist/commands/machineReadableHelp.d.ts.map +1 -1
  41. package/dist/commands/machineReadableHelp.js +80 -0
  42. package/dist/commands/machineReadableHelp.js.map +1 -1
  43. package/dist/commands/network.d.ts.map +1 -1
  44. package/dist/commands/network.js +28 -2
  45. package/dist/commands/network.js.map +1 -1
  46. package/dist/commands/peek.d.ts.map +1 -1
  47. package/dist/commands/peek.js +16 -7
  48. package/dist/commands/peek.js.map +1 -1
  49. package/dist/commands/shared/CommandRunner.d.ts +2 -0
  50. package/dist/commands/shared/CommandRunner.d.ts.map +1 -1
  51. package/dist/commands/shared/CommandRunner.js +3 -0
  52. package/dist/commands/shared/CommandRunner.js.map +1 -1
  53. package/dist/commands/shared/validation.d.ts +2 -0
  54. package/dist/commands/shared/validation.d.ts.map +1 -1
  55. package/dist/commands/shared/validation.js +5 -2
  56. package/dist/commands/shared/validation.js.map +1 -1
  57. package/dist/commands/stop.d.ts.map +1 -1
  58. package/dist/commands/stop.js +2 -1
  59. package/dist/commands/stop.js.map +1 -1
  60. package/dist/commands/tail.d.ts.map +1 -1
  61. package/dist/commands/tail.js +6 -0
  62. package/dist/commands/tail.js.map +1 -1
  63. package/dist/connection/cdp.js +9 -9
  64. package/dist/constants.d.ts +5 -1
  65. package/dist/constants.d.ts.map +1 -1
  66. package/dist/constants.js +9 -3
  67. package/dist/constants.js.map +1 -1
  68. package/dist/daemon/patternDefinitions.d.ts +47 -0
  69. package/dist/daemon/patternDefinitions.d.ts.map +1 -0
  70. package/dist/daemon/patternDefinitions.js +71 -0
  71. package/dist/daemon/patternDefinitions.js.map +1 -0
  72. package/dist/daemon/patternDetector.d.ts +60 -0
  73. package/dist/daemon/patternDetector.d.ts.map +1 -0
  74. package/dist/daemon/patternDetector.js +74 -0
  75. package/dist/daemon/patternDetector.js.map +1 -0
  76. package/dist/daemon/worker/__tests__/PatternDetector.unit.test.d.ts +11 -0
  77. package/dist/daemon/worker/__tests__/PatternDetector.unit.test.d.ts.map +1 -0
  78. package/dist/daemon/worker/__tests__/PatternDetector.unit.test.js +136 -0
  79. package/dist/daemon/worker/__tests__/PatternDetector.unit.test.js.map +1 -0
  80. package/dist/daemon/worker/commandRegistry.d.ts.map +1 -1
  81. package/dist/daemon/worker/commandRegistry.js +13 -2
  82. package/dist/daemon/worker/commandRegistry.js.map +1 -1
  83. package/dist/ipc/__tests__/jsonl.unit.test.js +5 -5
  84. package/dist/ipc/__tests__/jsonl.unit.test.js.map +1 -1
  85. package/dist/ipc/protocol/commands.d.ts +2 -0
  86. package/dist/ipc/protocol/commands.d.ts.map +1 -1
  87. package/dist/ipc/protocol/commands.js.map +1 -1
  88. package/dist/session/cleanup.d.ts.map +1 -1
  89. package/dist/session/cleanup.js +2 -0
  90. package/dist/session/cleanup.js.map +1 -1
  91. package/dist/session/queryCache.d.ts +35 -0
  92. package/dist/session/queryCache.d.ts.map +1 -0
  93. package/dist/session/queryCache.js +83 -0
  94. package/dist/session/queryCache.js.map +1 -0
  95. package/dist/telemetry/a11y.d.ts +8 -5
  96. package/dist/telemetry/a11y.d.ts.map +1 -1
  97. package/dist/telemetry/a11y.js +34 -19
  98. package/dist/telemetry/a11y.js.map +1 -1
  99. package/dist/types/dom.d.ts +9 -0
  100. package/dist/types/dom.d.ts.map +1 -1
  101. package/dist/ui/errors/CommandError.d.ts +5 -1
  102. package/dist/ui/errors/CommandError.d.ts.map +1 -1
  103. package/dist/ui/errors/CommandError.js.map +1 -1
  104. package/dist/ui/formatters/__tests__/preview.unit.test.d.ts +13 -0
  105. package/dist/ui/formatters/__tests__/preview.unit.test.d.ts.map +1 -0
  106. package/dist/ui/formatters/__tests__/preview.unit.test.js +180 -0
  107. package/dist/ui/formatters/__tests__/preview.unit.test.js.map +1 -0
  108. package/dist/ui/formatters/a11y.d.ts +19 -0
  109. package/dist/ui/formatters/a11y.d.ts.map +1 -1
  110. package/dist/ui/formatters/a11y.js +60 -0
  111. package/dist/ui/formatters/a11y.js.map +1 -1
  112. package/dist/ui/formatters/dom.d.ts.map +1 -1
  113. package/dist/ui/formatters/dom.js +5 -2
  114. package/dist/ui/formatters/dom.js.map +1 -1
  115. package/dist/ui/formatters/helpFormatters.d.ts +41 -0
  116. package/dist/ui/formatters/helpFormatters.d.ts.map +1 -0
  117. package/dist/ui/formatters/helpFormatters.js +80 -0
  118. package/dist/ui/formatters/helpFormatters.js.map +1 -0
  119. package/dist/ui/formatters/preview.d.ts +4 -0
  120. package/dist/ui/formatters/preview.d.ts.map +1 -1
  121. package/dist/ui/formatters/preview.js +91 -23
  122. package/dist/ui/formatters/preview.js.map +1 -1
  123. package/dist/ui/formatters/sessionFormatters.d.ts +58 -0
  124. package/dist/ui/formatters/sessionFormatters.d.ts.map +1 -0
  125. package/dist/ui/formatters/sessionFormatters.js +118 -0
  126. package/dist/ui/formatters/sessionFormatters.js.map +1 -0
  127. package/dist/ui/messages/commands.d.ts +9 -0
  128. package/dist/ui/messages/commands.d.ts.map +1 -1
  129. package/dist/ui/messages/commands.js +11 -1
  130. package/dist/ui/messages/commands.js.map +1 -1
  131. package/dist/ui/messages/errors.d.ts +53 -0
  132. package/dist/ui/messages/errors.d.ts.map +1 -1
  133. package/dist/ui/messages/errors.js +59 -0
  134. package/dist/ui/messages/errors.js.map +1 -1
  135. package/dist/ui/messages/hints.d.ts +33 -0
  136. package/dist/ui/messages/hints.d.ts.map +1 -0
  137. package/dist/ui/messages/hints.js +37 -0
  138. package/dist/ui/messages/hints.js.map +1 -0
  139. package/dist/ui/messages/session.d.ts +11 -1
  140. package/dist/ui/messages/session.d.ts.map +1 -1
  141. package/dist/ui/messages/session.js +14 -21
  142. package/dist/ui/messages/session.js.map +1 -1
  143. package/dist/utils/decisionTrees.d.ts +54 -0
  144. package/dist/utils/decisionTrees.d.ts.map +1 -0
  145. package/dist/utils/decisionTrees.js +176 -0
  146. package/dist/utils/decisionTrees.js.map +1 -0
  147. package/dist/utils/taskMappings.d.ts +47 -0
  148. package/dist/utils/taskMappings.d.ts.map +1 -0
  149. package/dist/utils/taskMappings.js +123 -0
  150. package/dist/utils/taskMappings.js.map +1 -0
  151. package/dist/utils/url.d.ts.map +1 -1
  152. package/dist/utils/url.js +15 -2
  153. package/dist/utils/url.js.map +1 -1
  154. package/package.json +3 -3
@@ -2,15 +2,50 @@
2
2
  * Form interaction helpers for filling inputs, clicking buttons, etc.
3
3
  */
4
4
  import { CommandError } from '../../ui/errors/index.js';
5
+ import { createLogger } from '../../ui/logging/index.js';
5
6
  import { EXIT_CODES } from '../../utils/exitCodes.js';
6
- import { REACT_FILL_SCRIPT, CLICK_ELEMENT_SCRIPT, GET_ELEMENT_BY_INDEX_SCRIPT, } from './reactEventHelpers.js';
7
+ import { getKeyDefinition, parseModifiers } from './keyMapping.js';
8
+ import { REACT_FILL_SCRIPT, CLICK_ELEMENT_SCRIPT, } from './reactEventHelpers.js';
9
+ const log = createLogger('dom');
10
+ /** Network idle threshold for post-action stability (ms) */
11
+ const ACTION_NETWORK_IDLE_MS = 150;
12
+ /** Maximum time to wait for post-action stability (ms) */
13
+ const ACTION_STABILITY_TIMEOUT_MS = 2000;
14
+ /** Check interval for stability polling (ms) */
15
+ const STABILITY_CHECK_INTERVAL_MS = 50;
16
+ /**
17
+ * Format exception details into a user-friendly error message with troubleshooting hints.
18
+ *
19
+ * @param exceptionDetails - CDP exception details
20
+ * @param selector - CSS selector that was used
21
+ * @param operationType - Type of operation (fill or click) for tailored hints
22
+ * @returns Formatted error message with context
23
+ */
24
+ function formatScriptExecutionError(exceptionDetails, selector, operationType = 'fill') {
25
+ const errorText = exceptionDetails.text || 'Unknown error';
26
+ const location = exceptionDetails.lineNumber !== undefined && exceptionDetails.columnNumber !== undefined
27
+ ? ` at line ${exceptionDetails.lineNumber + 1}, column ${exceptionDetails.columnNumber + 1}`
28
+ : '';
29
+ const troubleshootingSteps = operationType === 'fill'
30
+ ? [
31
+ `1. Verify element exists: bdg dom query "${selector}"`,
32
+ '2. Check element is visible and not disabled',
33
+ `3. Try direct eval: bdg dom eval "document.querySelector('${escapeSelectorForJS(selector)}').value = 'your-value'"`,
34
+ ]
35
+ : [
36
+ `1. Verify element exists: bdg dom query "${selector}"`,
37
+ '2. Check element is visible and clickable',
38
+ `3. Try direct eval: bdg dom eval "document.querySelector('${escapeSelectorForJS(selector)}').click()"`,
39
+ ];
40
+ return `Script execution failed: ${errorText}${location}\n\nTroubleshooting:\n ${troubleshootingSteps.join('\n ')}`;
41
+ }
7
42
  /**
8
43
  * Fill a form element with a value in a React-compatible way.
9
44
  *
10
45
  * @param cdp - CDP connection
11
46
  * @param selector - CSS selector for the element
12
47
  * @param value - Value to fill
13
- * @param options - Fill options
48
+ * @param options - Fill options including optional index for multiple matches
14
49
  * @returns Promise resolving to fill result
15
50
  *
16
51
  * @throws CommandError When element operations fail
@@ -24,18 +59,11 @@ import { REACT_FILL_SCRIPT, CLICK_ELEMENT_SCRIPT, GET_ELEMENT_BY_INDEX_SCRIPT, }
24
59
  * ```
25
60
  */
26
61
  export async function fillElement(cdp, selector, value, options = {}) {
27
- let targetSelector = selector;
28
- if (options.index !== undefined) {
29
- const indexResult = await getElementByIndex(cdp, selector, options.index);
30
- if (!indexResult.success) {
31
- return indexResult;
32
- }
33
- if (!indexResult.uniqueSelector) {
34
- throw new CommandError('Failed to resolve unique selector for element index', { note: 'Element index resolution returned no unique selector' }, EXIT_CODES.SOFTWARE_ERROR);
35
- }
36
- targetSelector = indexResult.uniqueSelector;
37
- }
38
- const expression = `(${REACT_FILL_SCRIPT})('${escapeSelectorForJS(targetSelector)}', '${escapeValueForJS(value)}', ${JSON.stringify({ blur: options.blur ?? true })})`;
62
+ const scriptOptions = {
63
+ blur: options.blur ?? true,
64
+ index: options.index,
65
+ };
66
+ const expression = `(${REACT_FILL_SCRIPT})('${escapeSelectorForJS(selector)}', '${escapeValueForJS(value)}', ${JSON.stringify(scriptOptions)})`;
39
67
  try {
40
68
  const response = await cdp.send('Runtime.evaluate', {
41
69
  expression,
@@ -44,7 +72,8 @@ export async function fillElement(cdp, selector, value, options = {}) {
44
72
  });
45
73
  const cdpResponse = response;
46
74
  if (cdpResponse.exceptionDetails) {
47
- throw new CommandError('Script execution failed', { note: cdpResponse.exceptionDetails.text ?? 'Unknown error' }, EXIT_CODES.SOFTWARE_ERROR);
75
+ const errorMessage = formatScriptExecutionError(cdpResponse.exceptionDetails, selector, 'fill');
76
+ throw new CommandError(errorMessage, {}, EXIT_CODES.SOFTWARE_ERROR);
48
77
  }
49
78
  if (cdpResponse.result?.value) {
50
79
  return cdpResponse.result.value;
@@ -64,7 +93,7 @@ export async function fillElement(cdp, selector, value, options = {}) {
64
93
  *
65
94
  * @param cdp - CDP connection
66
95
  * @param selector - CSS selector for the element
67
- * @param options - Click options
96
+ * @param options - Click options including optional index for multiple matches
68
97
  * @returns Promise resolving to click result
69
98
  *
70
99
  * @throws CommandError When element operations fail
@@ -78,18 +107,8 @@ export async function fillElement(cdp, selector, value, options = {}) {
78
107
  * ```
79
108
  */
80
109
  export async function clickElement(cdp, selector, options = {}) {
81
- let targetSelector = selector;
82
- if (options.index !== undefined) {
83
- const indexResult = await getElementByIndex(cdp, selector, options.index);
84
- if (!indexResult.success) {
85
- return indexResult;
86
- }
87
- if (!indexResult.uniqueSelector) {
88
- throw new CommandError('Failed to resolve unique selector for element index', { note: 'Element index resolution returned no unique selector' }, EXIT_CODES.SOFTWARE_ERROR);
89
- }
90
- targetSelector = indexResult.uniqueSelector;
91
- }
92
- const expression = `(${CLICK_ELEMENT_SCRIPT})('${escapeSelectorForJS(targetSelector)}')`;
110
+ const indexArg = options.index ?? 'null';
111
+ const expression = `(${CLICK_ELEMENT_SCRIPT})('${escapeSelectorForJS(selector)}', ${indexArg})`;
93
112
  try {
94
113
  const response = await cdp.send('Runtime.evaluate', {
95
114
  expression,
@@ -98,7 +117,8 @@ export async function clickElement(cdp, selector, options = {}) {
98
117
  });
99
118
  const cdpResponse = response;
100
119
  if (cdpResponse.exceptionDetails) {
101
- throw new CommandError('Script execution failed', { note: cdpResponse.exceptionDetails.text ?? 'Unknown error' }, EXIT_CODES.SOFTWARE_ERROR);
120
+ const errorMessage = formatScriptExecutionError(cdpResponse.exceptionDetails, selector, 'click');
121
+ throw new CommandError(errorMessage, {}, EXIT_CODES.SOFTWARE_ERROR);
102
122
  }
103
123
  if (cdpResponse.result?.value) {
104
124
  return cdpResponse.result.value;
@@ -114,63 +134,217 @@ export async function clickElement(cdp, selector, options = {}) {
114
134
  }
115
135
  }
116
136
  /**
117
- * Get element by index when selector matches multiple elements.
137
+ * Escape CSS selector for safe inclusion in JavaScript single-quoted string.
138
+ * Uses JSON.stringify for special characters, then escapes single quotes
139
+ * since the expression wrapper uses single quotes.
118
140
  *
119
- * @param cdp - CDP connection
120
- * @param selector - CSS selector
121
- * @param index - Element index (1-based)
122
- * @returns Promise resolving to element info
141
+ * @param selector - CSS selector to escape
142
+ * @returns Escaped selector safe for single-quoted JS string
123
143
  *
124
- * @throws CommandError When element operations fail
144
+ * @internal
145
+ */
146
+ function escapeSelectorForJS(selector) {
147
+ return JSON.stringify(selector).slice(1, -1).replace(/'/g, "\\'");
148
+ }
149
+ /**
150
+ * Escape value for safe inclusion in JavaScript single-quoted string.
151
+ * Uses JSON.stringify for special characters, then escapes single quotes
152
+ * since the expression wrapper uses single quotes.
153
+ *
154
+ * @param value - Value to escape
155
+ * @returns Escaped value safe for single-quoted JS string
125
156
  *
126
157
  * @internal
127
158
  */
128
- async function getElementByIndex(cdp, selector, index) {
129
- const expression = `(${GET_ELEMENT_BY_INDEX_SCRIPT})('${escapeSelectorForJS(selector)}', ${index})`;
159
+ function escapeValueForJS(value) {
160
+ return JSON.stringify(value).slice(1, -1).replace(/'/g, "\\'");
161
+ }
162
+ /**
163
+ * Wait for page to stabilize after an action (click, fill, etc.).
164
+ *
165
+ * This is a lightweight stability check designed for post-action waiting:
166
+ * - Waits for network to be idle for 150ms
167
+ * - Times out after 2s to avoid hanging
168
+ * - Does not block on slow background requests
169
+ *
170
+ * @param cdp - CDP connection
171
+ * @returns Promise that resolves when stable or timeout reached
172
+ *
173
+ * @remarks
174
+ * This is intentionally less strict than page readiness on initial load.
175
+ * It's designed to catch immediate reactions to user actions (AJAX, re-renders)
176
+ * without waiting for unrelated background activity.
177
+ */
178
+ export async function waitForActionStability(cdp) {
179
+ const deadline = Date.now() + ACTION_STABILITY_TIMEOUT_MS;
180
+ let activeRequests = 0;
181
+ let lastActivity = Date.now();
182
+ const requestHandler = () => {
183
+ activeRequests++;
184
+ lastActivity = Date.now();
185
+ };
186
+ const finishHandler = () => {
187
+ activeRequests--;
188
+ if (activeRequests === 0) {
189
+ lastActivity = Date.now();
190
+ }
191
+ };
192
+ await cdp.send('Network.enable');
193
+ const cleanupRequest = cdp.on('Network.requestWillBeSent', requestHandler);
194
+ const cleanupFinished = cdp.on('Network.loadingFinished', finishHandler);
195
+ const cleanupFailed = cdp.on('Network.loadingFailed', finishHandler);
130
196
  try {
131
- const response = await cdp.send('Runtime.evaluate', {
132
- expression,
197
+ while (Date.now() < deadline) {
198
+ if (activeRequests === 0) {
199
+ const idleTime = Date.now() - lastActivity;
200
+ if (idleTime >= ACTION_NETWORK_IDLE_MS) {
201
+ log.debug(`Network stable after ${idleTime}ms idle`);
202
+ return;
203
+ }
204
+ }
205
+ await delay(STABILITY_CHECK_INTERVAL_MS);
206
+ }
207
+ log.debug('Stability timeout reached, proceeding');
208
+ }
209
+ finally {
210
+ cleanupRequest();
211
+ cleanupFinished();
212
+ cleanupFailed();
213
+ }
214
+ }
215
+ /**
216
+ * Delay utility.
217
+ *
218
+ * @param ms - Milliseconds to delay
219
+ */
220
+ function delay(ms) {
221
+ return new Promise((resolve) => setTimeout(resolve, ms));
222
+ }
223
+ /**
224
+ * Script to focus an element by selector and optional index.
225
+ *
226
+ * @returns Object with success status and element info
227
+ */
228
+ const FOCUS_ELEMENT_SCRIPT = `
229
+ (function(selector, index) {
230
+ const allMatches = document.querySelectorAll(selector);
231
+ if (allMatches.length === 0) {
232
+ return { success: false, error: 'No elements found matching selector: ' + selector };
233
+ }
234
+
235
+ let el;
236
+ if (typeof index === 'number' && index > 0) {
237
+ if (index > allMatches.length) {
238
+ return {
239
+ success: false,
240
+ error: 'Index ' + index + ' out of range (found ' + allMatches.length + ' elements)'
241
+ };
242
+ }
243
+ el = allMatches[index - 1];
244
+ } else {
245
+ el = allMatches[0];
246
+ }
247
+
248
+ el.focus();
249
+
250
+ return {
251
+ success: true,
252
+ selector: selector,
253
+ elementType: el.tagName.toLowerCase(),
254
+ focused: document.activeElement === el
255
+ };
256
+ })`;
257
+ /**
258
+ * Press a key on an element.
259
+ *
260
+ * Focuses the element first, then dispatches keyDown and keyUp events via CDP.
261
+ *
262
+ * @param cdp - CDP connection
263
+ * @param selector - CSS selector for the element
264
+ * @param keyName - Key name (e.g., "Enter", "Tab", "a")
265
+ * @param options - Press key options
266
+ * @returns Promise resolving to press key result
267
+ *
268
+ * @throws CommandError When element or key operations fail
269
+ *
270
+ * @example
271
+ * ```typescript
272
+ * // Press Enter on a todo input
273
+ * const result = await pressKeyElement(cdp, '.new-todo', 'Enter');
274
+ *
275
+ * // Press Tab 3 times with Shift held
276
+ * const result = await pressKeyElement(cdp, 'input', 'Tab', {
277
+ * times: 3,
278
+ * modifiers: 'shift'
279
+ * });
280
+ * ```
281
+ */
282
+ export async function pressKeyElement(cdp, selector, keyName, options = {}) {
283
+ const keyDef = getKeyDefinition(keyName);
284
+ if (!keyDef) {
285
+ return {
286
+ success: false,
287
+ error: `Unknown key: "${keyName}". Supported keys: Enter, Tab, Escape, Space, Backspace, Delete, ArrowUp/Down/Left/Right, Home, End, PageUp, PageDown, F1-F12, a-z, 0-9`,
288
+ };
289
+ }
290
+ const times = options.times ?? 1;
291
+ const modifierFlags = parseModifiers(options.modifiers);
292
+ const indexArg = options.index ?? 'null';
293
+ const focusExpression = `(${FOCUS_ELEMENT_SCRIPT})('${escapeSelectorForJS(selector)}', ${indexArg})`;
294
+ try {
295
+ const focusResponse = await cdp.send('Runtime.evaluate', {
296
+ expression: focusExpression,
133
297
  returnByValue: true,
134
298
  });
135
- const cdpResponse = response;
136
- if (cdpResponse.exceptionDetails) {
137
- throw new CommandError('Script execution failed', { note: cdpResponse.exceptionDetails.text ?? 'Unknown error' }, EXIT_CODES.SOFTWARE_ERROR);
299
+ const focusCdpResponse = focusResponse;
300
+ if (focusCdpResponse.exceptionDetails) {
301
+ throw new CommandError(`Failed to focus element: ${focusCdpResponse.exceptionDetails.text}`, { note: `Selector: ${selector}` }, EXIT_CODES.SOFTWARE_ERROR);
138
302
  }
139
- if (cdpResponse.result?.value) {
140
- return cdpResponse.result.value;
303
+ const focusResult = focusCdpResponse.result?.value;
304
+ if (!focusResult?.success) {
305
+ return {
306
+ success: false,
307
+ error: focusResult?.error ?? 'Failed to focus element',
308
+ selector,
309
+ };
141
310
  }
142
- throw new CommandError('Unexpected response format', { note: 'CDP response missing result.value' }, EXIT_CODES.SOFTWARE_ERROR);
311
+ for (let i = 0; i < times; i++) {
312
+ await dispatchKeyEvent(cdp, 'keyDown', keyDef, modifierFlags);
313
+ await dispatchKeyEvent(cdp, 'keyUp', keyDef, modifierFlags);
314
+ }
315
+ return {
316
+ success: true,
317
+ selector,
318
+ key: keyName,
319
+ times,
320
+ modifiers: modifierFlags,
321
+ elementType: focusResult.elementType,
322
+ };
143
323
  }
144
324
  catch (error) {
145
325
  if (error instanceof CommandError) {
146
326
  throw error;
147
327
  }
148
328
  const errorMessage = error instanceof Error ? error.message : String(error);
149
- throw new CommandError('Failed to get element by index', { note: errorMessage }, EXIT_CODES.SOFTWARE_ERROR);
329
+ throw new CommandError('Failed to press key', { note: errorMessage }, EXIT_CODES.SOFTWARE_ERROR);
150
330
  }
151
331
  }
152
332
  /**
153
- * Escape CSS selector for safe inclusion in JavaScript string.
154
- * Uses JSON.stringify to handle all special characters safely.
155
- *
156
- * @param selector - CSS selector to escape
157
- * @returns Escaped selector (without surrounding quotes)
333
+ * Dispatch a single key event via CDP Input.dispatchKeyEvent.
158
334
  *
159
- * @internal
160
- */
161
- function escapeSelectorForJS(selector) {
162
- return JSON.stringify(selector).slice(1, -1);
163
- }
164
- /**
165
- * Escape value for safe inclusion in JavaScript string.
166
- * Uses JSON.stringify to handle all special characters safely.
167
- *
168
- * @param value - Value to escape
169
- * @returns Escaped value (without surrounding quotes)
170
- *
171
- * @internal
335
+ * @param cdp - CDP connection
336
+ * @param type - Event type (keyDown or keyUp)
337
+ * @param keyDef - Key definition with code, key, and keyCode
338
+ * @param modifiers - Modifier bit flags
172
339
  */
173
- function escapeValueForJS(value) {
174
- return JSON.stringify(value).slice(1, -1);
340
+ async function dispatchKeyEvent(cdp, type, keyDef, modifiers) {
341
+ await cdp.send('Input.dispatchKeyEvent', {
342
+ type,
343
+ code: keyDef.code,
344
+ key: keyDef.key,
345
+ windowsVirtualKeyCode: keyDef.keyCode,
346
+ nativeVirtualKeyCode: keyDef.keyCode,
347
+ modifiers,
348
+ });
175
349
  }
176
350
  //# sourceMappingURL=formFillHelpers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"formFillHelpers.js","sourceRoot":"","sources":["../../../src/commands/dom/formFillHelpers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"formFillHelpers.js","sourceRoot":"","sources":["../../../src/commands/dom/formFillHelpers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAElD,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAsB,MAAM,iBAAiB,CAAC;AACvF,OAAO,EACL,iBAAiB,EACjB,oBAAoB,GAIrB,MAAM,wBAAwB,CAAC;AAEhC,MAAM,GAAG,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;AAEhC,4DAA4D;AAC5D,MAAM,sBAAsB,GAAG,GAAG,CAAC;AACnC,0DAA0D;AAC1D,MAAM,2BAA2B,GAAG,IAAI,CAAC;AACzC,gDAAgD;AAChD,MAAM,2BAA2B,GAAG,EAAE,CAAC;AAEvC;;;;;;;GAOG;AACH,SAAS,0BAA0B,CACjC,gBAAmD,EACnD,QAAgB,EAChB,gBAAkC,MAAM;IAExC,MAAM,SAAS,GAAG,gBAAgB,CAAC,IAAI,IAAI,eAAe,CAAC;IAC3D,MAAM,QAAQ,GACZ,gBAAgB,CAAC,UAAU,KAAK,SAAS,IAAI,gBAAgB,CAAC,YAAY,KAAK,SAAS;QACtF,CAAC,CAAC,YAAY,gBAAgB,CAAC,UAAU,GAAG,CAAC,YAAY,gBAAgB,CAAC,YAAY,GAAG,CAAC,EAAE;QAC5F,CAAC,CAAC,EAAE,CAAC;IAET,MAAM,oBAAoB,GACxB,aAAa,KAAK,MAAM;QACtB,CAAC,CAAC;YACE,4CAA4C,QAAQ,GAAG;YACvD,8CAA8C;YAC9C,6DAA6D,mBAAmB,CAAC,QAAQ,CAAC,0BAA0B;SACrH;QACH,CAAC,CAAC;YACE,4CAA4C,QAAQ,GAAG;YACvD,2CAA2C;YAC3C,6DAA6D,mBAAmB,CAAC,QAAQ,CAAC,aAAa;SACxG,CAAC;IAER,OAAO,4BAA4B,SAAS,GAAG,QAAQ,2BAA2B,oBAAoB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;AACxH,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,GAAkB,EAClB,QAAgB,EAChB,KAAa,EACb,UAAuB,EAAE;IAEzB,MAAM,aAAa,GAAG;QACpB,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,IAAI;QAC1B,KAAK,EAAE,OAAO,CAAC,KAAK;KACrB,CAAC;IAEF,MAAM,UAAU,GAAG,IAAI,iBAAiB,MAAM,mBAAmB,CAAC,QAAQ,CAAC,OAAO,gBAAgB,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,GAAG,CAAC;IAEhJ,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,kBAAkB,EAAE;YAClD,UAAU;YACV,aAAa,EAAE,IAAI;YACnB,WAAW,EAAE,IAAI,EAAE,iCAAiC;SACrD,CAAC,CAAC;QAEH,MAAM,WAAW,GAAG,QAGnB,CAAC;QAEF,IAAI,WAAW,CAAC,gBAAgB,EAAE,CAAC;YACjC,MAAM,YAAY,GAAG,0BAA0B,CAC7C,WAAW,CAAC,gBAAgB,EAC5B,QAAQ,EACR,MAAM,CACP,CAAC;YACF,MAAM,IAAI,YAAY,CAAC,YAAY,EAAE,EAAE,EAAE,UAAU,CAAC,cAAc,CAAC,CAAC;QACtE,CAAC;QAED,IAAI,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC;YAC9B,OAAO,WAAW,CAAC,MAAM,CAAC,KAAmB,CAAC;QAChD,CAAC;QAED,MAAM,IAAI,YAAY,CACpB,4BAA4B,EAC5B,EAAE,IAAI,EAAE,mCAAmC,EAAE,EAC7C,UAAU,CAAC,cAAc,CAC1B,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,YAAY,EAAE,CAAC;YAClC,MAAM,KAAK,CAAC;QACd,CAAC;QACD,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5E,MAAM,IAAI,YAAY,CACpB,wBAAwB,EACxB,EAAE,IAAI,EAAE,YAAY,EAAE,EACtB,UAAU,CAAC,cAAc,CAC1B,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,GAAkB,EAClB,QAAgB,EAChB,UAA8B,EAAE;IAEhC,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,IAAI,MAAM,CAAC;IACzC,MAAM,UAAU,GAAG,IAAI,oBAAoB,MAAM,mBAAmB,CAAC,QAAQ,CAAC,MAAM,QAAQ,GAAG,CAAC;IAEhG,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,kBAAkB,EAAE;YAClD,UAAU;YACV,aAAa,EAAE,IAAI;YACnB,WAAW,EAAE,IAAI;SAClB,CAAC,CAAC;QAEH,MAAM,WAAW,GAAG,QAGnB,CAAC;QAEF,IAAI,WAAW,CAAC,gBAAgB,EAAE,CAAC;YACjC,MAAM,YAAY,GAAG,0BAA0B,CAC7C,WAAW,CAAC,gBAAgB,EAC5B,QAAQ,EACR,OAAO,CACR,CAAC;YACF,MAAM,IAAI,YAAY,CAAC,YAAY,EAAE,EAAE,EAAE,UAAU,CAAC,cAAc,CAAC,CAAC;QACtE,CAAC;QAED,IAAI,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC;YAC9B,OAAO,WAAW,CAAC,MAAM,CAAC,KAAoB,CAAC;QACjD,CAAC;QAED,MAAM,IAAI,YAAY,CACpB,4BAA4B,EAC5B,EAAE,IAAI,EAAE,mCAAmC,EAAE,EAC7C,UAAU,CAAC,cAAc,CAC1B,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,YAAY,EAAE,CAAC;YAClC,MAAM,KAAK,CAAC;QACd,CAAC;QACD,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5E,MAAM,IAAI,YAAY,CACpB,yBAAyB,EACzB,EAAE,IAAI,EAAE,YAAY,EAAE,EACtB,UAAU,CAAC,cAAc,CAC1B,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,mBAAmB,CAAC,QAAgB;IAC3C,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AACpE,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,gBAAgB,CAAC,KAAa;IACrC,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AACjE,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,GAAkB;IAC7D,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,2BAA2B,CAAC;IAE1D,IAAI,cAAc,GAAG,CAAC,CAAC;IACvB,IAAI,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAE9B,MAAM,cAAc,GAAG,GAAS,EAAE;QAChC,cAAc,EAAE,CAAC;QACjB,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC5B,CAAC,CAAC;IAEF,MAAM,aAAa,GAAG,GAAS,EAAE;QAC/B,cAAc,EAAE,CAAC;QACjB,IAAI,cAAc,KAAK,CAAC,EAAE,CAAC;YACzB,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC5B,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,GAAG,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAEjC,MAAM,cAAc,GAAG,GAAG,CAAC,EAAE,CAAC,2BAA2B,EAAE,cAAc,CAAC,CAAC;IAC3E,MAAM,eAAe,GAAG,GAAG,CAAC,EAAE,CAAC,yBAAyB,EAAE,aAAa,CAAC,CAAC;IACzE,MAAM,aAAa,GAAG,GAAG,CAAC,EAAE,CAAC,uBAAuB,EAAE,aAAa,CAAC,CAAC;IAErE,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;YAC7B,IAAI,cAAc,KAAK,CAAC,EAAE,CAAC;gBACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,YAAY,CAAC;gBAC3C,IAAI,QAAQ,IAAI,sBAAsB,EAAE,CAAC;oBACvC,GAAG,CAAC,KAAK,CAAC,wBAAwB,QAAQ,SAAS,CAAC,CAAC;oBACrD,OAAO;gBACT,CAAC;YACH,CAAC;YAED,MAAM,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAC3C,CAAC;QAED,GAAG,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAC;IACrD,CAAC;YAAS,CAAC;QACT,cAAc,EAAE,CAAC;QACjB,eAAe,EAAE,CAAC;QAClB,aAAa,EAAE,CAAC;IAClB,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AA2BD;;;;GAIG;AACH,MAAM,oBAAoB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4B1B,CAAC;AAEJ;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,GAAkB,EAClB,QAAgB,EAChB,OAAe,EACf,UAA2B,EAAE;IAE7B,MAAM,MAAM,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACzC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO;YACL,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,iBAAiB,OAAO,yIAAyI;SACzK,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC;IACjC,MAAM,aAAa,GAAG,cAAc,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACxD,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,IAAI,MAAM,CAAC;IACzC,MAAM,eAAe,GAAG,IAAI,oBAAoB,MAAM,mBAAmB,CAAC,QAAQ,CAAC,MAAM,QAAQ,GAAG,CAAC;IAErG,IAAI,CAAC;QACH,MAAM,aAAa,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,kBAAkB,EAAE;YACvD,UAAU,EAAE,eAAe;YAC3B,aAAa,EAAE,IAAI;SACpB,CAAC,CAAC;QAEH,MAAM,gBAAgB,GAAG,aAGxB,CAAC;QAEF,IAAI,gBAAgB,CAAC,gBAAgB,EAAE,CAAC;YACtC,MAAM,IAAI,YAAY,CACpB,4BAA4B,gBAAgB,CAAC,gBAAgB,CAAC,IAAI,EAAE,EACpE,EAAE,IAAI,EAAE,aAAa,QAAQ,EAAE,EAAE,EACjC,UAAU,CAAC,cAAc,CAC1B,CAAC;QACJ,CAAC;QAED,MAAM,WAAW,GAAG,gBAAgB,CAAC,MAAM,EAAE,KAI5C,CAAC;QAEF,IAAI,CAAC,WAAW,EAAE,OAAO,EAAE,CAAC;YAC1B,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,WAAW,EAAE,KAAK,IAAI,yBAAyB;gBACtD,QAAQ;aACT,CAAC;QACJ,CAAC;QAED,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/B,MAAM,gBAAgB,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;YAC9D,MAAM,gBAAgB,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;QAC9D,CAAC;QAED,OAAO;YACL,OAAO,EAAE,IAAI;YACb,QAAQ;YACR,GAAG,EAAE,OAAO;YACZ,KAAK;YACL,SAAS,EAAE,aAAa;YACxB,WAAW,EAAE,WAAW,CAAC,WAAW;SACrC,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,YAAY,EAAE,CAAC;YAClC,MAAM,KAAK,CAAC;QACd,CAAC;QACD,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5E,MAAM,IAAI,YAAY,CACpB,qBAAqB,EACrB,EAAE,IAAI,EAAE,YAAY,EAAE,EACtB,UAAU,CAAC,cAAc,CAC1B,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,KAAK,UAAU,gBAAgB,CAC7B,GAAkB,EAClB,IAAyB,EACzB,MAAqB,EACrB,SAAiB;IAEjB,MAAM,GAAG,CAAC,IAAI,CAAC,wBAAwB,EAAE;QACvC,IAAI;QACJ,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,GAAG,EAAE,MAAM,CAAC,GAAG;QACf,qBAAqB,EAAE,MAAM,CAAC,OAAO;QACrC,oBAAoB,EAAE,MAAM,CAAC,OAAO;QACpC,SAAS;KACV,CAAC,CAAC;AACL,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"formInteraction.d.ts","sourceRoot":"","sources":["../../../src/commands/dom/formInteraction.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA8DzC;;;;;;;;;;GAUG;AACH,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgItE"}
1
+ {"version":3,"file":"formInteraction.d.ts","sourceRoot":"","sources":["../../../src/commands/dom/formInteraction.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqEzC;;;;;;;;;;GAUG;AACH,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAkOtE"}
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * Form interaction commands for filling inputs, clicking buttons, and submitting forms.
3
3
  */
4
- import { fillElement, clickElement } from '../../commands/dom/formFillHelpers.js';
4
+ import { fillElement, clickElement, pressKeyElement, waitForActionStability, } from '../../commands/dom/formFillHelpers.js';
5
5
  import { submitForm } from '../../commands/dom/formSubmitHelpers.js';
6
+ import { resolveElementTarget } from '../../commands/dom/helpers.js';
6
7
  import { runCommand } from '../../commands/shared/CommandRunner.js';
7
8
  import { jsonOption } from '../../commands/shared/commonOptions.js';
8
9
  import { CommandError } from '../../ui/errors/index.js';
@@ -63,20 +64,30 @@ export function registerFormInteractionCommands(program) {
63
64
  }
64
65
  domCommand
65
66
  .command('fill')
66
- .description('Fill a form field with a value (React-compatible)')
67
- .argument('<selector>', 'CSS selector for the element')
67
+ .description('Fill a form field with a value (React-compatible, waits for stability)')
68
+ .argument('<selectorOrIndex>', 'CSS selector or numeric index from query results (0-based)')
68
69
  .argument('<value>', 'Value to fill')
69
70
  .option('--index <n>', 'Element index if selector matches multiple (1-based)', parseInt)
70
71
  .option('--no-blur', 'Do not blur after filling (keeps focus on element)')
72
+ .option('--no-wait', 'Skip waiting for network stability after fill')
71
73
  .addOption(jsonOption)
72
- .action(async (selector, value, options) => {
74
+ .action(async (selectorOrIndex, value, options) => {
73
75
  await runCommand(async () => {
76
+ const target = await resolveElementTarget(selectorOrIndex, options.index);
77
+ if (!target.success) {
78
+ return {
79
+ success: false,
80
+ error: target.error ?? 'Failed to resolve element target',
81
+ exitCode: target.exitCode ?? EXIT_CODES.INVALID_ARGUMENTS,
82
+ suggestion: target.suggestion,
83
+ };
84
+ }
74
85
  return await withCDPConnection(async (cdp) => {
75
86
  const fillOptions = filterDefined({
76
- index: options.index,
87
+ index: target.index,
77
88
  blur: options.blur,
78
89
  });
79
- const result = await fillElement(cdp, selector, value, fillOptions);
90
+ const result = await fillElement(cdp, target.selector, value, fillOptions);
80
91
  if (!result.success) {
81
92
  return {
82
93
  success: false,
@@ -86,23 +97,36 @@ export function registerFormInteractionCommands(program) {
86
97
  : EXIT_CODES.INVALID_ARGUMENTS,
87
98
  };
88
99
  }
100
+ if (options.wait !== false) {
101
+ await waitForActionStability(cdp);
102
+ }
89
103
  return { success: true, data: result };
90
104
  });
91
105
  }, options, formatFillOutput);
92
106
  });
93
107
  domCommand
94
108
  .command('click')
95
- .description('Click an element')
96
- .argument('<selector>', 'CSS selector for the element')
109
+ .description('Click an element and wait for stability (accepts selector or index)')
110
+ .argument('<selectorOrIndex>', 'CSS selector or numeric index from query results (0-based)')
97
111
  .option('--index <n>', 'Element index if selector matches multiple (1-based)', parseInt)
112
+ .option('--no-wait', 'Skip waiting for network stability after click')
98
113
  .addOption(jsonOption)
99
- .action(async (selector, options) => {
114
+ .action(async (selectorOrIndex, options) => {
100
115
  await runCommand(async () => {
116
+ const target = await resolveElementTarget(selectorOrIndex, options.index);
117
+ if (!target.success) {
118
+ return {
119
+ success: false,
120
+ error: target.error ?? 'Failed to resolve element target',
121
+ exitCode: target.exitCode ?? EXIT_CODES.INVALID_ARGUMENTS,
122
+ suggestion: target.suggestion,
123
+ };
124
+ }
101
125
  return await withCDPConnection(async (cdp) => {
102
126
  const clickOptions = filterDefined({
103
- index: options.index,
127
+ index: target.index,
104
128
  });
105
- const result = await clickElement(cdp, selector, clickOptions);
129
+ const result = await clickElement(cdp, target.selector, clickOptions);
106
130
  if (!result.success) {
107
131
  return {
108
132
  success: false,
@@ -112,6 +136,9 @@ export function registerFormInteractionCommands(program) {
112
136
  : EXIT_CODES.INVALID_ARGUMENTS,
113
137
  };
114
138
  }
139
+ if (options.wait !== false) {
140
+ await waitForActionStability(cdp);
141
+ }
115
142
  return { success: true, data: result };
116
143
  });
117
144
  }, options, formatClickOutput);
@@ -119,22 +146,31 @@ export function registerFormInteractionCommands(program) {
119
146
  domCommand
120
147
  .command('submit')
121
148
  .description('Submit a form by clicking submit button and waiting for completion')
122
- .argument('<selector>', 'CSS selector for the submit button')
149
+ .argument('<selectorOrIndex>', 'CSS selector or numeric index from query results (0-based)')
123
150
  .option('--index <n>', 'Element index if selector matches multiple (1-based)', parseInt)
124
151
  .option('--wait-navigation', 'Wait for page navigation after submit')
125
152
  .option('--wait-network <ms>', 'Wait for network idle after submit (milliseconds)', '1000')
126
153
  .option('--timeout <ms>', 'Maximum time to wait (milliseconds)', '10000')
127
154
  .addOption(jsonOption)
128
- .action(async (selector, options) => {
155
+ .action(async (selectorOrIndex, options) => {
129
156
  await runCommand(async () => {
157
+ const target = await resolveElementTarget(selectorOrIndex, options.index);
158
+ if (!target.success) {
159
+ return {
160
+ success: false,
161
+ error: target.error ?? 'Failed to resolve element target',
162
+ exitCode: target.exitCode ?? EXIT_CODES.INVALID_ARGUMENTS,
163
+ suggestion: target.suggestion,
164
+ };
165
+ }
130
166
  return await withCDPConnection(async (cdp) => {
131
167
  const submitOptions = filterDefined({
132
- index: options.index,
168
+ index: target.index,
133
169
  waitNavigation: options.waitNavigation,
134
170
  waitNetwork: parseInt(options.waitNetwork, 10),
135
171
  timeout: parseInt(options.timeout, 10),
136
172
  });
137
- const result = await submitForm(cdp, selector, submitOptions);
173
+ const result = await submitForm(cdp, target.selector, submitOptions);
138
174
  if (!result.success) {
139
175
  return {
140
176
  success: false,
@@ -150,6 +186,50 @@ export function registerFormInteractionCommands(program) {
150
186
  });
151
187
  }, options, formatSubmitOutput);
152
188
  });
189
+ domCommand
190
+ .command('pressKey')
191
+ .description('Press a key on an element (for Enter-to-submit, keyboard navigation)')
192
+ .argument('<selectorOrIndex>', 'CSS selector or numeric index from query results (0-based)')
193
+ .argument('<key>', 'Key to press (Enter, Tab, Escape, Space, ArrowUp, etc.)')
194
+ .option('--index <n>', 'Element index if selector matches multiple (1-based)', parseInt)
195
+ .option('--times <n>', 'Press key multiple times (default: 1)', parseInt)
196
+ .option('--modifiers <mods>', 'Modifier keys: shift,ctrl,alt,meta (comma-separated)')
197
+ .option('--no-wait', 'Skip waiting for network stability after key press')
198
+ .addOption(jsonOption)
199
+ .action(async (selectorOrIndex, key, options) => {
200
+ await runCommand(async () => {
201
+ const target = await resolveElementTarget(selectorOrIndex, options.index);
202
+ if (!target.success) {
203
+ return {
204
+ success: false,
205
+ error: target.error ?? 'Failed to resolve element target',
206
+ exitCode: target.exitCode ?? EXIT_CODES.INVALID_ARGUMENTS,
207
+ suggestion: target.suggestion,
208
+ };
209
+ }
210
+ return await withCDPConnection(async (cdp) => {
211
+ const pressKeyOptions = filterDefined({
212
+ index: target.index,
213
+ times: options.times,
214
+ modifiers: options.modifiers,
215
+ });
216
+ const result = await pressKeyElement(cdp, target.selector, key, pressKeyOptions);
217
+ if (!result.success) {
218
+ return {
219
+ success: false,
220
+ error: result.error ?? 'Failed to press key',
221
+ exitCode: result.error?.includes('not found')
222
+ ? EXIT_CODES.RESOURCE_NOT_FOUND
223
+ : EXIT_CODES.INVALID_ARGUMENTS,
224
+ };
225
+ }
226
+ if (options.wait !== false) {
227
+ await waitForActionStability(cdp);
228
+ }
229
+ return { success: true, data: result };
230
+ });
231
+ }, options, formatPressKeyOutput);
232
+ });
153
233
  }
154
234
  /**
155
235
  * Format fill command output for human-readable display.
@@ -231,4 +311,37 @@ function formatSubmitOutput(result) {
231
311
  ]);
232
312
  return fmt.build();
233
313
  }
314
+ /**
315
+ * Format pressKey command output for human-readable display.
316
+ *
317
+ * @param result - Press key result
318
+ * @returns Formatted string
319
+ */
320
+ function formatPressKeyOutput(result) {
321
+ const fmt = new OutputFormatter();
322
+ fmt.text('✓ Key Pressed');
323
+ fmt.blank();
324
+ const details = [
325
+ ['Key', result.key ?? 'unknown'],
326
+ ['Selector', result.selector ?? 'unknown'],
327
+ ['Element Type', result.elementType ?? 'unknown'],
328
+ ];
329
+ if (result.times && result.times > 1) {
330
+ details.push(['Times', result.times.toString()]);
331
+ }
332
+ if (result.modifiers && result.modifiers > 0) {
333
+ const mods = [];
334
+ if (result.modifiers & 1)
335
+ mods.push('Shift');
336
+ if (result.modifiers & 2)
337
+ mods.push('Ctrl');
338
+ if (result.modifiers & 4)
339
+ mods.push('Alt');
340
+ if (result.modifiers & 8)
341
+ mods.push('Meta');
342
+ details.push(['Modifiers', mods.join('+')]);
343
+ }
344
+ fmt.keyValueList(details, 15);
345
+ return fmt.build();
346
+ }
234
347
  //# sourceMappingURL=formInteraction.js.map