@weavetab/mcp 2.5.0-beta.0
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 +48 -0
- package/ARCHITECTURE.md +590 -0
- package/LICENSE +631 -0
- package/README.md +909 -0
- package/SECURITY.md +196 -0
- package/TROUBLESHOOTING.md +365 -0
- package/dist/anticaptcha/detector.d.ts +49 -0
- package/dist/anticaptcha/detector.js +465 -0
- package/dist/anticaptcha/integration.d.ts +36 -0
- package/dist/anticaptcha/integration.js +123 -0
- package/dist/anticaptcha/solver.d.ts +27 -0
- package/dist/anticaptcha/solver.js +552 -0
- package/dist/audit/logger.d.ts +13 -0
- package/dist/audit/logger.js +177 -0
- package/dist/audit/telemetry.d.ts +36 -0
- package/dist/audit/telemetry.js +162 -0
- package/dist/cdp/bridge.d.ts +16 -0
- package/dist/cdp/bridge.js +826 -0
- package/dist/cdp/confirm.d.ts +26 -0
- package/dist/cdp/confirm.js +79 -0
- package/dist/cdp/connector.d.ts +37 -0
- package/dist/cdp/connector.js +661 -0
- package/dist/cdp/extension.d.ts +36 -0
- package/dist/cdp/extension.js +118 -0
- package/dist/cdp/finder.d.ts +1 -0
- package/dist/cdp/finder.js +2 -0
- package/dist/cdp/helpers.d.ts +11 -0
- package/dist/cdp/helpers.js +195 -0
- package/dist/cdp/label_cache.d.ts +12 -0
- package/dist/cdp/label_cache.js +51 -0
- package/dist/cdp/lock.d.ts +33 -0
- package/dist/cdp/lock.js +206 -0
- package/dist/cdp/omni_broker.d.ts +17 -0
- package/dist/cdp/omni_broker.js +119 -0
- package/dist/cdp/profiles.d.ts +1 -0
- package/dist/cdp/profiles.js +87 -0
- package/dist/cdp/pruner.d.ts +41 -0
- package/dist/cdp/pruner.js +263 -0
- package/dist/cdp/ref_cache.d.ts +101 -0
- package/dist/cdp/ref_cache.js +234 -0
- package/dist/cdp/retry.d.ts +5 -0
- package/dist/cdp/retry.js +39 -0
- package/dist/cdp/retry_enhanced.d.ts +44 -0
- package/dist/cdp/retry_enhanced.js +102 -0
- package/dist/cdp/security.d.ts +50 -0
- package/dist/cdp/security.js +237 -0
- package/dist/cdp/verify.d.ts +39 -0
- package/dist/cdp/verify.js +143 -0
- package/dist/cdp/walker.d.ts +91 -0
- package/dist/cdp/walker.js +896 -0
- package/dist/cdp/walker.test.d.ts +2 -0
- package/dist/cdp/walker.test.js +172 -0
- package/dist/cdp/websocket_transport.d.ts +15 -0
- package/dist/cdp/websocket_transport.js +118 -0
- package/dist/cli/plugin.d.ts +9 -0
- package/dist/cli/plugin.js +1134 -0
- package/dist/cli/updater.d.ts +1 -0
- package/dist/cli/updater.js +72 -0
- package/dist/config/loader.d.ts +120 -0
- package/dist/config/loader.js +704 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1248 -0
- package/dist/intelligence/hints.d.ts +11 -0
- package/dist/intelligence/hints.js +39 -0
- package/dist/intelligence/intent.d.ts +33 -0
- package/dist/intelligence/intent.js +171 -0
- package/dist/intelligence/sessionMemory.d.ts +66 -0
- package/dist/intelligence/sessionMemory.js +284 -0
- package/dist/intelligence/sessionMemory.test.d.ts +2 -0
- package/dist/intelligence/sessionMemory.test.js +62 -0
- package/dist/intelligence/trail.d.ts +64 -0
- package/dist/intelligence/trail.js +233 -0
- package/dist/intelligence/trail.test.d.ts +2 -0
- package/dist/intelligence/trail.test.js +110 -0
- package/dist/intelligence/vision.d.ts +24 -0
- package/dist/intelligence/vision.js +151 -0
- package/dist/lifecycle/mission_manager.d.ts +16 -0
- package/dist/lifecycle/mission_manager.js +44 -0
- package/dist/overlay/bubble.d.ts +16 -0
- package/dist/overlay/bubble.js +37 -0
- package/dist/overlay/ghost.d.ts +35 -0
- package/dist/overlay/ghost.js +508 -0
- package/dist/overlay/injector.d.ts +22 -0
- package/dist/overlay/injector.js +198 -0
- package/dist/plugin/interface.d.ts +134 -0
- package/dist/plugin/interface.js +1 -0
- package/dist/plugin/loader.d.ts +18 -0
- package/dist/plugin/loader.js +155 -0
- package/dist/plugin/registry.d.ts +50 -0
- package/dist/plugin/registry.js +234 -0
- package/dist/security/blacklist.d.ts +24 -0
- package/dist/security/blacklist.js +80 -0
- package/dist/security/filter.d.ts +2 -0
- package/dist/security/filter.js +20 -0
- package/dist/security/guard.d.ts +8 -0
- package/dist/security/guard.js +35 -0
- package/dist/security/ratelimit.d.ts +11 -0
- package/dist/security/ratelimit.js +57 -0
- package/dist/security/secrets.d.ts +75 -0
- package/dist/security/secrets.js +365 -0
- package/dist/sensors/actionability.d.ts +81 -0
- package/dist/sensors/actionability.js +303 -0
- package/dist/sensors/guard.d.ts +25 -0
- package/dist/sensors/guard.js +168 -0
- package/dist/sensors/mutations.d.ts +23 -0
- package/dist/sensors/mutations.js +93 -0
- package/dist/sensors/network.d.ts +67 -0
- package/dist/sensors/network.js +193 -0
- package/dist/sensors/operation_monitor.d.ts +80 -0
- package/dist/sensors/operation_monitor.js +203 -0
- package/dist/sensors/pacing.d.ts +44 -0
- package/dist/sensors/pacing.js +111 -0
- package/dist/sensors/thoughts.d.ts +2 -0
- package/dist/sensors/thoughts.js +59 -0
- package/dist/sensors/wait.d.ts +12 -0
- package/dist/sensors/wait.js +44 -0
- package/dist/server.d.ts +7 -0
- package/dist/server.js +1687 -0
- package/dist/state/agent.d.ts +36 -0
- package/dist/state/agent.js +101 -0
- package/dist/state/cache.d.ts +16 -0
- package/dist/state/cache.js +92 -0
- package/dist/state/cache.test.d.ts +2 -0
- package/dist/state/cache.test.js +76 -0
- package/dist/state/delta.d.ts +71 -0
- package/dist/state/delta.js +234 -0
- package/dist/state/delta.test.d.ts +2 -0
- package/dist/state/delta.test.js +279 -0
- package/dist/state/groups.d.ts +15 -0
- package/dist/state/groups.js +80 -0
- package/dist/state/session.d.ts +30 -0
- package/dist/state/session.js +155 -0
- package/dist/tools/accessibility.d.ts +14 -0
- package/dist/tools/accessibility.js +32 -0
- package/dist/tools/ask.d.ts +14 -0
- package/dist/tools/ask.js +49 -0
- package/dist/tools/automation.d.ts +36 -0
- package/dist/tools/automation.js +218 -0
- package/dist/tools/browser_detect.d.ts +13 -0
- package/dist/tools/browser_detect.js +73 -0
- package/dist/tools/burst.d.ts +35 -0
- package/dist/tools/burst.js +344 -0
- package/dist/tools/canvas.d.ts +25 -0
- package/dist/tools/canvas.js +132 -0
- package/dist/tools/captcha.d.ts +38 -0
- package/dist/tools/captcha.js +209 -0
- package/dist/tools/click.d.ts +36 -0
- package/dist/tools/click.js +561 -0
- package/dist/tools/click_and_wait.d.ts +33 -0
- package/dist/tools/click_and_wait.js +149 -0
- package/dist/tools/clipboard.d.ts +29 -0
- package/dist/tools/clipboard.js +279 -0
- package/dist/tools/console.d.ts +21 -0
- package/dist/tools/console.js +107 -0
- package/dist/tools/cookies.d.ts +67 -0
- package/dist/tools/cookies.js +121 -0
- package/dist/tools/design_extract.d.ts +109 -0
- package/dist/tools/design_extract.js +641 -0
- package/dist/tools/dialog.d.ts +24 -0
- package/dist/tools/dialog.js +109 -0
- package/dist/tools/drag.d.ts +15 -0
- package/dist/tools/drag.js +29 -0
- package/dist/tools/emulation.d.ts +33 -0
- package/dist/tools/emulation.js +69 -0
- package/dist/tools/eval.d.ts +20 -0
- package/dist/tools/eval.js +92 -0
- package/dist/tools/fill.d.ts +41 -0
- package/dist/tools/fill.js +667 -0
- package/dist/tools/find.d.ts +30 -0
- package/dist/tools/find.js +60 -0
- package/dist/tools/geo.d.ts +19 -0
- package/dist/tools/geo.js +29 -0
- package/dist/tools/github/analyze.d.ts +4 -0
- package/dist/tools/github/analyze.js +113 -0
- package/dist/tools/github/cache.d.ts +21 -0
- package/dist/tools/github/cache.js +28 -0
- package/dist/tools/github/index.d.ts +4 -0
- package/dist/tools/github/index.js +4 -0
- package/dist/tools/github/issues.d.ts +9 -0
- package/dist/tools/github/issues.js +88 -0
- package/dist/tools/github/pr.d.ts +5 -0
- package/dist/tools/github/pr.js +88 -0
- package/dist/tools/github/rateLimiter.d.ts +4 -0
- package/dist/tools/github/rateLimiter.js +46 -0
- package/dist/tools/github/read.d.ts +34 -0
- package/dist/tools/github/read.js +74 -0
- package/dist/tools/github/repoContext.d.ts +20 -0
- package/dist/tools/github/repoContext.js +129 -0
- package/dist/tools/highlight.d.ts +20 -0
- package/dist/tools/highlight.js +140 -0
- package/dist/tools/hover.d.ts +19 -0
- package/dist/tools/hover.js +57 -0
- package/dist/tools/inspect.d.ts +17 -0
- package/dist/tools/inspect.js +125 -0
- package/dist/tools/integration.test.d.ts +2 -0
- package/dist/tools/integration.test.js +96 -0
- package/dist/tools/key.d.ts +25 -0
- package/dist/tools/key.js +173 -0
- package/dist/tools/macro_compiler.d.ts +7 -0
- package/dist/tools/macro_compiler.js +82 -0
- package/dist/tools/mouse.d.ts +21 -0
- package/dist/tools/mouse.js +62 -0
- package/dist/tools/navigate.d.ts +16 -0
- package/dist/tools/navigate.js +188 -0
- package/dist/tools/network_intercept.d.ts +17 -0
- package/dist/tools/network_intercept.js +151 -0
- package/dist/tools/office/index.d.ts +9 -0
- package/dist/tools/office/index.js +7 -0
- package/dist/tools/office/list.d.ts +15 -0
- package/dist/tools/office/list.js +41 -0
- package/dist/tools/office/parse.d.ts +20 -0
- package/dist/tools/office/parse.js +172 -0
- package/dist/tools/pdf.d.ts +22 -0
- package/dist/tools/pdf.js +42 -0
- package/dist/tools/performance.d.ts +12 -0
- package/dist/tools/performance.js +47 -0
- package/dist/tools/performance_test.d.ts +70 -0
- package/dist/tools/performance_test.js +128 -0
- package/dist/tools/plan.d.ts +39 -0
- package/dist/tools/plan.js +161 -0
- package/dist/tools/pointer.d.ts +35 -0
- package/dist/tools/pointer.js +522 -0
- package/dist/tools/read.d.ts +30 -0
- package/dist/tools/read.js +488 -0
- package/dist/tools/recording.d.ts +45 -0
- package/dist/tools/recording.js +124 -0
- package/dist/tools/response.d.ts +129 -0
- package/dist/tools/response.js +5 -0
- package/dist/tools/scrape.d.ts +24 -0
- package/dist/tools/scrape.js +140 -0
- package/dist/tools/screenshot.d.ts +40 -0
- package/dist/tools/screenshot.js +79 -0
- package/dist/tools/scroll.d.ts +23 -0
- package/dist/tools/scroll.js +196 -0
- package/dist/tools/select.d.ts +13 -0
- package/dist/tools/select.js +217 -0
- package/dist/tools/snapshot.d.ts +27 -0
- package/dist/tools/snapshot.js +99 -0
- package/dist/tools/storage.d.ts +18 -0
- package/dist/tools/storage.js +29 -0
- package/dist/tools/tabs.d.ts +77 -0
- package/dist/tools/tabs.js +342 -0
- package/dist/tools/thought.d.ts +16 -0
- package/dist/tools/thought.js +79 -0
- package/dist/tools/type.d.ts +65 -0
- package/dist/tools/type.js +1061 -0
- package/dist/tools/type_secret.d.ts +23 -0
- package/dist/tools/type_secret.js +229 -0
- package/dist/tools/upload.d.ts +19 -0
- package/dist/tools/upload.js +103 -0
- package/dist/tools/video_compiler.d.ts +24 -0
- package/dist/tools/video_compiler.js +199 -0
- package/dist/tools/viewport.d.ts +44 -0
- package/dist/tools/viewport.js +230 -0
- package/dist/tools/wait.d.ts +19 -0
- package/dist/tools/wait.js +202 -0
- package/dist/tools/wait_for.d.ts +23 -0
- package/dist/tools/wait_for.js +30 -0
- package/dist/tools/windows.d.ts +22 -0
- package/dist/tools/windows.js +57 -0
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.js +12 -0
- package/docs/DEV_MODE.md +68 -0
- package/docs/DOCUMENTATION.md +532 -0
- package/docs/PLUGINS.md +538 -0
- package/docs/TOOLS.md +104 -0
- package/docs/dev_plugin.md +310 -0
- package/ext-dist/background.js +467 -0
- package/ext-dist/content.js +3089 -0
- package/ext-dist/dashboard.html +583 -0
- package/ext-dist/hud/hud.js +73 -0
- package/ext-dist/hud/index.html +20 -0
- package/ext-dist/manifest.json +77 -0
- package/ext-dist/sidepanel/sidepanel.js +25 -0
- package/ext-dist/sidepanel.html +547 -0
- package/ext-dist/styles.css +218 -0
- package/icon.png +0 -0
- package/mcp.json +796 -0
- package/package.json +113 -0
- package/server.json +23 -0
- package/smithery.yaml +11 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weavetab — cdp/omni_broker.ts
|
|
3
|
+
* Omni-Context Execution Broker
|
|
4
|
+
* Handles Target.setAutoAttach to seamlessly execute code in cross-origin iframes.
|
|
5
|
+
* Weavetab by fy2ne
|
|
6
|
+
*/
|
|
7
|
+
import CDP from "chrome-remote-interface";
|
|
8
|
+
/**
|
|
9
|
+
* Initializes the Omni-Context Broker for a parent page session.
|
|
10
|
+
* Automatically attaches to all OOPIFs (Out-of-Process Iframes) and caches their CDP clients.
|
|
11
|
+
*/
|
|
12
|
+
export declare function initOmniBroker(parentSession: CDP.Client, parentTargetId: string, port: number): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Executes a CDP command across all known iframe contexts if it fails in the top-level context.
|
|
15
|
+
*/
|
|
16
|
+
export declare function omniEvaluate(parentSession: CDP.Client, parentTargetId: string, expression: string, returnByValue?: boolean): Promise<any>;
|
|
17
|
+
export declare function cleanupOmniBroker(parentTargetId: string): void;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weavetab — cdp/omni_broker.ts
|
|
3
|
+
* Omni-Context Execution Broker
|
|
4
|
+
* Handles Target.setAutoAttach to seamlessly execute code in cross-origin iframes.
|
|
5
|
+
* Weavetab by fy2ne
|
|
6
|
+
*/
|
|
7
|
+
import CDP from "chrome-remote-interface";
|
|
8
|
+
/**
|
|
9
|
+
* Stores active iframe CDP sessions mapped by their frame targetId.
|
|
10
|
+
* Keyed by the parent page's targetId to ensure isolation.
|
|
11
|
+
*/
|
|
12
|
+
const omniSessions = new Map();
|
|
13
|
+
/**
|
|
14
|
+
* Initializes the Omni-Context Broker for a parent page session.
|
|
15
|
+
* Automatically attaches to all OOPIFs (Out-of-Process Iframes) and caches their CDP clients.
|
|
16
|
+
*/
|
|
17
|
+
export async function initOmniBroker(parentSession, parentTargetId, port) {
|
|
18
|
+
if (!omniSessions.has(parentTargetId)) {
|
|
19
|
+
omniSessions.set(parentTargetId, new Map());
|
|
20
|
+
}
|
|
21
|
+
parentSession.Target.on("attachedToTarget", async (event) => {
|
|
22
|
+
const { targetInfo } = event;
|
|
23
|
+
if (targetInfo.type === 'iframe') {
|
|
24
|
+
try {
|
|
25
|
+
// Connect a flat session to the iframe target
|
|
26
|
+
const iframeSession = await CDP({ host: "127.0.0.1", port, target: targetInfo.targetId });
|
|
27
|
+
await iframeSession.Runtime.enable();
|
|
28
|
+
await iframeSession.DOM.enable();
|
|
29
|
+
omniSessions.get(parentTargetId).set(targetInfo.targetId, iframeSession);
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
// Fallback or ignore
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
// Handle detachedFromTarget so iframe CDP sessions are closed when iframes
|
|
37
|
+
// navigate away or are removed from the DOM. Without this, WebSocket connections
|
|
38
|
+
// leak on pages with dynamic iframes (e.g. news sites with ad iframes).
|
|
39
|
+
parentSession.Target.on("detachedFromTarget", async (event) => {
|
|
40
|
+
const { targetId } = event;
|
|
41
|
+
const iframeMap = omniSessions.get(parentTargetId);
|
|
42
|
+
if (iframeMap && targetId && iframeMap.has(targetId)) {
|
|
43
|
+
try {
|
|
44
|
+
iframeMap.get(targetId).close();
|
|
45
|
+
}
|
|
46
|
+
catch { }
|
|
47
|
+
iframeMap.delete(targetId);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
try {
|
|
51
|
+
await parentSession.Target.setAutoAttach({
|
|
52
|
+
autoAttach: true,
|
|
53
|
+
waitForDebuggerOnStart: false,
|
|
54
|
+
flatten: true, // Use flat sessions (sessionId)
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
console.warn("[OmniBroker] Failed to setAutoAttach", e);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Executes a CDP command across all known iframe contexts if it fails in the top-level context.
|
|
63
|
+
*/
|
|
64
|
+
export async function omniEvaluate(parentSession, parentTargetId, expression, returnByValue = true) {
|
|
65
|
+
// Try parent first
|
|
66
|
+
const { result } = await parentSession.Runtime.evaluate({
|
|
67
|
+
expression,
|
|
68
|
+
returnByValue
|
|
69
|
+
});
|
|
70
|
+
if (result && !result.subtype && result.value !== null && result.value !== undefined) {
|
|
71
|
+
const parsed = typeof result.value === 'string' ? parseJSON(result.value) : result.value;
|
|
72
|
+
if (parsed && (parsed.exists === false || parsed.ok === false || parsed === false)) {
|
|
73
|
+
// Proceed to try iframes
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
return result.value;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Fallback to all known iframes
|
|
80
|
+
const iframeMap = omniSessions.get(parentTargetId);
|
|
81
|
+
if (iframeMap) {
|
|
82
|
+
for (const iframeSession of iframeMap.values()) {
|
|
83
|
+
try {
|
|
84
|
+
const { result: iframeResult } = await iframeSession.Runtime.evaluate({
|
|
85
|
+
expression,
|
|
86
|
+
returnByValue
|
|
87
|
+
});
|
|
88
|
+
if (iframeResult && iframeResult.value) {
|
|
89
|
+
const parsed = typeof iframeResult.value === 'string' ? parseJSON(iframeResult.value) : iframeResult.value;
|
|
90
|
+
if (parsed && parsed.exists !== false && parsed.ok !== false) {
|
|
91
|
+
return iframeResult.value;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch (e) { }
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return result?.value; // Return the failed parent result if no iframe succeeds
|
|
99
|
+
}
|
|
100
|
+
function parseJSON(str) {
|
|
101
|
+
try {
|
|
102
|
+
return JSON.parse(str);
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return str;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
export function cleanupOmniBroker(parentTargetId) {
|
|
109
|
+
const iframeMap = omniSessions.get(parentTargetId);
|
|
110
|
+
if (iframeMap) {
|
|
111
|
+
for (const session of iframeMap.values()) {
|
|
112
|
+
try {
|
|
113
|
+
session.close();
|
|
114
|
+
}
|
|
115
|
+
catch { }
|
|
116
|
+
}
|
|
117
|
+
omniSessions.delete(parentTargetId);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getSessionProfile(cookies: boolean, customProfileDir?: string, syncDataAcrossBrowsers?: boolean, browserPath?: string): string;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { logDebug } from "../audit/logger.js";
|
|
2
|
+
/**
|
|
3
|
+
* Weavetab — cdp/profiles.ts
|
|
4
|
+
* Isolated browser session profile resolution with real-browser protection guard.
|
|
5
|
+
* Weavetab NEVER reads from or writes to the user's real browser profile.
|
|
6
|
+
* Weavetab by fy2ne
|
|
7
|
+
*/
|
|
8
|
+
import * as path from "node:path";
|
|
9
|
+
import * as os from "node:os";
|
|
10
|
+
import * as fs from "node:fs";
|
|
11
|
+
import * as crypto from "node:crypto";
|
|
12
|
+
import { PLATFORM } from "../config/loader.js";
|
|
13
|
+
// ─── Real-browser profile isolation guard ────────────────────────────────────
|
|
14
|
+
// These are the known default user data directories for every major browser
|
|
15
|
+
// on all supported platforms. Weavetab will REFUSE to use any of these paths
|
|
16
|
+
// as a session profile. This is an enterprise-critical security guarantee.
|
|
17
|
+
const HOME = os.homedir();
|
|
18
|
+
// Platform-specific real browser paths
|
|
19
|
+
const REAL_BROWSER_PATHS = [];
|
|
20
|
+
if (PLATFORM === 'win32') {
|
|
21
|
+
// Windows paths
|
|
22
|
+
REAL_BROWSER_PATHS.push(path.join(HOME, "AppData", "Local", "Google", "Chrome", "User Data"), path.join(HOME, "AppData", "Local", "Microsoft", "Edge", "User Data"), path.join(HOME, "AppData", "Local", "BraveSoftware", "Brave-Browser", "User Data"), path.join(HOME, "AppData", "Roaming", "Mozilla", "Firefox", "Profiles"), path.join(HOME, "AppData", "Roaming", "Opera Software", "Opera Stable"), path.join(HOME, "AppData", "Local", "Vivaldi", "User Data"));
|
|
23
|
+
}
|
|
24
|
+
else if (PLATFORM === 'darwin') {
|
|
25
|
+
// macOS paths
|
|
26
|
+
REAL_BROWSER_PATHS.push(path.join(HOME, "Library", "Application Support", "Google", "Chrome"), path.join(HOME, "Library", "Application Support", "Microsoft Edge"), path.join(HOME, "Library", "Application Support", "BraveSoftware", "Brave-Browser"), path.join(HOME, "Library", "Application Support", "Firefox", "Profiles"), path.join(HOME, "Library", "Application Support", "Vivaldi"));
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
// Linux paths
|
|
30
|
+
REAL_BROWSER_PATHS.push(path.join(HOME, ".config", "google-chrome"), path.join(HOME, ".config", "microsoft-edge"), path.join(HOME, ".config", "BraveSoftware", "Brave-Browser"), path.join(HOME, ".mozilla", "firefox"), path.join(HOME, ".config", "opera"), path.join(HOME, ".config", "vivaldi"));
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Asserts that the given profile directory is NOT a real browser profile.
|
|
34
|
+
* Throws a clear security error if it matches any known real-browser path.
|
|
35
|
+
* This prevents Weavetab from ever accidentally accessing user's saved passwords,
|
|
36
|
+
* cookies, history, or personal data from their personal browser.
|
|
37
|
+
*/
|
|
38
|
+
function assertNotRealBrowserProfile(profileDir) {
|
|
39
|
+
const resolved = path.resolve(profileDir).toLowerCase();
|
|
40
|
+
for (const realPath of REAL_BROWSER_PATHS) {
|
|
41
|
+
const realResolved = path.resolve(realPath).toLowerCase();
|
|
42
|
+
// Block exact match and any subdirectory of a real browser profile
|
|
43
|
+
if (resolved === realResolved || resolved.startsWith(realResolved + path.sep)) {
|
|
44
|
+
throw new Error(`[Weavetab] ✗ SECURITY BLOCK: The profileDir "${profileDir}" points to your real browser data.\n` +
|
|
45
|
+
` Weavetab will never read from or write to your personal browser profile.\n` +
|
|
46
|
+
` Please use a separate, empty directory or leave profileDir empty to use Weavetab's isolated profile.\n` +
|
|
47
|
+
` Real browser path detected: ${realPath}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// ─── Session profile resolver ─────────────────────────────────────────────────
|
|
52
|
+
export function getSessionProfile(cookies, customProfileDir, syncDataAcrossBrowsers, browserPath) {
|
|
53
|
+
// If the user specified a custom profile directory, validate and use it
|
|
54
|
+
if (customProfileDir && customProfileDir.trim()) {
|
|
55
|
+
const resolved = path.resolve(customProfileDir.trim());
|
|
56
|
+
// Enterprise safety: refuse to use real browser profiles
|
|
57
|
+
assertNotRealBrowserProfile(resolved);
|
|
58
|
+
if (!fs.existsSync(resolved)) {
|
|
59
|
+
fs.mkdirSync(resolved, { recursive: true });
|
|
60
|
+
logDebug(`[Weavetab] ✓ Created custom profile directory: ${resolved}`);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
logDebug(`[Weavetab] ✓ Using custom profile directory: ${resolved}`);
|
|
64
|
+
}
|
|
65
|
+
return resolved;
|
|
66
|
+
}
|
|
67
|
+
// Default: use Weavetab's fully isolated session profile
|
|
68
|
+
// This directory is completely separate from the user's real browser data.
|
|
69
|
+
let sessionProfile = path.join(HOME, ".weavetab-system", "session-profile");
|
|
70
|
+
if (syncDataAcrossBrowsers === false && browserPath && browserPath !== "auto") {
|
|
71
|
+
// Isolate profiles per browser executable path
|
|
72
|
+
const hash = crypto.createHash("md5").update(browserPath).digest("hex").slice(0, 8);
|
|
73
|
+
// e.g. .weavetab-system/session-profile-a1b2c3d4
|
|
74
|
+
sessionProfile = path.join(HOME, ".weavetab-system", `session-profile-${hash}`);
|
|
75
|
+
}
|
|
76
|
+
// Defense-in-depth: even though the hardcoded path should never match a real
|
|
77
|
+
// browser profile, validate it anyway (belt-and-suspenders).
|
|
78
|
+
assertNotRealBrowserProfile(sessionProfile);
|
|
79
|
+
if (!fs.existsSync(sessionProfile)) {
|
|
80
|
+
fs.mkdirSync(sessionProfile, { recursive: true });
|
|
81
|
+
logDebug(`[Weavetab] ✓ Browser isolation verified — new isolated profile at: ${sessionProfile}`);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
logDebug(`[Weavetab] ✓ Browser isolation verified — using isolated profile at: ${sessionProfile}`);
|
|
85
|
+
}
|
|
86
|
+
return sessionProfile;
|
|
87
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weavetab — cdp/pruner.ts
|
|
3
|
+
* Semantic DOM Pruning: one-shot compression of a single DOM snapshot.
|
|
4
|
+
*
|
|
5
|
+
* DISTINCT FROM Delta Mapping:
|
|
6
|
+
* Delta = diffs snapshots across time (previous call → now).
|
|
7
|
+
* Pruning = compresses ONE snapshot before it is returned.
|
|
8
|
+
* They are complementary — delta runs after pruning.
|
|
9
|
+
*
|
|
10
|
+
* Execution model: 1 Runtime.evaluate call (2 if page exceeds perf gate —
|
|
11
|
+
* the split path costs one extra CDP round trip; see ARCHITECTURE.md).
|
|
12
|
+
*
|
|
13
|
+
* Weavetab by fy2ne
|
|
14
|
+
*/
|
|
15
|
+
import CDP from "chrome-remote-interface";
|
|
16
|
+
import type { ActionElement } from "./walker.js";
|
|
17
|
+
export interface PruneStats {
|
|
18
|
+
total: number;
|
|
19
|
+
pruned: number;
|
|
20
|
+
svgReplaced: number;
|
|
21
|
+
elapsedMs: number;
|
|
22
|
+
/** true when the perf-gate fallback triggered a second Runtime.evaluate */
|
|
23
|
+
splitPath: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface PruneResult {
|
|
26
|
+
elements: ActionElement[];
|
|
27
|
+
stats: PruneStats;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* pruneSnapshot — filters rawElements in-place using three ordered in-browser passes.
|
|
31
|
+
*
|
|
32
|
+
* Ordering guarantee (stable ref-id assignment):
|
|
33
|
+
* 1. Synthetic-id assignment (pre-Pass 1)
|
|
34
|
+
* 2. Pass 1 — controlsMap
|
|
35
|
+
* 3. Pass 2 — post-order hasInteractiveDescendant
|
|
36
|
+
* 4. Pass 3 — per-element prune decision
|
|
37
|
+
* └─ data-wt-secret check fires on the ATTRIBUTE, not the id,
|
|
38
|
+
* so it is safe even when an element is running on a synthetic id.
|
|
39
|
+
* 5. Synthetic-id strip (post-filter, before return)
|
|
40
|
+
*/
|
|
41
|
+
export declare function pruneSnapshot(session: CDP.Client, rawElements: ActionElement[], _targetId?: string, structuralIdioms?: string[]): Promise<ActionElement[]>;
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { logDebug } from "../audit/logger.js";
|
|
2
|
+
/**
|
|
3
|
+
* pruneSnapshot — filters rawElements in-place using three ordered in-browser passes.
|
|
4
|
+
*
|
|
5
|
+
* Ordering guarantee (stable ref-id assignment):
|
|
6
|
+
* 1. Synthetic-id assignment (pre-Pass 1)
|
|
7
|
+
* 2. Pass 1 — controlsMap
|
|
8
|
+
* 3. Pass 2 — post-order hasInteractiveDescendant
|
|
9
|
+
* 4. Pass 3 — per-element prune decision
|
|
10
|
+
* └─ data-wt-secret check fires on the ATTRIBUTE, not the id,
|
|
11
|
+
* so it is safe even when an element is running on a synthetic id.
|
|
12
|
+
* 5. Synthetic-id strip (post-filter, before return)
|
|
13
|
+
*/
|
|
14
|
+
export async function pruneSnapshot(session, rawElements, _targetId = "default", structuralIdioms = []) {
|
|
15
|
+
if (rawElements.length === 0)
|
|
16
|
+
return rawElements;
|
|
17
|
+
// Extract the raw numeric ids (strip "w:" prefix) so the in-browser script
|
|
18
|
+
// can match against data-wt-id attribute values.
|
|
19
|
+
const rawIds = rawElements.map((el) => el.id.startsWith("w:") ? el.id.slice(2) : el.id);
|
|
20
|
+
// ── Build the in-browser evaluate expression ───────────────────────────────
|
|
21
|
+
const expression = buildEvalExpression(rawIds, structuralIdioms);
|
|
22
|
+
// ── Execute (single evaluate, perf-gate split if needed) ──────────────────
|
|
23
|
+
const { result } = await session.Runtime.evaluate({
|
|
24
|
+
expression,
|
|
25
|
+
returnByValue: true,
|
|
26
|
+
awaitPromise: false,
|
|
27
|
+
});
|
|
28
|
+
if (!result?.value) {
|
|
29
|
+
// Evaluate failed — return raw elements unchanged (graceful degradation)
|
|
30
|
+
logDebug("[Pruner] Runtime.evaluate returned no value — skipping prune pass");
|
|
31
|
+
return rawElements;
|
|
32
|
+
}
|
|
33
|
+
let parsed;
|
|
34
|
+
try {
|
|
35
|
+
parsed = JSON.parse(result.value);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
logDebug("[Pruner] Failed to parse Runtime.evaluate result — skipping prune pass");
|
|
39
|
+
return rawElements;
|
|
40
|
+
}
|
|
41
|
+
const { keptIds, svgLabels, stats } = parsed;
|
|
42
|
+
logDebug(`[Pruner] ${stats.total} → ${stats.total - stats.pruned} elements` +
|
|
43
|
+
` (pruned ${stats.pruned}, ${stats.svgReplaced} SVG labels replaced,` +
|
|
44
|
+
` ${stats.elapsedMs.toFixed(1)}ms${stats.splitPath ? ", split-path" : ""})`);
|
|
45
|
+
const keptSet = new Set(keptIds);
|
|
46
|
+
// Filter and patch SVG labels
|
|
47
|
+
const result2 = [];
|
|
48
|
+
for (const el of rawElements) {
|
|
49
|
+
const rawId = el.id.startsWith("w:") ? el.id.slice(2) : el.id;
|
|
50
|
+
if (!keptSet.has(rawId))
|
|
51
|
+
continue;
|
|
52
|
+
const svgLabel = svgLabels[rawId];
|
|
53
|
+
if (svgLabel) {
|
|
54
|
+
result2.push({ ...el, label: svgLabel });
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
result2.push(el);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return result2;
|
|
61
|
+
}
|
|
62
|
+
// ─── In-browser evaluate expression builder ───────────────────────────────────
|
|
63
|
+
function buildEvalExpression(rawIds, structuralIdioms = []) {
|
|
64
|
+
// We JSON-encode the id list so the script can build a lookup Set.
|
|
65
|
+
const idsJson = JSON.stringify(rawIds);
|
|
66
|
+
const idiomsJson = JSON.stringify(structuralIdioms);
|
|
67
|
+
return `(function() {
|
|
68
|
+
var t0 = performance.now();
|
|
69
|
+
|
|
70
|
+
// ── Interactive selector (mirrors walker.ts) ─────────────────────────────
|
|
71
|
+
var INTERACTIVE_SEL = [
|
|
72
|
+
'a', 'button', 'input', 'textarea', 'select',
|
|
73
|
+
'[role="button"]', '[role="link"]', '[role="textbox"]',
|
|
74
|
+
'[role="checkbox"]', '[role="radio"]', '[role="combobox"]',
|
|
75
|
+
'[role="searchbox"]', '[role="menuitem"]', '[role="tab"]',
|
|
76
|
+
'[tabindex]:not([tabindex="-1"])'
|
|
77
|
+
].join(', ');
|
|
78
|
+
|
|
79
|
+
// SVG ancestor selector (includes menuitem + tab for Jira/AWS patterns)
|
|
80
|
+
var SVG_ANCESTOR_SEL = 'button, a, [role="button"], [role="menuitem"], [role="tab"]';
|
|
81
|
+
|
|
82
|
+
var targetIds = new Set(${idsJson});
|
|
83
|
+
|
|
84
|
+
// ── Assign synthetic ids to any element missing data-wt-id ──
|
|
85
|
+
// data-wt-secret guard fires on the ATTRIBUTE (not the id), so secret fields
|
|
86
|
+
// are safe even when running on a synthetic id.
|
|
87
|
+
var syntheticPrefix = '__prune_idx_';
|
|
88
|
+
var syntheticEls = [];
|
|
89
|
+
var allEls = document.querySelectorAll('[data-wt-id], [data-wt-secret]');
|
|
90
|
+
var synIdx = 0;
|
|
91
|
+
for (var si = 0; si < allEls.length; si++) {
|
|
92
|
+
var sel = allEls[si];
|
|
93
|
+
if (!sel.getAttribute('data-wt-id')) {
|
|
94
|
+
var synthId = syntheticPrefix + (synIdx++);
|
|
95
|
+
sel.setAttribute('data-wt-id', synthId);
|
|
96
|
+
syntheticEls.push(sel);
|
|
97
|
+
targetIds.add(synthId);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ── Pass 1: Document-level controls map ──────────────────────────────────
|
|
102
|
+
// Built from the WHOLE document — stale or unrelated aria-controls values
|
|
103
|
+
// can force-keep unrelated targets. Accepted over-keep bias: prefer recall
|
|
104
|
+
// over precision so the agent never loses a toggle target it needs.
|
|
105
|
+
var controlsMap = new Set();
|
|
106
|
+
var CONTROLS_ATTRS = ['aria-controls', 'aria-owns', 'data-target', 'data-bs-target'];
|
|
107
|
+
var extraIdioms = ${idiomsJson};
|
|
108
|
+
for (var ei = 0; ei < extraIdioms.length; ei++) {
|
|
109
|
+
if (CONTROLS_ATTRS.indexOf(extraIdioms[ei]) === -1) {
|
|
110
|
+
CONTROLS_ATTRS.push(extraIdioms[ei]);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
var allNodes = document.querySelectorAll(CONTROLS_ATTRS.map(function(a) { return '[' + a + ']'; }).join(', '));
|
|
115
|
+
for (var ci = 0; ci < allNodes.length; ci++) {
|
|
116
|
+
for (var ai = 0; ai < CONTROLS_ATTRS.length; ai++) {
|
|
117
|
+
var val = allNodes[ci].getAttribute(CONTROLS_ATTRS[ai]);
|
|
118
|
+
if (!val) continue;
|
|
119
|
+
var tokens = val.split(/\\s+/);
|
|
120
|
+
for (var ti = 0; ti < tokens.length; ti++) {
|
|
121
|
+
if (tokens[ti]) controlsMap.add(tokens[ti]);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
} catch(e) {}
|
|
126
|
+
|
|
127
|
+
// ── Pass 2: Post-order hasInteractiveDescendant annotation ───────────────
|
|
128
|
+
// Single bottom-up pass — O(n). Each hidden-element check is then O(1).
|
|
129
|
+
try {
|
|
130
|
+
var walker = document.createTreeWalker(document.body || document, NodeFilter.SHOW_ELEMENT, null, false);
|
|
131
|
+
var nodeStack = [];
|
|
132
|
+
var node = walker.nextNode();
|
|
133
|
+
while (node) {
|
|
134
|
+
nodeStack.push(node);
|
|
135
|
+
node = walker.nextNode();
|
|
136
|
+
}
|
|
137
|
+
// Process in reverse (post-order)
|
|
138
|
+
for (var ni = nodeStack.length - 1; ni >= 0; ni--) {
|
|
139
|
+
var n = nodeStack[ni];
|
|
140
|
+
var isInteractive = false;
|
|
141
|
+
try { isInteractive = n.matches(INTERACTIVE_SEL); } catch(e) {}
|
|
142
|
+
var childHas = false;
|
|
143
|
+
try {
|
|
144
|
+
var children = n.children;
|
|
145
|
+
for (var ki = 0; ki < children.length; ki++) {
|
|
146
|
+
if (children[ki].__wtHasInteractive) { childHas = true; break; }
|
|
147
|
+
}
|
|
148
|
+
} catch(e) {}
|
|
149
|
+
n.__wtHasInteractive = isInteractive || childHas;
|
|
150
|
+
}
|
|
151
|
+
} catch(e) {}
|
|
152
|
+
|
|
153
|
+
// ── Pass 3: Per-element prune decision ───────────────────────────────────
|
|
154
|
+
var keptIds = [];
|
|
155
|
+
var svgLabels = {};
|
|
156
|
+
var prunedCount = 0;
|
|
157
|
+
var svgCount = 0;
|
|
158
|
+
|
|
159
|
+
targetIds.forEach(function(wtId) {
|
|
160
|
+
var el;
|
|
161
|
+
try {
|
|
162
|
+
el = document.querySelector('[data-wt-id="' + wtId + '"]');
|
|
163
|
+
} catch(e) {}
|
|
164
|
+
if (!el) { prunedCount++; return; }
|
|
165
|
+
|
|
166
|
+
// Secret guard — fires on ATTRIBUTE, independent of whether id is synthetic
|
|
167
|
+
if (el.hasAttribute('data-wt-secret')) {
|
|
168
|
+
keptIds.push(String(wtId));
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Visibility check
|
|
173
|
+
var hidden = false;
|
|
174
|
+
try {
|
|
175
|
+
var rect = el.getBoundingClientRect();
|
|
176
|
+
if (rect.width === 0 && rect.height === 0) {
|
|
177
|
+
hidden = true;
|
|
178
|
+
} else {
|
|
179
|
+
var cs = window.getComputedStyle(el);
|
|
180
|
+
if (cs.visibility === 'hidden' || cs.display === 'none') hidden = true;
|
|
181
|
+
}
|
|
182
|
+
} catch(e) {}
|
|
183
|
+
|
|
184
|
+
if (!hidden) {
|
|
185
|
+
// Visible — keep; handle SVG label replacement
|
|
186
|
+
keptIds.push(String(wtId));
|
|
187
|
+
if (el.tagName && el.tagName.toLowerCase() === 'svg') {
|
|
188
|
+
var label = resolveSvgLabel(el, SVG_ANCESTOR_SEL);
|
|
189
|
+
svgLabels[String(wtId)] = label;
|
|
190
|
+
svgCount++;
|
|
191
|
+
}
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Hidden — keep if has interactive descendant
|
|
196
|
+
if (el.__wtHasInteractive) {
|
|
197
|
+
keptIds.push(String(wtId));
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Hidden — keep if referenced by a known toggle trigger (controlsMap)
|
|
202
|
+
var elId = el.getAttribute('id');
|
|
203
|
+
if (elId && controlsMap.has(elId)) {
|
|
204
|
+
keptIds.push(String(wtId));
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Drop
|
|
209
|
+
prunedCount++;
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// ── Strip synthetic ids from DOM ─────────────────────────────────────────
|
|
213
|
+
for (var ri = 0; ri < syntheticEls.length; ri++) {
|
|
214
|
+
try { syntheticEls[ri].removeAttribute('data-wt-id'); } catch(e) {}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// ── SVG label resolution helper ──────────────────────────────────────────
|
|
218
|
+
function resolveSvgLabel(svg, ancestorSel) {
|
|
219
|
+
// 1. SVG's own aria-label
|
|
220
|
+
var label = svg.getAttribute('aria-label');
|
|
221
|
+
if (label && label.trim()) return '[SVG Icon: ' + label.trim() + ']';
|
|
222
|
+
|
|
223
|
+
// 2. SVG's own <title> child
|
|
224
|
+
try {
|
|
225
|
+
var title = svg.querySelector('title');
|
|
226
|
+
if (title && title.textContent && title.textContent.trim()) {
|
|
227
|
+
return '[SVG Icon: ' + title.textContent.trim() + ']';
|
|
228
|
+
}
|
|
229
|
+
} catch(e) {}
|
|
230
|
+
|
|
231
|
+
// 3. Closest interactive ancestor's aria-label
|
|
232
|
+
// Covers: <button aria-label="Delete"><svg/></button>
|
|
233
|
+
// <a role="menuitem" aria-label="More actions"><svg/></a> (Jira)
|
|
234
|
+
// <div role="tab" aria-label="Overview"><svg/></div> (AWS)
|
|
235
|
+
try {
|
|
236
|
+
var ancestor = svg.closest(ancestorSel);
|
|
237
|
+
if (ancestor) {
|
|
238
|
+
var aLabel = ancestor.getAttribute('aria-label');
|
|
239
|
+
if (aLabel && aLabel.trim()) return '[SVG Icon: ' + aLabel.trim() + ']';
|
|
240
|
+
}
|
|
241
|
+
} catch(e) {}
|
|
242
|
+
|
|
243
|
+
return '[SVG Icon]';
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
var elapsedMs = performance.now() - t0;
|
|
247
|
+
|
|
248
|
+
// ── Perf-gate: if this took > 5ms, flag it — caller will decide on split ─
|
|
249
|
+
// (In this single-evaluate path we can only report, not split retroactively.
|
|
250
|
+
// The split path would be invoked by the caller on a second evaluate call.)
|
|
251
|
+
return JSON.stringify({
|
|
252
|
+
keptIds: keptIds,
|
|
253
|
+
svgLabels: svgLabels,
|
|
254
|
+
stats: {
|
|
255
|
+
total: targetIds.size,
|
|
256
|
+
pruned: prunedCount,
|
|
257
|
+
svgReplaced: svgCount,
|
|
258
|
+
elapsedMs: elapsedMs,
|
|
259
|
+
splitPath: false
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
})()`;
|
|
263
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weavetab — cdp/ref_cache.ts
|
|
3
|
+
* Unified Ref Coordinate Cache (URC)
|
|
4
|
+
*
|
|
5
|
+
* Single in-memory, per-tab store of refId → {selector, x, y, backendNodeId, fingerprint}
|
|
6
|
+
* populated once by buildActionMap/buildAXTreeMap and read by every interaction tool.
|
|
7
|
+
*
|
|
8
|
+
* Design goals:
|
|
9
|
+
* - Zero extra CDP calls on cache hit (< TTL)
|
|
10
|
+
* - One atomic Runtime.evaluate on stale/miss (atomicResolve)
|
|
11
|
+
* - Keyed strictly by targetId (frame-scoped) — iframe contexts never bleed
|
|
12
|
+
* - Auto-invalidated on frameNavigated
|
|
13
|
+
*
|
|
14
|
+
* Weavetab by fy2ne
|
|
15
|
+
*/
|
|
16
|
+
import CDP from "chrome-remote-interface";
|
|
17
|
+
export interface RefCoordEntry {
|
|
18
|
+
/** Primary selector — [data-wt-id="..."] form preferred */
|
|
19
|
+
selector: string;
|
|
20
|
+
/** Center X at capture time */
|
|
21
|
+
x: number;
|
|
22
|
+
/** Center Y at capture time */
|
|
23
|
+
y: number;
|
|
24
|
+
/** Epoch ms — for TTL gate */
|
|
25
|
+
capturedAt: number;
|
|
26
|
+
/** CDP backendNodeId for re-resolution via DOM.getBoxModel when selector fails */
|
|
27
|
+
backendNodeId?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Lightweight identity string: "tag:role:labelPrefix"
|
|
30
|
+
* Checked inside atomicResolve to detect DOM replacement (re-render).
|
|
31
|
+
*/
|
|
32
|
+
fingerprint: string;
|
|
33
|
+
/** Holographic: visual bounding box hash for deduplication */
|
|
34
|
+
visualHash?: string;
|
|
35
|
+
/** Holographic: full or partial XPath */
|
|
36
|
+
xpath?: string;
|
|
37
|
+
/** Holographic: current value for stateful tracking */
|
|
38
|
+
value?: string;
|
|
39
|
+
/** Holographic: label text */
|
|
40
|
+
label?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface AtomicResolveResult {
|
|
43
|
+
status: "OK" | "NOT_FOUND" | "STALE" | "INVISIBLE" | "SHADOW";
|
|
44
|
+
x?: number;
|
|
45
|
+
y?: number;
|
|
46
|
+
}
|
|
47
|
+
/** Default TTL: 30 seconds. Overridden by config.refCacheTtlMs at call sites. */
|
|
48
|
+
export declare const DEFAULT_URC_TTL_MS = 30000;
|
|
49
|
+
/** Returns stale miss rate [0–1] for the last observation window. */
|
|
50
|
+
export declare function getStaleMissRate(hostname: string): number;
|
|
51
|
+
/** Write an entry into the URC for a given tab and refId. */
|
|
52
|
+
export declare function urcSet(targetId: string, refId: string, entry: RefCoordEntry): void;
|
|
53
|
+
/**
|
|
54
|
+
* Read an entry from the URC.
|
|
55
|
+
* Returns null if: not found, OR entry is older than maxAgeMs.
|
|
56
|
+
*/
|
|
57
|
+
export declare function urcGet(targetId: string, refId: string, maxAgeMs?: number): RefCoordEntry | null;
|
|
58
|
+
/** Return all cached entries for a tab (used by health checks and delta validation). */
|
|
59
|
+
export declare function urcGetAll(targetId: string): Map<string, RefCoordEntry>;
|
|
60
|
+
/**
|
|
61
|
+
* Invalidate all URC entries for a tab.
|
|
62
|
+
* Must be called on: frameNavigated, full map rebuild, tab close.
|
|
63
|
+
*/
|
|
64
|
+
export declare function urcInvalidate(targetId: string): void;
|
|
65
|
+
/**
|
|
66
|
+
* Bulk-populate the URC from a freshly built ActionElement array.
|
|
67
|
+
* Called at the end of buildActionMap and buildAXTreeMap.
|
|
68
|
+
* Only writes entries that have both x and selector defined.
|
|
69
|
+
*/
|
|
70
|
+
export declare function urcPopulate(targetId: string, elements: Array<{
|
|
71
|
+
id: string;
|
|
72
|
+
selector?: string;
|
|
73
|
+
x?: number;
|
|
74
|
+
y?: number;
|
|
75
|
+
backendNodeId?: number;
|
|
76
|
+
role?: string;
|
|
77
|
+
label?: string;
|
|
78
|
+
value?: string;
|
|
79
|
+
type?: string;
|
|
80
|
+
fingerprint?: string;
|
|
81
|
+
visualHash?: string;
|
|
82
|
+
xpath?: string;
|
|
83
|
+
}>): void;
|
|
84
|
+
/**
|
|
85
|
+
* Single CDP Runtime.evaluate that simultaneously:
|
|
86
|
+
* 1. Finds the element via selector (with WeakMap re-hydration fallback)
|
|
87
|
+
* 2. Validates tag/role fingerprint (lightweight string check)
|
|
88
|
+
* 3. Returns fresh coordinates from getBoundingClientRect
|
|
89
|
+
*
|
|
90
|
+
* Cost: 1 CDP call (~2–5ms). On a URC cache hit this is the ONLY extra call made.
|
|
91
|
+
* On a full miss (Stage 3+) it is still cheaper than re-running buildActionMap.
|
|
92
|
+
*/
|
|
93
|
+
export declare function atomicResolve(session: CDP.Client, selector: string, fingerprint?: string, hostname?: string): Promise<AtomicResolveResult>;
|
|
94
|
+
/**
|
|
95
|
+
* Attempt coordinate resolution via CDP DOM.getBoxModel using a backendNodeId.
|
|
96
|
+
* Used as Stage 4 in the click fallback chain when the selector path is unavailable.
|
|
97
|
+
*/
|
|
98
|
+
export declare function resolveViaBackendNodeId(session: CDP.Client, backendNodeId: number): Promise<{
|
|
99
|
+
x: number;
|
|
100
|
+
y: number;
|
|
101
|
+
} | null>;
|