@weavetab/mcp 2.5.0 → 2.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.
Files changed (168) hide show
  1. package/AGENTS.md +49 -49
  2. package/ARCHITECTURE.md +590 -590
  3. package/LICENSE +216 -216
  4. package/README.md +269 -269
  5. package/SECURITY.md +207 -207
  6. package/TROUBLESHOOTING.md +373 -365
  7. package/dist/anticaptcha/detector.js +395 -395
  8. package/dist/anticaptcha/solver.js +130 -130
  9. package/dist/cdp/bridge.js +73 -73
  10. package/dist/cdp/connector.js +46 -22
  11. package/dist/cdp/extension.js +34 -34
  12. package/dist/cdp/helpers.js +174 -174
  13. package/dist/cdp/pruner.js +195 -195
  14. package/dist/cdp/ref_cache.js +66 -66
  15. package/dist/cdp/security.js +10 -10
  16. package/dist/cdp/verify.js +57 -57
  17. package/dist/cdp/walker.js +563 -525
  18. package/dist/cli/plugin.js +26 -3
  19. package/dist/config/loader.d.ts +0 -3
  20. package/dist/config/loader.js +0 -9
  21. package/dist/index.js +0 -4
  22. package/dist/intelligence/intent.js +81 -81
  23. package/dist/intelligence/sessionMemory.js +2 -2
  24. package/dist/overlay/ghost.js +22 -22
  25. package/dist/overlay/injector.js +89 -89
  26. package/dist/plugin/extension_bridge.js +54 -54
  27. package/dist/plugin/interface.d.ts +7 -0
  28. package/dist/plugin/loader.js +3 -4
  29. package/dist/plugin/schema.d.ts +11 -0
  30. package/dist/plugin/schema.js +59 -0
  31. package/dist/security/guard.js +18 -18
  32. package/dist/sensors/actionability.js +167 -167
  33. package/dist/sensors/guard.js +29 -29
  34. package/dist/sensors/network.js +15 -17
  35. package/dist/server.d.ts +1 -1
  36. package/dist/server.js +173 -144
  37. package/dist/state/delta.js +6 -7
  38. package/dist/tools/automation.js +85 -85
  39. package/dist/tools/canvas.js +48 -25
  40. package/dist/tools/checkpoint.js +94 -35
  41. package/dist/tools/click.js +52 -41
  42. package/dist/tools/clipboard.js +80 -74
  43. package/dist/tools/design_extract.js +441 -441
  44. package/dist/tools/dialog.js +13 -13
  45. package/dist/tools/emulation.d.ts +2 -0
  46. package/dist/tools/emulation.js +7 -0
  47. package/dist/tools/fill.js +344 -347
  48. package/dist/tools/find.js +78 -52
  49. package/dist/tools/highlight.js +67 -67
  50. package/dist/tools/inspect.js +54 -25
  51. package/dist/tools/key.js +42 -35
  52. package/dist/tools/navigate.d.ts +1 -1
  53. package/dist/tools/navigate.js +35 -35
  54. package/dist/tools/office/parse.js +62 -18
  55. package/dist/tools/pdf.d.ts +9 -0
  56. package/dist/tools/pdf.js +7 -4
  57. package/dist/tools/performance.d.ts +11 -4
  58. package/dist/tools/performance.js +94 -42
  59. package/dist/tools/performance_test.d.ts +2 -2
  60. package/dist/tools/performance_test.js +69 -69
  61. package/dist/tools/pointer.js +116 -116
  62. package/dist/tools/read.js +99 -67
  63. package/dist/tools/recording.js +9 -0
  64. package/dist/tools/scrape.js +146 -146
  65. package/dist/tools/screenshot.d.ts +2 -0
  66. package/dist/tools/screenshot.js +46 -4
  67. package/dist/tools/scroll.d.ts +3 -1
  68. package/dist/tools/scroll.js +13 -15
  69. package/dist/tools/select.d.ts +3 -0
  70. package/dist/tools/select.js +131 -124
  71. package/dist/tools/snapshot.js +8 -6
  72. package/dist/tools/storage.d.ts +5 -1
  73. package/dist/tools/storage.js +16 -0
  74. package/dist/tools/tabs.d.ts +7 -5
  75. package/dist/tools/tabs.js +79 -26
  76. package/dist/tools/thought.js +8 -8
  77. package/dist/tools/type.js +274 -274
  78. package/dist/tools/type_secret.js +23 -23
  79. package/dist/tools/upload.js +3 -3
  80. package/dist/tools/video_compiler.js +9 -0
  81. package/dist/tools/wait.js +35 -35
  82. package/dist/version.d.ts +1 -1
  83. package/dist/version.js +1 -1
  84. package/docs/DOCUMENTATION.md +583 -585
  85. package/docs/PLUGINS.md +139 -121
  86. package/docs/TOOLS.md +3 -4
  87. package/docs/architecture/blind-injection.md +83 -83
  88. package/docs/architecture/overview.md +49 -49
  89. package/docs/architecture/security.md +36 -36
  90. package/docs/architecture/transport.md +37 -37
  91. package/docs/cli/cli-commands.md +50 -50
  92. package/docs/cli/cli-flags.md +28 -28
  93. package/docs/cli/cli-scripting.md +35 -35
  94. package/docs/configuration/config-locations.md +23 -23
  95. package/docs/configuration/configuration.md +39 -27
  96. package/docs/configuration/env-variables.md +19 -19
  97. package/docs/configuration/folders.md +62 -55
  98. package/docs/desktop-automation/electron-apps.md +225 -214
  99. package/docs/get-started/installation.md +1 -1
  100. package/docs/get-started/introduction.md +60 -60
  101. package/docs/get-started/quickstart.md +2 -2
  102. package/docs/get-started/setup-wizard.md +27 -27
  103. package/docs/intelligence/site-profiles.md +151 -151
  104. package/docs/tools/tools.md +582 -631
  105. package/docs/troubleshooting/trouble-browser.md +27 -27
  106. package/docs/troubleshooting/trouble-loop.md +31 -31
  107. package/ext-dist/background.js +415 -415
  108. package/ext-dist/content.js +2527 -2527
  109. package/ext-dist/dashboard.html +583 -583
  110. package/ext-dist/hud/hud.js +3 -3
  111. package/ext-dist/hud/index.html +20 -20
  112. package/ext-dist/manifest.json +73 -73
  113. package/ext-dist/styles.css +223 -223
  114. package/mcp.json +796 -805
  115. package/package.json +118 -118
  116. package/server.json +23 -23
  117. package/dist/cdp/walker.test.d.ts +0 -2
  118. package/dist/cdp/walker.test.js +0 -172
  119. package/dist/intelligence/sessionMemory.test.d.ts +0 -2
  120. package/dist/intelligence/sessionMemory.test.js +0 -62
  121. package/dist/intelligence/trail.test.d.ts +0 -2
  122. package/dist/intelligence/trail.test.js +0 -110
  123. package/dist/state/cache.test.d.ts +0 -2
  124. package/dist/state/cache.test.js +0 -76
  125. package/dist/state/delta.test.d.ts +0 -2
  126. package/dist/state/delta.test.js +0 -279
  127. package/dist/state/groups.d.ts +0 -15
  128. package/dist/state/groups.js +0 -80
  129. package/dist/tools/accessibility.d.ts +0 -14
  130. package/dist/tools/accessibility.js +0 -32
  131. package/dist/tools/ask.d.ts +0 -14
  132. package/dist/tools/ask.js +0 -49
  133. package/dist/tools/click_and_wait.d.ts +0 -33
  134. package/dist/tools/click_and_wait.js +0 -149
  135. package/dist/tools/drag.d.ts +0 -15
  136. package/dist/tools/drag.js +0 -29
  137. package/dist/tools/geo.d.ts +0 -19
  138. package/dist/tools/geo.js +0 -29
  139. package/dist/tools/github/analyze.d.ts +0 -4
  140. package/dist/tools/github/analyze.js +0 -113
  141. package/dist/tools/github/cache.d.ts +0 -21
  142. package/dist/tools/github/cache.js +0 -28
  143. package/dist/tools/github/index.d.ts +0 -19
  144. package/dist/tools/github/index.js +0 -40
  145. package/dist/tools/github/issues.d.ts +0 -9
  146. package/dist/tools/github/issues.js +0 -88
  147. package/dist/tools/github/pr.d.ts +0 -5
  148. package/dist/tools/github/pr.js +0 -88
  149. package/dist/tools/github/rateLimiter.d.ts +0 -4
  150. package/dist/tools/github/rateLimiter.js +0 -46
  151. package/dist/tools/github/read.d.ts +0 -34
  152. package/dist/tools/github/read.js +0 -74
  153. package/dist/tools/github/repoContext.d.ts +0 -20
  154. package/dist/tools/github/repoContext.js +0 -129
  155. package/dist/tools/hover.d.ts +0 -19
  156. package/dist/tools/hover.js +0 -57
  157. package/dist/tools/integration.test.d.ts +0 -2
  158. package/dist/tools/integration.test.js +0 -96
  159. package/dist/tools/macro_compiler.d.ts +0 -7
  160. package/dist/tools/macro_compiler.js +0 -82
  161. package/dist/tools/mouse.d.ts +0 -21
  162. package/dist/tools/mouse.js +0 -62
  163. package/dist/tools/response.d.ts +0 -129
  164. package/dist/tools/response.js +0 -5
  165. package/dist/tools/wait_for.d.ts +0 -23
  166. package/dist/tools/wait_for.js +0 -30
  167. package/dist/tools/windows.d.ts +0 -22
  168. package/dist/tools/windows.js +0 -57
