@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,237 @@
|
|
|
1
|
+
import { logDebug } from "../audit/logger.js";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import * as os from "node:os";
|
|
4
|
+
import { execSync, spawnSync } from "node:child_process";
|
|
5
|
+
import { getSessionProfile } from "./profiles.js";
|
|
6
|
+
import { getConfigCookies, getConfigProfileDir } from "../config/loader.js";
|
|
7
|
+
// ─── Profile Verification Cache ───────────────────────────────────────────────────
|
|
8
|
+
// Cap profileVerificationCache at 100 entries (evict oldest on overflow) and
|
|
9
|
+
// sweep entries for closed sessions every 5 minutes via an unref'd interval so
|
|
10
|
+
// the cache doesn't grow without bound across many tabs / reconnects.
|
|
11
|
+
const PROFILE_CACHE_MAX = 100;
|
|
12
|
+
let profileVerificationCache = new Map();
|
|
13
|
+
function profileCacheSet(key, value) {
|
|
14
|
+
if (profileVerificationCache.size >= PROFILE_CACHE_MAX && !profileVerificationCache.has(key)) {
|
|
15
|
+
const firstKey = profileVerificationCache.keys().next().value;
|
|
16
|
+
if (firstKey !== undefined)
|
|
17
|
+
profileVerificationCache.delete(firstKey);
|
|
18
|
+
}
|
|
19
|
+
profileVerificationCache.set(key, value);
|
|
20
|
+
}
|
|
21
|
+
// Periodic sweep: clear the whole cache every 5 min so stale session IDs don't persist
|
|
22
|
+
setInterval(() => { profileVerificationCache.clear(); }, 5 * 60_000).unref();
|
|
23
|
+
/**
|
|
24
|
+
* Verifies that the browser is actually using the isolated profile directory.
|
|
25
|
+
* Uses CDP to check the actual user data directory being used by the browser.
|
|
26
|
+
* Results are cached per session to avoid per-call overhead.
|
|
27
|
+
*/
|
|
28
|
+
export async function verifyProfileIsolation(session, config, targetId) {
|
|
29
|
+
const cacheKey = targetId;
|
|
30
|
+
// Return cached result if available
|
|
31
|
+
if (profileVerificationCache.has(cacheKey)) {
|
|
32
|
+
const cached = profileVerificationCache.get(cacheKey);
|
|
33
|
+
const expectedProfile = getSessionProfile(getConfigCookies(config), getConfigProfileDir(config));
|
|
34
|
+
return { verified: cached, expectedProfile };
|
|
35
|
+
}
|
|
36
|
+
const expectedProfile = getSessionProfile(getConfigCookies(config), getConfigProfileDir(config));
|
|
37
|
+
try {
|
|
38
|
+
// Method 1: Check browser info via CDP (most reliable)
|
|
39
|
+
const browserInfo = await session.Browser.getVersion();
|
|
40
|
+
// Parse user agent to check for Weavetab session markers
|
|
41
|
+
// Weavetab adds custom flags that should be present in the browser
|
|
42
|
+
const userAgent = browserInfo.userAgent || "";
|
|
43
|
+
// Method 2: Check if browser has the Weavetab extension loaded
|
|
44
|
+
// This confirms it's our launched instance
|
|
45
|
+
const extensionCheck = await session.Runtime.evaluate({
|
|
46
|
+
expression: '!!(window.__wt_session_id || window.__wt_config)',
|
|
47
|
+
returnByValue: true,
|
|
48
|
+
});
|
|
49
|
+
// Method 3: Verify process command line contains the isolated profile path
|
|
50
|
+
// This requires OS-specific process inspection
|
|
51
|
+
const profileFromProcess = await getProfileFromProcess(session, config);
|
|
52
|
+
// Verification passes if:
|
|
53
|
+
// 1. Extension is loaded (we launched this browser)
|
|
54
|
+
// 2. OR we can verify the profile path from process info
|
|
55
|
+
const extensionLoaded = extensionCheck.result?.value === true;
|
|
56
|
+
const profileMatches = profileFromProcess ? normalizePath(profileFromProcess) === normalizePath(expectedProfile) : false;
|
|
57
|
+
const verified = extensionLoaded || profileMatches;
|
|
58
|
+
profileCacheSet(cacheKey, verified);
|
|
59
|
+
return {
|
|
60
|
+
verified,
|
|
61
|
+
actualProfile: profileFromProcess || undefined,
|
|
62
|
+
expectedProfile,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
logDebug(`[Weavetab Security] Profile verification failed: ${error}`);
|
|
67
|
+
// Fail closed: return verified:false and let the caller decide how to handle
|
|
68
|
+
// it. Verification errors — including a compromised or hijacked session —
|
|
69
|
+
// must never be treated as success.
|
|
70
|
+
profileCacheSet(cacheKey, false);
|
|
71
|
+
return { verified: false, expectedProfile };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Clears the profile verification cache for a specific session.
|
|
76
|
+
* Called when a session is closed or reconnected.
|
|
77
|
+
*/
|
|
78
|
+
export function clearProfileVerificationCache(targetId) {
|
|
79
|
+
profileVerificationCache.delete(targetId);
|
|
80
|
+
}
|
|
81
|
+
let currentFingerprint = null;
|
|
82
|
+
/**
|
|
83
|
+
* Creates a unique fingerprint for the launched browser process.
|
|
84
|
+
* This is stored in the lock file and verified on reconnection.
|
|
85
|
+
*/
|
|
86
|
+
export function createBrowserFingerprint(pid, port, profileDir, browserPath) {
|
|
87
|
+
return {
|
|
88
|
+
pid,
|
|
89
|
+
port,
|
|
90
|
+
profileDir,
|
|
91
|
+
browserPath,
|
|
92
|
+
launchTimestamp: Date.now(),
|
|
93
|
+
userAgent: "", // Will be filled after CDP connection
|
|
94
|
+
cdpVersion: "", // Will be filled after CDP connection
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Verifies that the connected browser matches the expected fingerprint.
|
|
99
|
+
* Runs at CDP connect time only.
|
|
100
|
+
*/
|
|
101
|
+
export async function verifyBrowserFingerprint(session, expectedFingerprint) {
|
|
102
|
+
try {
|
|
103
|
+
const browserInfo = await session.Browser.getVersion();
|
|
104
|
+
// Verify CDP version matches (or is compatible)
|
|
105
|
+
const currentCDPVersion = browserInfo.product || "";
|
|
106
|
+
// Verify user agent is from a Chromium-based browser
|
|
107
|
+
const userAgent = browserInfo.userAgent || "";
|
|
108
|
+
if (!userAgent.includes("Chrome") && !userAgent.includes("Edge") && !userAgent.includes("Brave")) {
|
|
109
|
+
return { valid: false, reason: "User agent does not match expected browser type" };
|
|
110
|
+
}
|
|
111
|
+
// Update fingerprint with runtime data
|
|
112
|
+
currentFingerprint = {
|
|
113
|
+
...expectedFingerprint,
|
|
114
|
+
userAgent,
|
|
115
|
+
cdpVersion: currentCDPVersion,
|
|
116
|
+
};
|
|
117
|
+
return { valid: true };
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
return { valid: false, reason: `Fingerprint verification failed: ${error}` };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
export function getCurrentFingerprint() {
|
|
124
|
+
return currentFingerprint;
|
|
125
|
+
}
|
|
126
|
+
// ─── Process Killing Safety ────────────────────────────────────────────────────
|
|
127
|
+
/**
|
|
128
|
+
* Safely kills browser processes that were launched by Weavetab in the current session.
|
|
129
|
+
* Only kills processes that match the exact profile directory from the lock file.
|
|
130
|
+
* Never matches against processes that weren't launched by Weavetab.
|
|
131
|
+
*/
|
|
132
|
+
const _killCooldown = new Map();
|
|
133
|
+
export function safelyKillWeavetabProcesses(profileDir, expectedPid) {
|
|
134
|
+
const now = Date.now();
|
|
135
|
+
const last = _killCooldown.get(profileDir) ?? 0;
|
|
136
|
+
if (now - last < 10_000) {
|
|
137
|
+
logDebug(`[Weavetab Security] safelyKillWeavetabProcesses: cooldown active, skipping.`);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
_killCooldown.set(profileDir, now);
|
|
141
|
+
const plat = os.platform();
|
|
142
|
+
const normalizedProfile = normalizePath(profileDir);
|
|
143
|
+
try {
|
|
144
|
+
if (plat === 'win32') {
|
|
145
|
+
// Windows: use tasklist with NO user data in the command string.
|
|
146
|
+
// Parse PIDs in JS, kill each with taskkill args array (no shell).
|
|
147
|
+
//
|
|
148
|
+
// Primary: PowerShell Get-WmiObject (works on all Win10+ without wmic.exe)
|
|
149
|
+
const psResult = spawnSync('powershell', [
|
|
150
|
+
'-NoProfile', '-NonInteractive', '-Command',
|
|
151
|
+
'Get-WmiObject Win32_Process | Select-Object ProcessId,CommandLine | ConvertTo-Csv -NoTypeInformation'
|
|
152
|
+
], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
153
|
+
if (psResult.status === 0 && psResult.stdout) {
|
|
154
|
+
const lines = psResult.stdout.split('\n').slice(1); // skip CSV header
|
|
155
|
+
for (const line of lines) {
|
|
156
|
+
const trimmed = line.trim();
|
|
157
|
+
if (!trimmed)
|
|
158
|
+
continue;
|
|
159
|
+
// CSV: "ProcessId","CommandLine"
|
|
160
|
+
const fields = trimmed.split(/,(?=(?:[^"]*"[^"]*")*[^"]*$)/).map(f => f.replace(/^"|"$/g, ''));
|
|
161
|
+
const pid = parseInt(fields[0] ?? '0', 10);
|
|
162
|
+
const cmdLine = (fields[1] ?? '').toLowerCase();
|
|
163
|
+
if (isNaN(pid) || pid <= 0)
|
|
164
|
+
continue;
|
|
165
|
+
if (expectedPid !== undefined && pid !== expectedPid)
|
|
166
|
+
continue;
|
|
167
|
+
// Filter in JS — no interpolation into any shell command
|
|
168
|
+
if (cmdLine.includes(normalizedProfile) && cmdLine.includes('--user-data-dir')) {
|
|
169
|
+
spawnSync('taskkill', ['/F', '/PID', String(pid)], { stdio: 'ignore' });
|
|
170
|
+
logDebug(`[Weavetab Security] Safely terminated Weavetab process: ${pid}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
// Unix: read ps output in JS, filter in JS, kill via process.kill — never interpolate into shell
|
|
177
|
+
const psResult = execSync('ps -ax -o pid= -o command=', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
178
|
+
for (const line of psResult.split('\n')) {
|
|
179
|
+
const trimmed = line.trim();
|
|
180
|
+
if (!trimmed)
|
|
181
|
+
continue;
|
|
182
|
+
const spaceIdx = trimmed.indexOf(' ');
|
|
183
|
+
if (spaceIdx === -1)
|
|
184
|
+
continue;
|
|
185
|
+
const pid = parseInt(trimmed.slice(0, spaceIdx), 10);
|
|
186
|
+
const cmd = trimmed.slice(spaceIdx + 1).toLowerCase();
|
|
187
|
+
if (isNaN(pid) || pid <= 0)
|
|
188
|
+
continue;
|
|
189
|
+
if (expectedPid !== undefined && pid !== expectedPid)
|
|
190
|
+
continue;
|
|
191
|
+
if (cmd.includes(normalizedProfile) && cmd.includes('--user-data-dir')) {
|
|
192
|
+
try {
|
|
193
|
+
process.kill(pid, 'SIGKILL');
|
|
194
|
+
logDebug(`[Weavetab Security] Safely terminated Weavetab process: ${pid}`);
|
|
195
|
+
}
|
|
196
|
+
catch { /* process already gone */ }
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
logDebug(`[Weavetab Security] Process killing failed (non-critical): ${error}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Returns a normalized, platform-independent path for comparison.
|
|
207
|
+
*/
|
|
208
|
+
function normalizePath(p) {
|
|
209
|
+
return path.resolve(p).toLowerCase();
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Attempts to extract the profile directory from the browser process.
|
|
213
|
+
* This is a fallback method for profile verification.
|
|
214
|
+
*/
|
|
215
|
+
async function getProfileFromProcess(session, config) {
|
|
216
|
+
try {
|
|
217
|
+
// Use extension-based method to check profile directory
|
|
218
|
+
const result = await session.Runtime.evaluate({
|
|
219
|
+
expression: `
|
|
220
|
+
(() => {
|
|
221
|
+
try {
|
|
222
|
+
// Check if we can read the user data directory from browser internals
|
|
223
|
+
if (window.__wt_profile_dir) return window.__wt_profile_dir;
|
|
224
|
+
return null;
|
|
225
|
+
} catch (e) {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
})()
|
|
229
|
+
`,
|
|
230
|
+
returnByValue: true,
|
|
231
|
+
});
|
|
232
|
+
return result.result?.value || null;
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verification helper functions for DOM state confirmation
|
|
3
|
+
* Provides utilities to verify element state, detect visible feedback, and infer next actions
|
|
4
|
+
*/
|
|
5
|
+
import CDP from "chrome-remote-interface";
|
|
6
|
+
export interface ElementState {
|
|
7
|
+
exists: boolean;
|
|
8
|
+
tagName?: string;
|
|
9
|
+
value?: string;
|
|
10
|
+
textContent?: string;
|
|
11
|
+
checked?: boolean;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
visible?: boolean;
|
|
14
|
+
attributes?: Record<string, string>;
|
|
15
|
+
className?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface VisibleFeedback {
|
|
18
|
+
type: 'error' | 'warning' | 'success' | 'info';
|
|
19
|
+
text: string;
|
|
20
|
+
selector?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Read full element state for verification
|
|
24
|
+
* Returns comprehensive state snapshot for before/after comparison
|
|
25
|
+
*/
|
|
26
|
+
export declare function verifyElementState(session: CDP.Client, selector: string): Promise<ElementState>;
|
|
27
|
+
/**
|
|
28
|
+
* Detect visible feedback messages (toasts, errors, validation messages)
|
|
29
|
+
* Scans common selectors for feedback elements
|
|
30
|
+
*/
|
|
31
|
+
export declare function detectVisibleFeedback(session: CDP.Client): Promise<VisibleFeedback[]>;
|
|
32
|
+
/**
|
|
33
|
+
* Infer likely next action based on page context
|
|
34
|
+
* Analyzes page state to suggest what the agent should do next
|
|
35
|
+
*/
|
|
36
|
+
export declare function inferNextAction(session: CDP.Client, lastAction: string, context?: {
|
|
37
|
+
filledFields?: string[];
|
|
38
|
+
clickedElement?: string;
|
|
39
|
+
}): Promise<string | undefined>;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verification helper functions for DOM state confirmation
|
|
3
|
+
* Provides utilities to verify element state, detect visible feedback, and infer next actions
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Read full element state for verification
|
|
7
|
+
* Returns comprehensive state snapshot for before/after comparison
|
|
8
|
+
*/
|
|
9
|
+
export async function verifyElementState(session, selector) {
|
|
10
|
+
try {
|
|
11
|
+
const { result } = await session.Runtime.evaluate({
|
|
12
|
+
expression: `(function(sel) {
|
|
13
|
+
try {
|
|
14
|
+
var el = document.querySelector(sel);
|
|
15
|
+
if (!el) return JSON.stringify({ exists: false });
|
|
16
|
+
|
|
17
|
+
var rect = el.getBoundingClientRect();
|
|
18
|
+
var attrs = {};
|
|
19
|
+
for (var i = 0; i < el.attributes.length; i++) {
|
|
20
|
+
attrs[el.attributes[i].name] = el.attributes[i].value;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return JSON.stringify({
|
|
24
|
+
exists: true,
|
|
25
|
+
tagName: el.tagName.toLowerCase(),
|
|
26
|
+
value: (el as any).value !== undefined ? String((el as any).value) : undefined,
|
|
27
|
+
textContent: el.textContent ? el.textContent.slice(0, 200) : undefined,
|
|
28
|
+
checked: (el as any).checked,
|
|
29
|
+
disabled: (el as any).disabled,
|
|
30
|
+
visible: rect.width > 0 && rect.height > 0 && rect.bottom >= 0 && rect.top <= window.innerHeight,
|
|
31
|
+
attributes: attrs,
|
|
32
|
+
className: el.className
|
|
33
|
+
});
|
|
34
|
+
} catch(e) {
|
|
35
|
+
return JSON.stringify({ exists: false, error: (e as Error).message });
|
|
36
|
+
}
|
|
37
|
+
})(${JSON.stringify(selector)})`,
|
|
38
|
+
returnByValue: true,
|
|
39
|
+
});
|
|
40
|
+
return result?.value || { exists: false };
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return { exists: false };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Detect visible feedback messages (toasts, errors, validation messages)
|
|
48
|
+
* Scans common selectors for feedback elements
|
|
49
|
+
*/
|
|
50
|
+
export async function detectVisibleFeedback(session) {
|
|
51
|
+
try {
|
|
52
|
+
const { result } = await session.Runtime.evaluate({
|
|
53
|
+
expression: (function () {
|
|
54
|
+
var feedback = [];
|
|
55
|
+
var selectors = [
|
|
56
|
+
'[role="alert"]', '[role="status"]', '[role="error"]',
|
|
57
|
+
'.error', '.warning', '.success', '.info',
|
|
58
|
+
'[class*="error"]', '[class*="warning"]', '[class*="success"]',
|
|
59
|
+
'[class*="toast"]', '[class*="notification"]',
|
|
60
|
+
'[data-testid*="error"]', '[data-testid*="message"]'
|
|
61
|
+
];
|
|
62
|
+
for (var i = 0; i < selectors.length; i++) {
|
|
63
|
+
var els = document.querySelectorAll(selectors[i]);
|
|
64
|
+
for (var j = 0; j < els.length; j++) {
|
|
65
|
+
var el = els[j];
|
|
66
|
+
var rect = el.getBoundingClientRect();
|
|
67
|
+
if (rect.width > 0 && rect.height > 0 && rect.bottom >= 0 && rect.top <= window.innerHeight) {
|
|
68
|
+
var text = el.textContent || el.innerText || '';
|
|
69
|
+
if (text.trim()) {
|
|
70
|
+
var type = 'info';
|
|
71
|
+
if (el.className.includes('error') || el.getAttribute('role') === 'error')
|
|
72
|
+
type = 'error';
|
|
73
|
+
else if (el.className.includes('warning'))
|
|
74
|
+
type = 'warning';
|
|
75
|
+
else if (el.className.includes('success'))
|
|
76
|
+
type = 'success';
|
|
77
|
+
feedback.push({
|
|
78
|
+
type: type,
|
|
79
|
+
text: text.trim().slice(0, 200),
|
|
80
|
+
selector: selectors[i]
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return JSON.stringify(feedback.slice(0, 5)); // Limit to 5 most recent
|
|
87
|
+
})(),
|
|
88
|
+
returnByValue: true,
|
|
89
|
+
});
|
|
90
|
+
return result?.value || [];
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Infer likely next action based on page context
|
|
98
|
+
* Analyzes page state to suggest what the agent should do next
|
|
99
|
+
*/
|
|
100
|
+
export async function inferNextAction(session, lastAction, context) {
|
|
101
|
+
try {
|
|
102
|
+
const { result } = await session.Runtime.evaluate({
|
|
103
|
+
expression: `
|
|
104
|
+
(function(lastAction, ctx) {
|
|
105
|
+
try {
|
|
106
|
+
var suggestions = [];
|
|
107
|
+
if (lastAction === 'fill' || lastAction === 'type') {
|
|
108
|
+
var inputs = document.querySelectorAll('input:not([type="hidden"]):not([type="submit"]):not([type="button"]), textarea, select[required]');
|
|
109
|
+
var emptyRequired = [];
|
|
110
|
+
for (var i = 0; i < inputs.length; i++) {
|
|
111
|
+
var inp = inputs[i];
|
|
112
|
+
if ((inp.required || inp.getAttribute('aria-required') === 'true') && !inp.value) {
|
|
113
|
+
emptyRequired.push(inp.getAttribute('placeholder') || inp.getAttribute('name') || inp.getAttribute('aria-label') || 'field');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (emptyRequired.length > 0) {
|
|
117
|
+
suggestions.push('Fill required field: ' + emptyRequired[0]);
|
|
118
|
+
}
|
|
119
|
+
var submitBtn = document.querySelector('button[type="submit"], input[type="submit"], [type="submit"]');
|
|
120
|
+
if (submitBtn) {
|
|
121
|
+
suggestions.push('Submit form');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (lastAction === 'click') {
|
|
125
|
+
var modal = document.querySelector('[role="dialog"], .modal, [class*="modal"]');
|
|
126
|
+
if (modal) {
|
|
127
|
+
suggestions.push('Interact with modal dialog');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return JSON.stringify(suggestions[0] || undefined);
|
|
131
|
+
} catch(e) {
|
|
132
|
+
return JSON.stringify(undefined);
|
|
133
|
+
}
|
|
134
|
+
})(${JSON.stringify(lastAction)}, ${JSON.stringify(context)})
|
|
135
|
+
`,
|
|
136
|
+
returnByValue: true,
|
|
137
|
+
});
|
|
138
|
+
return result?.value;
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weavetab — cdp/walker.ts
|
|
3
|
+
* Accessibility tree walker: builds w:N ref IDs, viewport culling, and heuristic pointer sweep.
|
|
4
|
+
* Weavetab by fy2ne
|
|
5
|
+
*/
|
|
6
|
+
import CDP from "chrome-remote-interface";
|
|
7
|
+
import type { RefEntry } from "../state/delta.js";
|
|
8
|
+
export declare const WT_REGISTRY_INIT_EXPR = "\n window.__weavetab = window.__weavetab || {};\n window.__weavetab.registry = window.__weavetab.registry || new WeakMap();\n";
|
|
9
|
+
export type HrefType = "same-origin" | "external" | "anchor" | "email" | "unknown";
|
|
10
|
+
export interface ActionElement {
|
|
11
|
+
id: string;
|
|
12
|
+
role: string;
|
|
13
|
+
label: string;
|
|
14
|
+
value?: string;
|
|
15
|
+
backendNodeId?: number;
|
|
16
|
+
selector?: string;
|
|
17
|
+
group: "navigation" | "main_content" | "sidebar" | "form" | "modal" | "utility";
|
|
18
|
+
type?: string;
|
|
19
|
+
href?: string;
|
|
20
|
+
hrefType?: HrefType;
|
|
21
|
+
placeholder?: string;
|
|
22
|
+
testId?: string;
|
|
23
|
+
textContent?: string;
|
|
24
|
+
className?: string;
|
|
25
|
+
frameUrl?: string;
|
|
26
|
+
fingerprint: string;
|
|
27
|
+
isFixed?: boolean;
|
|
28
|
+
disabled?: boolean;
|
|
29
|
+
depth: number;
|
|
30
|
+
x?: number;
|
|
31
|
+
y?: number;
|
|
32
|
+
viewport?: string;
|
|
33
|
+
structuralIdiom?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface ActionMap {
|
|
36
|
+
url: string;
|
|
37
|
+
title: string;
|
|
38
|
+
elements: ActionElement[];
|
|
39
|
+
pageType?: string;
|
|
40
|
+
targetId?: string;
|
|
41
|
+
domHash?: number;
|
|
42
|
+
}
|
|
43
|
+
export interface WalkerOptions {
|
|
44
|
+
scope?: ActionElement["group"];
|
|
45
|
+
query?: string;
|
|
46
|
+
limit?: number;
|
|
47
|
+
lite?: boolean;
|
|
48
|
+
fast?: boolean;
|
|
49
|
+
/** If true, returns delta info instead of full element list (caller handles) */
|
|
50
|
+
forDelta?: boolean;
|
|
51
|
+
/** Viewport dimensions — used for culling. Defaults fetched from browser if not passed. */
|
|
52
|
+
viewport?: {
|
|
53
|
+
width: number;
|
|
54
|
+
height: number;
|
|
55
|
+
scrollX: number;
|
|
56
|
+
scrollY: number;
|
|
57
|
+
};
|
|
58
|
+
/** If true, skip the flat-first optimization and go straight to deep shadow DOM traversal */
|
|
59
|
+
deep?: boolean;
|
|
60
|
+
/** Pre-resolved page URL — avoids redundant Page.getFrameTree() call */
|
|
61
|
+
url?: string;
|
|
62
|
+
/** Pre-resolved page title */
|
|
63
|
+
title?: string;
|
|
64
|
+
/**
|
|
65
|
+
* If true, run semantic DOM pruning before returning elements.
|
|
66
|
+
* Drops hidden elements with no interactive descendants and no known toggle-trigger.
|
|
67
|
+
* Replaces SVG icons with [SVG Icon: label] tokens.
|
|
68
|
+
* NOTE: distinct from delta (which diffs across time). Pruning compresses the
|
|
69
|
+
* current snapshot once, before it is returned.
|
|
70
|
+
*/
|
|
71
|
+
prune?: boolean;
|
|
72
|
+
}
|
|
73
|
+
export declare function clearViewportCache(targetId: string): void;
|
|
74
|
+
export declare function resetFallbackIdCounter(): void;
|
|
75
|
+
export declare function setShadowDomCache(targetId: string, hasShadow: boolean): void;
|
|
76
|
+
export declare function clearShadowDomCache(targetId: string): void;
|
|
77
|
+
export declare function buildActionMap(session: CDP.Client, options?: WalkerOptions, targetId?: string): Promise<ActionMap>;
|
|
78
|
+
export declare function toRefEntries(elements: ActionElement[]): RefEntry[];
|
|
79
|
+
export declare function findAllMatches(elements: ActionElement[], query: string): ActionElement[];
|
|
80
|
+
export declare function findBestMatch(elements: ActionElement[], query: string): ActionElement | undefined;
|
|
81
|
+
export declare function toMarkdownTable(elements: ActionElement[], url: string, title: string, hint?: string): string;
|
|
82
|
+
export declare function safeSelector(el?: ActionElement): string | undefined;
|
|
83
|
+
export declare function resolveByAria(session: CDP.Client, query: string): Promise<{
|
|
84
|
+
id: string;
|
|
85
|
+
label: string;
|
|
86
|
+
x: number;
|
|
87
|
+
y: number;
|
|
88
|
+
selector: string;
|
|
89
|
+
} | null>;
|
|
90
|
+
export declare function clearAXTreeCache(targetId: string): void;
|
|
91
|
+
export declare function buildAXTreeMap(session: CDP.Client, _options?: WalkerOptions, targetId?: string): Promise<ActionMap>;
|