@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/cdp/connector.js
CHANGED
|
@@ -7,9 +7,9 @@ import { logDebug } from "../audit/logger.js";
|
|
|
7
7
|
*/
|
|
8
8
|
import CDP from "chrome-remote-interface";
|
|
9
9
|
import { blockStorageAccess } from "../security/guard.js";
|
|
10
|
-
import { launchWithBridge, injectStealthScript } from "./bridge.js";
|
|
10
|
+
import { launchWithBridge, launchElectronApp, injectStealthScript } from "./bridge.js";
|
|
11
11
|
import { setupOverlay, ensureVisualLayer } from "../overlay/injector.js";
|
|
12
|
-
import { onConfigChange, saveConfig, loadConfigSync, getCachedConfig, getConfigAgentId, getConfigCookies, getConfigProfileDir, getConfigMode } from "../config/loader.js";
|
|
12
|
+
import { onConfigChange, saveConfig, loadConfigSync, getCachedConfig, getConfigAgentId, getConfigCookies, getConfigProfileDir, getConfigMode, getElectronAppConfig, getConfigExtension } from "../config/loader.js";
|
|
13
13
|
import { sessionRegistry } from "../state/session.js";
|
|
14
14
|
import { initConsoleBuffering } from "../tools/console.js";
|
|
15
15
|
import { registerMutationListener, clearTabCache } from "../state/cache.js";
|
|
@@ -95,12 +95,21 @@ async function waitForPageReady(session) {
|
|
|
95
95
|
});
|
|
96
96
|
}
|
|
97
97
|
// ─── Session init (overlay + re-injection) ───────────────────────────────────
|
|
98
|
-
async function initSession(session, targetId, port, config) {
|
|
98
|
+
async function initSession(session, targetId, port, config, isElectron) {
|
|
99
99
|
initDialogHandler(session);
|
|
100
100
|
// Initialize Omni-Context Broker for iframes
|
|
101
101
|
const { initOmniBroker } = await import("./omni_broker.js");
|
|
102
102
|
await initOmniBroker(session, targetId, port);
|
|
103
|
-
if (
|
|
103
|
+
if (isElectron) {
|
|
104
|
+
// Electron desktop app initialization
|
|
105
|
+
try {
|
|
106
|
+
await session.Page.addScriptToEvaluateOnNewDocument({ source: WT_REGISTRY_INIT_EXPR });
|
|
107
|
+
}
|
|
108
|
+
catch { /* non-fatal */ }
|
|
109
|
+
// Direct DOM minimal overlay injection without Chrome Extension
|
|
110
|
+
await ensureVisualLayer(session, config);
|
|
111
|
+
}
|
|
112
|
+
else if (activeEngine === "chromium") {
|
|
104
113
|
// Only block cookies/storage when cookies mode is disabled
|
|
105
114
|
if (!getConfigCookies(config)) {
|
|
106
115
|
await blockStorageAccess(session);
|
|
@@ -142,7 +151,7 @@ async function initSession(session, targetId, port, config) {
|
|
|
142
151
|
clearTabCache(targetId);
|
|
143
152
|
// Invalidate URC on navigation — elements from the old page are stale
|
|
144
153
|
urcInvalidate(targetId);
|
|
145
|
-
if (activeEngine === "chromium") {
|
|
154
|
+
if (isElectron || activeEngine === "chromium") {
|
|
146
155
|
try {
|
|
147
156
|
await waitForPageReady(session);
|
|
148
157
|
const cfg = getCachedConfig();
|
|
@@ -150,15 +159,12 @@ async function initSession(session, targetId, port, config) {
|
|
|
150
159
|
if (clientRef)
|
|
151
160
|
clientRef.overlayInstalled = false;
|
|
152
161
|
await ensureVisualLayer(session, cfg, clientRef ?? undefined);
|
|
153
|
-
// Re-inject active plan into the new page
|
|
154
|
-
const { restoreActivePlanIntoPage } = await import("../tools/plan.js");
|
|
155
|
-
await restoreActivePlanIntoPage(session);
|
|
156
162
|
}
|
|
157
163
|
catch { /* ignore */ }
|
|
158
164
|
}
|
|
159
165
|
});
|
|
160
|
-
// Diagnostic: verify extension content script loaded
|
|
161
|
-
if (activeEngine === "chromium") {
|
|
166
|
+
// Diagnostic: verify extension content script loaded (Standard browser only)
|
|
167
|
+
if (!isElectron && activeEngine === "chromium" && getConfigExtension(config) !== false) {
|
|
162
168
|
try {
|
|
163
169
|
// Check for multiple indicators that extension is loaded
|
|
164
170
|
const { result: extCheck } = await session.Runtime.evaluate({
|
|
@@ -177,29 +183,12 @@ async function initSession(session, targetId, port, config) {
|
|
|
177
183
|
else {
|
|
178
184
|
logDebug(`[Weavetab] [!] Extension content script NOT detected — HUD/overlay may not work`);
|
|
179
185
|
logDebug(`[Weavetab] [!] Try reloading page or check if extension loaded in browser://extensions`);
|
|
180
|
-
// Try to inject a basic error indicator into the page
|
|
181
|
-
try {
|
|
182
|
-
await session.Runtime.evaluate({
|
|
183
|
-
expression: `
|
|
184
|
-
(function() {
|
|
185
|
-
const div = document.createElement('div');
|
|
186
|
-
div.style.cssText = 'position:fixed;top:10px;right:10px;background:red;color:white;padding:10px;z-index:999999;font-family:sans-serif;font-size:14px;';
|
|
187
|
-
div.textContent = 'WeaveTab Extension Not Loaded';
|
|
188
|
-
document.body.appendChild(div);
|
|
189
|
-
setTimeout(() => div.remove(), 5000);
|
|
190
|
-
})()
|
|
191
|
-
`
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
catch { /* ignore */ }
|
|
195
186
|
}
|
|
196
187
|
}
|
|
197
188
|
catch (checkErr) {
|
|
198
189
|
logDebug(`[Weavetab] [!] Extension health check failed: ${checkErr}`);
|
|
199
190
|
}
|
|
200
|
-
|
|
201
|
-
// Push config + initial idle state to extension right away (Chrome only)
|
|
202
|
-
if (activeEngine === "chromium") {
|
|
191
|
+
// Push config + initial idle state to extension right away (Chrome only)
|
|
203
192
|
const { sendToExtension } = await import("../cdp/helpers.js");
|
|
204
193
|
sendToExtension(session, {
|
|
205
194
|
type: 'Weavetab_CONFIG',
|
|
@@ -210,8 +199,7 @@ async function initSession(session, targetId, port, config) {
|
|
|
210
199
|
state: 'idle',
|
|
211
200
|
}).catch(() => { });
|
|
212
201
|
}
|
|
213
|
-
// Analytics ping every
|
|
214
|
-
// independently cancelled when the tab closes or cleanupAll() is called.
|
|
202
|
+
// Analytics ping every 30s
|
|
215
203
|
const sessionStartTime = Date.now();
|
|
216
204
|
const analyticsInterval = setInterval(async () => {
|
|
217
205
|
try {
|
|
@@ -228,34 +216,135 @@ async function initSession(session, targetId, port, config) {
|
|
|
228
216
|
}).catch(() => { });
|
|
229
217
|
}
|
|
230
218
|
catch { /* ignore */ }
|
|
231
|
-
}, 30_000);
|
|
232
|
-
// Register in the per-tab map so cleanupAll() and the session-close handler can
|
|
233
|
-
// cancel it without relying on a single overwriteable global slot.
|
|
219
|
+
}, 30_000);
|
|
234
220
|
analyticsIntervals.set(targetId, analyticsInterval);
|
|
235
|
-
// unref() so this polling interval does not pin the Node.js event loop.
|
|
236
|
-
// An orphaned MCP process (IDE disconnected, browser closed) would otherwise
|
|
237
|
-
// stay alive indefinitely due to this 30s interval firing forever.
|
|
238
221
|
analyticsInterval.unref();
|
|
239
222
|
}
|
|
240
223
|
// ─── Main connection entry point ──────────────────────────────────────────────
|
|
241
|
-
export async function getOrConnect(config) {
|
|
242
|
-
if (getConfigMode(config) === "attach") {
|
|
224
|
+
export async function getOrConnect(config, targetApp) {
|
|
225
|
+
if (getConfigMode(config) === "attach" && !targetApp) {
|
|
243
226
|
throw new Error("[!] Attach mode (connect to your real browser) coming soon. Use session mode for now.");
|
|
244
227
|
}
|
|
245
|
-
|
|
228
|
+
// 1. Check existing active session for this agent & app
|
|
229
|
+
const activeClient = sessionRegistry.getActive(getConfigAgentId(config), targetApp);
|
|
246
230
|
if (activeClient) {
|
|
247
231
|
try {
|
|
248
232
|
await activeClient.session.Browser.getVersion();
|
|
249
233
|
return activeClient;
|
|
250
234
|
}
|
|
251
235
|
catch {
|
|
252
|
-
logDebug(
|
|
236
|
+
logDebug(`[Weavetab CDP] Connection lost for ${targetApp || "browser"}. Reconnecting...`);
|
|
253
237
|
clearProfileVerificationCache(activeClient.targetId);
|
|
254
238
|
sessionRegistry.unregister(activeClient.targetId);
|
|
255
239
|
}
|
|
256
240
|
}
|
|
257
|
-
const releaseMutex = await acquireMutex();
|
|
241
|
+
const releaseMutex = await acquireMutex(targetApp);
|
|
258
242
|
try {
|
|
243
|
+
if (targetApp) {
|
|
244
|
+
// ──────────────── ELECTRON APP CONNECTION FLOW ────────────────
|
|
245
|
+
const appCfg = getElectronAppConfig(targetApp, config);
|
|
246
|
+
if (!appCfg) {
|
|
247
|
+
throw new Error(`[Weavetab] ✗ Electron app "${targetApp}" is not configured in config.json.\n` +
|
|
248
|
+
` Please add an entry under "electron": { "${targetApp}": "C:\\\\path\\\\to\\\\app.exe" } in your config.`);
|
|
249
|
+
}
|
|
250
|
+
let port = null;
|
|
251
|
+
const sessionLock = readLock(targetApp);
|
|
252
|
+
if (sessionLock && sessionLock.mode === "session") {
|
|
253
|
+
if (isLockPending(sessionLock)) {
|
|
254
|
+
logDebug(`[Weavetab] Launch in progress for ${targetApp} by PID ${sessionLock.pid}. Waiting up to 5s...`);
|
|
255
|
+
for (let i = 0; i < 5; i++) {
|
|
256
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
257
|
+
const updatedLock = readLock(targetApp);
|
|
258
|
+
if (!updatedLock || !isLockPending(updatedLock))
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
const freshLock = readLock(targetApp);
|
|
263
|
+
if (freshLock && (await isLockValid(freshLock))) {
|
|
264
|
+
port = freshLock.port;
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
clearLock(targetApp);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
let targets = [];
|
|
271
|
+
if (port) {
|
|
272
|
+
try {
|
|
273
|
+
targets = await withCDPTimeout(CDP.List({ host: "127.0.0.1", port }), 3000, "CDP.List existing Electron");
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
port = null;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (!port || targets.length === 0) {
|
|
280
|
+
logDebug(`[Weavetab CDP] Launching Electron app ${targetApp}...`);
|
|
281
|
+
const launchResult = await launchElectronApp(targetApp, config);
|
|
282
|
+
port = launchResult.port;
|
|
283
|
+
// Poll for target window availability (Electron may take up to 2-3s to render initial window)
|
|
284
|
+
for (let retry = 0; retry < 10; retry++) {
|
|
285
|
+
try {
|
|
286
|
+
targets = await withCDPTimeout(CDP.List({ host: "127.0.0.1", port }), 2000, "CDP.List Electron retry");
|
|
287
|
+
if (targets.length > 0)
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
catch { /* wait */ }
|
|
291
|
+
await new Promise(r => setTimeout(r, 500));
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
let pageTarget = targets.find(t => t.type === "page" || t.type === "webview" || t.type === "app") ?? targets[0];
|
|
295
|
+
if (!pageTarget) {
|
|
296
|
+
throw new Error(`[Weavetab] ✗ No window or webview target found in Electron app "${targetApp}".`);
|
|
297
|
+
}
|
|
298
|
+
const targetId = pageTarget.id || pageTarget.targetId;
|
|
299
|
+
const session = await withCDPTimeout(CDP({ host: "127.0.0.1", port, target: targetId }), 5000, "CDP connect Electron target");
|
|
300
|
+
session.on("close", () => {
|
|
301
|
+
logDebug(`[Weavetab CDP] Electron session WebSocket closed (${targetApp}) — unregistering.`);
|
|
302
|
+
const iv = analyticsIntervals.get(targetId);
|
|
303
|
+
if (iv) {
|
|
304
|
+
clearInterval(iv);
|
|
305
|
+
analyticsIntervals.delete(targetId);
|
|
306
|
+
}
|
|
307
|
+
clearProfileVerificationCache(targetId);
|
|
308
|
+
import("./omni_broker.js").then(m => m.cleanupOmniBroker(targetId)).catch(() => { });
|
|
309
|
+
import("../tools/network_intercept.js").then(m => m.cleanupNetworkIntercept(session)).catch(() => { });
|
|
310
|
+
sessionRegistry.unregister(targetId);
|
|
311
|
+
});
|
|
312
|
+
session.on("error", (err) => {
|
|
313
|
+
logDebug(`[Weavetab CDP] Electron session WebSocket error (${targetApp}):`, err?.message ?? err);
|
|
314
|
+
});
|
|
315
|
+
// Validate connection health
|
|
316
|
+
try {
|
|
317
|
+
await session.Browser.getVersion();
|
|
318
|
+
}
|
|
319
|
+
catch {
|
|
320
|
+
throw new Error("Failed health check: CDP session connected to Electron app but getVersion failed.");
|
|
321
|
+
}
|
|
322
|
+
await Promise.all([
|
|
323
|
+
session.Accessibility.enable(),
|
|
324
|
+
session.DOM.enable(),
|
|
325
|
+
session.Page.enable(),
|
|
326
|
+
session.Runtime.enable(),
|
|
327
|
+
session.Network.enable().catch(() => { }),
|
|
328
|
+
session.Performance.enable().catch(() => { })
|
|
329
|
+
]);
|
|
330
|
+
await wireUiConfigBinding(session);
|
|
331
|
+
await initSession(session, targetId, port, config, true);
|
|
332
|
+
await session.Target.setDiscoverTargets({ discover: true }).catch(() => { });
|
|
333
|
+
logDebug(`[Weavetab CDP] [OK] Active Electron target: ${pageTarget.title || pageTarget.url || targetApp}`);
|
|
334
|
+
const client = {
|
|
335
|
+
session,
|
|
336
|
+
targetId,
|
|
337
|
+
url: pageTarget.url || "",
|
|
338
|
+
title: pageTarget.title || targetApp,
|
|
339
|
+
overlayInstalled: false,
|
|
340
|
+
agentId: getConfigAgentId(config),
|
|
341
|
+
appName: targetApp,
|
|
342
|
+
isElectron: true,
|
|
343
|
+
};
|
|
344
|
+
sessionRegistry.register(targetId, client);
|
|
345
|
+
return client;
|
|
346
|
+
}
|
|
347
|
+
// ──────────────── STANDARD WEB BROWSER FLOW ────────────────
|
|
259
348
|
const sessionLock = readLock();
|
|
260
349
|
if (sessionLock && sessionLock.mode === "session") {
|
|
261
350
|
if (isLockPending(sessionLock)) {
|
|
@@ -440,7 +529,7 @@ export async function getOrConnect(config) {
|
|
|
440
529
|
if (activatedId) {
|
|
441
530
|
const client = sessionRegistry.getAll().find(c => c.targetId === activatedId);
|
|
442
531
|
if (client) {
|
|
443
|
-
sessionRegistry.setActive(activatedId, client.agentId);
|
|
532
|
+
sessionRegistry.setActive(activatedId, client.agentId, client.appName);
|
|
444
533
|
}
|
|
445
534
|
}
|
|
446
535
|
});
|
|
@@ -494,28 +583,34 @@ export async function getOrConnect(config) {
|
|
|
494
583
|
await releaseMutex();
|
|
495
584
|
}
|
|
496
585
|
}
|
|
497
|
-
export async function getTabList(agentId) {
|
|
586
|
+
export async function getTabList(agentId, targetApp) {
|
|
498
587
|
const config = getCachedConfig();
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
588
|
+
let port = null;
|
|
589
|
+
if (targetApp) {
|
|
590
|
+
const lock = readLock(targetApp);
|
|
591
|
+
if (lock && (await isLockValid(lock))) {
|
|
592
|
+
port = lock.port;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
port = actualPort;
|
|
597
|
+
if (!port) {
|
|
598
|
+
const lock = readLock();
|
|
599
|
+
if (lock && lock.mode === "session") {
|
|
600
|
+
const valid = await isLockValid(lock);
|
|
601
|
+
if (valid) {
|
|
602
|
+
port = lock.port;
|
|
603
|
+
}
|
|
604
|
+
else {
|
|
605
|
+
logDebug("[Weavetab CDP] getTabList: stale lock detected — clearing.");
|
|
606
|
+
clearLock();
|
|
607
|
+
port = null;
|
|
608
|
+
}
|
|
514
609
|
}
|
|
515
610
|
}
|
|
516
611
|
}
|
|
517
612
|
if (!port)
|
|
518
|
-
throw new Error(
|
|
613
|
+
throw new Error(`[!] No active ${targetApp ? targetApp : "browser"} session. Run browser_go or launch app first.`);
|
|
519
614
|
try {
|
|
520
615
|
const metaSession = await withCDPTimeout(CDP({ host: "127.0.0.1", port }), 5000, "getTabList CDP");
|
|
521
616
|
const { targetInfos } = await withCDPTimeout(metaSession.Target.getTargets(), 5000, "getTabList getTargets");
|
|
@@ -523,21 +618,21 @@ export async function getTabList(agentId) {
|
|
|
523
618
|
const aid = agentId || getConfigAgentId(config);
|
|
524
619
|
const bctxId = sessionRegistry.getBrowserContextIdForAgent(aid);
|
|
525
620
|
return targetInfos
|
|
526
|
-
.filter(t => t.type === "page" && (!bctxId || t.browserContextId === bctxId))
|
|
621
|
+
.filter(t => (t.type === "page" || t.type === "webview" || t.type === "app" || t.type === "other" || t.type === "iframe") && (!bctxId || t.browserContextId === bctxId))
|
|
527
622
|
.map(t => ({
|
|
528
623
|
id: t.targetId,
|
|
529
|
-
title: t.title,
|
|
624
|
+
title: t.title || t.url || t.type,
|
|
530
625
|
url: t.url,
|
|
531
626
|
type: t.type,
|
|
532
627
|
}));
|
|
533
628
|
}
|
|
534
629
|
catch {
|
|
535
|
-
throw new Error(`[!] Weavetab cannot reach browser on port ${port}.`);
|
|
630
|
+
throw new Error(`[!] Weavetab cannot reach ${targetApp ? targetApp : "browser"} on port ${port}.`);
|
|
536
631
|
}
|
|
537
632
|
}
|
|
538
633
|
// Only allow the UI to update cosmetic/dev settings via the binding.
|
|
539
634
|
// Never allow overwriting security-critical fields (role, browserPath, block, allow, profileDir, etc.).
|
|
540
|
-
const UI_ALLOWED_FIELDS = new Set(["devMode", "
|
|
635
|
+
const UI_ALLOWED_FIELDS = new Set(["devMode", "extension"]);
|
|
541
636
|
async function wireUiConfigBinding(session) {
|
|
542
637
|
try {
|
|
543
638
|
await session.Runtime.addBinding({ name: "__wt_save_ui_config" });
|
|
@@ -565,21 +660,38 @@ async function wireUiConfigBinding(session) {
|
|
|
565
660
|
}
|
|
566
661
|
catch { /* already bound */ }
|
|
567
662
|
}
|
|
568
|
-
export async function connectToTab(targetId, port, agentId, browserContextId) {
|
|
663
|
+
export async function connectToTab(targetId, port, agentId, browserContextId, appName) {
|
|
569
664
|
const existing = sessionRegistry.getAll().find(c => c.targetId === targetId);
|
|
570
665
|
if (existing)
|
|
571
666
|
return existing;
|
|
572
|
-
const lock = readLock();
|
|
573
|
-
const p = port || actualPort || lock?.port;
|
|
667
|
+
const lock = readLock(appName);
|
|
668
|
+
const p = port || (appName ? lock?.port : (actualPort || lock?.port));
|
|
574
669
|
if (!p)
|
|
575
|
-
throw new Error(
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
670
|
+
throw new Error(`[!] No active ${appName || "browser"} session. Run browser_go first.`);
|
|
671
|
+
let t = null;
|
|
672
|
+
try {
|
|
673
|
+
const targets = await withCDPTimeout(CDP.List({ host: "127.0.0.1", port: p }), 3000, "connectToTab CDP.List");
|
|
674
|
+
t = targets.find((item) => (item.id || item.targetId) === targetId);
|
|
675
|
+
}
|
|
676
|
+
catch { }
|
|
677
|
+
if (!t) {
|
|
678
|
+
try {
|
|
679
|
+
const metaSession = await withCDPTimeout(CDP({ host: "127.0.0.1", port: p }), 3000, "connectToTab meta CDP");
|
|
680
|
+
const { targetInfos } = await withCDPTimeout(metaSession.Target.getTargets(), 3000, "connectToTab meta getTargets");
|
|
681
|
+
await metaSession.close();
|
|
682
|
+
const ti = targetInfos.find(item => item.targetId === targetId);
|
|
683
|
+
if (ti) {
|
|
684
|
+
t = { id: ti.targetId, targetId: ti.targetId, url: ti.url, title: ti.title, type: ti.type };
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
catch { }
|
|
688
|
+
}
|
|
689
|
+
if (!t) {
|
|
690
|
+
t = { id: targetId, targetId, url: "", title: appName || "Tab", type: "page" };
|
|
691
|
+
}
|
|
580
692
|
const session = await withCDPTimeout(CDP({ host: "127.0.0.1", port: p, target: targetId }), 5000, "connectToTab CDP");
|
|
581
693
|
session.on("close", () => {
|
|
582
|
-
logDebug(
|
|
694
|
+
logDebug(`[Weavetab CDP] connectToTab session closed (${appName || "browser"}) — unregistering.`);
|
|
583
695
|
// Cancel this tab's analytics interval so it doesn't outlive the session
|
|
584
696
|
const iv = analyticsIntervals.get(targetId);
|
|
585
697
|
if (iv) {
|
|
@@ -592,7 +704,7 @@ export async function connectToTab(targetId, port, agentId, browserContextId) {
|
|
|
592
704
|
sessionRegistry.unregister(targetId);
|
|
593
705
|
});
|
|
594
706
|
session.on("error", (err) => {
|
|
595
|
-
logDebug(
|
|
707
|
+
logDebug(`[Weavetab CDP] connectToTab session error (${appName || "browser"}):`, err?.message ?? err);
|
|
596
708
|
});
|
|
597
709
|
await Promise.all([
|
|
598
710
|
session.Accessibility.enable(),
|
|
@@ -604,7 +716,7 @@ export async function connectToTab(targetId, port, agentId, browserContextId) {
|
|
|
604
716
|
]);
|
|
605
717
|
await wireUiConfigBinding(session);
|
|
606
718
|
const currentConfig = getCachedConfig();
|
|
607
|
-
await initSession(session, targetId, p, currentConfig);
|
|
719
|
+
await initSession(session, targetId, p, currentConfig, !!appName);
|
|
608
720
|
// NOTE: initSession() already installs a frameNavigated handler that handles
|
|
609
721
|
// clearSnapshot, clearViewportCache, and ensureVisualLayer. Do NOT add a second
|
|
610
722
|
// one here — it would double the CDP work on every navigation.
|
|
@@ -617,6 +729,8 @@ export async function connectToTab(targetId, port, agentId, browserContextId) {
|
|
|
617
729
|
overlayInstalled: false,
|
|
618
730
|
agentId: actualAgentId,
|
|
619
731
|
browserContextId,
|
|
732
|
+
appName,
|
|
733
|
+
isElectron: !!appName,
|
|
620
734
|
};
|
|
621
735
|
sessionRegistry.register(targetId, client);
|
|
622
736
|
return client;
|
package/dist/cdp/extension.js
CHANGED
|
@@ -55,7 +55,6 @@ export async function extensionHealth(session) {
|
|
|
55
55
|
if (document.querySelector('weavetab-host') || document.getElementById('__Weavetab_cursor_host')) features.push('cursor');
|
|
56
56
|
if (document.querySelector('[data-wt-internal]') || document.getElementById('wt-ui-styles')) features.push('styles');
|
|
57
57
|
if (document.querySelector('.wt-bubble')) features.push('bubble');
|
|
58
|
-
if (document.getElementById('wt-plan-panel')) features.push('plan');
|
|
59
58
|
return JSON.stringify({
|
|
60
59
|
loaded: ${loaded},
|
|
61
60
|
features
|
package/dist/cdp/helpers.js
CHANGED
|
@@ -164,7 +164,6 @@ export async function hideWeaveTabOverlay(session) {
|
|
|
164
164
|
// - [data-wt-ignore] : any element flagged as agent-invisible
|
|
165
165
|
// - .wt-bubble : thought bubble / action text popups
|
|
166
166
|
// - #__wt_ask_bridge : question input bridge element
|
|
167
|
-
// - #wt-plan-dashboard : mission plan dashboard
|
|
168
167
|
// - #wt-debug-overlay : debug overlay panel
|
|
169
168
|
// - #wt-main-container : WeaveTab OS panel (todos + chat)
|
|
170
169
|
style.textContent = [
|
|
@@ -175,7 +174,6 @@ export async function hideWeaveTabOverlay(session) {
|
|
|
175
174
|
'[data-wt-ignore]',
|
|
176
175
|
'.wt-bubble',
|
|
177
176
|
'#__wt_ask_bridge',
|
|
178
|
-
'#wt-plan-dashboard',
|
|
179
177
|
'#wt-debug-overlay',
|
|
180
178
|
'#wt-main-container',
|
|
181
179
|
].join(', ') + ' { display: none !important; visibility: hidden !important; opacity: 0 !important; }';
|
package/dist/cdp/lock.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export interface LockData {
|
|
|
4
4
|
profileDir: string;
|
|
5
5
|
browserPath: string;
|
|
6
6
|
mode: "session" | "attach";
|
|
7
|
+
appName?: string;
|
|
7
8
|
pending?: boolean;
|
|
8
9
|
timestamp?: number;
|
|
9
10
|
fingerprint?: {
|
|
@@ -12,17 +13,19 @@ export interface LockData {
|
|
|
12
13
|
cdpVersion?: string;
|
|
13
14
|
};
|
|
14
15
|
}
|
|
15
|
-
declare function
|
|
16
|
-
export declare function
|
|
17
|
-
|
|
18
|
-
export declare function
|
|
19
|
-
export declare function
|
|
16
|
+
export declare function getLockFilePath(appName?: string): string;
|
|
17
|
+
export declare function getMutexFilePath(appName?: string): string;
|
|
18
|
+
declare function acquireMutex(appName?: string): Promise<() => Promise<void>>;
|
|
19
|
+
export declare function writeLockSafe(data: LockData, appName?: string): Promise<void>;
|
|
20
|
+
export declare function writeLock(data: LockData, appName?: string): Promise<void>;
|
|
21
|
+
export declare function readLock(appName?: string): LockData | null;
|
|
22
|
+
export declare function clearLock(appName?: string): void;
|
|
20
23
|
/**
|
|
21
24
|
* Safe variant: only clears the lock if it was written by THIS process (process.pid).
|
|
22
25
|
* Prevents a second MCP server instance from wiping the first instance's browser lock
|
|
23
26
|
* during its retry loop, which would allow a race condition where two browsers spawn.
|
|
24
27
|
*/
|
|
25
|
-
export declare function clearLockIfOwned(): void;
|
|
28
|
+
export declare function clearLockIfOwned(appName?: string): void;
|
|
26
29
|
export declare function isProcessRunning(pid: number): boolean;
|
|
27
30
|
/**
|
|
28
31
|
* Two-factor lock validation: PID alive AND port accepting TCP connections.
|