@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.
- package/AGENTS.md +49 -49
- package/ARCHITECTURE.md +590 -590
- package/LICENSE +216 -216
- package/README.md +269 -269
- package/SECURITY.md +207 -207
- package/TROUBLESHOOTING.md +373 -365
- package/dist/anticaptcha/detector.js +395 -395
- package/dist/anticaptcha/solver.js +130 -130
- package/dist/cdp/bridge.js +73 -73
- package/dist/cdp/connector.js +46 -22
- package/dist/cdp/extension.js +34 -34
- package/dist/cdp/helpers.js +174 -174
- package/dist/cdp/pruner.js +195 -195
- package/dist/cdp/ref_cache.js +66 -66
- package/dist/cdp/security.js +10 -10
- package/dist/cdp/verify.js +57 -57
- package/dist/cdp/walker.js +563 -525
- package/dist/cli/plugin.js +26 -3
- package/dist/config/loader.d.ts +0 -3
- package/dist/config/loader.js +0 -9
- package/dist/index.js +0 -4
- package/dist/intelligence/intent.js +81 -81
- package/dist/intelligence/sessionMemory.js +2 -2
- package/dist/overlay/ghost.js +22 -22
- package/dist/overlay/injector.js +89 -89
- package/dist/plugin/extension_bridge.js +54 -54
- package/dist/plugin/interface.d.ts +7 -0
- package/dist/plugin/loader.js +3 -4
- package/dist/plugin/schema.d.ts +11 -0
- package/dist/plugin/schema.js +59 -0
- package/dist/security/guard.js +18 -18
- package/dist/sensors/actionability.js +167 -167
- package/dist/sensors/guard.js +29 -29
- package/dist/sensors/network.js +15 -17
- package/dist/server.d.ts +1 -1
- package/dist/server.js +173 -144
- package/dist/state/delta.js +6 -7
- package/dist/tools/automation.js +85 -85
- package/dist/tools/canvas.js +48 -25
- package/dist/tools/checkpoint.js +94 -35
- package/dist/tools/click.js +52 -41
- package/dist/tools/clipboard.js +80 -74
- package/dist/tools/design_extract.js +441 -441
- package/dist/tools/dialog.js +13 -13
- package/dist/tools/emulation.d.ts +2 -0
- package/dist/tools/emulation.js +7 -0
- package/dist/tools/fill.js +344 -347
- package/dist/tools/find.js +78 -52
- package/dist/tools/highlight.js +67 -67
- package/dist/tools/inspect.js +54 -25
- package/dist/tools/key.js +42 -35
- package/dist/tools/navigate.d.ts +1 -1
- package/dist/tools/navigate.js +35 -35
- package/dist/tools/office/parse.js +62 -18
- package/dist/tools/pdf.d.ts +9 -0
- package/dist/tools/pdf.js +7 -4
- package/dist/tools/performance.d.ts +11 -4
- package/dist/tools/performance.js +94 -42
- package/dist/tools/performance_test.d.ts +2 -2
- package/dist/tools/performance_test.js +69 -69
- package/dist/tools/pointer.js +116 -116
- package/dist/tools/read.js +99 -67
- package/dist/tools/recording.js +9 -0
- package/dist/tools/scrape.js +146 -146
- package/dist/tools/screenshot.d.ts +2 -0
- package/dist/tools/screenshot.js +46 -4
- package/dist/tools/scroll.d.ts +3 -1
- package/dist/tools/scroll.js +13 -15
- package/dist/tools/select.d.ts +3 -0
- package/dist/tools/select.js +131 -124
- package/dist/tools/snapshot.js +8 -6
- package/dist/tools/storage.d.ts +5 -1
- package/dist/tools/storage.js +16 -0
- package/dist/tools/tabs.d.ts +7 -5
- package/dist/tools/tabs.js +79 -26
- package/dist/tools/thought.js +8 -8
- package/dist/tools/type.js +274 -274
- package/dist/tools/type_secret.js +23 -23
- package/dist/tools/upload.js +3 -3
- package/dist/tools/video_compiler.js +9 -0
- package/dist/tools/wait.js +35 -35
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/DOCUMENTATION.md +583 -585
- package/docs/PLUGINS.md +139 -121
- package/docs/TOOLS.md +3 -4
- package/docs/architecture/blind-injection.md +83 -83
- package/docs/architecture/overview.md +49 -49
- package/docs/architecture/security.md +36 -36
- package/docs/architecture/transport.md +37 -37
- package/docs/cli/cli-commands.md +50 -50
- package/docs/cli/cli-flags.md +28 -28
- package/docs/cli/cli-scripting.md +35 -35
- package/docs/configuration/config-locations.md +23 -23
- package/docs/configuration/configuration.md +39 -27
- package/docs/configuration/env-variables.md +19 -19
- package/docs/configuration/folders.md +62 -55
- package/docs/desktop-automation/electron-apps.md +225 -214
- package/docs/get-started/installation.md +1 -1
- package/docs/get-started/introduction.md +60 -60
- package/docs/get-started/quickstart.md +2 -2
- package/docs/get-started/setup-wizard.md +27 -27
- package/docs/intelligence/site-profiles.md +151 -151
- package/docs/tools/tools.md +582 -631
- package/docs/troubleshooting/trouble-browser.md +27 -27
- package/docs/troubleshooting/trouble-loop.md +31 -31
- package/ext-dist/background.js +415 -415
- package/ext-dist/content.js +2527 -2527
- package/ext-dist/dashboard.html +583 -583
- package/ext-dist/hud/hud.js +3 -3
- package/ext-dist/hud/index.html +20 -20
- package/ext-dist/manifest.json +73 -73
- package/ext-dist/styles.css +223 -223
- package/mcp.json +796 -805
- package/package.json +118 -118
- package/server.json +23 -23
- package/dist/cdp/walker.test.d.ts +0 -2
- package/dist/cdp/walker.test.js +0 -172
- package/dist/intelligence/sessionMemory.test.d.ts +0 -2
- package/dist/intelligence/sessionMemory.test.js +0 -62
- package/dist/intelligence/trail.test.d.ts +0 -2
- package/dist/intelligence/trail.test.js +0 -110
- package/dist/state/cache.test.d.ts +0 -2
- package/dist/state/cache.test.js +0 -76
- package/dist/state/delta.test.d.ts +0 -2
- package/dist/state/delta.test.js +0 -279
- package/dist/state/groups.d.ts +0 -15
- package/dist/state/groups.js +0 -80
- package/dist/tools/accessibility.d.ts +0 -14
- package/dist/tools/accessibility.js +0 -32
- package/dist/tools/ask.d.ts +0 -14
- package/dist/tools/ask.js +0 -49
- package/dist/tools/click_and_wait.d.ts +0 -33
- package/dist/tools/click_and_wait.js +0 -149
- package/dist/tools/drag.d.ts +0 -15
- package/dist/tools/drag.js +0 -29
- package/dist/tools/geo.d.ts +0 -19
- package/dist/tools/geo.js +0 -29
- package/dist/tools/github/analyze.d.ts +0 -4
- package/dist/tools/github/analyze.js +0 -113
- package/dist/tools/github/cache.d.ts +0 -21
- package/dist/tools/github/cache.js +0 -28
- package/dist/tools/github/index.d.ts +0 -19
- package/dist/tools/github/index.js +0 -40
- package/dist/tools/github/issues.d.ts +0 -9
- package/dist/tools/github/issues.js +0 -88
- package/dist/tools/github/pr.d.ts +0 -5
- package/dist/tools/github/pr.js +0 -88
- package/dist/tools/github/rateLimiter.d.ts +0 -4
- package/dist/tools/github/rateLimiter.js +0 -46
- package/dist/tools/github/read.d.ts +0 -34
- package/dist/tools/github/read.js +0 -74
- package/dist/tools/github/repoContext.d.ts +0 -20
- package/dist/tools/github/repoContext.js +0 -129
- package/dist/tools/hover.d.ts +0 -19
- package/dist/tools/hover.js +0 -57
- package/dist/tools/integration.test.d.ts +0 -2
- package/dist/tools/integration.test.js +0 -96
- package/dist/tools/macro_compiler.d.ts +0 -7
- package/dist/tools/macro_compiler.js +0 -82
- package/dist/tools/mouse.d.ts +0 -21
- package/dist/tools/mouse.js +0 -62
- package/dist/tools/response.d.ts +0 -129
- package/dist/tools/response.js +0 -5
- package/dist/tools/wait_for.d.ts +0 -23
- package/dist/tools/wait_for.js +0 -30
- package/dist/tools/windows.d.ts +0 -22
- package/dist/tools/windows.js +0 -57
package/dist/tools/select.js
CHANGED
|
@@ -7,8 +7,9 @@ import { buildActionMap, safeSelector } from "../cdp/walker.js";
|
|
|
7
7
|
async function getSelector(session, options, config) {
|
|
8
8
|
if (options.selector)
|
|
9
9
|
return options.selector;
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const targetRef = options.ref || options.id || options.target;
|
|
11
|
+
if (targetRef) {
|
|
12
|
+
const ref = targetRef;
|
|
12
13
|
if (ref.startsWith("w:")) {
|
|
13
14
|
return `[data-wt-id="${ref.slice(2)}"]`;
|
|
14
15
|
}
|
|
@@ -43,60 +44,60 @@ async function getSelector(session, options, config) {
|
|
|
43
44
|
throw new Error("Could not resolve node");
|
|
44
45
|
const { result } = await session.Runtime.callFunctionOn({
|
|
45
46
|
objectId: object.objectId,
|
|
46
|
-
functionDeclaration: `function() {
|
|
47
|
-
if (this.id) return '#' + CSS.escape(this.id);
|
|
48
|
-
if (this.name) return 'select[name="' + CSS.escape(this.name) + '"]';
|
|
49
|
-
var idx = 0, el = this;
|
|
50
|
-
while (el.previousElementSibling) { idx++; el = el.previousElementSibling; }
|
|
51
|
-
var parent = this.parentElement;
|
|
52
|
-
if (!parent) return 'select:last-of-type';
|
|
53
|
-
var tag = this.tagName.toLowerCase();
|
|
54
|
-
return tag + ':nth-child(' + (idx + 1) + ')';
|
|
47
|
+
functionDeclaration: `function() {
|
|
48
|
+
if (this.id) return '#' + CSS.escape(this.id);
|
|
49
|
+
if (this.name) return 'select[name="' + CSS.escape(this.name) + '"]';
|
|
50
|
+
var idx = 0, el = this;
|
|
51
|
+
while (el.previousElementSibling) { idx++; el = el.previousElementSibling; }
|
|
52
|
+
var parent = this.parentElement;
|
|
53
|
+
if (!parent) return 'select:last-of-type';
|
|
54
|
+
var tag = this.tagName.toLowerCase();
|
|
55
|
+
return tag + ':nth-child(' + (idx + 1) + ')';
|
|
55
56
|
}()`,
|
|
56
57
|
returnByValue: true
|
|
57
58
|
});
|
|
58
59
|
return result.value;
|
|
59
60
|
}
|
|
60
|
-
throw new Error("browser_select requires either selector, nodeId, ref, or id");
|
|
61
|
+
throw new Error("browser_select requires either selector, nodeId, ref, target, or id");
|
|
61
62
|
}
|
|
62
63
|
function escapeSelector(sel) {
|
|
63
64
|
return JSON.stringify(sel);
|
|
64
65
|
}
|
|
65
66
|
export async function browserSelect(session, options, _config) {
|
|
66
|
-
if (!options.selector && !options.nodeId && !options.ref && !options.id) {
|
|
67
|
-
throw new Error("browser_select requires either selector, nodeId, ref, or id");
|
|
67
|
+
if (!options.selector && !options.nodeId && !options.ref && !options.id && !options.target) {
|
|
68
|
+
throw new Error("browser_select requires either selector, nodeId, ref, target, or id");
|
|
68
69
|
}
|
|
69
70
|
const selector = await getSelector(session, options, _config);
|
|
70
71
|
const value = options.value;
|
|
71
72
|
const getSelectState = async () => {
|
|
72
73
|
const { result } = await session.Runtime.evaluate({
|
|
73
|
-
expression: `(function() {
|
|
74
|
-
try {
|
|
75
|
-
var el = document.querySelector(${escapeSelector(selector)});
|
|
76
|
-
if (!el) return JSON.stringify({ exists: "false" });
|
|
77
|
-
if (el.tagName === "SELECT") {
|
|
78
|
-
var idx = el.selectedIndex;
|
|
79
|
-
var opt = idx >= 0 ? el.options[idx] : null;
|
|
80
|
-
return JSON.stringify({
|
|
81
|
-
exists: "true",
|
|
82
|
-
isNative: "true",
|
|
83
|
-
selectedIndex: String(idx),
|
|
84
|
-
value: el.value,
|
|
85
|
-
selectedText: opt ? opt.text : ""
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
// Custom UI Dropdown / Combobox / Popover
|
|
89
|
-
var text = (el.innerText || el.textContent || "").trim();
|
|
90
|
-
var val = el.getAttribute("data-value") || el.getAttribute("aria-valuenow") || text;
|
|
91
|
-
return JSON.stringify({
|
|
92
|
-
exists: "true",
|
|
93
|
-
isNative: "false",
|
|
94
|
-
value: val,
|
|
95
|
-
selectedText: text
|
|
96
|
-
});
|
|
97
|
-
} catch(e) {
|
|
98
|
-
return JSON.stringify({ exists: "false", error: e.message });
|
|
99
|
-
}
|
|
74
|
+
expression: `(function() {
|
|
75
|
+
try {
|
|
76
|
+
var el = document.querySelector(${escapeSelector(selector)});
|
|
77
|
+
if (!el) return JSON.stringify({ exists: "false" });
|
|
78
|
+
if (el.tagName === "SELECT") {
|
|
79
|
+
var idx = el.selectedIndex;
|
|
80
|
+
var opt = idx >= 0 ? el.options[idx] : null;
|
|
81
|
+
return JSON.stringify({
|
|
82
|
+
exists: "true",
|
|
83
|
+
isNative: "true",
|
|
84
|
+
selectedIndex: String(idx),
|
|
85
|
+
value: el.value,
|
|
86
|
+
selectedText: opt ? opt.text : ""
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
// Custom UI Dropdown / Combobox / Popover
|
|
90
|
+
var text = (el.innerText || el.textContent || "").trim();
|
|
91
|
+
var val = el.getAttribute("data-value") || el.getAttribute("aria-valuenow") || text;
|
|
92
|
+
return JSON.stringify({
|
|
93
|
+
exists: "true",
|
|
94
|
+
isNative: "false",
|
|
95
|
+
value: val,
|
|
96
|
+
selectedText: text
|
|
97
|
+
});
|
|
98
|
+
} catch(e) {
|
|
99
|
+
return JSON.stringify({ exists: "false", error: e.message });
|
|
100
|
+
}
|
|
100
101
|
})()`,
|
|
101
102
|
returnByValue: true
|
|
102
103
|
});
|
|
@@ -104,88 +105,88 @@ export async function browserSelect(session, options, _config) {
|
|
|
104
105
|
};
|
|
105
106
|
const action = async () => {
|
|
106
107
|
const { result } = await session.Runtime.evaluate({
|
|
107
|
-
expression: `(function(selector, value) {
|
|
108
|
-
try {
|
|
109
|
-
const el = document.querySelector(selector);
|
|
110
|
-
if (!el) return 'Element not found';
|
|
111
|
-
|
|
112
|
-
// ── NATIVE <select> ELEMENT ──────────────────────────────────────
|
|
113
|
-
if (el.tagName === 'SELECT') {
|
|
114
|
-
let foundIndex = -1;
|
|
115
|
-
for (let i = 0; i < el.options.length; i++) {
|
|
116
|
-
const opt = el.options[i];
|
|
117
|
-
if (opt.value === value || opt.text === value || opt.text.trim() === value.trim()) {
|
|
118
|
-
foundIndex = i;
|
|
119
|
-
break;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
if (foundIndex === -1) return 'Option not found in <select>: ' + value;
|
|
123
|
-
|
|
124
|
-
el.selectedIndex = foundIndex;
|
|
125
|
-
|
|
126
|
-
const nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLSelectElement.prototype, 'value')?.set;
|
|
127
|
-
if (nativeSetter) nativeSetter.call(el, el.options[foundIndex].value);
|
|
128
|
-
|
|
129
|
-
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
130
|
-
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
131
|
-
el.options[foundIndex].dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
|
132
|
-
|
|
133
|
-
return 'selected:' + el.selectedIndex;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// ── CUSTOM ARIA / REACT / RADIX / SHADCN DROPDOWN ────────────────
|
|
137
|
-
// 1. Open popover if closed
|
|
138
|
-
const isExpanded = el.getAttribute('aria-expanded') === 'true';
|
|
139
|
-
if (!isExpanded) {
|
|
140
|
-
el.click();
|
|
141
|
-
el.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }));
|
|
142
|
-
el.dispatchEvent(new MouseEvent('mouseup', { bubbles: true }));
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// 2. Scan options in open popover / listbox
|
|
146
|
-
const targetText = value.toLowerCase().trim();
|
|
147
|
-
const optionSelectors = [
|
|
148
|
-
'[role="option"]',
|
|
149
|
-
'[role="menuitem"]',
|
|
150
|
-
'[role="treeitem"]',
|
|
151
|
-
'[data-radix-collection-item]',
|
|
152
|
-
'li',
|
|
153
|
-
'.option',
|
|
154
|
-
'.select-option',
|
|
155
|
-
'[data-value]'
|
|
156
|
-
];
|
|
157
|
-
|
|
158
|
-
let candidates = Array.from(document.querySelectorAll(optionSelectors.join(',')));
|
|
159
|
-
// Fallback to all visible clickable items inside active popovers/listboxes
|
|
160
|
-
if (candidates.length === 0) {
|
|
161
|
-
const containers = document.querySelectorAll('[role="listbox"], [role="menu"], [data-radix-popper-content-wrapper], .select-popover, .dropdown-menu');
|
|
162
|
-
containers.forEach(c => {
|
|
163
|
-
candidates.push(...Array.from(c.querySelectorAll('*')));
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
let matchedEl = null;
|
|
168
|
-
for (const cand of candidates) {
|
|
169
|
-
const text = (cand.innerText || cand.textContent || '').toLowerCase().trim();
|
|
170
|
-
const valAttr = (cand.getAttribute('data-value') || cand.getAttribute('value') || '').toLowerCase().trim();
|
|
171
|
-
if (text === targetText || valAttr === targetText || text.includes(targetText)) {
|
|
172
|
-
matchedEl = cand;
|
|
173
|
-
break;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (!matchedEl) {
|
|
178
|
-
return 'Custom option not found: ' + value;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
matchedEl.click();
|
|
182
|
-
matchedEl.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }));
|
|
183
|
-
matchedEl.dispatchEvent(new MouseEvent('mouseup', { bubbles: true }));
|
|
184
|
-
|
|
185
|
-
return 'selected:custom';
|
|
186
|
-
} catch (e) {
|
|
187
|
-
return e.message || 'Unknown error';
|
|
188
|
-
}
|
|
108
|
+
expression: `(function(selector, value) {
|
|
109
|
+
try {
|
|
110
|
+
const el = document.querySelector(selector);
|
|
111
|
+
if (!el) return 'Element not found';
|
|
112
|
+
|
|
113
|
+
// ── NATIVE <select> ELEMENT ──────────────────────────────────────
|
|
114
|
+
if (el.tagName === 'SELECT') {
|
|
115
|
+
let foundIndex = -1;
|
|
116
|
+
for (let i = 0; i < el.options.length; i++) {
|
|
117
|
+
const opt = el.options[i];
|
|
118
|
+
if (opt.value === value || opt.text === value || opt.text.trim() === value.trim()) {
|
|
119
|
+
foundIndex = i;
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (foundIndex === -1) return 'Option not found in <select>: ' + value;
|
|
124
|
+
|
|
125
|
+
el.selectedIndex = foundIndex;
|
|
126
|
+
|
|
127
|
+
const nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLSelectElement.prototype, 'value')?.set;
|
|
128
|
+
if (nativeSetter) nativeSetter.call(el, el.options[foundIndex].value);
|
|
129
|
+
|
|
130
|
+
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
131
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
132
|
+
el.options[foundIndex].dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
|
133
|
+
|
|
134
|
+
return 'selected:' + el.selectedIndex;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ── CUSTOM ARIA / REACT / RADIX / SHADCN DROPDOWN ────────────────
|
|
138
|
+
// 1. Open popover if closed
|
|
139
|
+
const isExpanded = el.getAttribute('aria-expanded') === 'true';
|
|
140
|
+
if (!isExpanded) {
|
|
141
|
+
el.click();
|
|
142
|
+
el.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }));
|
|
143
|
+
el.dispatchEvent(new MouseEvent('mouseup', { bubbles: true }));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// 2. Scan options in open popover / listbox
|
|
147
|
+
const targetText = value.toLowerCase().trim();
|
|
148
|
+
const optionSelectors = [
|
|
149
|
+
'[role="option"]',
|
|
150
|
+
'[role="menuitem"]',
|
|
151
|
+
'[role="treeitem"]',
|
|
152
|
+
'[data-radix-collection-item]',
|
|
153
|
+
'li',
|
|
154
|
+
'.option',
|
|
155
|
+
'.select-option',
|
|
156
|
+
'[data-value]'
|
|
157
|
+
];
|
|
158
|
+
|
|
159
|
+
let candidates = Array.from(document.querySelectorAll(optionSelectors.join(',')));
|
|
160
|
+
// Fallback to all visible clickable items inside active popovers/listboxes
|
|
161
|
+
if (candidates.length === 0) {
|
|
162
|
+
const containers = document.querySelectorAll('[role="listbox"], [role="menu"], [data-radix-popper-content-wrapper], .select-popover, .dropdown-menu');
|
|
163
|
+
containers.forEach(c => {
|
|
164
|
+
candidates.push(...Array.from(c.querySelectorAll('*')));
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
let matchedEl = null;
|
|
169
|
+
for (const cand of candidates) {
|
|
170
|
+
const text = (cand.innerText || cand.textContent || '').toLowerCase().trim();
|
|
171
|
+
const valAttr = (cand.getAttribute('data-value') || cand.getAttribute('value') || '').toLowerCase().trim();
|
|
172
|
+
if (text === targetText || valAttr === targetText || text.includes(targetText)) {
|
|
173
|
+
matchedEl = cand;
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (!matchedEl) {
|
|
179
|
+
return 'Custom option not found: ' + value;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
matchedEl.click();
|
|
183
|
+
matchedEl.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }));
|
|
184
|
+
matchedEl.dispatchEvent(new MouseEvent('mouseup', { bubbles: true }));
|
|
185
|
+
|
|
186
|
+
return 'selected:custom';
|
|
187
|
+
} catch (e) {
|
|
188
|
+
return e.message || 'Unknown error';
|
|
189
|
+
}
|
|
189
190
|
})(${escapeSelector(selector)}, ${escapeSelector(value)})`,
|
|
190
191
|
returnByValue: true
|
|
191
192
|
});
|
|
@@ -208,7 +209,12 @@ export async function browserSelect(session, options, _config) {
|
|
|
208
209
|
input: { ref: selector, value },
|
|
209
210
|
result: { success: true, stuck: false },
|
|
210
211
|
});
|
|
211
|
-
return {
|
|
212
|
+
return {
|
|
213
|
+
success: true,
|
|
214
|
+
value,
|
|
215
|
+
selected: value,
|
|
216
|
+
message: `Selected value "${value}" on ${beforeState.isNative === "true" ? "native <select>" : "custom UI dropdown"} (${confirm.attempts} attempts, settled ${confirm.settleMs}ms)`
|
|
217
|
+
};
|
|
212
218
|
}
|
|
213
219
|
recordAction({
|
|
214
220
|
timestamp: new Date().toISOString(),
|
|
@@ -218,6 +224,7 @@ export async function browserSelect(session, options, _config) {
|
|
|
218
224
|
});
|
|
219
225
|
return {
|
|
220
226
|
success: false,
|
|
227
|
+
value,
|
|
221
228
|
message: `Selection did not take effect. Before: value="${beforeState.value}". After: value="${confirm.after.value}".`
|
|
222
229
|
};
|
|
223
230
|
}
|
package/dist/tools/snapshot.js
CHANGED
|
@@ -14,12 +14,14 @@ export async function browserSnapshot(session, options, config) {
|
|
|
14
14
|
if (!filename || !filename.trim()) {
|
|
15
15
|
throw new Error("browser_snapshot requires a filename.");
|
|
16
16
|
}
|
|
17
|
-
//
|
|
18
|
-
|
|
19
|
-
if (!
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
// If caller provided an absolute path, respect it; otherwise store in snapshots directory
|
|
18
|
+
let filePath = filename;
|
|
19
|
+
if (!path.isAbsolute(filePath)) {
|
|
20
|
+
const snapshotDir = getOutputDir("snapshots", config);
|
|
21
|
+
filePath = path.join(snapshotDir, path.basename(filename));
|
|
22
|
+
}
|
|
23
|
+
if (!filePath.endsWith(".mhtml"))
|
|
24
|
+
filePath += ".mhtml";
|
|
23
25
|
if (action === "capture") {
|
|
24
26
|
// Always hide overlay before capture (default true); set hideOverlay: false to skip.
|
|
25
27
|
const shouldHide = options.hideOverlay !== false;
|
package/dist/tools/storage.d.ts
CHANGED
|
@@ -7,12 +7,16 @@ import CDP from "chrome-remote-interface";
|
|
|
7
7
|
import type { Config } from "../config/loader.js";
|
|
8
8
|
export interface StorageOptions {
|
|
9
9
|
scope: "local" | "session";
|
|
10
|
-
action: "get" | "set" | "clear";
|
|
10
|
+
action: "get" | "set" | "remove" | "list" | "clear";
|
|
11
11
|
key?: string;
|
|
12
12
|
value?: string;
|
|
13
13
|
}
|
|
14
14
|
export declare function browserStorage(session: CDP.Client, options: StorageOptions, _config: Config): Promise<{
|
|
15
15
|
success: boolean;
|
|
16
16
|
result?: string | null;
|
|
17
|
+
data?: {
|
|
18
|
+
key: string;
|
|
19
|
+
value: string | null;
|
|
20
|
+
}[];
|
|
17
21
|
message?: string;
|
|
18
22
|
}>;
|
package/dist/tools/storage.js
CHANGED
|
@@ -19,6 +19,22 @@ export async function browserStorage(session, options, _config) {
|
|
|
19
19
|
});
|
|
20
20
|
return { success: true, message: `Set ${scope}Storage key ${key}` };
|
|
21
21
|
}
|
|
22
|
+
else if (action === "remove") {
|
|
23
|
+
if (!key)
|
|
24
|
+
throw new Error("browser_storage remove requires a 'key'");
|
|
25
|
+
await session.Runtime.evaluate({
|
|
26
|
+
expression: `(function(k){ ${scope}Storage.removeItem(k); })(${JSON.stringify(key)})`
|
|
27
|
+
});
|
|
28
|
+
return { success: true, message: `Removed ${scope}Storage key ${key}` };
|
|
29
|
+
}
|
|
30
|
+
else if (action === "list") {
|
|
31
|
+
const { result } = await session.Runtime.evaluate({
|
|
32
|
+
expression: `(function(){ var out = []; var s = ${scope}Storage; for (var i = 0; i < s.length; i++) { var k = s.key(i); out.push({ key: k, value: s.getItem(k) }); } return out; })()`,
|
|
33
|
+
returnByValue: true
|
|
34
|
+
});
|
|
35
|
+
const data = Array.isArray(result.value) ? result.value : [];
|
|
36
|
+
return { success: true, data, message: `${scope}Storage has ${data.length} key(s)` };
|
|
37
|
+
}
|
|
22
38
|
else if (action === "clear") {
|
|
23
39
|
await session.Runtime.evaluate({
|
|
24
40
|
expression: `${scope}Storage.clear()`
|
package/dist/tools/tabs.d.ts
CHANGED
|
@@ -18,11 +18,13 @@ export declare function WeavetabOpen(url: string, config: Config, reuse?: boolea
|
|
|
18
18
|
reused?: boolean;
|
|
19
19
|
}>;
|
|
20
20
|
export declare function WeavetabSwitch(tabId: string, config: Config, appName?: string): Promise<{
|
|
21
|
-
success:
|
|
22
|
-
tab_id
|
|
23
|
-
title
|
|
24
|
-
url
|
|
25
|
-
contextSwitchCostMs
|
|
21
|
+
success: boolean;
|
|
22
|
+
tab_id?: string;
|
|
23
|
+
title?: string;
|
|
24
|
+
url?: string;
|
|
25
|
+
contextSwitchCostMs?: number;
|
|
26
|
+
error_code?: string;
|
|
27
|
+
message?: string;
|
|
26
28
|
}>;
|
|
27
29
|
export declare function WeavetabList(config: Config, appName?: string): Promise<{
|
|
28
30
|
tabs: TabInfo[];
|
package/dist/tools/tabs.js
CHANGED
|
@@ -109,14 +109,31 @@ async function enforceMaxTabs(config) {
|
|
|
109
109
|
lockedTabIds.delete(victim.id);
|
|
110
110
|
logAction("browser_tabs", victim.id, `auto-closed idle tab: ${victim.title}`);
|
|
111
111
|
}
|
|
112
|
+
function normalizeUrl(u) {
|
|
113
|
+
try {
|
|
114
|
+
const parsed = new URL(u);
|
|
115
|
+
let path = parsed.pathname;
|
|
116
|
+
if (path.length > 1 && path.endsWith('/')) {
|
|
117
|
+
path = path.slice(0, -1);
|
|
118
|
+
}
|
|
119
|
+
return `${parsed.protocol}//${parsed.host.toLowerCase()}${path}${parsed.search}${parsed.hash}`.replace(/\/$/, "");
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return u.trim().replace(/\/+$/, "").toLowerCase();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
112
125
|
// ─── Tools ────────────────────────────────────────────────────────────────────
|
|
113
126
|
export async function WeavetabOpen(url, config, reuse = true, appName) {
|
|
114
|
-
// Infer appName from active session if omitted
|
|
115
127
|
let resolvedAppName = appName;
|
|
116
128
|
if (!resolvedAppName) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
129
|
+
// Only inherit the sticky Electron app when no web-browser session is active
|
|
130
|
+
// (mirrors inferTargetApp()). Otherwise a plain `open` must target the browser:
|
|
131
|
+
// Electron targets cannot create new tabs via CDP (Target.createTarget fails).
|
|
132
|
+
if (!sessionRegistry.isBrowserSessionActive()) {
|
|
133
|
+
const lastApp = sessionRegistry.getLastActiveApp();
|
|
134
|
+
if (lastApp) {
|
|
135
|
+
resolvedAppName = lastApp;
|
|
136
|
+
}
|
|
120
137
|
}
|
|
121
138
|
}
|
|
122
139
|
const port = resolvePort(config, resolvedAppName);
|
|
@@ -124,7 +141,8 @@ export async function WeavetabOpen(url, config, reuse = true, appName) {
|
|
|
124
141
|
if (reuse && url) {
|
|
125
142
|
try {
|
|
126
143
|
const targets = await getTabList(getConfigAgentId(config), resolvedAppName);
|
|
127
|
-
const
|
|
144
|
+
const targetNorm = normalizeUrl(url);
|
|
145
|
+
const existing = targets.find((t) => normalizeUrl(t.url) === targetNorm);
|
|
128
146
|
if (existing) {
|
|
129
147
|
const client = await connectToTab(existing.id, undefined, undefined, undefined, resolvedAppName);
|
|
130
148
|
await client.session.Target.activateTarget({ targetId: existing.id });
|
|
@@ -141,39 +159,74 @@ export async function WeavetabOpen(url, config, reuse = true, appName) {
|
|
|
141
159
|
}
|
|
142
160
|
const bctxId = resolvedAppName ? undefined : sessionRegistry.getBrowserContextIdForAgent(getConfigAgentId(config));
|
|
143
161
|
const metaSession = await CDP({ host: "127.0.0.1", port });
|
|
144
|
-
|
|
162
|
+
let targetId;
|
|
163
|
+
try {
|
|
164
|
+
({ targetId } = await metaSession.Target.createTarget({ url, browserContextId: bctxId }));
|
|
165
|
+
}
|
|
166
|
+
catch (err) {
|
|
167
|
+
await metaSession.close().catch(() => { });
|
|
168
|
+
if (resolvedAppName) {
|
|
169
|
+
throw new Error(`[!] Electron app "${resolvedAppName}" does not support opening new tabs or windows via CDP ` +
|
|
170
|
+
`(Target.createTarget: ${err?.message || err}). Use browser_navigate with ` +
|
|
171
|
+
`{ app: "${resolvedAppName}", url: "${url}" } to load this URL in the app's existing window instead.`);
|
|
172
|
+
}
|
|
173
|
+
throw err;
|
|
174
|
+
}
|
|
145
175
|
await metaSession.close();
|
|
146
176
|
recordTabActivity(targetId);
|
|
147
|
-
await connectToTab(targetId, port, getConfigAgentId(config), bctxId,
|
|
148
|
-
|
|
149
|
-
|
|
177
|
+
const client = await connectToTab(targetId, port, getConfigAgentId(config), bctxId, resolvedAppName);
|
|
178
|
+
try {
|
|
179
|
+
await client.session.Target.activateTarget({ targetId });
|
|
180
|
+
}
|
|
181
|
+
catch { /* non-fatal */ }
|
|
182
|
+
sessionRegistry.setActive(targetId, getConfigAgentId(config), resolvedAppName);
|
|
183
|
+
await evaluateTabSemantics(targetId, resolvedAppName);
|
|
184
|
+
logAction("browser_tabs", url, `opened and activated ${targetId}`);
|
|
150
185
|
return { success: true, tabId: targetId, url };
|
|
151
186
|
}
|
|
152
187
|
export async function WeavetabSwitch(tabId, config, appName) {
|
|
153
|
-
const client = await connectToTab(tabId, undefined, undefined, undefined, appName);
|
|
154
|
-
const startSwitch = Date.now();
|
|
155
|
-
await client.session.Target.activateTarget({ targetId: tabId });
|
|
156
|
-
sessionRegistry.setActive(tabId, getConfigAgentId(config), appName);
|
|
157
|
-
recordTabActivity(tabId);
|
|
158
|
-
const { frameTree } = await client.session.Page.getFrameTree();
|
|
159
|
-
let title = tabId;
|
|
160
188
|
try {
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
189
|
+
const client = await connectToTab(tabId, undefined, undefined, undefined, appName);
|
|
190
|
+
const startSwitch = Date.now();
|
|
191
|
+
await client.session.Target.activateTarget({ targetId: tabId });
|
|
192
|
+
sessionRegistry.setActive(tabId, getConfigAgentId(config), appName);
|
|
193
|
+
recordTabActivity(tabId);
|
|
194
|
+
const { frameTree } = await client.session.Page.getFrameTree();
|
|
195
|
+
let title = client.title || "";
|
|
196
|
+
try {
|
|
197
|
+
const titleRes = await client.session.Runtime.evaluate({ expression: 'document.title', returnByValue: true });
|
|
198
|
+
if (titleRes?.result?.value) {
|
|
199
|
+
title = titleRes.result.value;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
catch { }
|
|
203
|
+
const url = frameTree?.frame?.url || client.url || "";
|
|
204
|
+
if (!title) {
|
|
205
|
+
title = frameTree?.frame?.name || url || tabId;
|
|
164
206
|
}
|
|
207
|
+
const cost = Date.now() - startSwitch;
|
|
208
|
+
logAction("browser_tabs", tabId, `switched to ${title} (cost: ${cost}ms)`);
|
|
209
|
+
resetLoopCounter("tab_switch");
|
|
210
|
+
return { success: true, tab_id: tabId, title, url, contextSwitchCostMs: cost };
|
|
211
|
+
}
|
|
212
|
+
catch (err) {
|
|
213
|
+
return {
|
|
214
|
+
success: false,
|
|
215
|
+
error_code: "TAB_NOT_FOUND",
|
|
216
|
+
message: `Failed to switch to tab ${tabId}: ${err?.message || err}`,
|
|
217
|
+
};
|
|
165
218
|
}
|
|
166
|
-
catch { }
|
|
167
|
-
const url = frameTree.frame.url;
|
|
168
|
-
const cost = Date.now() - startSwitch;
|
|
169
|
-
logAction("browser_tabs", tabId, `switched to ${title} (cost: ${cost}ms)`);
|
|
170
|
-
resetLoopCounter("tab_switch");
|
|
171
|
-
return { success: true, tab_id: tabId, title, url, contextSwitchCostMs: cost };
|
|
172
219
|
}
|
|
173
220
|
export async function WeavetabList(config, appName) {
|
|
174
221
|
const targets = await getTabList(getConfigAgentId(config), appName);
|
|
175
222
|
const activeTargetId = sessionRegistry.getActive(getConfigAgentId(config), appName)?.targetId;
|
|
176
223
|
const tabs = targets
|
|
224
|
+
.filter((t) => {
|
|
225
|
+
if (t.url && (t.url.startsWith("chrome-extension://") || t.url.includes("/hud/index.html"))) {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
return true;
|
|
229
|
+
})
|
|
177
230
|
.map((t) => ({
|
|
178
231
|
id: t.id,
|
|
179
232
|
title: t.title,
|
|
@@ -186,7 +239,7 @@ export async function WeavetabList(config, appName) {
|
|
|
186
239
|
? "locked"
|
|
187
240
|
: isIdleTab(t.id, activeTargetId)
|
|
188
241
|
? "idle"
|
|
189
|
-
: "
|
|
242
|
+
: "alive",
|
|
190
243
|
}));
|
|
191
244
|
return { tabs };
|
|
192
245
|
}
|
package/dist/tools/thought.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/tools/thought.ts
|
|
2
2
|
// browser_thought — agent-to-user communication via green thought bubbles
|
|
3
|
-
// Merged: browser_feedback (one-way) + browser_ask (two-way with input)
|
|
3
|
+
// Merged: browser_feedback (one-way) + legacy browser_ask (two-way with input) into browser_thoughts
|
|
4
4
|
import { logAction } from "../audit/logger.js";
|
|
5
5
|
import { sendToExtension } from "../cdp/helpers.js";
|
|
6
6
|
function sleep(ms) {
|
|
@@ -38,10 +38,10 @@ export async function browserThought(session, args) {
|
|
|
38
38
|
await sleep(500);
|
|
39
39
|
try {
|
|
40
40
|
const { result } = await session.Runtime.evaluate({
|
|
41
|
-
expression: `(() => { try {
|
|
42
|
-
var el = document.getElementById('__wt_ask_bridge');
|
|
43
|
-
if (!el || el.dataset.status === 'pending') return null;
|
|
44
|
-
return JSON.stringify({ status: el.dataset.status, result: el.dataset.result });
|
|
41
|
+
expression: `(() => { try {
|
|
42
|
+
var el = document.getElementById('__wt_ask_bridge');
|
|
43
|
+
if (!el || el.dataset.status === 'pending') return null;
|
|
44
|
+
return JSON.stringify({ status: el.dataset.status, result: el.dataset.result });
|
|
45
45
|
} catch(e) { return null; } })()`,
|
|
46
46
|
returnByValue: true,
|
|
47
47
|
});
|
|
@@ -50,9 +50,9 @@ export async function browserThought(session, args) {
|
|
|
50
50
|
if (data.status === 'submitted') {
|
|
51
51
|
logAction("browser_thought", "answered", data.result);
|
|
52
52
|
await session.Runtime.evaluate({
|
|
53
|
-
expression: `(function() {
|
|
54
|
-
var el = document.getElementById('__wt_ask_bridge');
|
|
55
|
-
if (el) el.remove();
|
|
53
|
+
expression: `(function() {
|
|
54
|
+
var el = document.getElementById('__wt_ask_bridge');
|
|
55
|
+
if (el) el.remove();
|
|
56
56
|
})()`
|
|
57
57
|
}).catch(() => { });
|
|
58
58
|
return { success: true, response: data.result, status: "answered" };
|