@weavetab/mcp 2.5.0-beta.0 → 2.5.0-beta.2
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 -0
- package/LICENSE +2 -2
- package/README.md +208 -43
- package/SECURITY.md +3 -3
- package/TROUBLESHOOTING.md +1 -1
- package/dist/cdp/bridge.d.ts +10 -0
- package/dist/cdp/bridge.js +288 -68
- package/dist/cdp/connector.d.ts +5 -3
- package/dist/cdp/connector.js +188 -74
- package/dist/cdp/extension.js +0 -1
- package/dist/cdp/helpers.js +0 -2
- package/dist/cdp/lock.d.ts +9 -6
- package/dist/cdp/lock.js +72 -59
- package/dist/cdp/omni_broker.d.ts +1 -0
- package/dist/cdp/omni_broker.js +12 -6
- package/dist/cdp/profiles.d.ts +10 -0
- package/dist/cdp/profiles.js +65 -0
- package/dist/cdp/walker.js +50 -0
- package/dist/cli/plugin.d.ts +0 -2
- package/dist/cli/plugin.js +31 -683
- package/dist/cli/updater.js +2 -2
- package/dist/config/loader.d.ts +25 -6
- package/dist/config/loader.js +133 -20
- package/dist/index.js +48 -52
- package/dist/lifecycle/mission_manager.d.ts +0 -1
- package/dist/lifecycle/mission_manager.js +0 -3
- package/dist/overlay/ghost.d.ts +4 -0
- package/dist/overlay/ghost.js +118 -94
- package/dist/overlay/injector.d.ts +1 -1
- package/dist/overlay/injector.js +1 -15
- package/dist/plugin/extension_bridge.d.ts +40 -0
- package/dist/plugin/extension_bridge.js +158 -0
- package/dist/plugin/interface.d.ts +78 -16
- package/dist/plugin/loader.d.ts +4 -0
- package/dist/plugin/loader.js +178 -57
- package/dist/plugin/registry.js +11 -1
- package/dist/plugin/storage.d.ts +51 -0
- package/dist/plugin/storage.js +164 -0
- package/dist/server.js +217 -151
- package/dist/state/session.d.ts +2 -2
- package/dist/state/session.js +33 -13
- package/dist/tools/browser_detect.d.ts +2 -1
- package/dist/tools/browser_detect.js +6 -5
- package/dist/tools/burst.d.ts +1 -0
- package/dist/tools/burst.js +4 -6
- package/dist/tools/click.js +23 -28
- package/dist/tools/clipboard.js +31 -24
- package/dist/tools/navigate.d.ts +1 -1
- package/dist/tools/navigate.js +28 -10
- package/dist/tools/read.js +1 -1
- package/dist/tools/tabs.d.ts +10 -10
- package/dist/tools/tabs.js +47 -39
- package/dist/tools/type.js +2 -2
- package/docs/DOCUMENTATION.md +63 -6
- package/docs/architecture/blind-injection.md +20 -0
- package/docs/architecture/dual-injection.md +32 -0
- package/docs/architecture/overview.md +49 -0
- package/docs/architecture/security.md +35 -0
- package/docs/architecture/transport.md +37 -0
- package/docs/captcha-stealth/captcha-solver.md +75 -0
- package/docs/captcha-stealth/stealth-physics.md +60 -0
- package/docs/cli/cli-commands.md +44 -0
- package/docs/cli/cli-flags.md +20 -0
- package/docs/cli/cli-introduction.md +20 -0
- package/docs/cli/cli-scripting.md +32 -0
- package/docs/cli/g-ide/ide-antigravity.md +24 -0
- package/docs/cli/g-ide/ide-claude-code.md +24 -0
- package/docs/cli/g-ide/ide-claude-desktop.md +32 -0
- package/docs/cli/g-ide/ide-cursor.md +31 -0
- package/docs/cli/g-ide/ide-generic.md +34 -0
- package/docs/cli/g-ide/ide-opencode.md +24 -0
- package/docs/cli/g-ide/ide-vscode-copilot.md +24 -0
- package/docs/cli/g-ide/ide-windsurf.md +24 -0
- package/docs/configuration/config-locations.md +24 -0
- package/docs/configuration/configuration.md +73 -0
- package/docs/configuration/env-variables.md +19 -0
- package/docs/configuration/folders.md +54 -0
- package/docs/desktop-automation/electron-apps.md +162 -0
- package/docs/get-started/installation.md +47 -0
- package/docs/get-started/introduction.md +60 -0
- package/docs/get-started/quickstart.md +54 -0
- package/docs/get-started/setup-wizard.md +26 -0
- package/docs/skills-ecosystem/skills-overview.md +35 -0
- package/docs/skills-ecosystem/skills-recipes.md +36 -0
- package/docs/tools/tools.md +293 -0
- package/docs/troubleshooting/trouble-browser.md +27 -0
- package/docs/troubleshooting/trouble-config.md +20 -0
- package/docs/troubleshooting/trouble-loop.md +31 -0
- package/ext-dist/content.js +126 -101
- package/mcp.json +8 -15
- package/package.json +11 -5
- package/server.json +3 -3
- package/dist/tools/plan.d.ts +0 -39
- package/dist/tools/plan.js +0 -161
- package/docs/DEV_MODE.md +0 -68
- package/docs/PLUGINS.md +0 -538
- package/docs/TOOLS.md +0 -104
- package/docs/dev_plugin.md +0 -310
package/dist/tools/tabs.js
CHANGED
|
@@ -8,7 +8,13 @@ import { readLock } from "../cdp/lock.js";
|
|
|
8
8
|
import { resetLoopCounter } from "../intelligence/trail.js";
|
|
9
9
|
import { getConfigAgentId, getConfigMaxTabs } from "../config/loader.js";
|
|
10
10
|
/** Resolve the active CDP port — live > lock file */
|
|
11
|
-
function resolvePort(_config) {
|
|
11
|
+
function resolvePort(_config, appName) {
|
|
12
|
+
if (appName) {
|
|
13
|
+
const lock = readLock(appName);
|
|
14
|
+
if (lock && lock.port)
|
|
15
|
+
return lock.port;
|
|
16
|
+
throw new Error(`[!] No active session for Electron app "${appName}". Launch it first.`);
|
|
17
|
+
}
|
|
12
18
|
const port = getPort() || readLock()?.port;
|
|
13
19
|
if (!port)
|
|
14
20
|
throw new Error("[!] No active browser session. Run browser_go first.");
|
|
@@ -44,9 +50,9 @@ function calculateSemanticScore(url, title) {
|
|
|
44
50
|
score += 30;
|
|
45
51
|
return Math.min(score, 100);
|
|
46
52
|
}
|
|
47
|
-
export async function evaluateTabSemantics(targetId) {
|
|
53
|
+
export async function evaluateTabSemantics(targetId, appName) {
|
|
48
54
|
try {
|
|
49
|
-
const client = await connectToTab(targetId);
|
|
55
|
+
const client = await connectToTab(targetId, undefined, undefined, undefined, appName);
|
|
50
56
|
const result = await client.session.Runtime.evaluate({
|
|
51
57
|
expression: `JSON.stringify({ title: document.title, url: location.href })`,
|
|
52
58
|
returnByValue: true
|
|
@@ -104,17 +110,17 @@ async function enforceMaxTabs(config) {
|
|
|
104
110
|
logAction("browser_tabs", victim.id, `auto-closed idle tab: ${victim.title}`);
|
|
105
111
|
}
|
|
106
112
|
// ─── Tools ────────────────────────────────────────────────────────────────────
|
|
107
|
-
export async function WeavetabOpen(url, config, reuse = true) {
|
|
108
|
-
const port = resolvePort(config);
|
|
113
|
+
export async function WeavetabOpen(url, config, reuse = true, appName) {
|
|
114
|
+
const port = resolvePort(config, appName);
|
|
109
115
|
// Tab reuse: if same URL is already open, focus it instead of duplicating
|
|
110
116
|
if (reuse && url) {
|
|
111
117
|
try {
|
|
112
|
-
const targets = await getTabList(getConfigAgentId(config));
|
|
118
|
+
const targets = await getTabList(getConfigAgentId(config), appName);
|
|
113
119
|
const existing = targets.find((t) => t.url === url);
|
|
114
120
|
if (existing) {
|
|
115
|
-
const client = await connectToTab(existing.id);
|
|
121
|
+
const client = await connectToTab(existing.id, undefined, undefined, undefined, appName);
|
|
116
122
|
await client.session.Target.activateTarget({ targetId: existing.id });
|
|
117
|
-
sessionRegistry.setActive(existing.id, getConfigAgentId(config));
|
|
123
|
+
sessionRegistry.setActive(existing.id, getConfigAgentId(config), appName);
|
|
118
124
|
recordTabActivity(existing.id);
|
|
119
125
|
logAction("browser_tabs", url, `reused existing tab ${existing.id}`);
|
|
120
126
|
return { success: true, tabId: existing.id, url, reused: true };
|
|
@@ -122,22 +128,24 @@ export async function WeavetabOpen(url, config, reuse = true) {
|
|
|
122
128
|
}
|
|
123
129
|
catch { /* fall through to open new */ }
|
|
124
130
|
}
|
|
125
|
-
|
|
126
|
-
|
|
131
|
+
if (!appName) {
|
|
132
|
+
await enforceMaxTabs(config);
|
|
133
|
+
}
|
|
134
|
+
const bctxId = appName ? undefined : sessionRegistry.getBrowserContextIdForAgent(getConfigAgentId(config));
|
|
127
135
|
const metaSession = await CDP({ host: "127.0.0.1", port });
|
|
128
136
|
const { targetId } = await metaSession.Target.createTarget({ url, browserContextId: bctxId });
|
|
129
137
|
await metaSession.close();
|
|
130
138
|
recordTabActivity(targetId);
|
|
131
|
-
await connectToTab(targetId, port, getConfigAgentId(config), bctxId);
|
|
132
|
-
await evaluateTabSemantics(targetId);
|
|
139
|
+
await connectToTab(targetId, port, getConfigAgentId(config), bctxId, appName);
|
|
140
|
+
await evaluateTabSemantics(targetId, appName);
|
|
133
141
|
logAction("browser_tabs", url, `opened ${targetId}`);
|
|
134
142
|
return { success: true, tabId: targetId, url };
|
|
135
143
|
}
|
|
136
|
-
export async function WeavetabSwitch(tabId, config) {
|
|
137
|
-
const client = await connectToTab(tabId);
|
|
144
|
+
export async function WeavetabSwitch(tabId, config, appName) {
|
|
145
|
+
const client = await connectToTab(tabId, undefined, undefined, undefined, appName);
|
|
138
146
|
const startSwitch = Date.now();
|
|
139
147
|
await client.session.Target.activateTarget({ targetId: tabId });
|
|
140
|
-
sessionRegistry.setActive(tabId, getConfigAgentId(config));
|
|
148
|
+
sessionRegistry.setActive(tabId, getConfigAgentId(config), appName);
|
|
141
149
|
recordTabActivity(tabId);
|
|
142
150
|
const { frameTree } = await client.session.Page.getFrameTree();
|
|
143
151
|
let title = tabId;
|
|
@@ -154,9 +162,9 @@ export async function WeavetabSwitch(tabId, config) {
|
|
|
154
162
|
resetLoopCounter("tab_switch");
|
|
155
163
|
return { success: true, tab_id: tabId, title, url, contextSwitchCostMs: cost };
|
|
156
164
|
}
|
|
157
|
-
export async function WeavetabList(config) {
|
|
158
|
-
const targets = await getTabList(getConfigAgentId(config));
|
|
159
|
-
const activeTargetId = sessionRegistry.getActive(getConfigAgentId(config))?.targetId;
|
|
165
|
+
export async function WeavetabList(config, appName) {
|
|
166
|
+
const targets = await getTabList(getConfigAgentId(config), appName);
|
|
167
|
+
const activeTargetId = sessionRegistry.getActive(getConfigAgentId(config), appName)?.targetId;
|
|
160
168
|
const tabs = targets
|
|
161
169
|
.map((t) => ({
|
|
162
170
|
id: t.id,
|
|
@@ -174,12 +182,12 @@ export async function WeavetabList(config) {
|
|
|
174
182
|
}));
|
|
175
183
|
return { tabs };
|
|
176
184
|
}
|
|
177
|
-
export async function WeavetabClose(tabId, config, force = false) {
|
|
185
|
+
export async function WeavetabClose(tabId, config, force = false, appName) {
|
|
178
186
|
const score = semanticScores.get(tabId) ?? 10;
|
|
179
187
|
if (score >= 70 && !force) {
|
|
180
188
|
throw new Error(`[!] Tab Armor block: This tab is semantically protected (score: ${score}). It likely contains a login, checkout, or critical work. Do not close it! If the user explicitly requested this closure, pass force: true.`);
|
|
181
189
|
}
|
|
182
|
-
const port = resolvePort(config);
|
|
190
|
+
const port = resolvePort(config, appName);
|
|
183
191
|
await CDP.Close({ host: "127.0.0.1", port, id: tabId });
|
|
184
192
|
clearTabCache(tabId);
|
|
185
193
|
sessionRegistry.unregister(tabId);
|
|
@@ -192,12 +200,12 @@ export async function WeavetabClose(tabId, config, force = false) {
|
|
|
192
200
|
/**
|
|
193
201
|
* Batch close multiple tabs at once — saves multiple round-trips.
|
|
194
202
|
*/
|
|
195
|
-
export async function WeavetabCloseBatch(tabIds, config, force = false) {
|
|
203
|
+
export async function WeavetabCloseBatch(tabIds, config, force = false, appName) {
|
|
196
204
|
const closed = [];
|
|
197
205
|
const skipped = [];
|
|
198
206
|
for (const tabId of tabIds) {
|
|
199
207
|
try {
|
|
200
|
-
await WeavetabClose(tabId, config, force);
|
|
208
|
+
await WeavetabClose(tabId, config, force, appName);
|
|
201
209
|
closed.push(tabId);
|
|
202
210
|
}
|
|
203
211
|
catch {
|
|
@@ -209,14 +217,14 @@ export async function WeavetabCloseBatch(tabIds, config, force = false) {
|
|
|
209
217
|
/**
|
|
210
218
|
* Consolidate: deduplicate tabs by URL, close duplicates.
|
|
211
219
|
*/
|
|
212
|
-
export async function WeavetabConsolidate(config) {
|
|
213
|
-
const port = resolvePort(config);
|
|
220
|
+
export async function WeavetabConsolidate(config, appName) {
|
|
221
|
+
const port = resolvePort(config, appName);
|
|
214
222
|
const targets = await CDP.List({ host: "127.0.0.1", port });
|
|
215
|
-
const pages = targets.filter((t) => t.type === "page");
|
|
223
|
+
const pages = targets.filter((t) => t.type === "page" || t.type === "webview" || t.type === "app");
|
|
216
224
|
const tabsBefore = pages.length;
|
|
217
225
|
const seenUrls = new Set();
|
|
218
226
|
let closedDuplicates = 0;
|
|
219
|
-
const activeTargetId = sessionRegistry.getActive(getConfigAgentId(config))?.targetId;
|
|
227
|
+
const activeTargetId = sessionRegistry.getActive(getConfigAgentId(config), appName)?.targetId;
|
|
220
228
|
for (const page of pages) {
|
|
221
229
|
if (page.id === activeTargetId) {
|
|
222
230
|
seenUrls.add(page.url);
|
|
@@ -240,17 +248,17 @@ export async function WeavetabConsolidate(config) {
|
|
|
240
248
|
}
|
|
241
249
|
}
|
|
242
250
|
const afterTargets = await CDP.List({ host: "127.0.0.1", port });
|
|
243
|
-
const tabsAfter = afterTargets.filter((t) => t.type === "page").length;
|
|
251
|
+
const tabsAfter = afterTargets.filter((t) => t.type === "page" || t.type === "webview" || t.type === "app").length;
|
|
244
252
|
logAction("browser_tabs", "consolidate", `${tabsBefore}→${tabsAfter} (closed ${closedDuplicates} duplicates)`);
|
|
245
253
|
return { success: true, tabs_before: tabsBefore, tabs_after: tabsAfter, closed_duplicates: closedDuplicates };
|
|
246
254
|
}
|
|
247
255
|
/**
|
|
248
256
|
* Heal: check all tabs for crashed state and reload them.
|
|
249
257
|
*/
|
|
250
|
-
export async function WeavetabHeal(config) {
|
|
251
|
-
const port = resolvePort(config);
|
|
258
|
+
export async function WeavetabHeal(config, appName) {
|
|
259
|
+
const port = resolvePort(config, appName);
|
|
252
260
|
const targets = await CDP.List({ host: "127.0.0.1", port });
|
|
253
|
-
const pages = targets.filter((t) => t.type === "page");
|
|
261
|
+
const pages = targets.filter((t) => t.type === "page" || t.type === "webview" || t.type === "app");
|
|
254
262
|
let healed = 0;
|
|
255
263
|
const healthSummary = {};
|
|
256
264
|
for (const page of pages) {
|
|
@@ -269,7 +277,7 @@ export async function WeavetabHeal(config) {
|
|
|
269
277
|
continue;
|
|
270
278
|
}
|
|
271
279
|
try {
|
|
272
|
-
const client = await connectToTab(page.id);
|
|
280
|
+
const client = await connectToTab(page.id, undefined, undefined, undefined, appName);
|
|
273
281
|
const { result } = await client.session.Runtime.evaluate({
|
|
274
282
|
expression: `document.readyState`,
|
|
275
283
|
returnByValue: true,
|
|
@@ -292,10 +300,10 @@ export async function WeavetabHeal(config) {
|
|
|
292
300
|
logAction("browser_tabs", "heal", `healed ${healed}/${pages.length} tabs`);
|
|
293
301
|
return { success: true, healed, tabs_checked: pages.length, health_summary: healthSummary };
|
|
294
302
|
}
|
|
295
|
-
export async function weaveWindowList(config) {
|
|
296
|
-
const port = resolvePort(config);
|
|
303
|
+
export async function weaveWindowList(config, appName) {
|
|
304
|
+
const port = resolvePort(config, appName);
|
|
297
305
|
const targets = await CDP.List({ host: "127.0.0.1", port });
|
|
298
|
-
const pages = targets.filter((t) => t.type === "page");
|
|
306
|
+
const pages = targets.filter((t) => t.type === "page" || t.type === "webview" || t.type === "app");
|
|
299
307
|
// Group by process — each unique window has its own set of tabs
|
|
300
308
|
// We approximate via CDP Target.getTargets windowId if available
|
|
301
309
|
const windows = [];
|
|
@@ -307,7 +315,7 @@ export async function weaveWindowList(config) {
|
|
|
307
315
|
const { windowId, bounds } = await session.Browser.getWindowForTarget({ targetId: page.id });
|
|
308
316
|
if (!seenWindowIds.has(windowId)) {
|
|
309
317
|
seenWindowIds.add(windowId);
|
|
310
|
-
const activeId = sessionRegistry.getActive(getConfigAgentId(config))?.targetId;
|
|
318
|
+
const activeId = sessionRegistry.getActive(getConfigAgentId(config), appName)?.targetId;
|
|
311
319
|
windows.push({
|
|
312
320
|
windowId,
|
|
313
321
|
state: bounds.windowState || "normal",
|
|
@@ -324,12 +332,12 @@ export async function weaveWindowList(config) {
|
|
|
324
332
|
}
|
|
325
333
|
return { windows };
|
|
326
334
|
}
|
|
327
|
-
export async function weaveFocusWindow(windowId, config) {
|
|
328
|
-
const port = resolvePort(config);
|
|
335
|
+
export async function weaveFocusWindow(windowId, config, appName) {
|
|
336
|
+
const port = resolvePort(config, appName);
|
|
329
337
|
const targets = await CDP.List({ host: "127.0.0.1", port });
|
|
330
|
-
const page = targets.find((t) => t.type === "page");
|
|
338
|
+
const page = targets.find((t) => t.type === "page" || t.type === "webview" || t.type === "app");
|
|
331
339
|
if (!page)
|
|
332
|
-
throw new Error("No page target found to focus window");
|
|
340
|
+
throw new Error("No page or window target found to focus window");
|
|
333
341
|
const session = await CDP({ host: "127.0.0.1", port, target: page.id });
|
|
334
342
|
try {
|
|
335
343
|
await session.Browser.setWindowBounds({ windowId, bounds: { windowState: "normal" } });
|
package/dist/tools/type.js
CHANGED
|
@@ -657,10 +657,10 @@ export async function weaveType(session, args, config) {
|
|
|
657
657
|
// Vue/React controlled inputs re-render the DOM node during focus, making
|
|
658
658
|
// Ctrl+V land on the old (detached) node. If el.value is still empty,
|
|
659
659
|
// fallback to Input.insertText which targets the currently-focused element.
|
|
660
|
-
if (selector
|
|
660
|
+
if (selector) {
|
|
661
661
|
const pasteCheck = await session.Runtime.evaluate({
|
|
662
662
|
expression: `(function(sel) {
|
|
663
|
-
var el = document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null);
|
|
663
|
+
var el = (sel === 'document.activeElement') ? document.activeElement : (document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null));
|
|
664
664
|
if (!el) return null;
|
|
665
665
|
return el.value !== undefined ? el.value : (el.textContent || null);
|
|
666
666
|
})(${JSON.stringify(selector)})`,
|
package/docs/DOCUMENTATION.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
## Overview
|
|
8
8
|
|
|
9
|
-
Weavetab is a production-grade, in-process MCP (Model Context Protocol) server
|
|
9
|
+
Weavetab is a production-grade, in-process MCP (Model Context Protocol) server providing AI agents with direct, physical Layer 0 control across Chromium browsers and Electron desktop applications. It communicates directly over the Chrome DevTools Protocol (CDP) WebSocket at Layer 0 — no WebDriver process, no browser binary download, no cloud middleware.
|
|
10
10
|
|
|
11
11
|
### Core Capabilities
|
|
12
12
|
|
|
@@ -261,10 +261,15 @@ The value is permanently masked as `[REDACTED]` in all subsequent DOM reads and
|
|
|
261
261
|
"role": "admin",
|
|
262
262
|
"telemetry": false,
|
|
263
263
|
"attachPort": 0,
|
|
264
|
-
"
|
|
264
|
+
"extension": true,
|
|
265
265
|
"cookies": false,
|
|
266
266
|
"browserFlags": [],
|
|
267
|
-
"policies": []
|
|
267
|
+
"policies": [],
|
|
268
|
+
"electron": {
|
|
269
|
+
"discord": "C:\\Users\\<user>\\AppData\\Local\\Discord\\app-1.0.9183\\Discord.exe",
|
|
270
|
+
"antigravity": "C:\\Users\\<user>\\AppData\\Local\\Programs\\antigravity\\Antigravity.exe",
|
|
271
|
+
"vscode": "C:\\Users\\<user>\\AppData\\Local\\Programs\\Microsoft VS Code\\Code.exe"
|
|
272
|
+
}
|
|
268
273
|
}
|
|
269
274
|
```
|
|
270
275
|
|
|
@@ -279,10 +284,11 @@ The value is permanently masked as `[REDACTED]` in all subsequent DOM reads and
|
|
|
279
284
|
| `role` | string | "admin" | RBAC role (admin, system, viewer, automation) |
|
|
280
285
|
| `telemetry` | boolean | false | Enable anonymous usage telemetry |
|
|
281
286
|
| `attachPort` | number | 0 | Chrome DevTools Protocol port |
|
|
282
|
-
| `
|
|
287
|
+
| `extension` | boolean | true | Enable Weavetab extension integration |
|
|
283
288
|
| `cookies` | boolean | false | Persist cookies/storage across sessions. When `false`, `document.cookie`, `localStorage`, and `sessionStorage` are all neutered at the browser level (returns empty/null for everything) + a fresh isolated profile is used each session. When `true`, cookies/storage work normally and persist via the profile directory. |
|
|
284
289
|
| `browserFlags` | string[] | `[]` | Extra Chromium command-line flags to append on launch (e.g., `["--disable-web-security"]`). The `--disable-sync` flag is mandatory and cannot be overridden — any conflicting flag is automatically dropped. |
|
|
285
290
|
| `policies` | PolicyEntry[] | `[]` | Domain-level access control policies (see [Domain Policy](#domain-policy) below). Supersedes the legacy `block`/`allow` lists. |
|
|
291
|
+
| `electron` | Record<string, string> | `{}` | Map of registered Electron desktop applications (e.g. Discord, VS Code, Figma, Antigravity, local dev apps) to executable paths or startup commands. |
|
|
286
292
|
|
|
287
293
|
---
|
|
288
294
|
|
|
@@ -448,10 +454,10 @@ export default plugin;
|
|
|
448
454
|
|
|
449
455
|
```bash
|
|
450
456
|
# Official plugins
|
|
451
|
-
|
|
457
|
+
weavetab plugin add @weavetab/plugin-sample
|
|
452
458
|
|
|
453
459
|
# Community plugins (any npm name)
|
|
454
|
-
|
|
460
|
+
weavetab plugin add wt-my-tool
|
|
455
461
|
# or via npm
|
|
456
462
|
npm install wt-my-tool
|
|
457
463
|
```
|
|
@@ -489,6 +495,57 @@ Skills are automatically discovered by MCP clients and provide:
|
|
|
489
495
|
|
|
490
496
|
---
|
|
491
497
|
|
|
498
|
+
## Electron & Desktop Application Automation
|
|
499
|
+
|
|
500
|
+
Weavetab natively supports **generic automation of any Electron or Chromium-based desktop application** (e.g. Discord, Slack, VS Code, Antigravity IDE, Figma, Obsidian, Postman, or local developer applications running from source).
|
|
501
|
+
|
|
502
|
+
### Why It Works at Layer 0
|
|
503
|
+
Electron desktop applications run a bundled Chromium/Blink engine under the hood. When started with Chrome DevTools Protocol debugging enabled (either automatically via Weavetab or via `--remote-debugging-port`), Weavetab connects directly to the desktop app's internal WebSockets and exposes all 45+ MCP tools without requiring external drivers or wrappers.
|
|
504
|
+
|
|
505
|
+
### Registering Applications
|
|
506
|
+
In `~/.weavetab/config.json` or project-scoped `.weavetab/config.json`:
|
|
507
|
+
|
|
508
|
+
```json
|
|
509
|
+
{
|
|
510
|
+
"electron": {
|
|
511
|
+
"discord": "C:\\Users\\<user>\\AppData\\Local\\Discord\\app-1.0.9183\\Discord.exe",
|
|
512
|
+
"antigravity": "C:\\Users\\<user>\\AppData\\Local\\Programs\\antigravity\\Antigravity.exe",
|
|
513
|
+
"vscode": "C:\\Users\\<user>\\AppData\\Local\\Programs\\Microsoft VS Code\\Code.exe",
|
|
514
|
+
"figma": "C:\\Users\\<user>\\AppData\\Local\\Figma\\Figma.exe",
|
|
515
|
+
"obsidian": "C:\\Users\\<user>\\AppData\\Local\\Obsidian\\Obsidian.exe",
|
|
516
|
+
"my-app": "C:\\path\\to\\my-project\\node_modules\\electron\\dist\\electron.exe"
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
### Automating Local / Source-Hosted Projects
|
|
522
|
+
When developing an Electron app locally (e.g. `localhost:3000` or running via Vite/Webpack/Electron forge):
|
|
523
|
+
|
|
524
|
+
1. **Launch with remote debugging enabled:**
|
|
525
|
+
```bash
|
|
526
|
+
npx electron . --remote-debugging-port=9222
|
|
527
|
+
# or with npm scripts:
|
|
528
|
+
npm run dev -- --remote-debugging-port=9222
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
2. **Automate via MCP tools:**
|
|
532
|
+
Set `"attachPort": 9222` or register the dev script in `"electron"` config.
|
|
533
|
+
|
|
534
|
+
### Tool Execution Flow with Desktop Apps
|
|
535
|
+
Every Weavetab tool supports the optional `app` parameter:
|
|
536
|
+
|
|
537
|
+
| Tool Call | Desktop Behavior |
|
|
538
|
+
| :--- | :--- |
|
|
539
|
+
| `browser_navigate({ app: "antigravity" })` | Launches or connects to Antigravity's CDP session |
|
|
540
|
+
| `browser_tabs({ action: "list", app: "antigravity" })` | Lists all windows, webviews, and workbench tabs in the app |
|
|
541
|
+
| `browser_tabs({ action: "switch", tabId: "<id>", app: "antigravity" })` | Switches active focus to the specified workbench or window |
|
|
542
|
+
| `browser_map({ app: "antigravity" })` | Generates semantic Ref IDs (`w:NN`) for the desktop app's active window |
|
|
543
|
+
| `browser_click({ ref: "w:12", app: "antigravity" })` | Sends Ghost Cursor click to the desktop app element |
|
|
544
|
+
| `browser_type({ ref: "w:15", text: "...", app: "discord" })` | Types text directly into the desktop app input field |
|
|
545
|
+
| `browser_eval({ expression: "...", app: "vscode" })` | Evaluates JavaScript inside the desktop app frame context |
|
|
546
|
+
|
|
547
|
+
---
|
|
548
|
+
|
|
492
549
|
## Troubleshooting
|
|
493
550
|
|
|
494
551
|
### Common Issues
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Blind Injection"
|
|
3
|
+
description: "Enterprise-grade credential injection that never exposes secrets to the LLM."
|
|
4
|
+
product: mcp
|
|
5
|
+
section: architecture
|
|
6
|
+
icon: Lock
|
|
7
|
+
slug: blind-injection
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Blind Injection
|
|
11
|
+
|
|
12
|
+
The `browser_type_secret` tool resolves passwords, tokens, and 2FA credentials directly from `secrets.json` on the server — **never exposing sensitive values to the AI model's context stream**.
|
|
13
|
+
|
|
14
|
+
## How It Works
|
|
15
|
+
|
|
16
|
+
1. Store a secret via `weavetab secrets set MY_KEY` or by editing `secrets.json`
|
|
17
|
+
2. The agent calls `browser_type_secret({ id: "w:2", envKey: "MY_KEY" })`
|
|
18
|
+
3. Weavetab resolves the secret from `secrets.json` in-process on the local machine
|
|
19
|
+
4. The value is typed directly into the DOM input field over CDP
|
|
20
|
+
5. All subsequent `browser_map` outputs, DOM snapshots, and audit logs permanently display `[REDACTED]` for that element
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Dual Injection"
|
|
3
|
+
description: "CDP early-script + Manifest V3 extension working in tandem."
|
|
4
|
+
product: mcp
|
|
5
|
+
section: architecture
|
|
6
|
+
icon: Combine
|
|
7
|
+
slug: dual-injection
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Dual Injection Strategy
|
|
11
|
+
|
|
12
|
+
Weavetab delivers overlay UI and stealth capabilities through **two complementary channels** that cover each other's blind spots.
|
|
13
|
+
|
|
14
|
+
## 1. CDP Early-Script Injection
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
await session.Page.addScriptToEvaluateOnNewDocument({ source: STEALTH_SCRIPT });
|
|
18
|
+
await session.Runtime.evaluate({ expression: STEALTH_SCRIPT }).catch(() => {});
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Runs **before any page JavaScript executes**. Handles:
|
|
22
|
+
- **Stealth patches**: `navigator.webdriver` → `undefined`, fake plugin arrays, WebGL/Canvas fingerprint noise
|
|
23
|
+
- **Shadow DOM bootstrap**: `#weavetab-root` host container created in every page
|
|
24
|
+
- **Config binding**: `window.__wt_setConfig`, `window.__wt_config` for live runtime updates
|
|
25
|
+
|
|
26
|
+
## 2. Manifest V3 Extension
|
|
27
|
+
|
|
28
|
+
Loaded dynamically with `--load-extension=${extPath}`. Owns:
|
|
29
|
+
- **Ghost Cursor animation** (Bézier curve path rendering via canvas overlay)
|
|
30
|
+
- **Thought bubble HUD** (agent status messages during tool execution)
|
|
31
|
+
- **Interactive Ask Modal** (user interaction with response queue)
|
|
32
|
+
- **SPA navigation persistence** — survives framework router transitions without flashing
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Overview"
|
|
3
|
+
description: "Five layers, deterministic pipeline, zero cloud."
|
|
4
|
+
product: mcp
|
|
5
|
+
section: architecture
|
|
6
|
+
icon: Layers
|
|
7
|
+
slug: overview
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Architecture Overview
|
|
11
|
+
|
|
12
|
+
Weavetab is decomposed into **five functional layers**, all running in-process without external daemon dependencies.
|
|
13
|
+
|
|
14
|
+
## Layer Map
|
|
15
|
+
|
|
16
|
+
| # | Layer | Responsibility |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| 1 | **Transport** | JSON-RPC 2.0 over stdio or WebSocket (`--ws --port 3000`) |
|
|
19
|
+
| 2 | **Security & Governance** | Domain blacklist, rate limiter (mutex), RBAC roles, policy engine, root guard |
|
|
20
|
+
| 3 | **Intelligence Engine** | Session memory, semantic delta diffing, DOM pruner, loop detection, intent intercept |
|
|
21
|
+
| 4 | **Sensor Systems** | Network telemetry, DOM mutation observer, human pacing, agent state machine |
|
|
22
|
+
| 5 | **CDP Bridge** | DOM walker, Ghost Cursor (Bézier trajectories), keystroke engine, shadow-DOM injector |
|
|
23
|
+
|
|
24
|
+
## Tool Execution Pipeline
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
Agent → withSecurity()
|
|
28
|
+
├── Rate limiter (mutex)
|
|
29
|
+
├── Domain policy check (allow/deny/audit/read-only)
|
|
30
|
+
├── RBAC role verification
|
|
31
|
+
├── User-stop polling
|
|
32
|
+
├── Chat queue draining
|
|
33
|
+
├── Agent state transition (idle → weaving → reading)
|
|
34
|
+
└── Tool handler → CDP Bridge → Browser / Desktop App
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Any validation or security failure short-circuits the pipeline immediately and returns a structured `WeaveError` — no partial side-effects are left on the browser.
|
|
38
|
+
|
|
39
|
+
## Why Direct CDP
|
|
40
|
+
|
|
41
|
+
| Feature | Weavetab | Playwright | Puppeteer | Selenium |
|
|
42
|
+
|---|---|---|---|---|
|
|
43
|
+
| **Architecture** | Direct CDP Layer 0 | Node bridge abstraction | CDP wrapper | WebDriver |
|
|
44
|
+
| **Mean latency** | **3–8 ms** | 15–40 ms | 10–25 ms | 50–150 ms |
|
|
45
|
+
| **Token efficiency** | **Semantic Deltas (97% reduction)** | Full DOM dumps | Full DOM dumps | Full DOM dumps |
|
|
46
|
+
| **Desktop Electron Apps** | **Native (any Electron app)** | Experimental | Limited | Third-party driver |
|
|
47
|
+
| **CAPTCHA Solver** | **Autonomous Zero-API** | None | None | None |
|
|
48
|
+
| **MCP native** | **Yes (45 tools)** | No | No | No |
|
|
49
|
+
| **Cloud dependency** | **Zero** | Optional | Optional | Grid required |
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Security Model"
|
|
3
|
+
description: "Layered defenses that gate every tool invocation."
|
|
4
|
+
product: mcp
|
|
5
|
+
section: architecture
|
|
6
|
+
icon: ShieldCheck
|
|
7
|
+
slug: security
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Security Model
|
|
11
|
+
|
|
12
|
+
Weavetab executes direct Layer 0 CDP commands — security is layered and **deny-by-default for risky surfaces**.
|
|
13
|
+
|
|
14
|
+
## Defense-in-Depth Mechanisms
|
|
15
|
+
|
|
16
|
+
| Mechanism | Description |
|
|
17
|
+
|---|---|
|
|
18
|
+
| **Domain Blacklist** | Hard-blocked high-risk domains (banking, IAM consoles, internal infrastructure). |
|
|
19
|
+
| **Domain Policy Engine** | Per-domain enforcement modes: `allow`, `read-only`, `deny`, `audit`. |
|
|
20
|
+
| **Localhost Binding** | WebSocket server and CDP endpoints strictly bound to `127.0.0.1`. |
|
|
21
|
+
| **Rate Limiting** | Mutex-enforced actions-per-minute rate limiter. |
|
|
22
|
+
| **RBAC Roles** | 4 strict roles gating tool access (`admin`, `operator`, `viewer`, `automation`). |
|
|
23
|
+
| **Root Guard** | Prevents execution as root/superuser on Unix systems. |
|
|
24
|
+
| **Blind Injection** | Credential injection from `secrets.json` without exposing raw text to LLM context. |
|
|
25
|
+
| **Shadow DOM Isolation** | All injected HUD and modal overlays run in isolated Shadow DOM trees. |
|
|
26
|
+
| **Path Traversal Guard** | File upload and download paths strictly validated against path traversal. |
|
|
27
|
+
|
|
28
|
+
## RBAC Role Matrix
|
|
29
|
+
|
|
30
|
+
| Tool Category | admin | operator | viewer | automation |
|
|
31
|
+
|---|:---:|:---:|:---:|:---:|
|
|
32
|
+
| Mutation tools (`click`, `type`, `fill`, etc.) | yes | yes | no | yes |
|
|
33
|
+
| Read-only tools (`map`, `find`, `scrape`, `screenshot`) | yes | yes | yes | yes |
|
|
34
|
+
| High-level orchestration (`burst`, `plan`, `wait`) | yes | yes | no | yes |
|
|
35
|
+
| System management (`detect`, `reset_loop_counter`) | yes | no | no | no |
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Transport Modes"
|
|
3
|
+
description: "stdio vs WebSocket — pick the right wire for your client."
|
|
4
|
+
product: mcp
|
|
5
|
+
section: architecture
|
|
6
|
+
icon: Cable
|
|
7
|
+
slug: transport
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Transport Modes
|
|
11
|
+
|
|
12
|
+
| Mode | Flag | Protocol | Use Case |
|
|
13
|
+
|---|---|---|---|
|
|
14
|
+
| **stdio** | *(default)* | JSON-RPC 2.0 / stdio | Claude Desktop, Cursor, VS Code Copilot, Antigravity |
|
|
15
|
+
| **WebSocket** | `--ws` | JSON-RPC 2.0 / WebSocket | Custom agents, Python/Go backends, remote daemons, n8n |
|
|
16
|
+
|
|
17
|
+
## stdio (Default)
|
|
18
|
+
|
|
19
|
+
The MCP client spawns Weavetab as a child process and pipes messages over stdin/stdout. No network port is opened:
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"mcpServers": {
|
|
24
|
+
"weavetab": {
|
|
25
|
+
"command": "weavetab"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## WebSocket
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
weavetab --ws --port 3000
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
WebSocket mode binds strictly to `127.0.0.1` for local security. Front it with your own TLS/auth layer if exposing across a private network.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "CAPTCHA Solver"
|
|
3
|
+
description: "Autonomous zero-API solver for Turnstile, GeeTest, and reCAPTCHA."
|
|
4
|
+
product: mcp
|
|
5
|
+
section: captcha-stealth
|
|
6
|
+
badge: new
|
|
7
|
+
icon: ShieldAlert
|
|
8
|
+
slug: captcha-solver
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Autonomous Zero-API CAPTCHA Solver
|
|
12
|
+
|
|
13
|
+
Weavetab features a built-in **Autonomous Zero-API CAPTCHA Solver** via the `browser_captcha` tool. It solves complex challenges locally using real CDP pointer physics, Bézier kinematics, and DOM heuristics — requiring **$0 external solver API costs** and zero third-party subscription keys (no 2Captcha, Anti-Captcha, or CapSolver needed).
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
// Detect and solve active challenge automatically
|
|
17
|
+
browser_captcha({ action: "solve", timeout: 60000 })
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Supported Challenge Matrix
|
|
23
|
+
|
|
24
|
+
| Challenge Type | Target Systems | Solving Technique | Zero API Cost |
|
|
25
|
+
|---|---|---|:---:|
|
|
26
|
+
| **Cloudflare Turnstile** | Cloudflare protected surfaces | Sustained mousedown + micro-jitter physics | Yes |
|
|
27
|
+
| **GeeTest Slider** | GeeTest v3 / v4 puzzle sliders | Bézier velocity drag + momentum overshoot correction | Yes |
|
|
28
|
+
| **Google reCAPTCHA v2** | Checkbox & iframe challenges | Nested cross-origin iframe pointer penetration | Yes |
|
|
29
|
+
| **Google reCAPTCHA v3** | Invisible score-based barriers | High-entropy behavioral simulation bursts | Yes |
|
|
30
|
+
| **Arkose Labs / FunCAPTCHA** | Interactive verification | Synthetic CDP pointer kinematics + rotation solver | Yes |
|
|
31
|
+
| **hCaptcha** | Checkbox challenges | Cross-domain iframe dispatch + heuristic click | Yes |
|
|
32
|
+
| **Math / Text Challenges** | Inline security prompts | DOM arithmetic evaluation + synthetic input | Yes |
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## How It Works
|
|
37
|
+
|
|
38
|
+
### 1. Cloudflare Turnstile & Interactive Checkboxes
|
|
39
|
+
Cloudflare Turnstile evaluates the physical trajectory and hold duration of the click. Weavetab:
|
|
40
|
+
- Calculates a randomized human Bézier path from current cursor coordinates to the checkbox center.
|
|
41
|
+
- Dispatches CDP `Input.dispatchMouseEvent` with `mousePressed` event.
|
|
42
|
+
- Applies **micro-jitter displacement** (1–3 pixel variations) for 120–280 ms simulating organic finger tremor.
|
|
43
|
+
- Releases `mouseReleased` event and monitors CDP DOM mutations for success tokens.
|
|
44
|
+
|
|
45
|
+
### 2. GeeTest & Slider Puzzles
|
|
46
|
+
For slider challenges, Weavetab calculates the drag distance, applies variable velocity profiling (rapid acceleration, mid-flight coasting, deceleration), and introduces subtle overshoot correction:
|
|
47
|
+
|
|
48
|
+
```text
|
|
49
|
+
Start (0px) ──[Fast Accel]──► Mid (70%) ──[Decel]──► Target (100%) ──[Overshoot +2px]──► Final Lock
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 3. reCAPTCHA v2 & Cross-Origin Iframes
|
|
53
|
+
reCAPTCHA isolates the checkbox inside cross-origin iframes (`anchor` frame). Weavetab penetrates the isolated iframe context via CDP `DOM.describeNode` and dispatches synthetic pointer events directly to the inner target.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Tool API Reference: `browser_captcha`
|
|
58
|
+
|
|
59
|
+
**Parameters:**
|
|
60
|
+
| Parameter | Type | Description |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| `action` | `"detect" | "solve" | "wait_for_user" | "status"` *(required)* | Operation to perform. |
|
|
63
|
+
| `timeout` | `number?` | Maximum timeout in milliseconds (default `120000`). |
|
|
64
|
+
|
|
65
|
+
### Example Usage
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
// Step 1: Detect if a CAPTCHA exists on page
|
|
69
|
+
const detection = await browser_captcha({ action: "detect" });
|
|
70
|
+
if (detection.detected) {
|
|
71
|
+
// Step 2: Solve autonomously
|
|
72
|
+
const result = await browser_captcha({ action: "solve", timeout: 45000 });
|
|
73
|
+
console.log("Solved:", result.success);
|
|
74
|
+
}
|
|
75
|
+
```
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Stealth & Physics"
|
|
3
|
+
description: "Ghost Cursor Bézier kinematics, typing jitter, and anti-fingerprinting."
|
|
4
|
+
product: mcp
|
|
5
|
+
section: captcha-stealth
|
|
6
|
+
icon: Sparkles
|
|
7
|
+
slug: stealth-physics
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Stealth & Kinematic Physics
|
|
11
|
+
|
|
12
|
+
Weavetab is built from the ground up to make AI automation indistinguishable from human input at the browser and DOM event levels.
|
|
13
|
+
|
|
14
|
+
## 1. Ghost Cursor Bézier Kinematics
|
|
15
|
+
|
|
16
|
+
Standard automation tools warp cursor coordinates instantaneously (`x: 0` → `x: 500` in 0ms), which triggers modern anti-bot heuristics.
|
|
17
|
+
|
|
18
|
+
Weavetab's **Ghost Cursor** engine generates dynamic **cubic Bézier curves** with randomized control points for every movement:
|
|
19
|
+
|
|
20
|
+
```text
|
|
21
|
+
P0 (Current Pos) ──────► P1 (Control Pt 1) ──────► P2 (Control Pt 2) ──────► P3 (Target)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
- **Acceleration Profiling**: Gaussian velocity distribution with natural ramp-up and ramp-down.
|
|
25
|
+
- **Micro-Jitter**: Per-frame sub-pixel deviations mimicking human muscle tremor.
|
|
26
|
+
- **Overshoot & Correction**: Intentional minor overshoots past the target element followed by corrective micro-adjustments.
|
|
27
|
+
- **Visual Overlay**: Real-time canvas overlay rendering the glowing Ghost Cursor path in the browser window.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 2. Human Keystroke Synthesis
|
|
32
|
+
|
|
33
|
+
The `browser_type` engine dispatches native CDP `Input.dispatchKeyEvent` calls with realistic human timing profiles:
|
|
34
|
+
|
|
35
|
+
- **Temporal Jitter**: Per-character delays sampled between 35 ms and 95 ms.
|
|
36
|
+
- **Burst Profiling**: Common character digraphs (e.g., `th`, `er`, `ing`) are typed faster in bursts.
|
|
37
|
+
- **Word Boundary Pauses**: Natural pauses (120–250 ms) after spaces and punctuation marks.
|
|
38
|
+
- **Stuck-Input Breaking**: DOM diffing detects if keystrokes are failing to register and breaks out of deadlocks.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 3. Layer 0 Fingerprint Spoofing
|
|
43
|
+
|
|
44
|
+
Weavetab's early-script CDP injection guarantees that the following patches execute **before any page JavaScript runs**:
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
// navigator.webdriver is permanently undefined
|
|
48
|
+
Object.defineProperty(navigator, 'webdriver', { get: () => undefined });
|
|
49
|
+
|
|
50
|
+
// Native plugin and mime-type arrays spoofed
|
|
51
|
+
Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5] });
|
|
52
|
+
|
|
53
|
+
// WebGL vendor and renderer noise injection
|
|
54
|
+
const getParameter = WebGLRenderingContext.prototype.getParameter;
|
|
55
|
+
WebGLRenderingContext.prototype.getParameter = function(parameter) {
|
|
56
|
+
if (parameter === 37445) return 'Google Inc. (Intel)';
|
|
57
|
+
if (parameter === 37446) return 'ANGLE (Intel, Intel(R) UHD Graphics Direct3D11)';
|
|
58
|
+
return getParameter.apply(this, arguments);
|
|
59
|
+
};
|
|
60
|
+
```
|