@@ -35,49 +35,49 @@ function esc(s) {
35
35
  */
36
36
  async function detectFieldType(session, selector) {
37
37
  const { result } = await session.Runtime.evaluate({
38
- expression: `(function() {
39
- var el = document.querySelector(${esc(selector)});
40
- if (!el) return JSON.stringify({ exists: false, fillable: false });
41
- if (el.tagName === 'LABEL') {
42
- var forId = el.getAttribute('for');
43
- var target = forId ? document.getElementById(forId) : el.querySelector('input, textarea, select');
44
- if (target) el = target;
45
- } else if (!['input', 'select', 'textarea'].includes(el.tagName.toLowerCase()) && !el.isContentEditable) {
46
- var inner = el.querySelector('input, textarea, select');
47
- if (inner) el = inner;
48
- }
49
- var tag = el.tagName.toLowerCase();
50
- var t = (el.getAttribute('type') || 'text').toLowerCase();
51
- var role = (el.getAttribute('role') || '').toLowerCase();
52
- var isCE = el.isContentEditable || el.getAttribute('contenteditable') === 'true' || el.getAttribute('contenteditable') === '';
53
- var isCombobox = role === 'combobox' || role === 'textbox';
54
- var type;
55
- if (tag === 'select') type = 'select';
56
- else if (tag === 'input' && t === 'checkbox') type = 'checkbox';
57
- else if (tag === 'input' && t === 'radio') type = 'radio';
58
- else if (role === 'checkbox') type = 'custom-checkbox';
59
- else if (role === 'radio') type = 'custom-radio';
60
- else if (role === 'switch') type = 'custom-checkbox';
61
- else if (role === 'listbox' || role === 'option') type = 'select';
62
- else if (tag === 'textarea' || (tag === 'input' && ['text','email','tel','url','password','search','number','time','date','datetime-local','month','week'].includes(t)) || isCE || isCombobox) type = 'text';
63
- else type = 'unknown';
64
- // Check fillability — disabled or readonly fields cannot be written to.
65
- var ariaDisabled = el.getAttribute('aria-disabled');
66
- var fillable = !el.disabled && !el.readOnly && ariaDisabled !== 'true' && ariaDisabled !== '';
67
- // For checkboxes/radios, readOnly doesn't apply — but disabled does.
68
- if (type === 'checkbox' || type === 'radio' || type === 'custom-checkbox' || type === 'custom-radio') {
69
- fillable = !el.disabled && ariaDisabled !== 'true';
70
- }
71
- var secretRegex = /password|passcode|pin|passphrase|secret|token|auth.?key|access.?code|mot.?de.?passe|contrase[nñ]a|kennwort|senha/i;
72
- var isSecret = el.hasAttribute('data-wt-secret') ||
73
- (el.tagName === 'INPUT' && (
74
- t === 'password' ||
75
- secretRegex.test(el.getAttribute('autocomplete') || '') ||
76
- secretRegex.test(el.getAttribute('name') || '') ||
77
- secretRegex.test(el.getAttribute('id') || '') ||
78
- secretRegex.test(el.getAttribute('aria-label') || '')
79
- ));
80
- return JSON.stringify({ exists: true, type: type, fillable: fillable, isSecret: isSecret });
38
+ expression: `(function() {
39
+ var el = document.querySelector(${esc(selector)});
40
+ if (!el) return JSON.stringify({ exists: false, fillable: false });
41
+ if (el.tagName === 'LABEL') {
42
+ var forId = el.getAttribute('for');
43
+ var target = forId ? document.getElementById(forId) : el.querySelector('input, textarea, select, [contenteditable]:not([contenteditable="false"]), [data-slate-editor], [role="textbox"]');
44
+ if (target) el = target;
45
+ } else if (!['input', 'select', 'textarea'].includes(el.tagName.toLowerCase()) && !el.isContentEditable) {
46
+ var inner = el.querySelector('input, textarea, select, [contenteditable]:not([contenteditable="false"]), [data-slate-editor], [role="textbox"]');
47
+ if (inner) el = inner;
48
+ }
49
+ var tag = el.tagName.toLowerCase();
50
+ var t = (el.getAttribute('type') || 'text').toLowerCase();
51
+ var role = (el.getAttribute('role') || '').toLowerCase();
52
+ var isCE = el.isContentEditable || el.getAttribute('contenteditable') === 'true' || el.getAttribute('contenteditable') === '';
53
+ var isCombobox = role === 'combobox' || role === 'textbox';
54
+ var type;
55
+ if (tag === 'select') type = 'select';
56
+ else if (tag === 'input' && t === 'checkbox') type = 'checkbox';
57
+ else if (tag === 'input' && t === 'radio') type = 'radio';
58
+ else if (role === 'checkbox') type = 'custom-checkbox';
59
+ else if (role === 'radio') type = 'custom-radio';
60
+ else if (role === 'switch') type = 'custom-checkbox';
61
+ else if (role === 'listbox' || role === 'option') type = 'select';
62
+ else if (tag === 'textarea' || (tag === 'input' && ['text','email','tel','url','password','search','number','time','date','datetime-local','month','week'].includes(t)) || isCE || isCombobox) type = 'text';
63
+ else type = 'unknown';
64
+ // Check fillability — disabled or readonly fields cannot be written to.
65
+ var ariaDisabled = el.getAttribute('aria-disabled');
66
+ var fillable = !el.disabled && !el.readOnly && ariaDisabled !== 'true' && ariaDisabled !== '';
67
+ // For checkboxes/radios, readOnly doesn't apply — but disabled does.
68
+ if (type === 'checkbox' || type === 'radio' || type === 'custom-checkbox' || type === 'custom-radio') {
69
+ fillable = !el.disabled && ariaDisabled !== 'true';
70
+ }
71
+ var secretRegex = /password|passcode|pin|passphrase|secret|token|auth.?key|access.?code|mot.?de.?passe|contrase[nñ]a|kennwort|senha/i;
72
+ var isSecret = el.hasAttribute('data-wt-secret') ||
73
+ (el.tagName === 'INPUT' && (
74
+ t === 'password' ||
75
+ secretRegex.test(el.getAttribute('autocomplete') || '') ||
76
+ secretRegex.test(el.getAttribute('name') || '') ||
77
+ secretRegex.test(el.getAttribute('id') || '') ||
78
+ secretRegex.test(el.getAttribute('aria-label') || '')
79
+ ));
80
+ return JSON.stringify({ exists: true, type: type, fillable: fillable, isSecret: isSecret });
81
81
  })()`,
82
82
  returnByValue: true,
83
83
  });
@@ -89,44 +89,44 @@ async function detectFieldType(session, selector) {
89
89
  */
90
90
  async function getFieldState(session, selector, type) {
91
91
  const { result } = await session.Runtime.evaluate({
92
- expression: `(function(type) {
93
- var el = document.querySelector(${esc(selector)});
94
- if (!el) return JSON.stringify({ exists: "false" });
95
- if (el.tagName === 'LABEL') {
96
- var forId = el.getAttribute('for');
97
- var target = forId ? document.getElementById(forId) : el.querySelector('input, textarea, select');
98
- if (target) el = target;
99
- } else if (!['input', 'select', 'textarea'].includes(el.tagName.toLowerCase()) && !el.isContentEditable) {
100
- var inner = el.querySelector('input, textarea, select');
101
- if (inner) el = inner;
102
- }
103
- var secretRegex = /password|passcode|pin|passphrase|secret|token|auth.?key|access.?code|mot.?de.?passe|contrase[nñ]a|kennwort|senha/i;
104
- var isSecret = el.hasAttribute('data-wt-secret') ||
105
- (el.tagName === 'INPUT' && (
106
- (el.type || '').toLowerCase() === 'password' ||
107
- secretRegex.test(el.getAttribute('autocomplete') || '') ||
108
- secretRegex.test(el.getAttribute('name') || '') ||
109
- secretRegex.test(el.getAttribute('id') || '') ||
110
- secretRegex.test(el.getAttribute('aria-label') || '')
111
- ));
112
- var s = { exists: "true", isSecret: isSecret ? "true" : "false" };
113
- if (el.tagName === 'SELECT') {
114
- var idx = el.selectedIndex;
115
- s.selectedIndex = String(idx);
116
- s.value = isSecret ? '[REDACTED]' : el.value;
117
- s.text = idx >= 0 ? el.options[idx].text : "";
118
- } else if (el.type === 'checkbox' || el.type === 'radio' || type === 'custom-checkbox' || type === 'custom-radio') {
119
- // Native checkbox/radio uses .checked; ARIA elements use aria-checked
120
- var ariaChecked = el.getAttribute('aria-checked');
121
- var isChecked = el.checked !== undefined ? el.checked : (ariaChecked === 'true' || ariaChecked === 'mixed');
122
- s.checked = isChecked ? "true" : "false";
123
- if (ariaChecked !== null) s.ariaChecked = ariaChecked;
124
- } else if (el.isContentEditable) {
125
- s.text = isSecret ? '[REDACTED]' : (el.textContent || "");
126
- } else {
127
- s.value = isSecret ? '[REDACTED]' : el.value;
128
- }
129
- return JSON.stringify(s);
92
+ expression: `(function(type) {
93
+ var el = document.querySelector(${esc(selector)});
94
+ if (!el) return JSON.stringify({ exists: "false" });
95
+ if (el.tagName === 'LABEL') {
96
+ var forId = el.getAttribute('for');
97
+ var target = forId ? document.getElementById(forId) : el.querySelector('input, textarea, select');
98
+ if (target) el = target;
99
+ } else if (!['input', 'select', 'textarea'].includes(el.tagName.toLowerCase()) && !el.isContentEditable) {
100
+ var inner = el.querySelector('input, textarea, select');
101
+ if (inner) el = inner;
102
+ }
103
+ var secretRegex = /password|passcode|pin|passphrase|secret|token|auth.?key|access.?code|mot.?de.?passe|contrase[nñ]a|kennwort|senha/i;
104
+ var isSecret = el.hasAttribute('data-wt-secret') ||
105
+ (el.tagName === 'INPUT' && (
106
+ (el.type || '').toLowerCase() === 'password' ||
107
+ secretRegex.test(el.getAttribute('autocomplete') || '') ||
108
+ secretRegex.test(el.getAttribute('name') || '') ||
109
+ secretRegex.test(el.getAttribute('id') || '') ||
110
+ secretRegex.test(el.getAttribute('aria-label') || '')
111
+ ));
112
+ var s = { exists: "true", isSecret: isSecret ? "true" : "false" };
113
+ if (el.tagName === 'SELECT') {
114
+ var idx = el.selectedIndex;
115
+ s.selectedIndex = String(idx);
116
+ s.value = isSecret ? '[REDACTED]' : el.value;
117
+ s.text = idx >= 0 ? el.options[idx].text : "";
118
+ } else if (el.type === 'checkbox' || el.type === 'radio' || type === 'custom-checkbox' || type === 'custom-radio') {
119
+ // Native checkbox/radio uses .checked; ARIA elements use aria-checked
120
+ var ariaChecked = el.getAttribute('aria-checked');
121
+ var isChecked = el.checked !== undefined ? el.checked : (ariaChecked === 'true' || ariaChecked === 'mixed');
122
+ s.checked = isChecked ? "true" : "false";
123
+ if (ariaChecked !== null) s.ariaChecked = ariaChecked;
124
+ } else if (el.isContentEditable) {
125
+ s.text = isSecret ? '[REDACTED]' : (el.textContent || "");
126
+ } else {
127
+ s.value = isSecret ? '[REDACTED]' : el.value;
128
+ }
129
+ return JSON.stringify(s);
130
130
  })(${esc(type)})`,
131
131
  returnByValue: true,
132
132
  });
@@ -138,105 +138,103 @@ async function getFieldState(session, selector, type) {
138
138
  */
139
139
  async function applyFieldValue(session, selector, value, type) {
140
140
  const { result } = await session.Runtime.evaluate({
141
- expression: `(function(selector, value, type) {
142
- try {
143
- var el = document.querySelector(selector);
144
- if (!el) return JSON.stringify({ ok: false, msg: 'Element not found' });
145
- if (el.tagName === 'LABEL') {
146
- var forId = el.getAttribute('for');
147
- var target = forId ? document.getElementById(forId) : el.querySelector('input, textarea, select');
148
- if (target) el = target;
149
- } else if (!['input', 'select', 'textarea'].includes(el.tagName.toLowerCase()) && !el.isContentEditable) {
150
- var inner = el.querySelector('input, textarea, select');
151
- if (inner) el = inner;
152
- }
153
-
154
- if (type === 'text') {
155
- el.focus();
156
- // NOTE: el.click() intentionally removed — it generates bubbling click events
157
- // that can activate extension UI elements (wt-minimize-btn etc.) when the
158
- // field is adjacent to the WeaveTab panel. el.focus() is sufficient; the
159
- // value is written via native setter + synthetic input/change events.
160
- if (el.isContentEditable) {
161
- // contenteditable: set via textContent and also try execCommand for rich editors
162
- el.textContent = value;
163
- try { document.execCommand('selectAll', false, null); document.execCommand('insertText', false, value); } catch(e2) {}
164
- } else {
165
- var tag = el.tagName.toLowerCase();
166
- var proto = tag === 'textarea' ? window.HTMLTextAreaElement.prototype : window.HTMLInputElement.prototype;
167
- var ns = Object.getOwnPropertyDescriptor(proto, 'value')?.set;
168
- if (ns) ns.call(el, value); else el.value = value;
169
- }
170
- el.dispatchEvent(new InputEvent('input', { bubbles: true, data: value, inputType: 'insertText' }));
171
- el.dispatchEvent(new Event('change', { bubbles: true }));
172
- return JSON.stringify({ ok: true, msg: 'filled' });
173
- }
174
- if (type === 'checkbox' || type === 'custom-checkbox') {
175
- var shouldCheck = value === 'true' || value === 'yes' || value === 'checked' || value === '1' || value === 'on';
176
- var isNative = el.tagName === 'INPUT' && el.type === 'checkbox';
177
- if (isNative) {
178
- var currentlyChecked = el.checked;
179
- if (currentlyChecked !== shouldCheck) {
180
- // Use native checked setter to bypass React's synthetic event system
181
- var nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'checked')?.set;
182
- if (nativeSetter) nativeSetter.call(el, shouldCheck); else el.checked = shouldCheck;
183
- el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
184
- el.dispatchEvent(new Event('change', { bubbles: true }));
185
- el.dispatchEvent(new Event('input', { bubbles: true }));
186
- }
187
- } else {
188
- // ARIA custom checkbox (role=checkbox/switch): toggle aria-checked and click
189
- var ariaChecked = el.getAttribute('aria-checked');
190
- var isCurrentlyChecked = ariaChecked === 'true';
191
- if (isCurrentlyChecked !== shouldCheck) {
192
- el.setAttribute('aria-checked', shouldCheck ? 'true' : 'false');
193
- el.click();
194
- el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
195
- el.dispatchEvent(new Event('change', { bubbles: true }));
196
- }
197
- }
198
- return JSON.stringify({ ok: true, msg: shouldCheck ? 'checked' : 'unchecked' });
199
- }
200
- if (type === 'radio' || type === 'custom-radio') {
201
- var isNativeRadio = el.tagName === 'INPUT' && el.type === 'radio';
202
- if (isNativeRadio) {
203
- if (!el.checked) {
204
- var nativeRadioSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'checked')?.set;
205
- if (nativeRadioSetter) nativeRadioSetter.call(el, true); else el.checked = true;
206
- el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
207
- el.dispatchEvent(new Event('change', { bubbles: true }));
208
- }
209
- } else {
210
- if (el.getAttribute('aria-checked') !== 'true') {
211
- el.setAttribute('aria-checked', 'true');
212
- el.click();
213
- el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
214
- el.dispatchEvent(new Event('change', { bubbles: true }));
215
- }
216
- }
217
- return JSON.stringify({ ok: true, msg: 'selected' });
218
- }
219
- if (type === 'select') {
220
- var foundIdx = -1;
221
- for (var j = 0; j < el.options.length; j++) {
222
- var opt = el.options[j];
223
- if (opt.value === value || opt.text === value || opt.text.trim() === value.trim()) {
224
- foundIdx = j; break;
225
- }
226
- }
227
- if (foundIdx === -1) return JSON.stringify({ ok: false, msg: 'Option not found: ' + value });
228
- var nativeSelectSetter = Object.getOwnPropertyDescriptor(window.HTMLSelectElement.prototype, 'value')?.set;
229
- if (nativeSelectSetter) nativeSelectSetter.call(el, el.options[foundIdx].value);
230
- else el.selectedIndex = foundIdx;
231
- el.dispatchEvent(new Event('input', { bubbles: true }));
232
- el.dispatchEvent(new Event('change', { bubbles: true }));
233
- el.options[foundIdx].dispatchEvent(new MouseEvent('click', { bubbles: true }));
234
- return JSON.stringify({ ok: true, msg: 'selected:' + el.options[foundIdx].text });
235
- }
236
- return JSON.stringify({ ok: false, msg: 'Unsupported field type: ' + type });
237
- } catch(e) {
238
- return JSON.stringify({ ok: false, msg: e.message || String(e) });
239
- }
141
+ expression: `(function(selector, value, type) {
142
+ try {
143
+ var el = document.querySelector(selector);
144
+ if (!el) return JSON.stringify({ ok: false, msg: 'Element not found' });
145
+ if (el.tagName === 'LABEL') {
146
+ var forId = el.getAttribute('for');
147
+ var target = forId ? document.getElementById(forId) : el.querySelector('input, textarea, select, [contenteditable]:not([contenteditable="false"]), [data-slate-editor], [role="textbox"]');
148
+ if (target) el = target;
149
+ } else if (!['input', 'select', 'textarea'].includes(el.tagName.toLowerCase()) && !el.isContentEditable) {
150
+ var inner = el.querySelector('input, textarea, select, [contenteditable]:not([contenteditable="false"]), [data-slate-editor], [role="textbox"]');
151
+ if (inner) el = inner;
152
+ }
153
+
154
+ if (type === 'text') {
155
+ el.focus();
156
+ if (el.isContentEditable || el.getAttribute('contenteditable') === 'true' || el.hasAttribute('data-slate-editor')) {
157
+ // contenteditable/Slate: use execCommand to preserve React/Slate virtual DOM tree
158
+ var isSlate = el.hasAttribute('data-slate-editor') || !!el.closest('[data-slate-editor]');
159
+ if (!isSlate) {
160
+ el.textContent = value;
161
+ }
162
+ try { document.execCommand('selectAll', false, null); document.execCommand('insertText', false, value); } catch(e2) {}
163
+ } else {
164
+ var tag = el.tagName.toLowerCase();
165
+ var proto = tag === 'textarea' ? window.HTMLTextAreaElement.prototype : window.HTMLInputElement.prototype;
166
+ var ns = Object.getOwnPropertyDescriptor(proto, 'value')?.set;
167
+ if (ns) ns.call(el, value); else el.value = value;
168
+ }
169
+ el.dispatchEvent(new InputEvent('input', { bubbles: true, data: value, inputType: 'insertText' }));
170
+ el.dispatchEvent(new Event('change', { bubbles: true }));
171
+ return JSON.stringify({ ok: true, msg: 'filled' });
172
+ }
173
+ if (type === 'checkbox' || type === 'custom-checkbox') {
174
+ var shouldCheck = value === 'true' || value === 'yes' || value === 'checked' || value === '1' || value === 'on';
175
+ var isNative = el.tagName === 'INPUT' && el.type === 'checkbox';
176
+ if (isNative) {
177
+ // A real click both toggles the box and fires trusted click/input/change
178
+ // events, keeping React/Vue controlled components in sync. Never set
179
+ // el.checked and then also dispatch a synthetic click: dispatching a
180
+ // click event runs the checkbox's default activation behavior and
181
+ // toggles it straight back, leaving the box unchanged.
182
+ if (el.checked !== shouldCheck) {
183
+ el.click();
184
+ }
185
+ } else {
186
+ // ARIA custom checkbox (role=checkbox/switch): toggle aria-checked and click
187
+ var ariaChecked = el.getAttribute('aria-checked');
188
+ var isCurrentlyChecked = ariaChecked === 'true';
189
+ if (isCurrentlyChecked !== shouldCheck) {
190
+ el.setAttribute('aria-checked', shouldCheck ? 'true' : 'false');
191
+ el.click();
192
+ el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
193
+ el.dispatchEvent(new Event('change', { bubbles: true }));
194
+ }
195
+ }
196
+ return JSON.stringify({ ok: true, msg: shouldCheck ? 'checked' : 'unchecked' });
197
+ }
198
+ if (type === 'radio' || type === 'custom-radio') {
199
+ var isNativeRadio = el.tagName === 'INPUT' && el.type === 'radio';
200
+ if (isNativeRadio) {
201
+ if (!el.checked) {
202
+ var nativeRadioSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'checked')?.set;
203
+ if (nativeRadioSetter) nativeRadioSetter.call(el, true); else el.checked = true;
204
+ el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
205
+ el.dispatchEvent(new Event('change', { bubbles: true }));
206
+ }
207
+ } else {
208
+ if (el.getAttribute('aria-checked') !== 'true') {
209
+ el.setAttribute('aria-checked', 'true');
210
+ el.click();
211
+ el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
212
+ el.dispatchEvent(new Event('change', { bubbles: true }));
213
+ }
214
+ }
215
+ return JSON.stringify({ ok: true, msg: 'selected' });
216
+ }
217
+ if (type === 'select') {
218
+ var foundIdx = -1;
219
+ for (var j = 0; j < el.options.length; j++) {
220
+ var opt = el.options[j];
221
+ if (opt.value === value || opt.text === value || opt.text.trim() === value.trim()) {
222
+ foundIdx = j; break;
223
+ }
224
+ }
225
+ if (foundIdx === -1) return JSON.stringify({ ok: false, msg: 'Option not found: ' + value });
226
+ var nativeSelectSetter = Object.getOwnPropertyDescriptor(window.HTMLSelectElement.prototype, 'value')?.set;
227
+ if (nativeSelectSetter) nativeSelectSetter.call(el, el.options[foundIdx].value);
228
+ else el.selectedIndex = foundIdx;
229
+ el.dispatchEvent(new Event('input', { bubbles: true }));
230
+ el.dispatchEvent(new Event('change', { bubbles: true }));
231
+ el.options[foundIdx].dispatchEvent(new MouseEvent('click', { bubbles: true }));
232
+ return JSON.stringify({ ok: true, msg: 'selected:' + el.options[foundIdx].text });
233
+ }
234
+ return JSON.stringify({ ok: false, msg: 'Unsupported field type: ' + type });
235
+ } catch(e) {
236
+ return JSON.stringify({ ok: false, msg: e.message || String(e) });
237
+ }
240
238
  })(${esc(selector)}, ${esc(value)}, ${esc(type)})`,
241
239
  returnByValue: true,
242
240
  });
@@ -436,69 +434,70 @@ export async function browserFill(session, options, config) {
436
434
  if (options.scan || options.inputs || (!options.fields || options.fields.length === 0)) {
437
435
  try {
438
436
  const scanRes = await session.Runtime.evaluate({
439
- expression: `(function() {
440
- const all = Array.from(document.querySelectorAll('input:not([type="hidden"]):not([type="submit"]):not([type="button"]):not([type="image"]), textarea, select, [role="textbox"], [role="searchbox"], [role="combobox"], [role="checkbox"], [role="radio"], [role="switch"], [contenteditable="true"]'));
441
- const activeEl = document.activeElement;
442
-
443
- return JSON.stringify(all.map((el, i) => {
444
- const rect = el.getBoundingClientRect();
445
- const isVisible = rect.width > 0 && rect.height > 0 && rect.top < window.innerHeight && rect.bottom > 0;
446
- const tag = el.tagName.toLowerCase();
447
- const type = (el.getAttribute('type') || (tag === 'textarea' ? 'textarea' : tag === 'select' ? 'select' : 'text')).toLowerCase();
448
- const role = el.getAttribute('role') || '';
449
-
450
- let label = el.getAttribute('aria-label') || el.getAttribute('placeholder') || el.getAttribute('title') || '';
451
- if (!label && el.id) {
452
- const lbl = document.querySelector('label[for="' + el.id + '"]');
453
- if (lbl) label = lbl.innerText.trim();
454
- }
455
- if (!label && el.closest('label')) {
456
- label = el.closest('label').innerText.trim();
457
- }
458
- if (!label) {
459
- label = el.getAttribute('name') || '';
460
- }
461
-
462
- var secretRegex = /password|passcode|pin|passphrase|secret|token|auth.?key|access.?code|mot.?de.?passe|contrase[nñ]a|kennwort|senha/i;
463
- var isSecret = el.hasAttribute('data-wt-secret') ||
464
- (el.tagName === 'INPUT' && (
465
- type === 'password' ||
466
- secretRegex.test(el.getAttribute('autocomplete') || '') ||
467
- secretRegex.test(el.getAttribute('name') || '') ||
468
- secretRegex.test(el.getAttribute('id') || '') ||
469
- secretRegex.test(el.getAttribute('aria-label') || '')
470
- ));
471
-
472
- let val = '';
473
- if (isSecret) {
474
- val = '[REDACTED]';
475
- } else if (type === 'checkbox' || type === 'radio' || role === 'checkbox' || role === 'radio' || role === 'switch') {
476
- val = el.checked !== undefined ? String(el.checked) : (el.getAttribute('aria-checked') || 'false');
477
- } else if (tag === 'select') {
478
- val = el.options && el.selectedIndex >= 0 && el.options[el.selectedIndex] ? el.options[el.selectedIndex].text : (el.value || '');
479
- } else if (el.isContentEditable) {
480
- val = (el.textContent || '').trim().slice(0, 40);
481
- } else {
482
- val = el.value !== undefined ? String(el.value).slice(0, 40) : '';
483
- }
484
-
485
- const wtId = el.getAttribute('data-wt-id');
486
- const ref = wtId ? ('w:' + wtId) : (el.id ? ('#' + el.id) : (el.name ? ('[name="' + el.name + '"]') : ('input:' + i)));
487
- const isFocused = el === activeEl || (activeEl && el.contains(activeEl));
488
-
489
- return {
490
- ref,
491
- tag,
492
- type: role || type,
493
- name: el.getAttribute('name') || undefined,
494
- label: label.slice(0, 50) || undefined,
495
- placeholder: el.getAttribute('placeholder') || undefined,
496
- value: val,
497
- focused: isFocused ? true : undefined,
498
- disabled: (el.disabled || el.getAttribute('aria-disabled') === 'true') ? true : undefined,
499
- visible: isVisible
500
- };
501
- }));
437
+ expression: `(function() {
438
+ const all = Array.from(document.querySelectorAll('input:not([type="hidden"]):not([type="submit"]):not([type="button"]):not([type="image"]), textarea, select, [role="textbox"], [role="searchbox"], [role="combobox"], [role="checkbox"], [role="radio"], [role="switch"], [contenteditable]:not([contenteditable="false"]), [data-slate-editor], [data-lexical-editor], .ProseMirror'));
439
+ const activeEl = document.activeElement;
440
+
441
+ return JSON.stringify(all.map((el, i) => {
442
+ const rect = el.getBoundingClientRect();
443
+ const isVisible = rect.width > 0 && rect.height > 0 && rect.top < window.innerHeight && rect.bottom > 0;
444
+ const tag = el.tagName.toLowerCase();
445
+ const type = (el.getAttribute('type') || (tag === 'textarea' ? 'textarea' : tag === 'select' ? 'select' : 'text')).toLowerCase();
446
+ const role = el.getAttribute('role') || '';
447
+
448
+ let label = el.getAttribute('aria-label') || el.getAttribute('placeholder') || el.getAttribute('title') || '';
449
+ if (!label && el.id) {
450
+ const lbl = document.querySelector('label[for="' + el.id + '"]');
451
+ if (lbl) label = lbl.innerText.trim();
452
+ }
453
+ if (!label && el.closest('label')) {
454
+ label = el.closest('label').innerText.trim();
455
+ }
456
+ if (!label) {
457
+ label = el.getAttribute('name') || '';
458
+ }
459
+
460
+ var isCheckable = type === 'checkbox' || type === 'radio' || role === 'checkbox' || role === 'radio' || role === 'switch';
461
+ var secretRegex = /password|passcode|pin|passphrase|secret|token|auth.?key|access.?code|mot.?de.?passe|contrase[nñ]a|kennwort|senha/i;
462
+ var isSecret = !isCheckable && (el.hasAttribute('data-wt-secret') ||
463
+ (el.tagName === 'INPUT' && (
464
+ type === 'password' ||
465
+ secretRegex.test(el.getAttribute('autocomplete') || '') ||
466
+ secretRegex.test(el.getAttribute('name') || '') ||
467
+ secretRegex.test(el.getAttribute('id') || '') ||
468
+ secretRegex.test(el.getAttribute('aria-label') || '')
469
+ )));
470
+
471
+ let val = '';
472
+ if (isCheckable) {
473
+ val = el.checked !== undefined ? String(el.checked) : (el.getAttribute('aria-checked') || 'false');
474
+ } else if (isSecret) {
475
+ val = '[REDACTED]';
476
+ } else if (tag === 'select') {
477
+ val = el.options && el.selectedIndex >= 0 && el.options[el.selectedIndex] ? el.options[el.selectedIndex].text : (el.value || '');
478
+ } else if (el.isContentEditable) {
479
+ val = (el.textContent || '').trim().slice(0, 40);
480
+ } else {
481
+ val = el.value !== undefined ? String(el.value).slice(0, 40) : '';
482
+ }
483
+
484
+ const wtId = el.getAttribute('data-wt-id');
485
+ const ref = wtId ? ('w:' + wtId) : (el.id ? ('#' + el.id) : (el.name ? ('[name="' + el.name + '"]') : ('input:' + i)));
486
+ const isFocused = el === activeEl || (activeEl && el.contains(activeEl));
487
+
488
+ return {
489
+ ref,
490
+ tag,
491
+ type: role || type,
492
+ name: el.getAttribute('name') || undefined,
493
+ label: label.slice(0, 50) || undefined,
494
+ placeholder: el.getAttribute('placeholder') || undefined,
495
+ value: val,
496
+ focused: isFocused ? true : undefined,
497
+ disabled: (el.disabled || el.getAttribute('aria-disabled') === 'true') ? true : undefined,
498
+ visible: isVisible
499
+ };
500
+ }));
502
501
  })()`,
503
502
  returnByValue: true,
504
503
  });
@@ -506,7 +505,7 @@ export async function browserFill(session, options, config) {
506
505
  return {
507
506
  success: true,
508
507
  total_fields: inputsList.length,
509
- succeeded: inputsList.length,
508
+ succeeded: 0,
510
509
  failed: 0,
511
510
  submitted: false,
512
511
  inputs: inputsList,
@@ -564,100 +563,98 @@ export async function browserFill(session, options, config) {
564
563
  if (options.blast_mode) {
565
564
  // blast_mode: single CDP round-trip filling all fields at once.
566
565
  // Handles text, checkbox, radio, select, contenteditable, and ARIA variants.
567
- const blastExpression = `(function(fields) {
568
- var results = [];
569
- var secretRegex = /password|passcode|pin|passphrase|secret|token|auth.?key|access.?code|mot.?de.?passe|contrase[nñ]a|kennwort|senha/i;
570
- fields.forEach(function(f) {
571
- var el = document.querySelector(f.selector);
572
- if (!el) { results.push({ ref: f.ref, ok: false, msg: 'not found', type: 'unknown', isSecret: false }); return; }
573
- var tag = el.tagName.toLowerCase();
574
- var inputType = (el.getAttribute('type') || '').toLowerCase();
575
- var role = (el.getAttribute('role') || '').toLowerCase();
576
- var isSecret = el.hasAttribute('data-wt-secret') ||
577
- (tag === 'input' && (
578
- inputType === 'password' ||
579
- secretRegex.test(el.getAttribute('autocomplete') || '') ||
580
- secretRegex.test(el.getAttribute('name') || '') ||
581
- secretRegex.test(el.getAttribute('id') || '') ||
582
- secretRegex.test(el.getAttribute('aria-label') || '')
583
- ));
584
- if (isSecret) {
585
- try { el.setAttribute('data-wt-secret', 'true'); } catch(_) {}
586
- }
587
- try {
588
- if (tag === 'select') {
589
- // Select element
590
- var foundIdx = -1;
591
- for (var j = 0; j < el.options.length; j++) {
592
- var opt = el.options[j];
593
- if (opt.value === f.value || opt.text === f.value || opt.text.trim() === f.value.trim()) { foundIdx = j; break; }
594
- }
595
- if (foundIdx === -1) { results.push({ ref: f.ref, ok: false, msg: 'Option not found: ' + f.value, type: 'select', isSecret: isSecret }); return; }
596
- var nativeSS = Object.getOwnPropertyDescriptor(window.HTMLSelectElement.prototype, 'value')?.set;
597
- if (nativeSS) nativeSS.call(el, el.options[foundIdx].value); else el.selectedIndex = foundIdx;
598
- el.dispatchEvent(new Event('input', { bubbles: true }));
599
- el.dispatchEvent(new Event('change', { bubbles: true }));
600
- results.push({ ref: f.ref, ok: true, msg: 'selected:' + el.options[foundIdx].text, type: 'select', isSecret: isSecret });
601
- } else if (tag === 'input' && inputType === 'checkbox') {
602
- // Native checkbox
603
- var want = f.value === 'true' || f.value === 'yes' || f.value === 'checked' || f.value === '1' || f.value === 'on';
604
- if (el.checked !== want) {
605
- var ns = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'checked')?.set;
606
- if (ns) ns.call(el, want); else el.checked = want;
607
- el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
608
- el.dispatchEvent(new Event('change', { bubbles: true }));
609
- }
610
- results.push({ ref: f.ref, ok: true, msg: want ? 'checked' : 'unchecked', type: 'checkbox', isSecret: isSecret });
611
- } else if (tag === 'input' && inputType === 'radio') {
612
- // Native radio
613
- if (!el.checked) {
614
- var nsr = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'checked')?.set;
615
- if (nsr) nsr.call(el, true); else el.checked = true;
616
- el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
617
- el.dispatchEvent(new Event('change', { bubbles: true }));
618
- }
619
- results.push({ ref: f.ref, ok: true, msg: 'selected', type: 'radio', isSecret: isSecret });
620
- } else if (role === 'checkbox' || role === 'switch') {
621
- // ARIA custom checkbox
622
- var wantAria = f.value === 'true' || f.value === 'yes' || f.value === 'checked' || f.value === '1' || f.value === 'on';
623
- var curAria = el.getAttribute('aria-checked') === 'true';
624
- if (curAria !== wantAria) {
625
- el.setAttribute('aria-checked', wantAria ? 'true' : 'false');
626
- el.click();
627
- el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
628
- el.dispatchEvent(new Event('change', { bubbles: true }));
629
- }
630
- results.push({ ref: f.ref, ok: true, msg: wantAria ? 'checked' : 'unchecked', type: 'custom-checkbox', isSecret: isSecret });
631
- } else if (role === 'radio') {
632
- // ARIA custom radio
633
- if (el.getAttribute('aria-checked') !== 'true') {
634
- el.setAttribute('aria-checked', 'true');
635
- el.click();
636
- el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
637
- el.dispatchEvent(new Event('change', { bubbles: true }));
638
- }
639
- results.push({ ref: f.ref, ok: true, msg: 'selected', type: 'custom-radio', isSecret: isSecret });
640
- } else if (el.isContentEditable || el.getAttribute('contenteditable') === 'true' || el.getAttribute('contenteditable') === '') {
641
- // contenteditable
642
- el.focus();
643
- el.textContent = f.value;
644
- try { document.execCommand('selectAll', false, null); document.execCommand('insertText', false, f.value); } catch(e2) {}
645
- el.dispatchEvent(new InputEvent('input', { bubbles: true, data: f.value, inputType: 'insertText' }));
646
- el.dispatchEvent(new Event('change', { bubbles: true }));
647
- results.push({ ref: f.ref, ok: true, msg: 'filled', type: 'contenteditable', isSecret: isSecret });
648
- } else {
649
- // text / textarea / other inputs
650
- el.focus();
651
- var proto2 = tag === 'textarea' ? window.HTMLTextAreaElement.prototype : window.HTMLInputElement.prototype;
652
- var ns2 = Object.getOwnPropertyDescriptor(proto2, 'value')?.set;
653
- if (ns2) ns2.call(el, f.value); else el.value = f.value;
654
- el.dispatchEvent(new InputEvent('input', { bubbles: true, data: f.value, inputType: 'insertText' }));
655
- el.dispatchEvent(new Event('change', { bubbles: true }));
656
- results.push({ ref: f.ref, ok: true, msg: 'filled', type: tag, isSecret: isSecret });
657
- }
658
- } catch(e) { results.push({ ref: f.ref, ok: false, msg: e.message || String(e), type: tag, isSecret: isSecret }); }
659
- });
660
- return JSON.stringify(results);
566
+ const blastExpression = `(function(fields) {
567
+ var results = [];
568
+ var secretRegex = /password|passcode|pin|passphrase|secret|token|auth.?key|access.?code|mot.?de.?passe|contrase[nñ]a|kennwort|senha/i;
569
+ fields.forEach(function(f) {
570
+ var el = document.querySelector(f.selector);
571
+ if (!el) { results.push({ ref: f.ref, ok: false, msg: 'not found', type: 'unknown', isSecret: false }); return; }
572
+ var tag = el.tagName.toLowerCase();
573
+ var inputType = (el.getAttribute('type') || '').toLowerCase();
574
+ var role = (el.getAttribute('role') || '').toLowerCase();
575
+ var isSecret = el.hasAttribute('data-wt-secret') ||
576
+ (tag === 'input' && (
577
+ inputType === 'password' ||
578
+ secretRegex.test(el.getAttribute('autocomplete') || '') ||
579
+ secretRegex.test(el.getAttribute('name') || '') ||
580
+ secretRegex.test(el.getAttribute('id') || '') ||
581
+ secretRegex.test(el.getAttribute('aria-label') || '')
582
+ ));
583
+ if (isSecret) {
584
+ try { el.setAttribute('data-wt-secret', 'true'); } catch(_) {}
585
+ }
586
+ try {
587
+ if (tag === 'select') {
588
+ // Select element
589
+ var foundIdx = -1;
590
+ for (var j = 0; j < el.options.length; j++) {
591
+ var opt = el.options[j];
592
+ if (opt.value === f.value || opt.text === f.value || opt.text.trim() === f.value.trim()) { foundIdx = j; break; }
593
+ }
594
+ if (foundIdx === -1) { results.push({ ref: f.ref, ok: false, msg: 'Option not found: ' + f.value, type: 'select', isSecret: isSecret }); return; }
595
+ var nativeSS = Object.getOwnPropertyDescriptor(window.HTMLSelectElement.prototype, 'value')?.set;
596
+ if (nativeSS) nativeSS.call(el, el.options[foundIdx].value); else el.selectedIndex = foundIdx;
597
+ el.dispatchEvent(new Event('input', { bubbles: true }));
598
+ el.dispatchEvent(new Event('change', { bubbles: true }));
599
+ results.push({ ref: f.ref, ok: true, msg: 'selected:' + el.options[foundIdx].text, type: 'select', isSecret: isSecret });
600
+ } else if (tag === 'input' && inputType === 'checkbox') {
601
+ // Native checkbox — a real click toggles once and fires click/input/change
602
+ // (dispatching a synthetic click after setting el.checked toggles it back).
603
+ var want = f.value === 'true' || f.value === 'yes' || f.value === 'checked' || f.value === '1' || f.value === 'on';
604
+ if (el.checked !== want) {
605
+ el.click();
606
+ }
607
+ results.push({ ref: f.ref, ok: true, msg: want ? 'checked' : 'unchecked', type: 'checkbox', isSecret: isSecret });
608
+ } else if (tag === 'input' && inputType === 'radio') {
609
+ // Native radio
610
+ if (!el.checked) {
611
+ var nsr = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'checked')?.set;
612
+ if (nsr) nsr.call(el, true); else el.checked = true;
613
+ el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
614
+ el.dispatchEvent(new Event('change', { bubbles: true }));
615
+ }
616
+ results.push({ ref: f.ref, ok: true, msg: 'selected', type: 'radio', isSecret: isSecret });
617
+ } else if (role === 'checkbox' || role === 'switch') {
618
+ // ARIA custom checkbox
619
+ var wantAria = f.value === 'true' || f.value === 'yes' || f.value === 'checked' || f.value === '1' || f.value === 'on';
620
+ var curAria = el.getAttribute('aria-checked') === 'true';
621
+ if (curAria !== wantAria) {
622
+ el.setAttribute('aria-checked', wantAria ? 'true' : 'false');
623
+ el.click();
624
+ el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
625
+ el.dispatchEvent(new Event('change', { bubbles: true }));
626
+ }
627
+ results.push({ ref: f.ref, ok: true, msg: wantAria ? 'checked' : 'unchecked', type: 'custom-checkbox', isSecret: isSecret });
628
+ } else if (role === 'radio') {
629
+ // ARIA custom radio
630
+ if (el.getAttribute('aria-checked') !== 'true') {
631
+ el.setAttribute('aria-checked', 'true');
632
+ el.click();
633
+ el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
634
+ el.dispatchEvent(new Event('change', { bubbles: true }));
635
+ }
636
+ results.push({ ref: f.ref, ok: true, msg: 'selected', type: 'custom-radio', isSecret: isSecret });
637
+ } else if (el.isContentEditable || el.getAttribute('contenteditable') === 'true' || el.getAttribute('contenteditable') === '') {
638
+ // contenteditable
639
+ el.focus();
640
+ el.textContent = f.value;
641
+ try { document.execCommand('selectAll', false, null); document.execCommand('insertText', false, f.value); } catch(e2) {}
642
+ el.dispatchEvent(new InputEvent('input', { bubbles: true, data: f.value, inputType: 'insertText' }));
643
+ el.dispatchEvent(new Event('change', { bubbles: true }));
644
+ results.push({ ref: f.ref, ok: true, msg: 'filled', type: 'contenteditable', isSecret: isSecret });
645
+ } else {
646
+ // text / textarea / other inputs
647
+ el.focus();
648
+ var proto2 = tag === 'textarea' ? window.HTMLTextAreaElement.prototype : window.HTMLInputElement.prototype;
649
+ var ns2 = Object.getOwnPropertyDescriptor(proto2, 'value')?.set;
650
+ if (ns2) ns2.call(el, f.value); else el.value = f.value;
651
+ el.dispatchEvent(new InputEvent('input', { bubbles: true, data: f.value, inputType: 'insertText' }));
652
+ el.dispatchEvent(new Event('change', { bubbles: true }));
653
+ results.push({ ref: f.ref, ok: true, msg: 'filled', type: tag, isSecret: isSecret });
654
+ }
655
+ } catch(e) { results.push({ ref: f.ref, ok: false, msg: e.message || String(e), type: tag, isSecret: isSecret }); }
656
+ });
657
+ return JSON.stringify(results);
661
658
  })(${JSON.stringify(options.fields.map(f => ({ ref: f.ref || f.selector || f.name || f.label || 'field', selector: toSelector(f), value: f.value })))})`;
662
659
  const { result } = await session.Runtime.evaluate({ expression: blastExpression, returnByValue: true });
663
660
  const blastResults = JSON.parse(result?.value || '[]');
@@ -793,15 +790,15 @@ export async function browserFill(session, options, config) {
793
790
  if (options.submit && failed === 0) {
794
791
  const subSelector = toSelector(options.submit);
795
792
  const { result: subResult } = await session.Runtime.evaluate({
796
- expression: `(function(sel) {
797
- try {
798
- var el = document.querySelector(sel);
799
- if (!el) return 'not found';
800
- el.focus();
801
- el.click();
802
- el.dispatchEvent(new Event('click', { bubbles: true }));
803
- return 'clicked';
804
- } catch(e) { return e.message; }
793
+ expression: `(function(sel) {
794
+ try {
795
+ var el = document.querySelector(sel);
796
+ if (!el) return 'not found';
797
+ el.focus();
798
+ el.click();
799
+ el.dispatchEvent(new Event('click', { bubbles: true }));
800
+ return 'clicked';
801
+ } catch(e) { return e.message; }
805
802
  })(${esc(subSelector)})`,
806
803
  returnByValue: true,
807
804
  });