@weavetab/mcp 2.5.0-beta.2 → 2.5.0-beta.5
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 +1 -1
- package/ARCHITECTURE.md +6 -6
- package/LICENSE +216 -631
- package/README.md +153 -1074
- package/SECURITY.md +33 -22
- package/TROUBLESHOOTING.md +2 -2
- package/dist/cdp/bridge.js +2 -1
- package/dist/cdp/connector.d.ts +2 -0
- package/dist/cdp/connector.js +10 -6
- package/dist/cdp/helpers.d.ts +4 -0
- package/dist/cdp/helpers.js +181 -0
- package/dist/cdp/omni_broker.d.ts +1 -1
- package/dist/cdp/omni_broker.js +1 -1
- package/dist/cdp/walker.d.ts +25 -1
- package/dist/cdp/walker.js +304 -7
- package/dist/cli/plugin.d.ts +1 -0
- package/dist/cli/plugin.js +81 -18
- package/dist/cli/updater.d.ts +1 -1
- package/dist/cli/updater.js +27 -7
- package/dist/config/loader.d.ts +9 -1
- package/dist/config/loader.js +42 -3
- package/dist/desktop/discovery.d.ts +44 -0
- package/dist/desktop/discovery.js +364 -0
- package/dist/index.js +137 -21
- package/dist/intelligence/marks.d.ts +36 -0
- package/dist/intelligence/marks.js +30 -0
- package/dist/intelligence/patternCompiler.d.ts +118 -0
- package/dist/intelligence/patternCompiler.js +259 -0
- package/dist/intelligence/sessionMemory.d.ts +2 -0
- package/dist/intelligence/sessionMemory.js +18 -8
- package/dist/overlay/injector.js +2 -2
- package/dist/plugin/interface.d.ts +88 -5
- package/dist/plugin/loader.d.ts +12 -1
- package/dist/plugin/loader.js +280 -70
- package/dist/plugin/pipeline.d.ts +75 -0
- package/dist/plugin/pipeline.js +183 -0
- package/dist/plugin/registry.d.ts +2 -1
- package/dist/plugin/registry.js +24 -14
- package/dist/security/secrets.d.ts +7 -0
- package/dist/security/secrets.js +37 -0
- package/dist/sensors/actionability.js +1 -2
- package/dist/sensors/guard.js +4 -10
- package/dist/sensors/mutations.js +12 -0
- package/dist/sensors/operation_monitor.js +1 -0
- package/dist/server.d.ts +1 -1
- package/dist/server.js +453 -178
- package/dist/state/agent.d.ts +1 -0
- package/dist/state/agent.js +3 -0
- package/dist/state/delta.d.ts +12 -4
- package/dist/state/delta.js +67 -14
- package/dist/state/session.d.ts +6 -0
- package/dist/state/session.js +27 -0
- package/dist/tools/browser_detect.d.ts +16 -3
- package/dist/tools/browser_detect.js +52 -7
- package/dist/tools/burst.d.ts +17 -8
- package/dist/tools/burst.js +236 -77
- package/dist/tools/captcha.js +1 -1
- package/dist/tools/checkpoint.d.ts +38 -0
- package/dist/tools/checkpoint.js +271 -0
- package/dist/tools/click.d.ts +1 -0
- package/dist/tools/click.js +74 -70
- package/dist/tools/design_extract.js +1 -1
- package/dist/tools/eval.js +1 -1
- package/dist/tools/fill.d.ts +20 -1
- package/dist/tools/fill.js +199 -40
- package/dist/tools/find.d.ts +16 -0
- package/dist/tools/find.js +67 -44
- package/dist/tools/github/index.d.ts +19 -4
- package/dist/tools/github/index.js +40 -4
- package/dist/tools/highlight.d.ts +6 -2
- package/dist/tools/highlight.js +158 -72
- package/dist/tools/inspect.js +1 -1
- package/dist/tools/key.js +65 -56
- package/dist/tools/navigate.d.ts +1 -1
- package/dist/tools/navigate.js +34 -39
- package/dist/tools/pdf.js +21 -13
- package/dist/tools/pointer.d.ts +18 -3
- package/dist/tools/pointer.js +89 -1
- package/dist/tools/read.d.ts +28 -8
- package/dist/tools/read.js +230 -56
- package/dist/tools/scrape.js +67 -13
- package/dist/tools/screenshot.js +21 -16
- package/dist/tools/scroll.js +6 -11
- package/dist/tools/select.js +8 -2
- package/dist/tools/snapshot.js +3 -34
- package/dist/tools/tabs.js +15 -7
- package/dist/tools/thought.d.ts +2 -0
- package/dist/tools/thought.js +13 -14
- package/dist/tools/type.d.ts +2 -0
- package/dist/tools/type.js +107 -58
- package/dist/tools/type_secret.js +42 -76
- package/dist/tools/upload.js +1 -1
- package/dist/tools/video_compiler.d.ts +1 -1
- package/dist/tools/video_compiler.js +11 -12
- package/dist/version.d.ts +5 -0
- package/dist/version.js +5 -0
- package/docs/DOCUMENTATION.md +37 -41
- package/docs/PLUGINS.md +121 -0
- package/docs/TOOLS.md +54 -0
- package/docs/architecture/blind-injection.md +83 -20
- package/docs/architecture/overview.md +49 -49
- package/docs/architecture/security.md +8 -7
- package/docs/architecture/transport.md +2 -2
- package/docs/cli/cli-commands.md +7 -1
- package/docs/cli/cli-flags.md +15 -7
- package/docs/cli/cli-scripting.md +6 -3
- package/docs/configuration/config-locations.md +3 -4
- package/docs/configuration/env-variables.md +4 -4
- package/docs/configuration/folders.md +55 -54
- package/docs/desktop-automation/electron-apps.md +214 -162
- package/docs/get-started/introduction.md +60 -60
- package/docs/get-started/setup-wizard.md +7 -6
- package/docs/intelligence/site-profiles.md +151 -0
- package/docs/tools/tools.md +631 -293
- package/docs/troubleshooting/trouble-browser.md +1 -1
- package/docs/troubleshooting/trouble-loop.md +31 -31
- package/ext-dist/background.js +10 -62
- package/ext-dist/content.js +333 -920
- package/ext-dist/hud/hud.js +9 -9
- package/ext-dist/manifest.json +2 -6
- package/ext-dist/styles.css +6 -1
- package/icon.png +0 -0
- package/mcp.json +87 -71
- package/package.json +118 -119
- package/server.json +5 -5
- package/ext-dist/sidepanel/sidepanel.js +0 -25
- package/ext-dist/sidepanel.html +0 -547
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Weavetab — tools/highlight.ts
|
|
3
|
-
* browser_highlight: draws
|
|
4
|
-
*
|
|
3
|
+
* browser_highlight: draws visual consideration, target lock, danger/success or shimmer overlays.
|
|
4
|
+
* Supports multiple candidate refs, custom styles, floating labels, and multi-tier fallbacks.
|
|
5
5
|
*/
|
|
6
6
|
import CDP from "chrome-remote-interface";
|
|
7
7
|
import type { Config } from "../config/loader.js";
|
|
8
|
+
export type HighlightStyle = "consideration" | "target" | "danger" | "success" | "shimmer";
|
|
8
9
|
export interface HighlightOptions {
|
|
9
10
|
selector?: string;
|
|
10
11
|
id?: string;
|
|
11
12
|
ref?: string;
|
|
13
|
+
refs?: string[];
|
|
12
14
|
target?: string;
|
|
13
15
|
label?: string;
|
|
14
16
|
nodeId?: number;
|
|
15
17
|
color?: string;
|
|
18
|
+
style?: HighlightStyle;
|
|
19
|
+
duration?: number;
|
|
16
20
|
}
|
|
17
21
|
export declare function browserHighlight(session: CDP.Client, options: HighlightOptions, _config: Config): Promise<{
|
|
18
22
|
success: boolean;
|
package/dist/tools/highlight.js
CHANGED
|
@@ -1,31 +1,95 @@
|
|
|
1
1
|
import { getConfigAgentId } from "../config/loader.js";
|
|
2
2
|
export async function browserHighlight(session, options, _config) {
|
|
3
|
-
const
|
|
4
|
-
|
|
3
|
+
const style = options.style || (options.refs && options.refs.length > 0 ? "consideration" : "shimmer");
|
|
4
|
+
const defaultDuration = style === "shimmer" ? 600 : 2000;
|
|
5
|
+
const duration = typeof options.duration === "number" && options.duration > 0 ? options.duration : defaultDuration;
|
|
6
|
+
const customLabel = options.label || "";
|
|
7
|
+
const customColor = options.color;
|
|
8
|
+
// Inject required keyframes once
|
|
5
9
|
await session.Runtime.evaluate({
|
|
6
10
|
expression: `(function(){
|
|
7
|
-
if (!document.getElementById('wt-
|
|
11
|
+
if (!document.getElementById('wt-highlight-styles')) {
|
|
8
12
|
var s = document.createElement('style');
|
|
9
|
-
s.id = 'wt-
|
|
10
|
-
s.textContent =
|
|
13
|
+
s.id = 'wt-highlight-styles';
|
|
14
|
+
s.textContent = \`
|
|
15
|
+
@keyframes wt-shimmer {
|
|
16
|
+
0% { opacity: 0; background-position: -200% 0; }
|
|
17
|
+
15% { opacity: 0.9; }
|
|
18
|
+
40% { opacity: 0.7; }
|
|
19
|
+
85% { opacity: 0.3; }
|
|
20
|
+
100% { opacity: 0; background-position: 200% 0; }
|
|
21
|
+
}
|
|
22
|
+
@keyframes wt-consider-pulse {
|
|
23
|
+
0%, 100% { box-shadow: 0 0 0 1px rgba(56,189,248,0.7), 0 0 14px rgba(56,189,248,0.35); opacity: 0.92; }
|
|
24
|
+
50% { box-shadow: 0 0 0 2px rgba(56,189,248,0.95), 0 0 22px rgba(56,189,248,0.6); opacity: 1; }
|
|
25
|
+
}
|
|
26
|
+
@keyframes wt-target-lock {
|
|
27
|
+
0% { transform: scale(1.05); opacity: 0.5; }
|
|
28
|
+
100% { transform: scale(1); opacity: 1; }
|
|
29
|
+
}
|
|
30
|
+
@keyframes wt-danger-pulse {
|
|
31
|
+
0%, 100% { box-shadow: 0 0 0 1px rgba(239,68,68,0.7), 0 0 14px rgba(239,68,68,0.35); opacity: 0.92; }
|
|
32
|
+
50% { box-shadow: 0 0 0 2px rgba(239,68,68,0.95), 0 0 22px rgba(239,68,68,0.6); opacity: 1; }
|
|
33
|
+
}
|
|
34
|
+
@keyframes wt-success-glow {
|
|
35
|
+
0% { opacity: 0.6; box-shadow: 0 0 0 1px rgba(16,185,129,0.7); }
|
|
36
|
+
100% { opacity: 1; box-shadow: 0 0 0 2px rgba(16,185,129,0.9), 0 0 18px rgba(16,185,129,0.45); }
|
|
37
|
+
}
|
|
38
|
+
\`;
|
|
11
39
|
(document.head || document.documentElement).appendChild(s);
|
|
12
40
|
}
|
|
13
41
|
})()`,
|
|
14
42
|
returnByValue: true,
|
|
15
43
|
}).catch(() => { });
|
|
16
|
-
|
|
17
|
-
|
|
44
|
+
function getOverlayCss(styleName, durMs, colorOverride) {
|
|
45
|
+
if (colorOverride) {
|
|
46
|
+
return `border:2px solid ${colorOverride};box-shadow:0 0 14px ${colorOverride};background:${colorOverride}1a;border-radius:4px;transition:opacity 0.2s ease-out;`;
|
|
47
|
+
}
|
|
48
|
+
switch (styleName) {
|
|
49
|
+
case "consideration":
|
|
50
|
+
return `border:1.5px solid rgba(56,189,248,0.9);background:rgba(56,189,248,0.08);box-shadow:0 0 14px rgba(56,189,248,0.4);border-radius:4px;animation:wt-consider-pulse 1.4s ease-in-out infinite;`;
|
|
51
|
+
case "target":
|
|
52
|
+
return `border:2px solid rgba(245,158,11,0.95);background:rgba(245,158,11,0.12);box-shadow:0 0 16px rgba(245,158,11,0.45);border-radius:4px;animation:wt-target-lock 0.2s cubic-bezier(0.16,1,0.3,1);`;
|
|
53
|
+
case "danger":
|
|
54
|
+
return `border:1.5px solid rgba(239,68,68,0.95);background:rgba(239,68,68,0.1);box-shadow:0 0 16px rgba(239,68,68,0.45);border-radius:4px;animation:wt-danger-pulse 1.4s ease-in-out infinite;`;
|
|
55
|
+
case "success":
|
|
56
|
+
return `border:1.5px solid rgba(16,185,129,0.95);background:rgba(16,185,129,0.1);box-shadow:0 0 16px rgba(16,185,129,0.45);border-radius:4px;animation:wt-success-glow 0.3s ease-out forwards;`;
|
|
57
|
+
case "shimmer":
|
|
58
|
+
default:
|
|
59
|
+
return `background:linear-gradient(120deg,transparent 30%,rgba(139,92,246,0.3) 50%,transparent 70%);background-size:200% 100%;animation:wt-shimmer ${durMs / 1000}s ease-out forwards;border:1.5px solid rgba(139,92,246,0.6);border-radius:3px;box-shadow:0 0 8px rgba(139,92,246,0.25);`;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Draw overlay at raw coordinates
|
|
63
|
+
function makeCoordOverlay(x, y, w, h, badgeLabel) {
|
|
64
|
+
const styleCss = getOverlayCss(style, duration, customColor);
|
|
65
|
+
const escapedBadge = badgeLabel ? badgeLabel.replace(/"/g, '"') : '';
|
|
66
|
+
const badgeHtml = escapedBadge
|
|
67
|
+
? `<span style="position:absolute;top:-20px;left:0;background:#09090b;border:1px solid #27272a;color:#f4f4f5;font-size:10px;font-family:-apple-system,BlinkMacSystemFont,\\'Segoe UI\\',sans-serif;font-weight:500;padding:1px 6px;border-radius:4px;white-space:nowrap;pointer-events:none;box-shadow:0 2px 6px rgba(0,0,0,0.5);">${escapedBadge}</span>`
|
|
68
|
+
: '';
|
|
18
69
|
return `(function(){
|
|
19
70
|
var div = document.createElement('div');
|
|
20
|
-
div.
|
|
71
|
+
div.setAttribute('data-wt-ignore', 'true');
|
|
72
|
+
div.style.cssText = 'position:fixed;left:${x}px;top:${y}px;width:${w}px;height:${h}px;z-index:2147483647;pointer-events:none;${styleCss}';
|
|
73
|
+
if (${JSON.stringify(badgeHtml)}) {
|
|
74
|
+
div.innerHTML = ${JSON.stringify(badgeHtml)};
|
|
75
|
+
}
|
|
21
76
|
(document.getElementById('Weavetab-root')?.shadowRoot || document.getElementById('Weavetab-root') || document.body).appendChild(div);
|
|
22
|
-
setTimeout(function(){
|
|
77
|
+
setTimeout(function(){
|
|
78
|
+
div.style.transition = 'opacity 0.2s ease-out';
|
|
79
|
+
div.style.opacity = '0';
|
|
80
|
+
setTimeout(function(){ if(div.parentNode) div.parentNode.removeChild(div); }, 220);
|
|
81
|
+
}, ${duration});
|
|
23
82
|
return true;
|
|
24
83
|
})()`;
|
|
25
84
|
}
|
|
26
|
-
// Draw
|
|
27
|
-
function
|
|
85
|
+
// Draw overlay by querying selector
|
|
86
|
+
function makeSelectorOverlay(sel, badgeLabel) {
|
|
28
87
|
const escaped = sel.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
|
88
|
+
const styleCss = getOverlayCss(style, duration, customColor);
|
|
89
|
+
const escapedBadge = badgeLabel ? badgeLabel.replace(/"/g, '"') : '';
|
|
90
|
+
const badgeHtml = escapedBadge
|
|
91
|
+
? `<span style="position:absolute;top:-20px;left:0;background:#09090b;border:1px solid #27272a;color:#f4f4f5;font-size:10px;font-family:-apple-system,BlinkMacSystemFont,\\'Segoe UI\\',sans-serif;font-weight:500;padding:1px 6px;border-radius:4px;white-space:nowrap;pointer-events:none;box-shadow:0 2px 6px rgba(0,0,0,0.5);">${escapedBadge}</span>`
|
|
92
|
+
: '';
|
|
29
93
|
return `(function(){
|
|
30
94
|
var el;
|
|
31
95
|
try { el = document.querySelector('${escaped}'); } catch(e) {}
|
|
@@ -36,19 +100,26 @@ export async function browserHighlight(session, options, _config) {
|
|
|
36
100
|
var r = el.getBoundingClientRect();
|
|
37
101
|
if (r.width === 0 && r.height === 0) return false;
|
|
38
102
|
var div = document.createElement('div');
|
|
39
|
-
div.
|
|
103
|
+
div.setAttribute('data-wt-ignore', 'true');
|
|
104
|
+
div.style.cssText = 'position:fixed;left:'+r.left+'px;top:'+r.top+'px;width:'+r.width+'px;height:'+r.height+'px;z-index:2147483647;pointer-events:none;${styleCss}';
|
|
105
|
+
if (${JSON.stringify(badgeHtml)}) {
|
|
106
|
+
div.innerHTML = ${JSON.stringify(badgeHtml)};
|
|
107
|
+
}
|
|
40
108
|
(document.getElementById('Weavetab-root')?.shadowRoot || document.getElementById('Weavetab-root') || document.body).appendChild(div);
|
|
41
|
-
setTimeout(function(){
|
|
109
|
+
setTimeout(function(){
|
|
110
|
+
div.style.transition = 'opacity 0.2s ease-out';
|
|
111
|
+
div.style.opacity = '0';
|
|
112
|
+
setTimeout(function(){ if(div.parentNode) div.parentNode.removeChild(div); }, 220);
|
|
113
|
+
}, ${duration});
|
|
42
114
|
return true;
|
|
43
115
|
})()`;
|
|
44
116
|
}
|
|
45
|
-
async function trySelector(sel) {
|
|
46
|
-
const r = await session.Runtime.evaluate({ expression:
|
|
117
|
+
async function trySelector(sel, badgeLabel) {
|
|
118
|
+
const r = await session.Runtime.evaluate({ expression: makeSelectorOverlay(sel, badgeLabel), returnByValue: true })
|
|
47
119
|
.catch(() => ({ result: { value: false } }));
|
|
48
120
|
return !!r?.result?.value;
|
|
49
121
|
}
|
|
50
|
-
|
|
51
|
-
async function tryNodeHighlight(bnId) {
|
|
122
|
+
async function tryNodeHighlight(bnId, badgeLabel) {
|
|
52
123
|
try {
|
|
53
124
|
const { nodeIds } = await session.DOM.pushNodesByBackendIdsToFrontend({ backendNodeIds: [bnId] });
|
|
54
125
|
if (!nodeIds?.[0])
|
|
@@ -60,81 +131,96 @@ export async function browserHighlight(session, options, _config) {
|
|
|
60
131
|
const x = q[0], y = q[1], w = q[2] - q[0], h = q[5] - q[1];
|
|
61
132
|
if (w <= 0 || h <= 0)
|
|
62
133
|
return false;
|
|
63
|
-
const r = await session.Runtime.evaluate({ expression:
|
|
134
|
+
const r = await session.Runtime.evaluate({ expression: makeCoordOverlay(x, y, w, h, badgeLabel), returnByValue: true });
|
|
64
135
|
return !!r?.result?.value;
|
|
65
136
|
}
|
|
66
137
|
catch {
|
|
67
138
|
return false;
|
|
68
139
|
}
|
|
69
140
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (refId) {
|
|
76
|
-
const stripped = refId.replace(/^w:/, '');
|
|
141
|
+
async function highlightSingleTarget(targetIdentifier, badge) {
|
|
142
|
+
let sel = targetIdentifier;
|
|
143
|
+
let numericId = null;
|
|
144
|
+
if (targetIdentifier.startsWith("w:")) {
|
|
145
|
+
const stripped = targetIdentifier.slice(2);
|
|
77
146
|
numericId = parseInt(stripped, 10);
|
|
78
|
-
|
|
147
|
+
sel = `[data-wt-id="${stripped}"]`;
|
|
79
148
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
// Strategy 1: CSS selector query
|
|
85
|
-
if (resolvedSelector && await trySelector(resolvedSelector)) {
|
|
86
|
-
return { success: true, message: `Highlighted ${resolvedSelector}` };
|
|
87
|
-
}
|
|
88
|
-
// Strategy 2: Extension oracle coords (__Weavetab_getCoords)
|
|
89
|
-
if (resolvedSelector) {
|
|
149
|
+
if (await trySelector(sel, badge))
|
|
150
|
+
return true;
|
|
151
|
+
// Extension oracle coords fallback
|
|
90
152
|
const coordsRes = await session.Runtime.evaluate({
|
|
91
|
-
expression: `(window.__Weavetab_getCoords ||
|
|
153
|
+
expression: `(window.__Weavetab_getCoords || function(){return null;})(${JSON.stringify(sel)})`,
|
|
92
154
|
returnByValue: true,
|
|
93
155
|
}).catch(() => null);
|
|
94
156
|
const cv = coordsRes?.result?.value;
|
|
95
157
|
if (cv?.raw) {
|
|
96
158
|
const { left, top, w, h } = cv.raw;
|
|
97
|
-
const r = await session.Runtime.evaluate({ expression:
|
|
159
|
+
const r = await session.Runtime.evaluate({ expression: makeCoordOverlay(left, top, w, h, badge), returnByValue: true });
|
|
98
160
|
if (r?.result?.value)
|
|
99
|
-
return
|
|
161
|
+
return true;
|
|
100
162
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const cachedMap = activeClient?.url ? getCachedActionMap(targetId, activeClient.url) : null;
|
|
114
|
-
if (cachedMap?.elements) {
|
|
115
|
-
const el = cachedMap.elements.find(e => {
|
|
116
|
-
const n = parseInt(String(e.id).replace(/^w:/, ''), 10);
|
|
117
|
-
return n === numericId;
|
|
118
|
-
});
|
|
119
|
-
if (el?.selector && await trySelector(el.selector)) {
|
|
120
|
-
return { success: true, message: `Highlighted w:${numericId} via cache selector` };
|
|
121
|
-
}
|
|
122
|
-
if (el?.backendNodeId && await tryNodeHighlight(el.backendNodeId)) {
|
|
123
|
-
return { success: true, message: `Highlighted w:${numericId} via action map backendNodeId` };
|
|
124
|
-
}
|
|
125
|
-
// Strategy 5: coordinate fallback from cached element position
|
|
126
|
-
if (el?.x !== undefined && el?.y !== undefined) {
|
|
127
|
-
const ew = el.width ?? 80, eh = el.height ?? 24;
|
|
128
|
-
const r = await session.Runtime.evaluate({
|
|
129
|
-
expression: makeCoordShimmer(el.x - ew / 2, el.y - eh / 2, ew, eh),
|
|
130
|
-
returnByValue: true,
|
|
163
|
+
// Action map cache fallback
|
|
164
|
+
if (numericId !== null) {
|
|
165
|
+
try {
|
|
166
|
+
const { getCachedActionMap } = await import('../state/cache.js');
|
|
167
|
+
const { sessionRegistry } = await import('../state/session.js');
|
|
168
|
+
const activeClient = sessionRegistry.getActive(getConfigAgentId(_config));
|
|
169
|
+
const targetId = activeClient?.targetId || 'default';
|
|
170
|
+
const cachedMap = activeClient?.url ? getCachedActionMap(targetId, activeClient.url) : null;
|
|
171
|
+
if (cachedMap?.elements) {
|
|
172
|
+
const el = cachedMap.elements.find(e => {
|
|
173
|
+
const n = parseInt(String(e.id).replace(/^w:/, ''), 10);
|
|
174
|
+
return n === numericId;
|
|
131
175
|
});
|
|
132
|
-
if (
|
|
133
|
-
return
|
|
176
|
+
if (el?.selector && await trySelector(el.selector, badge))
|
|
177
|
+
return true;
|
|
178
|
+
if (el?.backendNodeId && await tryNodeHighlight(el.backendNodeId, badge))
|
|
179
|
+
return true;
|
|
180
|
+
if (el?.x !== undefined && el?.y !== undefined) {
|
|
181
|
+
const ew = el.width ?? 80, eh = el.height ?? 24;
|
|
182
|
+
const r = await session.Runtime.evaluate({
|
|
183
|
+
expression: makeCoordOverlay(el.x - ew / 2, el.y - eh / 2, ew, eh, badge),
|
|
184
|
+
returnByValue: true,
|
|
185
|
+
});
|
|
186
|
+
if (r?.result?.value)
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
134
189
|
}
|
|
135
190
|
}
|
|
191
|
+
catch { /* ignore */ }
|
|
192
|
+
}
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
// ── Multi-ref consideration overlay ───────────────────────────────────────
|
|
196
|
+
if (options.refs && Array.isArray(options.refs) && options.refs.length > 0) {
|
|
197
|
+
let successCount = 0;
|
|
198
|
+
for (let i = 0; i < options.refs.length; i++) {
|
|
199
|
+
const refItem = options.refs[i];
|
|
200
|
+
const itemBadge = customLabel ? (options.refs.length > 1 ? `${customLabel} [${i + 1}]` : customLabel) : refItem;
|
|
201
|
+
const ok = await highlightSingleTarget(refItem, itemBadge);
|
|
202
|
+
if (ok)
|
|
203
|
+
successCount++;
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
success: successCount > 0,
|
|
207
|
+
message: `Highlighted ${successCount}/${options.refs.length} element(s) with style '${style}' (${duration}ms)`
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
// ── Single target resolution ──────────────────────────────────────────────
|
|
211
|
+
if (options.nodeId && await tryNodeHighlight(options.nodeId, customLabel)) {
|
|
212
|
+
return { success: true, message: `Highlighted backendNodeId ${options.nodeId} with style '${style}'` };
|
|
213
|
+
}
|
|
214
|
+
let singleRef = options.selector || options.id || options.ref || options.target;
|
|
215
|
+
if (!singleRef && options.label) {
|
|
216
|
+
singleRef = `[aria-label="${options.label.replace(/"/g, '\\"')}"]`;
|
|
217
|
+
}
|
|
218
|
+
if (singleRef) {
|
|
219
|
+
const badgeText = customLabel || (singleRef.startsWith("w:") ? singleRef : undefined);
|
|
220
|
+
const ok = await highlightSingleTarget(singleRef, badgeText);
|
|
221
|
+
if (ok) {
|
|
222
|
+
return { success: true, message: `Highlighted ${singleRef} with style '${style}' (${duration}ms)` };
|
|
136
223
|
}
|
|
137
|
-
catch { /* no cache */ }
|
|
138
224
|
}
|
|
139
225
|
return { success: false, message: `Could not find element to highlight` };
|
|
140
226
|
}
|
package/dist/tools/inspect.js
CHANGED
|
@@ -121,5 +121,5 @@ export async function browserInspect(session, options, _config) {
|
|
|
121
121
|
computedStyles: computedStylesData,
|
|
122
122
|
eventListeners: listeners
|
|
123
123
|
};
|
|
124
|
-
return { success: true, data, message: "
|
|
124
|
+
return { success: true, data, message: "inspected element" };
|
|
125
125
|
}
|
package/dist/tools/key.js
CHANGED
|
@@ -4,11 +4,8 @@
|
|
|
4
4
|
* Weavetab by fy2ne
|
|
5
5
|
*/
|
|
6
6
|
import { recordAction } from "../intelligence/trail.js";
|
|
7
|
-
import { waitForDOMStable } from "../sensors/guard.js";
|
|
8
|
-
import { waitForNetworkIdle } from "../sensors/wait.js";
|
|
9
7
|
import { buildActionMap, toRefEntries } from "../cdp/walker.js";
|
|
10
|
-
import {
|
|
11
|
-
import { computeDelta, storeSnapshot } from "../state/delta.js";
|
|
8
|
+
import { computeDelta } from "../state/delta.js";
|
|
12
9
|
import { sessionRegistry } from "../state/session.js";
|
|
13
10
|
import { getConfigAgentId } from "../config/loader.js";
|
|
14
11
|
import { sendToExtension } from "../cdp/helpers.js";
|
|
@@ -20,36 +17,74 @@ const ALLOWED_KEYS = new Set([
|
|
|
20
17
|
]);
|
|
21
18
|
export async function weaveKey(session, args, _config) {
|
|
22
19
|
const { key, modifiers } = args;
|
|
20
|
+
let effectiveKey = key.trim();
|
|
21
|
+
const effectiveModifiers = [...(modifiers || [])];
|
|
22
|
+
// Auto-parse combo strings e.g. "Ctrl+A", "Control+Shift+P", "Cmd+K", "Alt+ArrowDown"
|
|
23
|
+
if (effectiveKey.includes("+")) {
|
|
24
|
+
const parts = effectiveKey.split("+").map(p => p.trim());
|
|
25
|
+
effectiveKey = parts[parts.length - 1] || "Enter";
|
|
26
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
27
|
+
const mod = parts[i].toLowerCase();
|
|
28
|
+
if ((mod === "ctrl" || mod === "control") && !effectiveModifiers.includes("Ctrl"))
|
|
29
|
+
effectiveModifiers.push("Ctrl");
|
|
30
|
+
else if (mod === "shift" && !effectiveModifiers.includes("Shift"))
|
|
31
|
+
effectiveModifiers.push("Shift");
|
|
32
|
+
else if ((mod === "alt" || mod === "option") && !effectiveModifiers.includes("Alt"))
|
|
33
|
+
effectiveModifiers.push("Alt");
|
|
34
|
+
else if ((mod === "meta" || mod === "cmd" || mod === "command" || mod === "win") && !effectiveModifiers.includes("Meta"))
|
|
35
|
+
effectiveModifiers.push("Meta");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Capitalize common key names if needed (e.g. "enter" -> "Enter", "escape" -> "Escape", "tab" -> "Tab")
|
|
39
|
+
const keyMap = {
|
|
40
|
+
enter: "Enter",
|
|
41
|
+
escape: "Escape",
|
|
42
|
+
esc: "Escape",
|
|
43
|
+
tab: "Tab",
|
|
44
|
+
backspace: "Backspace",
|
|
45
|
+
delete: "Delete",
|
|
46
|
+
space: "Space",
|
|
47
|
+
arrowup: "ArrowUp",
|
|
48
|
+
arrowdown: "ArrowDown",
|
|
49
|
+
arrowleft: "ArrowLeft",
|
|
50
|
+
arrowright: "ArrowRight",
|
|
51
|
+
up: "ArrowUp",
|
|
52
|
+
down: "ArrowDown",
|
|
53
|
+
left: "ArrowLeft",
|
|
54
|
+
right: "ArrowRight",
|
|
55
|
+
home: "Home",
|
|
56
|
+
end: "End",
|
|
57
|
+
pageup: "PageUp",
|
|
58
|
+
pagedown: "PageDown",
|
|
59
|
+
};
|
|
60
|
+
if (keyMap[effectiveKey.toLowerCase()]) {
|
|
61
|
+
effectiveKey = keyMap[effectiveKey.toLowerCase()];
|
|
62
|
+
}
|
|
23
63
|
// Allow single printable chars or control keys from the allowlist
|
|
24
|
-
if (
|
|
64
|
+
if (effectiveKey.length > 1 && !ALLOWED_KEYS.has(effectiveKey)) {
|
|
25
65
|
return {
|
|
26
66
|
success: false,
|
|
27
67
|
error_code: "INVALID_KEY",
|
|
28
|
-
message: `[!] Key "${
|
|
68
|
+
message: `[!] Key "${effectiveKey}" is not a valid control key. Use single characters or: ${[...ALLOWED_KEYS].join(", ")}`,
|
|
29
69
|
};
|
|
30
70
|
}
|
|
31
71
|
let modMask = 0;
|
|
32
|
-
if (
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
const label = modifiers && modifiers.length > 0 ? `${modifiers.join("+")}+${key}` : key;
|
|
72
|
+
if (effectiveModifiers.includes("Alt"))
|
|
73
|
+
modMask |= 1;
|
|
74
|
+
if (effectiveModifiers.includes("Ctrl"))
|
|
75
|
+
modMask |= 2;
|
|
76
|
+
if (effectiveModifiers.includes("Meta"))
|
|
77
|
+
modMask |= 4;
|
|
78
|
+
if (effectiveModifiers.includes("Shift"))
|
|
79
|
+
modMask |= 8;
|
|
80
|
+
const label = effectiveModifiers.length > 0 ? `${effectiveModifiers.join("+")}+${effectiveKey}` : effectiveKey;
|
|
43
81
|
await sendToExtension(session, { type: 'Weavetab_STATE', hud: { action: `pressing ${label}` } });
|
|
44
|
-
const { frameTree: beforeFrameTree } = await session.Page.getFrameTree();
|
|
82
|
+
const { frameTree: beforeFrameTree } = await session.Page.getFrameTree().catch(() => ({ frameTree: { frame: { url: '' } } }));
|
|
45
83
|
const urlBefore = beforeFrameTree.frame.url;
|
|
46
|
-
// Track map before for delta if Enter is pressed
|
|
47
84
|
let targetId = "default";
|
|
48
85
|
const active = sessionRegistry.getActive(getConfigAgentId(_config));
|
|
49
86
|
if (active)
|
|
50
87
|
targetId = active.targetId;
|
|
51
|
-
const mapBefore = await buildActionMap(session, { lite: true }, targetId);
|
|
52
|
-
storeSnapshot(targetId, urlBefore, toRefEntries(mapBefore.elements));
|
|
53
88
|
// We removed the direct toast injection here because the extension handles HUD display via Weavetab_STATE (line 66)
|
|
54
89
|
await session.Input.dispatchKeyEvent({ type: "rawKeyDown", key, modifiers: modMask, windowsVirtualKeyCode: key === "Enter" ? 13 : undefined, nativeVirtualKeyCode: key === "Enter" ? 13 : undefined });
|
|
55
90
|
if (key === "Enter") {
|
|
@@ -92,49 +127,23 @@ export async function weaveKey(session, args, _config) {
|
|
|
92
127
|
})();
|
|
93
128
|
` }).catch(() => { });
|
|
94
129
|
}
|
|
95
|
-
await session.Input.dispatchKeyEvent({ type: "keyUp", key, modifiers: modMask, windowsVirtualKeyCode:
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const { nodeIds } = await session.DOM.pushNodesByBackendIdsToFrontend({ backendNodeIds: [el.backendNodeId] }).catch(() => ({ nodeIds: [] }));
|
|
104
|
-
if (nodeIds[0]) {
|
|
105
|
-
const box = await session.DOM.getBoxModel({ nodeId: nodeIds[0] }).catch(() => null);
|
|
106
|
-
if (box)
|
|
107
|
-
cacheBoxModel(targetId, el.backendNodeId, box.model);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
catch { /* ignore pre-fetch errors */ }
|
|
113
|
-
};
|
|
114
|
-
const settleTimeout = key === "Enter" ? 5000 : 2500;
|
|
115
|
-
await Promise.all([
|
|
116
|
-
waitForDOMStable(session, 50, settleTimeout),
|
|
117
|
-
waitForNetworkIdle(session, 50, settleTimeout).catch(() => { }),
|
|
118
|
-
predictiveQueue()
|
|
119
|
-
]);
|
|
130
|
+
await session.Input.dispatchKeyEvent({ type: "keyUp", key: effectiveKey, modifiers: modMask, windowsVirtualKeyCode: effectiveKey === "Enter" ? 13 : undefined, nativeVirtualKeyCode: effectiveKey === "Enter" ? 13 : undefined });
|
|
131
|
+
// Fast return for non-submission keys (Tab, Escape, Arrows, Shortcuts)
|
|
132
|
+
if (effectiveKey === "Enter") {
|
|
133
|
+
await new Promise(r => setTimeout(r, 60));
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
await new Promise(r => setTimeout(r, 15));
|
|
137
|
+
}
|
|
120
138
|
// Post-key location context
|
|
121
139
|
let postUrl = "";
|
|
122
140
|
let postTitle = "";
|
|
123
141
|
try {
|
|
124
142
|
const { frameTree } = await session.Page.getFrameTree();
|
|
125
143
|
postUrl = frameTree.frame.url;
|
|
126
|
-
try {
|
|
127
|
-
const titleRes = await session.Runtime.evaluate({ expression: 'document.title', returnByValue: true });
|
|
128
|
-
postTitle = titleRes?.result?.value || postUrl;
|
|
129
|
-
}
|
|
130
|
-
catch {
|
|
131
|
-
postTitle = postUrl;
|
|
132
|
-
}
|
|
133
144
|
const activeSession = sessionRegistry.getActive(getConfigAgentId(_config));
|
|
134
|
-
if (activeSession)
|
|
145
|
+
if (activeSession)
|
|
135
146
|
activeSession.url = postUrl;
|
|
136
|
-
activeSession.title = postTitle;
|
|
137
|
-
}
|
|
138
147
|
}
|
|
139
148
|
catch { /* ignore */ }
|
|
140
149
|
const urlChanged = postUrl !== urlBefore;
|
package/dist/tools/navigate.d.ts
CHANGED
package/dist/tools/navigate.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Weavetab
|
|
2
|
+
* Weavetab — tools/navigate.ts
|
|
3
3
|
* URL navigation with domain blocklist enforcement and post-navigation DOM stability wait.
|
|
4
4
|
* Weavetab by fy2ne
|
|
5
5
|
*/
|
|
@@ -71,10 +71,10 @@ export async function weaveNavigate(session, url, config, targetId = "default",
|
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
// Extension pages: open in new tab instead of navigating current tab
|
|
74
|
-
// (chrome-extension:// pages use a different process context
|
|
74
|
+
// (chrome-extension:// pages use a different process context — Page.navigate
|
|
75
75
|
// can break the CDP session or not fire loadEventFired properly)
|
|
76
76
|
if (parsed.protocol === 'chrome-extension:') {
|
|
77
|
-
logDebug(`[weaveNavigate] Extension URL detected
|
|
77
|
+
logDebug(`[weaveNavigate] Extension URL detected — opening in new tab: ${parsed.href}`);
|
|
78
78
|
const { sendToExtension } = await import("../cdp/helpers.js");
|
|
79
79
|
await sendToExtension(session, { type: 'Weavetab_STATE', hud: { action: `opening extension page...` } });
|
|
80
80
|
const { WeavetabOpen } = await import("./tabs.js");
|
|
@@ -98,7 +98,7 @@ export async function weaveNavigate(session, url, config, targetId = "default",
|
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
catch { /* overlay install is best-effort */ }
|
|
101
|
-
logAction("
|
|
101
|
+
logAction("browser_navigate", url, `opened extension tab ${openRes.tabId}`);
|
|
102
102
|
return {
|
|
103
103
|
success: true,
|
|
104
104
|
url: openRes.url,
|
|
@@ -115,7 +115,7 @@ export async function weaveNavigate(session, url, config, targetId = "default",
|
|
|
115
115
|
expression: `new Promise((resolve) => {
|
|
116
116
|
let done = false;
|
|
117
117
|
const finish = (val) => { if (!done) { done = true; resolve(val); } };
|
|
118
|
-
setTimeout(() => finish(false),
|
|
118
|
+
setTimeout(() => finish(false), 30);
|
|
119
119
|
try {
|
|
120
120
|
const browserAPI = typeof browser !== 'undefined' ? browser : (typeof chrome !== 'undefined' ? chrome : null);
|
|
121
121
|
if (browserAPI && browserAPI.runtime && browserAPI.runtime.id) {
|
|
@@ -129,7 +129,7 @@ export async function weaveNavigate(session, url, config, targetId = "default",
|
|
|
129
129
|
})`,
|
|
130
130
|
awaitPromise: true,
|
|
131
131
|
returnByValue: true
|
|
132
|
-
});
|
|
132
|
+
}).catch(() => null);
|
|
133
133
|
isAnchor = res?.result?.value === true;
|
|
134
134
|
}
|
|
135
135
|
catch (e) {
|
|
@@ -151,55 +151,50 @@ export async function weaveNavigate(session, url, config, targetId = "default",
|
|
|
151
151
|
sendToExtension(session, { type: 'Weavetab_STATE', hud: { action: `navigating to ${parsed.hostname}...` } }).catch(() => { });
|
|
152
152
|
sendToExtension(session, { type: 'Weavetab_STATE', cursor: { x: 10, y: 10 }, state: 'thinking' }).catch(() => { });
|
|
153
153
|
await session.Page.navigate({ url: parsed.href });
|
|
154
|
-
//
|
|
154
|
+
// Fast navigation wait
|
|
155
155
|
try {
|
|
156
156
|
await awaitNavigation(session, config.navigationTimeout || 30000);
|
|
157
157
|
}
|
|
158
158
|
catch (navErr) {
|
|
159
159
|
logDebug(`[weaveNavigate] Navigation timeout: ${navErr}`);
|
|
160
|
-
// Continue — page may be partially loaded
|
|
161
160
|
}
|
|
162
|
-
// Clear delta snapshot
|
|
161
|
+
// Clear delta snapshot & caches
|
|
163
162
|
clearSnapshot(resolvedTargetId);
|
|
164
163
|
resetLoopCounter("navigation");
|
|
165
|
-
clearToolCallWindow();
|
|
164
|
+
clearToolCallWindow();
|
|
166
165
|
resetFallbackIdCounter();
|
|
167
166
|
clearShadowDomCache(resolvedTargetId);
|
|
168
167
|
clearAXTreeCache(resolvedTargetId);
|
|
169
168
|
clearTabCache(resolvedTargetId);
|
|
170
|
-
//
|
|
171
|
-
await
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
title = titleRes.result.value;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
catch { }
|
|
169
|
+
// Parallelize post-navigation metadata & cookie banner dismissal
|
|
170
|
+
const [frameRes, titleRes] = await Promise.all([
|
|
171
|
+
session.Page.getFrameTree().catch(() => ({ frameTree: { frame: { url: parsed.href } } })),
|
|
172
|
+
session.Runtime.evaluate({ expression: 'document.title', returnByValue: true }).catch(() => null),
|
|
173
|
+
session.Runtime.evaluate({
|
|
174
|
+
expression: `
|
|
175
|
+
(function(){
|
|
176
|
+
var selectors = [
|
|
177
|
+
'[id*="cookie"] button','[class*="cookie"] button',
|
|
178
|
+
'[id*="consent"] button','[class*="consent"] button',
|
|
179
|
+
'[id*="gdpr"] button','[class*="gdpr"] button',
|
|
180
|
+
'[aria-label*="Accept"]','[aria-label*="Agree"]',
|
|
181
|
+
'button[data-cookiebanner]'
|
|
182
|
+
];
|
|
183
|
+
for(var i=0;i<selectors.length;i++){
|
|
184
|
+
var el=document.querySelector(selectors[i]);
|
|
185
|
+
if(el){try{el.click();}catch(e){}break;}
|
|
186
|
+
}
|
|
187
|
+
})()
|
|
188
|
+
`
|
|
189
|
+
}).catch(() => { })
|
|
190
|
+
]);
|
|
191
|
+
const finalUrl = frameRes?.frameTree?.frame?.url || parsed.href;
|
|
192
|
+
const title = titleRes?.result?.value || parsed.hostname;
|
|
198
193
|
// Keep session registry in sync with the new URL
|
|
199
194
|
if (active)
|
|
200
195
|
active.url = finalUrl;
|
|
201
196
|
sendToExtension(session, { type: 'Weavetab_STATE', cursor: { x: 10, y: 10 }, state: 'idle', hud: { action: '' } }).catch(() => { });
|
|
202
|
-
logAction("
|
|
197
|
+
logAction("browser_navigate", url, `arrived at ${title}`);
|
|
203
198
|
// Bump the visit counter for this domain exactly once per navigation
|
|
204
199
|
bumpVisitCount(parsed.hostname).catch(() => { });
|
|
205
200
|
return { success: true, url: finalUrl, title, tab_id: resolvedTargetId };
|