@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
@@ -4,79 +4,79 @@ import { sessionRegistry } from "../state/session.js";
4
4
  export async function browserScrape(session, options, _config) {
5
5
  const { query, type = "css", attributes = ["textContent"], multiple = true } = options;
6
6
  const res = await session.Runtime.evaluate({
7
- expression: `(function() {
8
- const results = [];
9
- let nodes = [];
10
-
11
- try {
12
- if (${JSON.stringify(type)} === 'css') {
13
- if (${multiple}) {
14
- nodes = Array.from(document.querySelectorAll(${JSON.stringify(query)}));
15
- // Shadow DOM fallback: if nothing found, try the extension's deep-shadow query helper.
16
- // This handles Vue/React components that attach to shadow roots.
17
- if (nodes.length === 0 && window.__Weavetab_shadowQuery_all) {
18
- try { nodes = Array.from(window.__Weavetab_shadowQuery_all(${JSON.stringify(query)})); } catch (_) {}
19
- }
20
- } else {
21
- const node = document.querySelector(${JSON.stringify(query)})
22
- || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(${JSON.stringify(query)}) : null);
23
- if (node) nodes = [node];
24
- }
25
- } else if (${JSON.stringify(type)} === 'xpath') {
26
- // Use SNAPSHOT type — immune to DOM mutations that would invalidate an ITERATOR mid-walk.
27
- const snap = document.evaluate(${JSON.stringify(query)}, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
28
- const limit = ${multiple} ? snap.snapshotLength : Math.min(snap.snapshotLength, 1);
29
- for (let i = 0; i < limit; i++) {
30
- const n = snap.snapshotItem(i);
31
- // Only Element nodes expose getAttribute / tagName / outerHTML safely.
32
- if (n && n.nodeType === 1) nodes.push(n);
33
- }
34
- }
35
- } catch (e) {
36
- return { error: e.message };
37
- }
38
-
39
- const attrs = ${JSON.stringify(attributes)};
40
- var secretRegex = /password|passcode|pin|passphrase|secret|token|auth.?key|access.?code|mot.?de.?passe|contrase[nñ]a|kennwort|senha/i;
41
- nodes.forEach(node => {
42
- const item = {};
43
-
44
- // Secret containment: redact any element marked as sensitive.
45
- const isSecret = node.hasAttribute && (
46
- node.hasAttribute('data-wt-secret') ||
47
- (node.tagName === 'INPUT' && (
48
- node.type === 'password' ||
49
- secretRegex.test(node.getAttribute('autocomplete') || '') ||
50
- secretRegex.test(node.getAttribute('name') || '') ||
51
- secretRegex.test(node.getAttribute('id') || '') ||
52
- secretRegex.test(node.getAttribute('aria-label') || '')
53
- ))
54
- );
55
-
56
- if (isSecret) {
57
- // Replace ALL requested attributes with [REDACTED] — value never crosses boundary
58
- attrs.forEach(attr => { item[attr] = '[REDACTED]'; });
59
- } else {
60
- attrs.forEach(attr => {
61
- if (attr === 'textContent' || attr === 'innerText') {
62
- item[attr] = node[attr]?.trim() || '';
63
- } else if (attr === 'outerHTML') {
64
- item[attr] = node.outerHTML;
65
- } else if (attr === 'value') {
66
- // Read live DOM property (not HTML attribute) so getter overrides work
67
- item[attr] = node.value !== undefined ? String(node.value) : '';
68
- } else {
69
- item[attr] = node.getAttribute(attr) || node[attr] || '';
70
- }
71
- });
72
- }
73
-
74
- results.push(item);
75
- });
76
-
77
- const isXpath = ${JSON.stringify(type)} === 'xpath';
78
- const shadowFallback = !isXpath && nodes.length > 0 && !document.querySelector(${JSON.stringify(query)});
79
- return { data: results, usedShadowFallback: shadowFallback };
7
+ expression: `(function() {
8
+ const results = [];
9
+ let nodes = [];
10
+
11
+ try {
12
+ if (${JSON.stringify(type)} === 'css') {
13
+ if (${multiple}) {
14
+ nodes = Array.from(document.querySelectorAll(${JSON.stringify(query)}));
15
+ // Shadow DOM fallback: if nothing found, try the extension's deep-shadow query helper.
16
+ // This handles Vue/React components that attach to shadow roots.
17
+ if (nodes.length === 0 && window.__Weavetab_shadowQuery_all) {
18
+ try { nodes = Array.from(window.__Weavetab_shadowQuery_all(${JSON.stringify(query)})); } catch (_) {}
19
+ }
20
+ } else {
21
+ const node = document.querySelector(${JSON.stringify(query)})
22
+ || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(${JSON.stringify(query)}) : null);
23
+ if (node) nodes = [node];
24
+ }
25
+ } else if (${JSON.stringify(type)} === 'xpath') {
26
+ // Use SNAPSHOT type — immune to DOM mutations that would invalidate an ITERATOR mid-walk.
27
+ const snap = document.evaluate(${JSON.stringify(query)}, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
28
+ const limit = ${multiple} ? snap.snapshotLength : Math.min(snap.snapshotLength, 1);
29
+ for (let i = 0; i < limit; i++) {
30
+ const n = snap.snapshotItem(i);
31
+ // Only Element nodes expose getAttribute / tagName / outerHTML safely.
32
+ if (n && n.nodeType === 1) nodes.push(n);
33
+ }
34
+ }
35
+ } catch (e) {
36
+ return { error: e.message };
37
+ }
38
+
39
+ const attrs = ${JSON.stringify(attributes)};
40
+ var secretRegex = /password|passcode|pin|passphrase|secret|token|auth.?key|access.?code|mot.?de.?passe|contrase[nñ]a|kennwort|senha/i;
41
+ nodes.forEach(node => {
42
+ const item = {};
43
+
44
+ // Secret containment: redact any element marked as sensitive.
45
+ const isSecret = node.hasAttribute && (
46
+ node.hasAttribute('data-wt-secret') ||
47
+ (node.tagName === 'INPUT' && (
48
+ node.type === 'password' ||
49
+ secretRegex.test(node.getAttribute('autocomplete') || '') ||
50
+ secretRegex.test(node.getAttribute('name') || '') ||
51
+ secretRegex.test(node.getAttribute('id') || '') ||
52
+ secretRegex.test(node.getAttribute('aria-label') || '')
53
+ ))
54
+ );
55
+
56
+ if (isSecret) {
57
+ // Replace ALL requested attributes with [REDACTED] — value never crosses boundary
58
+ attrs.forEach(attr => { item[attr] = '[REDACTED]'; });
59
+ } else {
60
+ attrs.forEach(attr => {
61
+ if (attr === 'textContent' || attr === 'innerText') {
62
+ item[attr] = node[attr]?.trim() || '';
63
+ } else if (attr === 'outerHTML') {
64
+ item[attr] = node.outerHTML;
65
+ } else if (attr === 'value') {
66
+ // Read live DOM property (not HTML attribute) so getter overrides work
67
+ item[attr] = node.value !== undefined ? String(node.value) : '';
68
+ } else {
69
+ item[attr] = node.getAttribute(attr) || node[attr] || '';
70
+ }
71
+ });
72
+ }
73
+
74
+ results.push(item);
75
+ });
76
+
77
+ const isXpath = ${JSON.stringify(type)} === 'xpath';
78
+ const shadowFallback = !isXpath && nodes.length > 0 && !document.querySelector(${JSON.stringify(query)});
79
+ return { data: results, usedShadowFallback: shadowFallback };
80
80
  })()`,
81
81
  returnByValue: true
82
82
  });
@@ -90,87 +90,87 @@ export async function browserScrape(session, options, _config) {
90
90
  await new Promise(r => setTimeout(r, 300));
91
91
  // Build a fresh count expression depending on selector type.
92
92
  const countExpr = type === 'xpath'
93
- ? `(function(){
94
- try {
95
- var s = document.evaluate(${JSON.stringify(query)}, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
96
- return s.snapshotLength;
97
- } catch(e) { return 0; }
93
+ ? `(function(){
94
+ try {
95
+ var s = document.evaluate(${JSON.stringify(query)}, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
96
+ return s.snapshotLength;
97
+ } catch(e) { return 0; }
98
98
  })()`
99
- : `(function(){
100
- var n = Array.from(document.querySelectorAll(${JSON.stringify(query)}));
101
- if (n.length === 0 && window.__Weavetab_shadowQuery_all) {
102
- try { n = Array.from(window.__Weavetab_shadowQuery_all(${JSON.stringify(query)})); } catch(_){}
103
- }
104
- return n.length;
99
+ : `(function(){
100
+ var n = Array.from(document.querySelectorAll(${JSON.stringify(query)}));
101
+ if (n.length === 0 && window.__Weavetab_shadowQuery_all) {
102
+ try { n = Array.from(window.__Weavetab_shadowQuery_all(${JSON.stringify(query)})); } catch(_){}
103
+ }
104
+ return n.length;
105
105
  })()`;
106
106
  const retry = await session.Runtime.evaluate({ expression: countExpr, returnByValue: true }).catch(() => null);
107
107
  if (retry?.result?.value && retry.result.value > 0) {
108
108
  // Re-run full scrape now that elements exist.
109
109
  // XPath path uses snapshot; CSS path uses querySelectorAll + shadow fallback.
110
110
  const fullExpr = type === 'xpath'
111
- ? `(function(){
112
- var attrs = ${JSON.stringify(attributes)};
113
- var secretRegex = /password|passcode|pin|passphrase|secret|token|auth.?key|access.?code|mot.?de.?passe|contrase[nñ]a|kennwort|senha/i;
114
- var snap = document.evaluate(${JSON.stringify(query)}, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
115
- var limit = ${multiple} ? snap.snapshotLength : Math.min(snap.snapshotLength, 1);
116
- var results = [];
117
- for (var i = 0; i < limit; i++) {
118
- var node = snap.snapshotItem(i);
119
- if (!node || node.nodeType !== 1) continue;
120
- var item = {};
121
- var isSecret = node.hasAttribute && (
122
- node.hasAttribute('data-wt-secret') ||
123
- (node.tagName === 'INPUT' && (
124
- node.type === 'password' ||
125
- secretRegex.test(node.getAttribute('autocomplete') || '') ||
126
- secretRegex.test(node.getAttribute('name') || '') ||
127
- secretRegex.test(node.getAttribute('id') || '') ||
128
- secretRegex.test(node.getAttribute('aria-label') || '')
129
- ))
130
- );
131
- if (isSecret) {
132
- attrs.forEach(function(attr) { item[attr] = '[REDACTED]'; });
133
- } else {
134
- attrs.forEach(function(attr) {
135
- if (attr === 'textContent' || attr === 'innerText') { item[attr] = (node[attr] || '').trim(); }
136
- else if (attr === 'outerHTML') { item[attr] = node.outerHTML; }
137
- else { item[attr] = node.getAttribute ? (node.getAttribute(attr) || node[attr] || '') : ''; }
138
- });
139
- }
140
- results.push(item);
141
- }
142
- return { data: results };
111
+ ? `(function(){
112
+ var attrs = ${JSON.stringify(attributes)};
113
+ var secretRegex = /password|passcode|pin|passphrase|secret|token|auth.?key|access.?code|mot.?de.?passe|contrase[nñ]a|kennwort|senha/i;
114
+ var snap = document.evaluate(${JSON.stringify(query)}, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
115
+ var limit = ${multiple} ? snap.snapshotLength : Math.min(snap.snapshotLength, 1);
116
+ var results = [];
117
+ for (var i = 0; i < limit; i++) {
118
+ var node = snap.snapshotItem(i);
119
+ if (!node || node.nodeType !== 1) continue;
120
+ var item = {};
121
+ var isSecret = node.hasAttribute && (
122
+ node.hasAttribute('data-wt-secret') ||
123
+ (node.tagName === 'INPUT' && (
124
+ node.type === 'password' ||
125
+ secretRegex.test(node.getAttribute('autocomplete') || '') ||
126
+ secretRegex.test(node.getAttribute('name') || '') ||
127
+ secretRegex.test(node.getAttribute('id') || '') ||
128
+ secretRegex.test(node.getAttribute('aria-label') || '')
129
+ ))
130
+ );
131
+ if (isSecret) {
132
+ attrs.forEach(function(attr) { item[attr] = '[REDACTED]'; });
133
+ } else {
134
+ attrs.forEach(function(attr) {
135
+ if (attr === 'textContent' || attr === 'innerText') { item[attr] = (node[attr] || '').trim(); }
136
+ else if (attr === 'outerHTML') { item[attr] = node.outerHTML; }
137
+ else { item[attr] = node.getAttribute ? (node.getAttribute(attr) || node[attr] || '') : ''; }
138
+ });
139
+ }
140
+ results.push(item);
141
+ }
142
+ return { data: results };
143
143
  })()`
144
- : `(function(){
145
- var nodes = Array.from(document.querySelectorAll(${JSON.stringify(query)}));
146
- if (nodes.length === 0 && window.__Weavetab_shadowQuery_all) {
147
- try { nodes = Array.from(window.__Weavetab_shadowQuery_all(${JSON.stringify(query)})); } catch(_){}
148
- }
149
- var attrs = ${JSON.stringify(attributes)};
150
- var secretRegex = /password|passcode|pin|passphrase|secret|token|auth.?key|access.?code|mot.?de.?passe|contrase[nñ]a|kennwort|senha/i;
151
- return { data: nodes.map(function(node) {
152
- var item = {};
153
- var isSecret = node.hasAttribute && (
154
- node.hasAttribute('data-wt-secret') ||
155
- (node.tagName === 'INPUT' && (
156
- node.type === 'password' ||
157
- secretRegex.test(node.getAttribute('autocomplete') || '') ||
158
- secretRegex.test(node.getAttribute('name') || '') ||
159
- secretRegex.test(node.getAttribute('id') || '') ||
160
- secretRegex.test(node.getAttribute('aria-label') || '')
161
- ))
162
- );
163
- if (isSecret) {
164
- attrs.forEach(function(attr) { item[attr] = '[REDACTED]'; });
165
- } else {
166
- attrs.forEach(function(attr) {
167
- if (attr === 'textContent' || attr === 'innerText') { item[attr] = (node[attr] || '').trim(); }
168
- else if (attr === 'outerHTML') { item[attr] = node.outerHTML; }
169
- else { item[attr] = node.getAttribute(attr) || node[attr] || ''; }
170
- });
171
- }
172
- return item;
173
- })};
144
+ : `(function(){
145
+ var nodes = Array.from(document.querySelectorAll(${JSON.stringify(query)}));
146
+ if (nodes.length === 0 && window.__Weavetab_shadowQuery_all) {
147
+ try { nodes = Array.from(window.__Weavetab_shadowQuery_all(${JSON.stringify(query)})); } catch(_){}
148
+ }
149
+ var attrs = ${JSON.stringify(attributes)};
150
+ var secretRegex = /password|passcode|pin|passphrase|secret|token|auth.?key|access.?code|mot.?de.?passe|contrase[nñ]a|kennwort|senha/i;
151
+ return { data: nodes.map(function(node) {
152
+ var item = {};
153
+ var isSecret = node.hasAttribute && (
154
+ node.hasAttribute('data-wt-secret') ||
155
+ (node.tagName === 'INPUT' && (
156
+ node.type === 'password' ||
157
+ secretRegex.test(node.getAttribute('autocomplete') || '') ||
158
+ secretRegex.test(node.getAttribute('name') || '') ||
159
+ secretRegex.test(node.getAttribute('id') || '') ||
160
+ secretRegex.test(node.getAttribute('aria-label') || '')
161
+ ))
162
+ );
163
+ if (isSecret) {
164
+ attrs.forEach(function(attr) { item[attr] = '[REDACTED]'; });
165
+ } else {
166
+ attrs.forEach(function(attr) {
167
+ if (attr === 'textContent' || attr === 'innerText') { item[attr] = (node[attr] || '').trim(); }
168
+ else if (attr === 'outerHTML') { item[attr] = node.outerHTML; }
169
+ else { item[attr] = node.getAttribute(attr) || node[attr] || ''; }
170
+ });
171
+ }
172
+ return item;
173
+ })};
174
174
  })()`;
175
175
  const res2 = await session.Runtime.evaluate({ expression: fullExpr, returnByValue: true }).catch(() => null);
176
176
  if (res2?.result?.value?.data) {
@@ -26,6 +26,8 @@ export interface ScreenshotOptions {
26
26
  width: number;
27
27
  height: number;
28
28
  };
29
+ /** CSS selector of an element — capture is cropped to its bounding rect. */
30
+ selector?: string;
29
31
  /** If false, keeps the WeaveTab UI overlay visible during capture.
30
32
  * Default: true — overlay is always hidden from agent captures. */
31
33
  hideOverlay?: boolean;
@@ -9,14 +9,42 @@ export async function browserScreenshot(session, options = {}, _config) {
9
9
  });
10
10
  const vp = JSON.parse(vpResult.value);
11
11
  let clip;
12
+ let beyondViewport = !!options.fullPage;
12
13
  if (options.clip) {
13
14
  clip = { ...options.clip, scale: 1 };
14
15
  }
16
+ else if (options.selector) {
17
+ const { result: selResult } = await session.Runtime.evaluate({
18
+ expression: `(() => {
19
+ try {
20
+ const el = document.querySelector(${JSON.stringify(options.selector)});
21
+ if (!el) return JSON.stringify({ notFound: true });
22
+ const r = el.getBoundingClientRect();
23
+ return JSON.stringify({ x: r.x, y: r.y, width: r.width, height: r.height, docX: r.x + window.scrollX, docY: r.y + window.scrollY });
24
+ } catch (e) {
25
+ return JSON.stringify({ error: String(e && e.message || e) });
26
+ }
27
+ })()`,
28
+ returnByValue: true,
29
+ });
30
+ const sel = selResult?.value ? JSON.parse(selResult.value) : { notFound: true };
31
+ if (sel.error) {
32
+ throw new Error(`browser_screenshot selector failed: ${sel.error}`);
33
+ }
34
+ if (sel.notFound || !selResult?.value) {
35
+ throw new Error(`browser_screenshot: no element found for selector '${options.selector}'`);
36
+ }
37
+ if (sel.width === 0 || sel.height === 0) {
38
+ throw new Error(`browser_screenshot: element matched by '${options.selector}' has no size (display: none or not laid out)`);
39
+ }
40
+ clip = { x: sel.docX, y: sel.docY, width: sel.width, height: sel.height, scale: 1 };
41
+ beyondViewport = true;
42
+ }
15
43
  else if (options.fullPage) {
16
44
  const { result: scrollResult } = await session.Runtime.evaluate({
17
- expression: `JSON.stringify({
18
- w: Math.max(document.documentElement.scrollWidth, document.body.scrollWidth),
19
- h: Math.max(document.documentElement.scrollHeight, document.body.scrollHeight)
45
+ expression: `JSON.stringify({
46
+ w: Math.max(document.documentElement.scrollWidth, document.body.scrollWidth),
47
+ h: Math.max(document.documentElement.scrollHeight, document.body.scrollHeight)
20
48
  })`,
21
49
  returnByValue: true,
22
50
  });
@@ -26,7 +54,7 @@ export async function browserScreenshot(session, options = {}, _config) {
26
54
  // ── Auto-compress: if viewport is wide, downscale to 1280px wide as JPEG 75% ──
27
55
  // This cuts token cost by 40-60% with no quality loss for text content.
28
56
  // Only applies when no explicit format/clip was requested.
29
- const params = { format, captureBeyondViewport: !!options.fullPage };
57
+ const params = { format, captureBeyondViewport: beyondViewport };
30
58
  if (!options.clip && !options.format && vp.w > 1280) {
31
59
  const scale = 1280 / vp.w;
32
60
  params.format = 'jpeg';
@@ -67,6 +95,20 @@ export async function browserScreenshot(session, options = {}, _config) {
67
95
  throw e;
68
96
  }
69
97
  }
98
+ const MAX_BASE64 = 2 * 1024 * 1024;
99
+ for (const q of [70, 40]) {
100
+ if (data.length <= MAX_BASE64)
101
+ break;
102
+ if (params.format === "jpeg" && (params.quality ?? 90) <= q)
103
+ break;
104
+ params.format = "jpeg";
105
+ params.quality = q;
106
+ const res = await session.Page.captureScreenshot(params);
107
+ data = res.data;
108
+ }
109
+ if (data.length > MAX_BASE64) {
110
+ throw new Error(`Screenshot payload exceeds the 2MB MCP size limit even at JPEG quality 40. Narrow the capture with clip or selector.`);
111
+ }
70
112
  }
71
113
  finally {
72
114
  // Restore secret fields and overlay visibility immediately after capture
@@ -11,7 +11,7 @@ export interface ScrollResult {
11
11
  scrolled: string;
12
12
  direction: string;
13
13
  distance: number;
14
- new_visible_count: number;
14
+ new_visible_count?: number;
15
15
  dom_changed: boolean;
16
16
  }
17
17
  export declare function browserScroll(session: CDP.Client, args: {
@@ -19,5 +19,7 @@ export declare function browserScroll(session: CDP.Client, args: {
19
19
  label?: string;
20
20
  direction: "up" | "down" | "left" | "right";
21
21
  distance?: number;
22
+ pixels?: number;
23
+ amount?: number;
22
24
  speed?: "human" | "read" | "fast";
23
25
  }, config: Config): Promise<ScrollResult | WeaveError>;
@@ -30,7 +30,8 @@ function applyEasing(t, type) {
30
30
  }
31
31
  }
32
32
  export async function browserScroll(session, args, config) {
33
- let { id, label, direction, distance = 400, speed = "human" } = args;
33
+ let { id, label, direction, speed = "human" } = args;
34
+ let distance = args.distance ?? args.pixels ?? args.amount ?? 400;
34
35
  if (distance < 0) {
35
36
  distance = Math.abs(distance);
36
37
  if (direction === "down")
@@ -111,14 +112,14 @@ export async function browserScroll(session, args, config) {
111
112
  let elementFound = false;
112
113
  try {
113
114
  const res = await session.Runtime.evaluate({
114
- expression: `(function(sel) {
115
- const el = document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null);
116
- if (!el) return null;
117
- const rect = el.getBoundingClientRect();
118
- return {
119
- x: rect.left + rect.width / 2,
120
- y: rect.top + rect.height / 2
121
- };
115
+ expression: `(function(sel) {
116
+ const el = document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null);
117
+ if (!el) return null;
118
+ const rect = el.getBoundingClientRect();
119
+ return {
120
+ x: rect.left + rect.width / 2,
121
+ y: rect.top + rect.height / 2
122
+ };
122
123
  })(${JSON.stringify(scrollSelector)})`,
123
124
  returnByValue: true
124
125
  });
@@ -151,12 +152,10 @@ export async function browserScroll(session, args, config) {
151
152
  const profile = profiles[speed];
152
153
  const totalPx = direction === 'down' ? distance : direction === 'up' ? -distance : 0;
153
154
  const totalPxX = direction === 'right' ? distance : direction === 'left' ? -distance : 0;
154
- let map = getCachedActionMap(targetId, url);
155
- if (!map) {
156
- map = await buildActionMap(session, { lite: true }, targetId);
157
- cacheActionMap(targetId, map);
155
+ const map = getCachedActionMap(targetId, url);
156
+ if (map) {
157
+ storeSnapshot(targetId, map.url, map.elements);
158
158
  }
159
- storeSnapshot(targetId, map.url, map.elements);
160
159
  const viewportRes = await session.Runtime.evaluate({
161
160
  expression: `JSON.stringify({ x: window.innerWidth / 2, y: window.innerHeight / 2 })`,
162
161
  returnByValue: true
@@ -185,7 +184,6 @@ export async function browserScroll(session, args, config) {
185
184
  scrolled: id ?? label ?? "window",
186
185
  direction,
187
186
  distance,
188
- new_visible_count: 0,
189
187
  dom_changed: true,
190
188
  };
191
189
  }
@@ -5,9 +5,12 @@ export interface SelectOptions {
5
5
  selector?: string;
6
6
  ref?: string;
7
7
  id?: string;
8
+ target?: string;
8
9
  value: string;
9
10
  }
10
11
  export declare function browserSelect(session: CDP.Client, options: SelectOptions, _config: Config): Promise<{
11
12
  success: boolean;
12
13
  message: string;
14
+ value?: string;
15
+ selected?: string;
13
16
  }>;