@weavetab/mcp 2.5.0-beta.5 → 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 (160) hide show
  1. package/AGENTS.md +49 -49
  2. package/ARCHITECTURE.md +590 -590
  3. package/README.md +216 -100
  4. package/SECURITY.md +207 -207
  5. package/TROUBLESHOOTING.md +373 -365
  6. package/dist/anticaptcha/detector.js +395 -395
  7. package/dist/anticaptcha/solver.js +130 -130
  8. package/dist/cdp/bridge.js +73 -73
  9. package/dist/cdp/connector.js +46 -22
  10. package/dist/cdp/extension.js +34 -34
  11. package/dist/cdp/helpers.js +174 -174
  12. package/dist/cdp/pruner.js +195 -195
  13. package/dist/cdp/ref_cache.js +66 -66
  14. package/dist/cdp/security.js +10 -10
  15. package/dist/cdp/verify.js +57 -57
  16. package/dist/cdp/walker.js +563 -525
  17. package/dist/cli/plugin.js +26 -3
  18. package/dist/config/loader.d.ts +0 -3
  19. package/dist/config/loader.js +0 -9
  20. package/dist/index.js +0 -4
  21. package/dist/intelligence/intent.js +81 -81
  22. package/dist/intelligence/sessionMemory.js +2 -2
  23. package/dist/overlay/ghost.js +22 -22
  24. package/dist/overlay/injector.js +89 -89
  25. package/dist/plugin/extension_bridge.js +54 -54
  26. package/dist/plugin/interface.d.ts +7 -0
  27. package/dist/plugin/loader.js +3 -4
  28. package/dist/plugin/schema.d.ts +11 -0
  29. package/dist/plugin/schema.js +59 -0
  30. package/dist/security/guard.js +18 -18
  31. package/dist/sensors/actionability.js +167 -167
  32. package/dist/sensors/guard.js +29 -29
  33. package/dist/sensors/network.js +15 -17
  34. package/dist/server.d.ts +1 -1
  35. package/dist/server.js +173 -144
  36. package/dist/state/delta.js +6 -7
  37. package/dist/tools/automation.js +85 -85
  38. package/dist/tools/canvas.js +48 -25
  39. package/dist/tools/checkpoint.js +94 -35
  40. package/dist/tools/click.js +52 -41
  41. package/dist/tools/clipboard.js +80 -74
  42. package/dist/tools/design_extract.js +441 -441
  43. package/dist/tools/dialog.js +13 -13
  44. package/dist/tools/emulation.d.ts +2 -0
  45. package/dist/tools/emulation.js +7 -0
  46. package/dist/tools/fill.js +344 -347
  47. package/dist/tools/find.js +78 -52
  48. package/dist/tools/highlight.js +67 -67
  49. package/dist/tools/inspect.js +54 -25
  50. package/dist/tools/key.js +42 -35
  51. package/dist/tools/navigate.d.ts +1 -1
  52. package/dist/tools/navigate.js +35 -35
  53. package/dist/tools/office/parse.js +62 -18
  54. package/dist/tools/pdf.d.ts +9 -0
  55. package/dist/tools/pdf.js +7 -4
  56. package/dist/tools/performance.d.ts +11 -4
  57. package/dist/tools/performance.js +94 -42
  58. package/dist/tools/performance_test.d.ts +2 -2
  59. package/dist/tools/performance_test.js +69 -69
  60. package/dist/tools/pointer.js +116 -116
  61. package/dist/tools/read.js +99 -67
  62. package/dist/tools/recording.js +9 -0
  63. package/dist/tools/scrape.js +146 -146
  64. package/dist/tools/screenshot.d.ts +2 -0
  65. package/dist/tools/screenshot.js +46 -4
  66. package/dist/tools/scroll.d.ts +3 -1
  67. package/dist/tools/scroll.js +13 -15
  68. package/dist/tools/select.d.ts +3 -0
  69. package/dist/tools/select.js +131 -124
  70. package/dist/tools/snapshot.js +8 -6
  71. package/dist/tools/storage.d.ts +5 -1
  72. package/dist/tools/storage.js +16 -0
  73. package/dist/tools/tabs.d.ts +7 -5
  74. package/dist/tools/tabs.js +79 -26
  75. package/dist/tools/thought.js +8 -8
  76. package/dist/tools/type.js +274 -274
  77. package/dist/tools/type_secret.js +23 -23
  78. package/dist/tools/upload.js +3 -3
  79. package/dist/tools/video_compiler.js +9 -0
  80. package/dist/tools/wait.js +35 -35
  81. package/dist/version.d.ts +1 -1
  82. package/dist/version.js +1 -1
  83. package/docs/DOCUMENTATION.md +583 -585
  84. package/docs/PLUGINS.md +139 -121
  85. package/docs/TOOLS.md +3 -4
  86. package/docs/architecture/blind-injection.md +83 -83
  87. package/docs/architecture/overview.md +49 -49
  88. package/docs/configuration/configuration.md +39 -27
  89. package/docs/configuration/folders.md +62 -55
  90. package/docs/desktop-automation/electron-apps.md +225 -214
  91. package/docs/get-started/installation.md +1 -1
  92. package/docs/get-started/introduction.md +60 -60
  93. package/docs/get-started/quickstart.md +2 -2
  94. package/docs/get-started/setup-wizard.md +1 -1
  95. package/docs/intelligence/site-profiles.md +151 -151
  96. package/docs/tools/tools.md +582 -631
  97. package/docs/troubleshooting/trouble-loop.md +31 -31
  98. package/ext-dist/background.js +415 -415
  99. package/ext-dist/content.js +2527 -2527
  100. package/ext-dist/dashboard.html +583 -583
  101. package/ext-dist/hud/hud.js +3 -3
  102. package/ext-dist/hud/index.html +20 -20
  103. package/ext-dist/manifest.json +73 -73
  104. package/ext-dist/styles.css +223 -223
  105. package/mcp.json +796 -805
  106. package/package.json +8 -8
  107. package/server.json +23 -23
  108. package/smithery.yaml +7 -11
  109. package/dist/cdp/walker.test.d.ts +0 -2
  110. package/dist/cdp/walker.test.js +0 -172
  111. package/dist/intelligence/sessionMemory.test.d.ts +0 -2
  112. package/dist/intelligence/sessionMemory.test.js +0 -62
  113. package/dist/intelligence/trail.test.d.ts +0 -2
  114. package/dist/intelligence/trail.test.js +0 -110
  115. package/dist/state/cache.test.d.ts +0 -2
  116. package/dist/state/cache.test.js +0 -76
  117. package/dist/state/delta.test.d.ts +0 -2
  118. package/dist/state/delta.test.js +0 -279
  119. package/dist/state/groups.d.ts +0 -15
  120. package/dist/state/groups.js +0 -80
  121. package/dist/tools/accessibility.d.ts +0 -14
  122. package/dist/tools/accessibility.js +0 -32
  123. package/dist/tools/ask.d.ts +0 -14
  124. package/dist/tools/ask.js +0 -49
  125. package/dist/tools/click_and_wait.d.ts +0 -33
  126. package/dist/tools/click_and_wait.js +0 -149
  127. package/dist/tools/drag.d.ts +0 -15
  128. package/dist/tools/drag.js +0 -29
  129. package/dist/tools/geo.d.ts +0 -19
  130. package/dist/tools/geo.js +0 -29
  131. package/dist/tools/github/analyze.d.ts +0 -4
  132. package/dist/tools/github/analyze.js +0 -113
  133. package/dist/tools/github/cache.d.ts +0 -21
  134. package/dist/tools/github/cache.js +0 -28
  135. package/dist/tools/github/index.d.ts +0 -19
  136. package/dist/tools/github/index.js +0 -40
  137. package/dist/tools/github/issues.d.ts +0 -9
  138. package/dist/tools/github/issues.js +0 -88
  139. package/dist/tools/github/pr.d.ts +0 -5
  140. package/dist/tools/github/pr.js +0 -88
  141. package/dist/tools/github/rateLimiter.d.ts +0 -4
  142. package/dist/tools/github/rateLimiter.js +0 -46
  143. package/dist/tools/github/read.d.ts +0 -34
  144. package/dist/tools/github/read.js +0 -74
  145. package/dist/tools/github/repoContext.d.ts +0 -20
  146. package/dist/tools/github/repoContext.js +0 -129
  147. package/dist/tools/hover.d.ts +0 -19
  148. package/dist/tools/hover.js +0 -57
  149. package/dist/tools/integration.test.d.ts +0 -2
  150. package/dist/tools/integration.test.js +0 -96
  151. package/dist/tools/macro_compiler.d.ts +0 -7
  152. package/dist/tools/macro_compiler.js +0 -82
  153. package/dist/tools/mouse.d.ts +0 -21
  154. package/dist/tools/mouse.js +0 -62
  155. package/dist/tools/response.d.ts +0 -129
  156. package/dist/tools/response.js +0 -5
  157. package/dist/tools/wait_for.d.ts +0 -23
  158. package/dist/tools/wait_for.js +0 -30
  159. package/dist/tools/windows.d.ts +0 -22
  160. 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
  });