@weavetab/mcp 2.5.0-beta.1 → 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 +284 -78
- package/dist/cdp/connector.d.ts +5 -3
- package/dist/cdp/connector.js +188 -71
- 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/overlay/ghost.js +2 -2
- package/dist/overlay/injector.js +1 -3
- 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 +213 -121
- 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 -3
- 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/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 +0 -1
- package/mcp.json +8 -6
- package/package.json +11 -5
- package/server.json +3 -3
- 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/state/session.d.ts
CHANGED
|
@@ -12,9 +12,9 @@ export declare class SessionRegistry {
|
|
|
12
12
|
getBrowserContextIdForAgent(agentId: string): string | undefined;
|
|
13
13
|
getAgentForBrowserContextId(contextId: string): string | undefined;
|
|
14
14
|
setBrowserContextIdForAgent(agentId: string, contextId: string): void;
|
|
15
|
-
getActive(agentId?: string): BrowserClient | null;
|
|
15
|
+
getActive(agentId?: string, appName?: string): BrowserClient | null;
|
|
16
16
|
getMostRecent(agentId?: string): BrowserClient | null;
|
|
17
|
-
setActive(targetId: string, agentId: string): void;
|
|
17
|
+
setActive(targetId: string, agentId: string, appName?: string): void;
|
|
18
18
|
getAll(): BrowserClient[];
|
|
19
19
|
getAllForAgent(agentId: string): BrowserClient[];
|
|
20
20
|
register(targetId: string, client: BrowserClient): void;
|
package/dist/state/session.js
CHANGED
|
@@ -50,21 +50,39 @@ export class SessionRegistry {
|
|
|
50
50
|
this.browserContextsByAgent.set(agentId, contextId);
|
|
51
51
|
this.savePersistence();
|
|
52
52
|
}
|
|
53
|
-
getActive(agentId) {
|
|
53
|
+
getActive(agentId, appName) {
|
|
54
54
|
if (!agentId)
|
|
55
55
|
return null;
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
const key = appName ? `${agentId}:app:${appName}` : agentId;
|
|
57
|
+
const targetId = this.activeTargetIdByAgent.get(key);
|
|
58
|
+
if (targetId && this.sessions.has(targetId)) {
|
|
59
|
+
return this.sessions.get(targetId) || null;
|
|
60
|
+
}
|
|
61
|
+
if (appName) {
|
|
62
|
+
const match = Array.from(this.sessions.values()).find(c => c.agentId === agentId && c.appName === appName);
|
|
63
|
+
if (match)
|
|
64
|
+
return match;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
const browserTargetId = this.activeTargetIdByAgent.get(agentId);
|
|
68
|
+
if (browserTargetId) {
|
|
69
|
+
const client = this.sessions.get(browserTargetId);
|
|
70
|
+
if (client && !client.isElectron)
|
|
71
|
+
return client;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
60
75
|
}
|
|
61
76
|
getMostRecent(agentId) {
|
|
62
77
|
const values = Array.from(this.sessions.values()).filter(c => c.agentId === agentId);
|
|
63
78
|
return values.length > 0 ? values[values.length - 1] : null;
|
|
64
79
|
}
|
|
65
|
-
setActive(targetId, agentId) {
|
|
80
|
+
setActive(targetId, agentId, appName) {
|
|
66
81
|
if (this.sessions.has(targetId)) {
|
|
67
|
-
this.
|
|
82
|
+
const client = this.sessions.get(targetId);
|
|
83
|
+
const targetApp = appName || client?.appName;
|
|
84
|
+
const key = targetApp ? `${agentId}:app:${targetApp}` : agentId;
|
|
85
|
+
this.activeTargetIdByAgent.set(key, targetId);
|
|
68
86
|
}
|
|
69
87
|
}
|
|
70
88
|
getAll() {
|
|
@@ -77,8 +95,9 @@ export class SessionRegistry {
|
|
|
77
95
|
this.sessions.set(targetId, client);
|
|
78
96
|
this.heartbeatTimestamps.set(targetId, Date.now());
|
|
79
97
|
this.startHeartbeat(targetId, client.session);
|
|
80
|
-
|
|
81
|
-
|
|
98
|
+
const key = client.appName ? `${client.agentId}:app:${client.appName}` : client.agentId;
|
|
99
|
+
if (!this.activeTargetIdByAgent.has(key)) {
|
|
100
|
+
this.activeTargetIdByAgent.set(key, targetId);
|
|
82
101
|
}
|
|
83
102
|
}
|
|
84
103
|
unregister(targetId) {
|
|
@@ -87,14 +106,15 @@ export class SessionRegistry {
|
|
|
87
106
|
if (client) {
|
|
88
107
|
client.session.close().catch(() => { });
|
|
89
108
|
this.sessions.delete(targetId);
|
|
90
|
-
const
|
|
109
|
+
const key = client.appName ? `${client.agentId}:app:${client.appName}` : client.agentId;
|
|
110
|
+
const activeForAgent = this.activeTargetIdByAgent.get(key);
|
|
91
111
|
if (activeForAgent === targetId) {
|
|
92
|
-
const agentTabs = Array.from(this.sessions.values()).filter(c => c.agentId === client.agentId);
|
|
112
|
+
const agentTabs = Array.from(this.sessions.values()).filter(c => c.agentId === client.agentId && c.appName === client.appName);
|
|
93
113
|
if (agentTabs.length > 0) {
|
|
94
|
-
this.activeTargetIdByAgent.set(
|
|
114
|
+
this.activeTargetIdByAgent.set(key, agentTabs[agentTabs.length - 1].targetId);
|
|
95
115
|
}
|
|
96
116
|
else {
|
|
97
|
-
this.activeTargetIdByAgent.delete(
|
|
117
|
+
this.activeTargetIdByAgent.delete(key);
|
|
98
118
|
}
|
|
99
119
|
}
|
|
100
120
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type BrowserEngine, type ElectronAppConfig } from "../config/loader.js";
|
|
2
2
|
export interface BrowserDetectResult {
|
|
3
3
|
available: {
|
|
4
4
|
engine: BrowserEngine;
|
|
@@ -6,6 +6,7 @@ export interface BrowserDetectResult {
|
|
|
6
6
|
path: string;
|
|
7
7
|
}[];
|
|
8
8
|
current: BrowserEngine;
|
|
9
|
+
electronApps?: Record<string, ElectronAppConfig>;
|
|
9
10
|
toolCoverage: Record<string, number>;
|
|
10
11
|
unsupportedTools: Record<string, string[]>;
|
|
11
12
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { findFirefox, probeAllBrowsers } from "../cdp/bridge.js";
|
|
2
|
+
import { getCachedConfig, getElectronApps } from "../config/loader.js";
|
|
2
3
|
// Phase 8c: Per-engine tool coverage map
|
|
3
4
|
// Firefox CDP is ~80% feature-complete; WebKit is ~60%
|
|
4
5
|
const ENGINE_COVERAGE = {
|
|
@@ -54,16 +55,18 @@ export function detectBrowsers(currentEngine) {
|
|
|
54
55
|
// WebKit detection is best-effort (Safari Technology Preview)
|
|
55
56
|
// We do not auto-detect WebKit — only report if manually configured
|
|
56
57
|
const toolCoverage = {};
|
|
57
|
-
const
|
|
58
|
+
const unsupportedMap = {};
|
|
58
59
|
for (const engine of ["chromium", "firefox", "webkit"]) {
|
|
59
60
|
toolCoverage[engine] = ENGINE_COVERAGE[engine].coverage;
|
|
60
|
-
|
|
61
|
+
unsupportedMap[engine] = [...ENGINE_COVERAGE[engine].unsupported];
|
|
61
62
|
}
|
|
63
|
+
const electronApps = getElectronApps(getCachedConfig());
|
|
62
64
|
return {
|
|
63
65
|
available,
|
|
64
66
|
current: currentEngine,
|
|
67
|
+
electronApps: Object.keys(electronApps).length > 0 ? electronApps : undefined,
|
|
65
68
|
toolCoverage,
|
|
66
|
-
unsupportedTools,
|
|
69
|
+
unsupportedTools: unsupportedMap,
|
|
67
70
|
};
|
|
68
71
|
}
|
|
69
72
|
export function isToolSupported(engine, toolName) {
|
package/dist/tools/burst.d.ts
CHANGED
package/dist/tools/burst.js
CHANGED
|
@@ -180,7 +180,8 @@ export async function browserBurst(session, args, config) {
|
|
|
180
180
|
result = await weaveType(session, step.args, config);
|
|
181
181
|
break;
|
|
182
182
|
case "browser_go":
|
|
183
|
-
|
|
183
|
+
case "browser_navigate":
|
|
184
|
+
result = await weaveNavigate(session, step.args.url, config, undefined, false, false, args.app);
|
|
184
185
|
break;
|
|
185
186
|
case "browser_key":
|
|
186
187
|
result = await weaveKey(session, step.args, config);
|
|
@@ -192,7 +193,7 @@ export async function browserBurst(session, args, config) {
|
|
|
192
193
|
result = await browserWait(session, step.args, config);
|
|
193
194
|
break;
|
|
194
195
|
case "browser_map": {
|
|
195
|
-
const mapTargetId = sessionRegistry.getActive(getConfigAgentId(config))?.targetId || "default";
|
|
196
|
+
const mapTargetId = sessionRegistry.getActive(getConfigAgentId(config), args.app)?.targetId || "default";
|
|
196
197
|
result = await browserMap(session, step.args, mapTargetId, config);
|
|
197
198
|
break;
|
|
198
199
|
}
|
|
@@ -207,7 +208,7 @@ export async function browserBurst(session, args, config) {
|
|
|
207
208
|
break;
|
|
208
209
|
case "browser_console": {
|
|
209
210
|
let targetId = "default";
|
|
210
|
-
const active = sessionRegistry.getActive(getConfigAgentId(config));
|
|
211
|
+
const active = sessionRegistry.getActive(getConfigAgentId(config), args.app);
|
|
211
212
|
if (active)
|
|
212
213
|
targetId = active.targetId;
|
|
213
214
|
result = await browserConsole(session, step.args, config, targetId);
|
|
@@ -219,9 +220,6 @@ export async function browserBurst(session, args, config) {
|
|
|
219
220
|
case "browser_fill":
|
|
220
221
|
result = await browserFill(session, step.args, config);
|
|
221
222
|
break;
|
|
222
|
-
case "browser_navigate":
|
|
223
|
-
result = await weaveNavigate(session, step.args.url, config);
|
|
224
|
-
break;
|
|
225
223
|
case "browser_type_secret":
|
|
226
224
|
result = await weaveTypeSecret(session, step.args, config);
|
|
227
225
|
break;
|
package/dist/tools/click.js
CHANGED
|
@@ -32,7 +32,8 @@ export async function weaveClick(session, args, config) {
|
|
|
32
32
|
const { id, label, intent, backendNodeId: directNodeId } = args;
|
|
33
33
|
const { Page, DOM, Input } = session;
|
|
34
34
|
const startMs = Date.now();
|
|
35
|
-
const
|
|
35
|
+
const clientMatch = sessionRegistry.getAll().find(c => c.session === session);
|
|
36
|
+
const targetId = clientMatch?.targetId ?? session.targetId ?? "default";
|
|
36
37
|
// Track URL before click
|
|
37
38
|
const initialUrl = (await Page.getFrameTree()).frameTree.frame.url;
|
|
38
39
|
// 1. Resolve Element and Selector
|
|
@@ -92,38 +93,42 @@ export async function weaveClick(session, args, config) {
|
|
|
92
93
|
if (!usedMemoryIntent) {
|
|
93
94
|
const query = label || intent;
|
|
94
95
|
if (args.id) {
|
|
95
|
-
const
|
|
96
|
-
element = map.elements.find((el) => el.id ===
|
|
97
|
-
//
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
96
|
+
const reqId = args.id;
|
|
97
|
+
element = map.elements.find((el) => el.id === reqId);
|
|
98
|
+
// If not found in cache, rebuild map once to catch recent DOM updates or cache misses
|
|
99
|
+
if (!element && reqId.startsWith("w:")) {
|
|
100
|
+
map = await buildActionMap(session, { lite: true }, targetId);
|
|
101
|
+
cacheActionMap(targetId, map);
|
|
102
|
+
element = map.elements.find((el) => el.id === reqId);
|
|
103
|
+
}
|
|
104
|
+
// Fast-fail if still not found
|
|
105
|
+
if (!element && reqId.startsWith("w:")) {
|
|
101
106
|
return {
|
|
102
107
|
success: false,
|
|
103
108
|
error_code: "ELEMENT_NOT_FOUND",
|
|
104
|
-
message: `[!] Ref '${
|
|
109
|
+
message: `[!] Ref '${reqId}' is no longer in the current page map — the DOM was updated since browser_map was called. Call browser_map() to get fresh ref IDs.`,
|
|
105
110
|
};
|
|
106
111
|
}
|
|
107
|
-
if (!element && (
|
|
108
|
-
const actCheck = await verifyActionability(session,
|
|
112
|
+
if (!element && (reqId.startsWith("#") || reqId.startsWith(".") || reqId.includes("[") || reqId.includes(">") || reqId.includes(" "))) {
|
|
113
|
+
const actCheck = await verifyActionability(session, reqId, {
|
|
109
114
|
stabilityTimeoutMs: config.actionabilityTimeoutMs ?? 400,
|
|
110
115
|
totalTimeoutMs: (config.actionabilityTimeoutMs ?? 400) * 3,
|
|
111
116
|
});
|
|
112
117
|
if (actCheck.status === 'ACTIONABLE') {
|
|
113
118
|
element = {
|
|
114
|
-
id:
|
|
119
|
+
id: reqId,
|
|
115
120
|
label: 'selector-match',
|
|
116
121
|
x: actCheck.x,
|
|
117
122
|
y: actCheck.y,
|
|
118
|
-
selector:
|
|
123
|
+
selector: reqId,
|
|
119
124
|
role: "button",
|
|
120
125
|
group: "utility",
|
|
121
126
|
depth: 0,
|
|
122
|
-
fingerprint: "selector:" +
|
|
127
|
+
fingerprint: "selector:" + reqId,
|
|
123
128
|
};
|
|
124
129
|
}
|
|
125
130
|
else if (actCheck.status === 'OCCLUDED' && !actCheck.occludedBy?.isWeavetabOwned) {
|
|
126
|
-
return { success: false, error_code: "ELEMENT_OCCLUDED_BY", message: `[!] ${describeOccluder(actCheck.occludedBy)} — element ${
|
|
131
|
+
return { success: false, error_code: "ELEMENT_OCCLUDED_BY", message: `[!] ${describeOccluder(actCheck.occludedBy)} — element ${reqId} is blocked.` };
|
|
127
132
|
}
|
|
128
133
|
}
|
|
129
134
|
}
|
|
@@ -532,28 +537,18 @@ export async function weaveClick(session, args, config) {
|
|
|
532
537
|
input: { ref: element_?.id ?? args.id ?? args.label, label: element_?.label },
|
|
533
538
|
result: { success: true, stuck: isStuck },
|
|
534
539
|
});
|
|
535
|
-
if (isStuck) {
|
|
536
|
-
return {
|
|
537
|
-
success: false,
|
|
538
|
-
error_code: "CLICK_NO_EFFECT",
|
|
539
|
-
message: "The click executed but resulted in no visible changes to the page. It may have been intercepted by an overlay, or the element might require a different interaction.",
|
|
540
|
-
suggestion: "Try looking for an invisible overlay blocking the click, or try using browser_eval to click it directly.",
|
|
541
|
-
tab_id: targetId,
|
|
542
|
-
url: finalUrl,
|
|
543
|
-
};
|
|
544
|
-
}
|
|
545
540
|
return {
|
|
546
541
|
success: true,
|
|
547
|
-
clicked: element_.label,
|
|
548
|
-
ref: element_.id,
|
|
542
|
+
clicked: element_?.label || (coords ? `coords(${coords.x},${coords.y})` : (args.label || args.id || "")),
|
|
543
|
+
ref: element_?.id || (coords ? "coords" : (args.id || "unknown")),
|
|
549
544
|
execution_ms: executionMs,
|
|
550
545
|
tab_id: targetId,
|
|
551
546
|
url: finalUrl,
|
|
552
547
|
title: newMap?.title || "",
|
|
553
548
|
url_changed: finalUrl !== initialUrl,
|
|
554
549
|
...(args.include_map ? { page_map: newMap ? toMarkdownTable(newMap.elements, finalUrl, newMap.title) : "" } : {}),
|
|
555
|
-
dom_changed:
|
|
556
|
-
stuck_warning:
|
|
550
|
+
dom_changed: !isStuck,
|
|
551
|
+
stuck_warning: isStuck,
|
|
557
552
|
...(stuckHint ? { stuck_hint: stuckHint } : {}),
|
|
558
553
|
...(sniperWarning ? { sniper_warning: sniperWarning } : {}),
|
|
559
554
|
...(postCheck.canvas_hint ? { canvas_hint: postCheck.canvas_hint } : {})
|
package/dist/tools/clipboard.js
CHANGED
|
@@ -69,32 +69,30 @@ export async function browserClipboard(session, options, _config) {
|
|
|
69
69
|
throw new Error("Failed to write image to clipboard: " + (val || "unknown"));
|
|
70
70
|
}
|
|
71
71
|
// Text-only write
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
72
|
+
if (hasText && !hasImage) {
|
|
73
|
+
try {
|
|
74
|
+
const clipboardy = (await import("clipboardy")).default;
|
|
75
|
+
await clipboardy.write(text);
|
|
76
|
+
return { success: true, message: "Copied to clipboard" };
|
|
77
|
+
}
|
|
78
|
+
catch { /* fallback to browser API */ }
|
|
79
|
+
const res = await session.Runtime.evaluate({
|
|
80
|
+
expression: `(async function() {
|
|
81
|
+
try {
|
|
82
|
+
${secureCtx ? `await navigator.clipboard.writeText(${JSON.stringify(text)});` : `throw new Error('non-secure');`}
|
|
83
|
+
return true;
|
|
84
|
+
} catch(e) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
})()`,
|
|
88
|
+
awaitPromise: true,
|
|
89
|
+
returnByValue: true
|
|
90
|
+
});
|
|
91
|
+
if (res.result?.value === true) {
|
|
92
|
+
return { success: true, message: "Copied to clipboard" };
|
|
93
|
+
}
|
|
92
94
|
return { success: true, message: "Copied to clipboard" };
|
|
93
95
|
}
|
|
94
|
-
if (!secureCtx) {
|
|
95
|
-
throw new Error("browser_clipboard: write failed on a non-secure context (file:// or http://). The Clipboard API is unavailable here; navigate to an https page and retry.");
|
|
96
|
-
}
|
|
97
|
-
throw new Error("Failed to write to clipboard (page may not be focused)");
|
|
98
96
|
}
|
|
99
97
|
// ── Read action ──────────────────────────────────────────────────
|
|
100
98
|
// 4-layer clipboard read strategy:
|
|
@@ -147,6 +145,15 @@ export async function browserClipboard(session, options, _config) {
|
|
|
147
145
|
});
|
|
148
146
|
return valRes?.value || null;
|
|
149
147
|
}
|
|
148
|
+
// Layer 0: Direct OS system clipboard read via clipboardy
|
|
149
|
+
try {
|
|
150
|
+
const clipboardy = (await import("clipboardy")).default;
|
|
151
|
+
const sysText = await clipboardy.read();
|
|
152
|
+
if (sysText && sysText.trim().length > 0) {
|
|
153
|
+
return { success: true, text: sysText, message: "Read from clipboard" };
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch { }
|
|
150
157
|
await grantUserActivation();
|
|
151
158
|
const secureCtx = await isSecureContext();
|
|
152
159
|
const originRes = await session.Runtime.evaluate({ expression: "window.location.origin" }).catch(() => null);
|
package/dist/tools/navigate.d.ts
CHANGED
|
@@ -12,5 +12,5 @@ interface NavigateResult {
|
|
|
12
12
|
title: string;
|
|
13
13
|
tab_id: string;
|
|
14
14
|
}
|
|
15
|
-
export declare function weaveNavigate(session: CDP.Client, url: string, config: Config, targetId?: string, force?: boolean, fast?: boolean): Promise<NavigateResult | WeaveError>;
|
|
15
|
+
export declare function weaveNavigate(session: CDP.Client, url: string, config: Config, targetId?: string, force?: boolean, fast?: boolean, appName?: string): Promise<NavigateResult | WeaveError>;
|
|
16
16
|
export {};
|
package/dist/tools/navigate.js
CHANGED
|
@@ -12,7 +12,7 @@ import { resetFallbackIdCounter, clearShadowDomCache, clearAXTreeCache } from ".
|
|
|
12
12
|
import { clearTabCache } from "../state/cache.js";
|
|
13
13
|
import { bumpVisitCount } from "../intelligence/sessionMemory.js";
|
|
14
14
|
import { sessionRegistry } from "../state/session.js";
|
|
15
|
-
export async function weaveNavigate(session, url, config, targetId = "default", force = false, fast = false) {
|
|
15
|
+
export async function weaveNavigate(session, url, config, targetId = "default", force = false, fast = false, appName) {
|
|
16
16
|
logDebug(`[weaveNavigate] Attempting navigation to: ${url}`);
|
|
17
17
|
let parsed;
|
|
18
18
|
if (url.startsWith('Weavetab://')) {
|
|
@@ -40,16 +40,35 @@ export async function weaveNavigate(session, url, config, targetId = "default",
|
|
|
40
40
|
return { success: false, error_code: "INVALID_URL", message: `[!] Invalid URL: ${url}` };
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
const active = sessionRegistry.getActive(config.agentId, appName);
|
|
44
|
+
const isElectron = !!(appName || active?.isElectron);
|
|
45
|
+
// Allowed web and desktop application protocols.
|
|
46
|
+
// Block script-execution protocols (javascript:, data:) that bypass safety policies.
|
|
45
47
|
const proto = parsed.protocol.toLowerCase();
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
const allowedProtocols = new Set([
|
|
49
|
+
"http:",
|
|
50
|
+
"https:",
|
|
51
|
+
"file:",
|
|
52
|
+
"chrome-extension:",
|
|
53
|
+
"app:",
|
|
54
|
+
"vscode-webview:",
|
|
55
|
+
"vscode-file:",
|
|
56
|
+
"discord:",
|
|
57
|
+
"slack:",
|
|
58
|
+
"devtools:",
|
|
59
|
+
"chrome:",
|
|
60
|
+
"about:",
|
|
61
|
+
]);
|
|
62
|
+
if (!allowedProtocols.has(proto)) {
|
|
63
|
+
return { success: false, error_code: "BLOCKED_PROTOCOL", message: `[!] Blocked protocol '${proto}'. Only web, file, and desktop app protocols are allowed.` };
|
|
48
64
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
65
|
+
// Domain policy check only applies to standard web navigations
|
|
66
|
+
if (!isElectron && (proto === "http:" || proto === "https:")) {
|
|
67
|
+
const domainCheck = checkDomain(parsed.href, config);
|
|
68
|
+
logDebug(`[weaveNavigate] Domain check for ${parsed.hostname}: ${JSON.stringify(domainCheck)}`);
|
|
69
|
+
if (!domainCheck.allowed) {
|
|
70
|
+
return { success: false, error_code: "DOMAIN_BLOCKED", message: `[!] Weave blocked: ${domainCheck.reason}` };
|
|
71
|
+
}
|
|
53
72
|
}
|
|
54
73
|
// Extension pages: open in new tab instead of navigating current tab
|
|
55
74
|
// (chrome-extension:// pages use a different process context — Page.navigate
|
|
@@ -88,7 +107,6 @@ export async function weaveNavigate(session, url, config, targetId = "default",
|
|
|
88
107
|
};
|
|
89
108
|
}
|
|
90
109
|
// Resolve active tab id
|
|
91
|
-
const active = sessionRegistry.getActive(config.agentId);
|
|
92
110
|
const resolvedTargetId = active?.targetId ?? targetId;
|
|
93
111
|
// 1. Anchor Tab Protection
|
|
94
112
|
let isAnchor = false;
|
package/dist/tools/tabs.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { Config } from "../config/loader.js";
|
|
|
2
2
|
export declare function recordTabActivity(targetId: string): void;
|
|
3
3
|
export declare function lockTab(targetId: string): void;
|
|
4
4
|
export declare function unlockTab(targetId: string): void;
|
|
5
|
-
export declare function evaluateTabSemantics(targetId: string): Promise<void>;
|
|
5
|
+
export declare function evaluateTabSemantics(targetId: string, appName?: string): Promise<void>;
|
|
6
6
|
export interface TabInfo {
|
|
7
7
|
id: string;
|
|
8
8
|
title: string;
|
|
@@ -11,30 +11,30 @@ export interface TabInfo {
|
|
|
11
11
|
health: "alive" | "dead" | "unreachable";
|
|
12
12
|
score: number;
|
|
13
13
|
}
|
|
14
|
-
export declare function WeavetabOpen(url: string, config: Config, reuse?: boolean): Promise<{
|
|
14
|
+
export declare function WeavetabOpen(url: string, config: Config, reuse?: boolean, appName?: string): Promise<{
|
|
15
15
|
success: true;
|
|
16
16
|
tabId: string;
|
|
17
17
|
url: string;
|
|
18
18
|
reused?: boolean;
|
|
19
19
|
}>;
|
|
20
|
-
export declare function WeavetabSwitch(tabId: string, config: Config): Promise<{
|
|
20
|
+
export declare function WeavetabSwitch(tabId: string, config: Config, appName?: string): Promise<{
|
|
21
21
|
success: true;
|
|
22
22
|
tab_id: string;
|
|
23
23
|
title: string;
|
|
24
24
|
url: string;
|
|
25
25
|
contextSwitchCostMs: number;
|
|
26
26
|
}>;
|
|
27
|
-
export declare function WeavetabList(config: Config): Promise<{
|
|
27
|
+
export declare function WeavetabList(config: Config, appName?: string): Promise<{
|
|
28
28
|
tabs: TabInfo[];
|
|
29
29
|
}>;
|
|
30
|
-
export declare function WeavetabClose(tabId: string, config: Config, force?: boolean): Promise<{
|
|
30
|
+
export declare function WeavetabClose(tabId: string, config: Config, force?: boolean, appName?: string): Promise<{
|
|
31
31
|
success: true;
|
|
32
32
|
tab_id: string;
|
|
33
33
|
}>;
|
|
34
34
|
/**
|
|
35
35
|
* Batch close multiple tabs at once — saves multiple round-trips.
|
|
36
36
|
*/
|
|
37
|
-
export declare function WeavetabCloseBatch(tabIds: string[], config: Config, force?: boolean): Promise<{
|
|
37
|
+
export declare function WeavetabCloseBatch(tabIds: string[], config: Config, force?: boolean, appName?: string): Promise<{
|
|
38
38
|
success: true;
|
|
39
39
|
closed: string[];
|
|
40
40
|
skipped: string[];
|
|
@@ -42,7 +42,7 @@ export declare function WeavetabCloseBatch(tabIds: string[], config: Config, for
|
|
|
42
42
|
/**
|
|
43
43
|
* Consolidate: deduplicate tabs by URL, close duplicates.
|
|
44
44
|
*/
|
|
45
|
-
export declare function WeavetabConsolidate(config: Config): Promise<{
|
|
45
|
+
export declare function WeavetabConsolidate(config: Config, appName?: string): Promise<{
|
|
46
46
|
success: true;
|
|
47
47
|
tabs_before: number;
|
|
48
48
|
tabs_after: number;
|
|
@@ -51,7 +51,7 @@ export declare function WeavetabConsolidate(config: Config): Promise<{
|
|
|
51
51
|
/**
|
|
52
52
|
* Heal: check all tabs for crashed state and reload them.
|
|
53
53
|
*/
|
|
54
|
-
export declare function WeavetabHeal(config: Config): Promise<{
|
|
54
|
+
export declare function WeavetabHeal(config: Config, appName?: string): Promise<{
|
|
55
55
|
success: true;
|
|
56
56
|
healed: number;
|
|
57
57
|
tabs_checked: number;
|
|
@@ -68,10 +68,10 @@ export interface WindowInfo {
|
|
|
68
68
|
};
|
|
69
69
|
focused: boolean;
|
|
70
70
|
}
|
|
71
|
-
export declare function weaveWindowList(config: Config): Promise<{
|
|
71
|
+
export declare function weaveWindowList(config: Config, appName?: string): Promise<{
|
|
72
72
|
windows: WindowInfo[];
|
|
73
73
|
}>;
|
|
74
|
-
export declare function weaveFocusWindow(windowId: number, config: Config): Promise<{
|
|
74
|
+
export declare function weaveFocusWindow(windowId: number, config: Config, appName?: string): Promise<{
|
|
75
75
|
success: true;
|
|
76
76
|
windowId: number;
|
|
77
77
|
}>;
|