@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
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type CDP from "chrome-remote-interface";
|
|
2
|
+
import type { WeavetabPermissions } from "./interface.js";
|
|
3
|
+
export interface IPluginExtensionBridge {
|
|
4
|
+
send(session: CDP.Client, message: object): Promise<boolean>;
|
|
5
|
+
setHudState(session: CDP.Client, state: {
|
|
6
|
+
action?: string;
|
|
7
|
+
badge?: string;
|
|
8
|
+
badgeColor?: string;
|
|
9
|
+
groupState?: string;
|
|
10
|
+
}): Promise<void>;
|
|
11
|
+
showThought(session: CDP.Client, text: string, options?: {
|
|
12
|
+
durationMs?: number;
|
|
13
|
+
}): Promise<void>;
|
|
14
|
+
clearThought(session: CDP.Client): Promise<void>;
|
|
15
|
+
injectStyles(session: CDP.Client, css: string): Promise<void>;
|
|
16
|
+
injectScript<T = any>(session: CDP.Client, js: string): Promise<T>;
|
|
17
|
+
hideOverlay(session: CDP.Client): Promise<void>;
|
|
18
|
+
restoreOverlay(session: CDP.Client): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
export declare class PluginExtensionBridge implements IPluginExtensionBridge {
|
|
21
|
+
readonly pluginName: string;
|
|
22
|
+
private readonly permissions?;
|
|
23
|
+
constructor(pluginName: string, permissions?: WeavetabPermissions | undefined);
|
|
24
|
+
private checkPermission;
|
|
25
|
+
send(session: CDP.Client, message: object): Promise<boolean>;
|
|
26
|
+
setHudState(session: CDP.Client, state: {
|
|
27
|
+
action?: string;
|
|
28
|
+
badge?: string;
|
|
29
|
+
badgeColor?: string;
|
|
30
|
+
groupState?: string;
|
|
31
|
+
}): Promise<void>;
|
|
32
|
+
showThought(session: CDP.Client, text: string, options?: {
|
|
33
|
+
durationMs?: number;
|
|
34
|
+
}): Promise<void>;
|
|
35
|
+
clearThought(session: CDP.Client): Promise<void>;
|
|
36
|
+
injectStyles(session: CDP.Client, css: string): Promise<void>;
|
|
37
|
+
injectScript<T = any>(session: CDP.Client, js: string): Promise<T>;
|
|
38
|
+
hideOverlay(session: CDP.Client): Promise<void>;
|
|
39
|
+
restoreOverlay(session: CDP.Client): Promise<void>;
|
|
40
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { sendToExtension, hideWeaveTabOverlay, restoreWeaveTabOverlay } from "../cdp/helpers.js";
|
|
2
|
+
import { logDebug } from "../audit/logger.js";
|
|
3
|
+
export class PluginExtensionBridge {
|
|
4
|
+
pluginName;
|
|
5
|
+
permissions;
|
|
6
|
+
constructor(pluginName, permissions) {
|
|
7
|
+
this.pluginName = pluginName;
|
|
8
|
+
this.permissions = permissions;
|
|
9
|
+
}
|
|
10
|
+
checkPermission(category) {
|
|
11
|
+
if (!this.permissions || !this.permissions.extension)
|
|
12
|
+
return true; // Default permissive if not restricted
|
|
13
|
+
return this.permissions.extension[category] !== false;
|
|
14
|
+
}
|
|
15
|
+
async send(session, message) {
|
|
16
|
+
if (!session)
|
|
17
|
+
return false;
|
|
18
|
+
return await sendToExtension(session, {
|
|
19
|
+
...message,
|
|
20
|
+
plugin: this.pluginName,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
async setHudState(session, state) {
|
|
24
|
+
if (!this.checkPermission("hud")) {
|
|
25
|
+
logDebug(`[Weavetab PluginExtension] Plugin "${this.pluginName}" lacks permissions.extension.hud`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
await this.send(session, {
|
|
29
|
+
type: "wt-hud-update",
|
|
30
|
+
action: state.action,
|
|
31
|
+
badge: state.badge,
|
|
32
|
+
badgeColor: state.badgeColor,
|
|
33
|
+
groupState: state.groupState,
|
|
34
|
+
timestamp: Date.now(),
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
async showThought(session, text, options) {
|
|
38
|
+
if (!this.checkPermission("hud"))
|
|
39
|
+
return;
|
|
40
|
+
if (!session || !session.Runtime)
|
|
41
|
+
return;
|
|
42
|
+
const duration = options?.durationMs || 4000;
|
|
43
|
+
const safeText = JSON.stringify(text);
|
|
44
|
+
try {
|
|
45
|
+
await session.Runtime.evaluate({
|
|
46
|
+
expression: `(function() {
|
|
47
|
+
var existing = document.getElementById('__wt_plugin_thought_${this.pluginName}');
|
|
48
|
+
if (existing) existing.remove();
|
|
49
|
+
|
|
50
|
+
var bubble = document.createElement('div');
|
|
51
|
+
bubble.id = '__wt_plugin_thought_${this.pluginName}';
|
|
52
|
+
bubble.className = 'wt-bubble';
|
|
53
|
+
bubble.setAttribute('data-wt-internal', 'true');
|
|
54
|
+
bubble.style.cssText = [
|
|
55
|
+
'position: fixed',
|
|
56
|
+
'bottom: 24px',
|
|
57
|
+
'right: 24px',
|
|
58
|
+
'background: linear-gradient(135deg, rgba(17, 24, 39, 0.95), rgba(30, 41, 59, 0.95))',
|
|
59
|
+
'color: #f8fafc',
|
|
60
|
+
'padding: 12px 18px',
|
|
61
|
+
'border-radius: 12px',
|
|
62
|
+
'font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
63
|
+
'font-size: 13px',
|
|
64
|
+
'font-weight: 500',
|
|
65
|
+
'box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.1)',
|
|
66
|
+
'z-index: 2147483647',
|
|
67
|
+
'backdrop-filter: blur(8px)',
|
|
68
|
+
'pointer-events: none',
|
|
69
|
+
'animation: wtThoughtFade 0.25s cubic-bezier(0.16, 1, 0.3, 1)',
|
|
70
|
+
'display: flex',
|
|
71
|
+
'align-items: center',
|
|
72
|
+
'gap: 8px'
|
|
73
|
+
].join(';');
|
|
74
|
+
|
|
75
|
+
var icon = document.createElement('span');
|
|
76
|
+
icon.textContent = '✨';
|
|
77
|
+
var textSpan = document.createElement('span');
|
|
78
|
+
textSpan.textContent = ${safeText};
|
|
79
|
+
|
|
80
|
+
bubble.appendChild(icon);
|
|
81
|
+
bubble.appendChild(textSpan);
|
|
82
|
+
document.body.appendChild(bubble);
|
|
83
|
+
|
|
84
|
+
setTimeout(function() {
|
|
85
|
+
if (bubble && bubble.parentNode) {
|
|
86
|
+
bubble.style.opacity = '0';
|
|
87
|
+
bubble.style.transition = 'opacity 0.3s ease';
|
|
88
|
+
setTimeout(function() { bubble.remove(); }, 300);
|
|
89
|
+
}
|
|
90
|
+
}, ${duration});
|
|
91
|
+
})()`,
|
|
92
|
+
returnByValue: false,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
catch (e) {
|
|
96
|
+
logDebug(`[Weavetab PluginExtension] Failed to show thought:`, e);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async clearThought(session) {
|
|
100
|
+
if (!session || !session.Runtime)
|
|
101
|
+
return;
|
|
102
|
+
try {
|
|
103
|
+
await session.Runtime.evaluate({
|
|
104
|
+
expression: `(function() {
|
|
105
|
+
var existing = document.getElementById('__wt_plugin_thought_${this.pluginName}');
|
|
106
|
+
if (existing) existing.remove();
|
|
107
|
+
})()`,
|
|
108
|
+
returnByValue: false,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
catch { /* ignore */ }
|
|
112
|
+
}
|
|
113
|
+
async injectStyles(session, css) {
|
|
114
|
+
if (!this.checkPermission("styles")) {
|
|
115
|
+
throw new Error(`Permission Denied: Plugin "${this.pluginName}" does not have permissions.extension.styles`);
|
|
116
|
+
}
|
|
117
|
+
if (!session || !session.Runtime)
|
|
118
|
+
return;
|
|
119
|
+
const safeCss = JSON.stringify(css);
|
|
120
|
+
await session.Runtime.evaluate({
|
|
121
|
+
expression: `(function() {
|
|
122
|
+
var styleId = '__wt_plugin_styles_${this.pluginName}';
|
|
123
|
+
var style = document.getElementById(styleId) || document.createElement('style');
|
|
124
|
+
style.id = styleId;
|
|
125
|
+
style.textContent = ${safeCss};
|
|
126
|
+
if (!style.parentNode) document.head.appendChild(style);
|
|
127
|
+
})()`,
|
|
128
|
+
returnByValue: false,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
async injectScript(session, js) {
|
|
132
|
+
if (!this.checkPermission("scripts")) {
|
|
133
|
+
throw new Error(`Permission Denied: Plugin "${this.pluginName}" does not have permissions.extension.scripts`);
|
|
134
|
+
}
|
|
135
|
+
if (!session || !session.Runtime)
|
|
136
|
+
throw new Error("No active CDP session available");
|
|
137
|
+
const res = await session.Runtime.evaluate({
|
|
138
|
+
expression: js,
|
|
139
|
+
returnByValue: true,
|
|
140
|
+
awaitPromise: true,
|
|
141
|
+
});
|
|
142
|
+
return res.result?.value ?? res;
|
|
143
|
+
}
|
|
144
|
+
async hideOverlay(session) {
|
|
145
|
+
if (!this.checkPermission("overlay"))
|
|
146
|
+
return;
|
|
147
|
+
if (!session)
|
|
148
|
+
return;
|
|
149
|
+
await hideWeaveTabOverlay(session);
|
|
150
|
+
}
|
|
151
|
+
async restoreOverlay(session) {
|
|
152
|
+
if (!this.checkPermission("overlay"))
|
|
153
|
+
return;
|
|
154
|
+
if (!session)
|
|
155
|
+
return;
|
|
156
|
+
await restoreWeaveTabOverlay(session);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { z } from "zod";
|
|
2
2
|
import type CDP from "chrome-remote-interface";
|
|
3
3
|
import type { Config } from "../config/loader.js";
|
|
4
|
+
import type { IPluginStorage } from "./storage.js";
|
|
5
|
+
import type { IPluginExtensionBridge } from "./extension_bridge.js";
|
|
4
6
|
export interface WeavetabToolDefinition {
|
|
5
7
|
name: string;
|
|
6
8
|
description: string;
|
|
@@ -23,10 +25,25 @@ export interface WeavetabManifestTool {
|
|
|
23
25
|
name: string;
|
|
24
26
|
description: string;
|
|
25
27
|
inputSchema?: Record<string, any>;
|
|
28
|
+
parameters?: Record<string, any>;
|
|
26
29
|
}
|
|
27
|
-
export interface
|
|
30
|
+
export interface WeavetabPermissions {
|
|
31
|
+
config?: {
|
|
32
|
+
read?: boolean;
|
|
33
|
+
write?: boolean;
|
|
34
|
+
default?: Record<string, any>;
|
|
35
|
+
reason?: string;
|
|
36
|
+
};
|
|
37
|
+
extension?: {
|
|
38
|
+
hud?: boolean;
|
|
39
|
+
overlay?: boolean;
|
|
40
|
+
styles?: boolean;
|
|
41
|
+
scripts?: boolean;
|
|
42
|
+
reason?: string;
|
|
43
|
+
};
|
|
28
44
|
network?: {
|
|
29
45
|
domains?: string[];
|
|
46
|
+
hosts?: string[];
|
|
30
47
|
intercept?: boolean;
|
|
31
48
|
reason?: string;
|
|
32
49
|
};
|
|
@@ -36,6 +53,7 @@ export interface WeavetabManifestPermissions {
|
|
|
36
53
|
click?: boolean;
|
|
37
54
|
input?: boolean;
|
|
38
55
|
screenshot?: boolean;
|
|
56
|
+
dom?: boolean;
|
|
39
57
|
reason?: string;
|
|
40
58
|
[key: string]: any;
|
|
41
59
|
};
|
|
@@ -49,14 +67,18 @@ export interface WeavetabManifestPermissions {
|
|
|
49
67
|
override?: string[];
|
|
50
68
|
/** List of built-in tool names this plugin is permitted to remove */
|
|
51
69
|
remove?: string[];
|
|
70
|
+
hooks?: boolean;
|
|
52
71
|
reason?: string;
|
|
53
72
|
};
|
|
73
|
+
[key: string]: any;
|
|
54
74
|
}
|
|
75
|
+
export type WeavetabManifestPermissions = WeavetabPermissions;
|
|
55
76
|
export interface WeavetabManifestAuthor {
|
|
56
77
|
name: string;
|
|
57
78
|
role?: string;
|
|
58
79
|
avatar?: string;
|
|
59
80
|
url?: string;
|
|
81
|
+
email?: string;
|
|
60
82
|
}
|
|
61
83
|
export interface WeavetabManifestLinks {
|
|
62
84
|
github?: string;
|
|
@@ -65,10 +87,6 @@ export interface WeavetabManifestLinks {
|
|
|
65
87
|
issues?: string;
|
|
66
88
|
homepage?: string;
|
|
67
89
|
}
|
|
68
|
-
export interface WeavetabCompatibility {
|
|
69
|
-
weavetab?: string;
|
|
70
|
-
node?: string;
|
|
71
|
-
}
|
|
72
90
|
export interface WeavetabManifest {
|
|
73
91
|
/** Schema identifier */
|
|
74
92
|
$schema?: string;
|
|
@@ -82,9 +100,9 @@ export interface WeavetabManifest {
|
|
|
82
100
|
author?: string | WeavetabManifestAuthor | (string | WeavetabManifestAuthor)[];
|
|
83
101
|
/** Array of authors */
|
|
84
102
|
authors?: (string | WeavetabManifestAuthor)[];
|
|
85
|
-
/** Path to plugin logo
|
|
103
|
+
/** Path to plugin logo */
|
|
86
104
|
logo?: string;
|
|
87
|
-
/** Path to plugin header banner
|
|
105
|
+
/** Path to plugin header banner */
|
|
88
106
|
banner?: string;
|
|
89
107
|
/** Path to README markdown file (default: "README.md") */
|
|
90
108
|
readme?: string;
|
|
@@ -92,43 +110,87 @@ export interface WeavetabManifest {
|
|
|
92
110
|
tags?: string[];
|
|
93
111
|
/** External links */
|
|
94
112
|
links?: WeavetabManifestLinks;
|
|
95
|
-
/**
|
|
96
|
-
|
|
113
|
+
/** Engine dependencies */
|
|
114
|
+
engines?: Record<string, string>;
|
|
115
|
+
/** Supported environments */
|
|
116
|
+
environments?: string[];
|
|
97
117
|
/** Tools exposed by this plugin to the MCP server */
|
|
98
118
|
tools?: WeavetabManifestTool[];
|
|
119
|
+
/** Default configuration template */
|
|
120
|
+
defaultConfig?: Record<string, any>;
|
|
99
121
|
/** Required permissions with security rationale */
|
|
100
|
-
permissions?:
|
|
122
|
+
permissions?: WeavetabPermissions;
|
|
101
123
|
/** Open source license */
|
|
102
124
|
license?: string;
|
|
125
|
+
[key: string]: any;
|
|
103
126
|
}
|
|
104
127
|
export interface WeavetabPluginTool<TArgs = any> {
|
|
105
128
|
name: string;
|
|
106
129
|
description: string;
|
|
107
130
|
schema?: Record<string, any> | Record<string, z.ZodType<any>>;
|
|
108
131
|
inputSchema?: Record<string, any>;
|
|
132
|
+
parameters?: Record<string, any>;
|
|
109
133
|
handler: (args: TArgs, session?: any, config?: any) => Promise<any>;
|
|
110
134
|
}
|
|
135
|
+
export interface PluginHooksContext {
|
|
136
|
+
beforeToolCall: (handler: (toolName: string, args: Record<string, any>, session?: any) => Promise<any> | void) => void;
|
|
137
|
+
afterToolCall: (handler: (toolName: string, args: Record<string, any>, result: any, session?: any) => Promise<any> | void) => void;
|
|
138
|
+
onPageNavigate?: (handler: (url: string, session?: any) => Promise<void> | void) => void;
|
|
139
|
+
}
|
|
140
|
+
export interface PluginMcpContext {
|
|
141
|
+
registerTool: (tool: WeavetabPluginTool | any) => void;
|
|
142
|
+
overrideTool?: (toolName: string, overrideHandler: any) => void;
|
|
143
|
+
removeTool?: (toolName: string) => void;
|
|
144
|
+
unregisterTool?: (toolName: string) => void;
|
|
145
|
+
[key: string]: any;
|
|
146
|
+
}
|
|
147
|
+
export interface PluginContext {
|
|
148
|
+
name: string;
|
|
149
|
+
version?: string;
|
|
150
|
+
pluginDir: string;
|
|
151
|
+
config: Record<string, any>;
|
|
152
|
+
storage: IPluginStorage;
|
|
153
|
+
extension: IPluginExtensionBridge;
|
|
154
|
+
mcp: PluginMcpContext;
|
|
155
|
+
hooks: PluginHooksContext;
|
|
156
|
+
logger: {
|
|
157
|
+
info: (...args: any[]) => void;
|
|
158
|
+
warn: (...args: any[]) => void;
|
|
159
|
+
error: (...args: any[]) => void;
|
|
160
|
+
debug: (...args: any[]) => void;
|
|
161
|
+
};
|
|
162
|
+
[key: string]: any;
|
|
163
|
+
}
|
|
111
164
|
export interface WeavetabPluginDefinition {
|
|
112
165
|
name: string;
|
|
113
|
-
version
|
|
166
|
+
version?: string;
|
|
114
167
|
description?: string;
|
|
115
168
|
tools?: WeavetabPluginTool[];
|
|
116
169
|
overrides?: WeavetabToolOverride[];
|
|
117
170
|
removeTools?: string[];
|
|
118
|
-
|
|
119
|
-
|
|
171
|
+
defaultConfig?: Record<string, any>;
|
|
172
|
+
onLoad?: (ctx: PluginContext) => Promise<void> | void;
|
|
173
|
+
onUnload?: (ctx: PluginContext) => Promise<void> | void;
|
|
120
174
|
}
|
|
121
175
|
export interface WeavetabPlugin {
|
|
122
176
|
name: string;
|
|
123
177
|
version: string;
|
|
124
178
|
description?: string;
|
|
125
179
|
/** Add new tools */
|
|
126
|
-
tools?: WeavetabToolDefinition[];
|
|
180
|
+
tools?: WeavetabToolDefinition[] | WeavetabPluginTool[];
|
|
127
181
|
/** Override or wrap existing tools */
|
|
128
182
|
overrides?: WeavetabToolOverride[];
|
|
129
183
|
/** Built-in tools to remove or disable */
|
|
130
184
|
removeTools?: string[];
|
|
185
|
+
defaultConfig?: Record<string, any>;
|
|
131
186
|
manifest?: WeavetabManifest;
|
|
132
|
-
|
|
133
|
-
|
|
187
|
+
storage?: IPluginStorage;
|
|
188
|
+
extension?: IPluginExtensionBridge;
|
|
189
|
+
hooks?: {
|
|
190
|
+
beforeToolCall: Array<(toolName: string, args: Record<string, any>, session?: any) => Promise<any> | void>;
|
|
191
|
+
afterToolCall: Array<(toolName: string, args: Record<string, any>, result: any, session?: any) => Promise<any> | void>;
|
|
192
|
+
onPageNavigate: Array<(url: string, session?: any) => Promise<void> | void>;
|
|
193
|
+
};
|
|
194
|
+
onLoad?: (ctx: PluginContext) => Promise<void> | void;
|
|
195
|
+
onUnload?: (ctx: PluginContext) => Promise<void> | void;
|
|
134
196
|
}
|
package/dist/plugin/loader.d.ts
CHANGED
|
@@ -16,3 +16,7 @@ export declare function inspectInstalledPackage(pkgDir: string): InstalledPlugin
|
|
|
16
16
|
*/
|
|
17
17
|
export declare function listInstalledPlugins(pluginDir?: string): InstalledPluginInfo[];
|
|
18
18
|
export declare function discoverPlugins(pluginDir?: string): WeavetabPlugin[];
|
|
19
|
+
/**
|
|
20
|
+
* Loads a specific plugin by name from node_modules.
|
|
21
|
+
*/
|
|
22
|
+
export declare function loadPluginByName(name: string, pluginDir?: string): WeavetabPlugin | null;
|
package/dist/plugin/loader.js
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
import { readdirSync, existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { join, basename } from "node:path";
|
|
4
|
+
import { PluginStorageManager } from "./storage.js";
|
|
5
|
+
import { PluginExtensionBridge } from "./extension_bridge.js";
|
|
4
6
|
const require = createRequire(import.meta.url);
|
|
7
|
+
function getNodeModulesDirs(customDir) {
|
|
8
|
+
if (customDir)
|
|
9
|
+
return [customDir];
|
|
10
|
+
const dirs = [
|
|
11
|
+
join(process.cwd(), "node_modules"),
|
|
12
|
+
join(process.cwd(), "MCP", "node_modules"),
|
|
13
|
+
];
|
|
14
|
+
return Array.from(new Set(dirs.filter((d) => existsSync(d))));
|
|
15
|
+
}
|
|
5
16
|
/**
|
|
6
17
|
* Reads the manifest (weavetab.json) and package.json for an installed package
|
|
7
18
|
* without executing its runtime code (safe for listing/inspection).
|
|
@@ -39,38 +50,43 @@ export function inspectInstalledPackage(pkgDir) {
|
|
|
39
50
|
*/
|
|
40
51
|
export function listInstalledPlugins(pluginDir) {
|
|
41
52
|
const results = [];
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
53
|
+
const seenPackages = new Set();
|
|
54
|
+
const modulesDirs = getNodeModulesDirs(pluginDir);
|
|
55
|
+
for (const modulesDir of modulesDirs) {
|
|
56
|
+
try {
|
|
57
|
+
const entries = readdirSync(modulesDir, { withFileTypes: true });
|
|
58
|
+
for (const entry of entries) {
|
|
59
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
60
|
+
continue;
|
|
61
|
+
if (entry.name.startsWith("@")) {
|
|
62
|
+
// Scoped packages (@weavetab/plugin-xyz, @scope/xyz)
|
|
63
|
+
const scopeDir = join(modulesDir, entry.name);
|
|
64
|
+
try {
|
|
65
|
+
const scopeEntries = readdirSync(scopeDir, { withFileTypes: true });
|
|
66
|
+
for (const scopeEntry of scopeEntries) {
|
|
67
|
+
if (!scopeEntry.isDirectory() && !scopeEntry.isSymbolicLink())
|
|
68
|
+
continue;
|
|
69
|
+
const info = inspectInstalledPackage(join(scopeDir, scopeEntry.name));
|
|
70
|
+
if (info && !seenPackages.has(info.packageName)) {
|
|
71
|
+
seenPackages.add(info.packageName);
|
|
72
|
+
results.push(info);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch { /* scope dir unreadable */ }
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
// Top-level packages (any name)
|
|
80
|
+
const info = inspectInstalledPackage(join(modulesDir, entry.name));
|
|
81
|
+
if (info && !seenPackages.has(info.packageName)) {
|
|
82
|
+
seenPackages.add(info.packageName);
|
|
83
|
+
results.push(info);
|
|
61
84
|
}
|
|
62
85
|
}
|
|
63
|
-
catch { /* scope dir unreadable */ }
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
// Top-level packages (any name)
|
|
67
|
-
const info = inspectInstalledPackage(join(modulesDir, entry.name));
|
|
68
|
-
if (info)
|
|
69
|
-
results.push(info);
|
|
70
86
|
}
|
|
71
87
|
}
|
|
88
|
+
catch { /* ignore filesystem errors */ }
|
|
72
89
|
}
|
|
73
|
-
catch { /* ignore filesystem errors */ }
|
|
74
90
|
return results;
|
|
75
91
|
}
|
|
76
92
|
/**
|
|
@@ -97,7 +113,8 @@ function tryLoadPlugin(pkgDir) {
|
|
|
97
113
|
(Array.isArray(plugin.tools) ||
|
|
98
114
|
Array.isArray(plugin.overrides) ||
|
|
99
115
|
Array.isArray(plugin.removeTools) ||
|
|
100
|
-
Boolean(manifest.permissions?.tools)
|
|
116
|
+
Boolean(manifest.permissions?.tools) ||
|
|
117
|
+
typeof plugin.onLoad === "function")) {
|
|
101
118
|
plugin.name = plugin.name || manifest.name || pkg.name || basename(pkgDir);
|
|
102
119
|
plugin.version = plugin.version || manifest.version || pkg.version || "1.0.0";
|
|
103
120
|
plugin.description = plugin.description || manifest.description || pkg.description || "";
|
|
@@ -107,49 +124,153 @@ function tryLoadPlugin(pkgDir) {
|
|
|
107
124
|
plugin.removeTools = Array.isArray(plugin.removeTools)
|
|
108
125
|
? plugin.removeTools
|
|
109
126
|
: manifest.permissions?.tools?.remove || [];
|
|
127
|
+
// Initialize isolated storage sandbox in ~/.weavetab/plugins/<plugin-name>/
|
|
128
|
+
const storageManager = new PluginStorageManager(plugin.name);
|
|
129
|
+
const extensionBridge = new PluginExtensionBridge(plugin.name, manifest.permissions);
|
|
130
|
+
const pluginConfig = storageManager.loadConfig(plugin.defaultConfig || manifest.defaultConfig || {});
|
|
131
|
+
const hooks = {
|
|
132
|
+
beforeToolCall: [],
|
|
133
|
+
afterToolCall: [],
|
|
134
|
+
onPageNavigate: [],
|
|
135
|
+
};
|
|
136
|
+
plugin.storage = storageManager;
|
|
137
|
+
plugin.extension = extensionBridge;
|
|
138
|
+
plugin.hooks = hooks;
|
|
139
|
+
// Construct rich PluginContext
|
|
140
|
+
const pluginContext = {
|
|
141
|
+
name: plugin.name,
|
|
142
|
+
version: plugin.version,
|
|
143
|
+
pluginDir: storageManager.getDirectory(),
|
|
144
|
+
config: pluginConfig,
|
|
145
|
+
storage: storageManager,
|
|
146
|
+
extension: extensionBridge,
|
|
147
|
+
mcp: {
|
|
148
|
+
registerTool: (tool) => {
|
|
149
|
+
plugin.tools = plugin.tools || [];
|
|
150
|
+
const existingIndex = plugin.tools.findIndex((t) => t.name === tool.name);
|
|
151
|
+
if (existingIndex >= 0) {
|
|
152
|
+
plugin.tools[existingIndex] = tool;
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
plugin.tools.push(tool);
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
overrideTool: (toolName, overrideHandler) => {
|
|
159
|
+
plugin.overrides = plugin.overrides || [];
|
|
160
|
+
plugin.overrides.push({ name: toolName, handler: overrideHandler });
|
|
161
|
+
},
|
|
162
|
+
removeTool: (toolName) => {
|
|
163
|
+
plugin.removeTools = plugin.removeTools || [];
|
|
164
|
+
plugin.removeTools.push(toolName);
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
hooks: {
|
|
168
|
+
beforeToolCall: (fn) => hooks.beforeToolCall.push(fn),
|
|
169
|
+
afterToolCall: (fn) => hooks.afterToolCall.push(fn),
|
|
170
|
+
onPageNavigate: (fn) => hooks.onPageNavigate.push(fn),
|
|
171
|
+
},
|
|
172
|
+
logger: {
|
|
173
|
+
info: (...args) => console.error(`[Plugin: ${plugin.name}] [INFO]`, ...args),
|
|
174
|
+
warn: (...args) => console.error(`[Plugin: ${plugin.name}] [WARN]`, ...args),
|
|
175
|
+
error: (...args) => console.error(`[Plugin: ${plugin.name}] [ERROR]`, ...args),
|
|
176
|
+
debug: (...args) => console.error(`[Plugin: ${plugin.name}] [DEBUG]`, ...args),
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
// Execute dynamic onLoad if present
|
|
180
|
+
if (typeof plugin.onLoad === "function") {
|
|
181
|
+
try {
|
|
182
|
+
const loadPromise = plugin.onLoad(pluginContext);
|
|
183
|
+
if (loadPromise && typeof loadPromise.then === "function") {
|
|
184
|
+
loadPromise.catch((err) => {
|
|
185
|
+
console.error(`[Weavetab] Error in plugin "${plugin.name}" onLoad:`, err);
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
catch (e) {
|
|
190
|
+
console.error(`[Weavetab] Synchronous error in plugin "${plugin.name}" onLoad:`, e);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
110
193
|
return plugin;
|
|
111
194
|
}
|
|
112
195
|
}
|
|
113
|
-
catch {
|
|
114
|
-
//
|
|
196
|
+
catch (e) {
|
|
197
|
+
// console.error(`Failed loading plugin at ${pkgDir}:`, e);
|
|
115
198
|
}
|
|
116
199
|
return null;
|
|
117
200
|
}
|
|
118
201
|
export function discoverPlugins(pluginDir) {
|
|
119
202
|
const plugins = [];
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
203
|
+
const seenPluginNames = new Set();
|
|
204
|
+
const modulesDirs = getNodeModulesDirs(pluginDir);
|
|
205
|
+
for (const modulesDir of modulesDirs) {
|
|
206
|
+
try {
|
|
207
|
+
const entries = readdirSync(modulesDir, { withFileTypes: true });
|
|
208
|
+
for (const entry of entries) {
|
|
209
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
210
|
+
continue;
|
|
211
|
+
if (entry.name.startsWith("@")) {
|
|
212
|
+
// Scoped package directory (e.g. @weavetab, @microsoft, @mycompany)
|
|
213
|
+
const scopeDir = join(modulesDir, entry.name);
|
|
214
|
+
try {
|
|
215
|
+
const scopeEntries = readdirSync(scopeDir, { withFileTypes: true });
|
|
216
|
+
for (const scopeEntry of scopeEntries) {
|
|
217
|
+
if (!scopeEntry.isDirectory() && !scopeEntry.isSymbolicLink())
|
|
218
|
+
continue;
|
|
219
|
+
const plugin = tryLoadPlugin(join(scopeDir, scopeEntry.name));
|
|
220
|
+
if (plugin && !seenPluginNames.has(plugin.name)) {
|
|
221
|
+
seenPluginNames.add(plugin.name);
|
|
222
|
+
plugins.push(plugin);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
139
225
|
}
|
|
226
|
+
catch { /* scope dir unreadable */ }
|
|
140
227
|
}
|
|
141
|
-
|
|
228
|
+
else {
|
|
229
|
+
// Top-level package — any name works: wt-github, my-cool-plugin, etc.
|
|
230
|
+
const plugin = tryLoadPlugin(join(modulesDir, entry.name));
|
|
231
|
+
if (plugin && !seenPluginNames.has(plugin.name)) {
|
|
232
|
+
seenPluginNames.add(plugin.name);
|
|
233
|
+
plugins.push(plugin);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
// node_modules may not exist or be inaccessible
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return plugins;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Loads a specific plugin by name from node_modules.
|
|
246
|
+
*/
|
|
247
|
+
export function loadPluginByName(name, pluginDir) {
|
|
248
|
+
try {
|
|
249
|
+
const modulesDirs = getNodeModulesDirs(pluginDir);
|
|
250
|
+
for (const modulesDir of modulesDirs) {
|
|
251
|
+
// Check if it's a top-level package
|
|
252
|
+
let pkgDir = join(modulesDir, name);
|
|
253
|
+
if (existsSync(pkgDir)) {
|
|
254
|
+
const p = tryLoadPlugin(pkgDir);
|
|
255
|
+
if (p)
|
|
256
|
+
return p;
|
|
142
257
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const
|
|
146
|
-
if (
|
|
147
|
-
|
|
258
|
+
// Check if it's a scoped package (e.g. @weavetab/name)
|
|
259
|
+
if (name.startsWith("@")) {
|
|
260
|
+
const scopeParts = name.split("/");
|
|
261
|
+
if (scopeParts.length === 2) {
|
|
262
|
+
pkgDir = join(modulesDir, scopeParts[0], scopeParts[1]);
|
|
263
|
+
if (existsSync(pkgDir)) {
|
|
264
|
+
const p = tryLoadPlugin(pkgDir);
|
|
265
|
+
if (p)
|
|
266
|
+
return p;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
148
269
|
}
|
|
149
270
|
}
|
|
150
271
|
}
|
|
151
272
|
catch {
|
|
152
|
-
//
|
|
273
|
+
// Ignore
|
|
153
274
|
}
|
|
154
|
-
return
|
|
275
|
+
return null;
|
|
155
276
|
}
|