@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.
Files changed (98) hide show
  1. package/AGENTS.md +1 -0
  2. package/LICENSE +2 -2
  3. package/README.md +208 -43
  4. package/SECURITY.md +3 -3
  5. package/TROUBLESHOOTING.md +1 -1
  6. package/dist/cdp/bridge.d.ts +10 -0
  7. package/dist/cdp/bridge.js +288 -68
  8. package/dist/cdp/connector.d.ts +5 -3
  9. package/dist/cdp/connector.js +188 -74
  10. package/dist/cdp/extension.js +0 -1
  11. package/dist/cdp/helpers.js +0 -2
  12. package/dist/cdp/lock.d.ts +9 -6
  13. package/dist/cdp/lock.js +72 -59
  14. package/dist/cdp/omni_broker.d.ts +1 -0
  15. package/dist/cdp/omni_broker.js +12 -6
  16. package/dist/cdp/profiles.d.ts +10 -0
  17. package/dist/cdp/profiles.js +65 -0
  18. package/dist/cdp/walker.js +50 -0
  19. package/dist/cli/plugin.d.ts +0 -2
  20. package/dist/cli/plugin.js +31 -683
  21. package/dist/cli/updater.js +2 -2
  22. package/dist/config/loader.d.ts +25 -6
  23. package/dist/config/loader.js +133 -20
  24. package/dist/index.js +48 -52
  25. package/dist/lifecycle/mission_manager.d.ts +0 -1
  26. package/dist/lifecycle/mission_manager.js +0 -3
  27. package/dist/overlay/ghost.d.ts +4 -0
  28. package/dist/overlay/ghost.js +118 -94
  29. package/dist/overlay/injector.d.ts +1 -1
  30. package/dist/overlay/injector.js +1 -15
  31. package/dist/plugin/extension_bridge.d.ts +40 -0
  32. package/dist/plugin/extension_bridge.js +158 -0
  33. package/dist/plugin/interface.d.ts +78 -16
  34. package/dist/plugin/loader.d.ts +4 -0
  35. package/dist/plugin/loader.js +178 -57
  36. package/dist/plugin/registry.js +11 -1
  37. package/dist/plugin/storage.d.ts +51 -0
  38. package/dist/plugin/storage.js +164 -0
  39. package/dist/server.js +217 -151
  40. package/dist/state/session.d.ts +2 -2
  41. package/dist/state/session.js +33 -13
  42. package/dist/tools/browser_detect.d.ts +2 -1
  43. package/dist/tools/browser_detect.js +6 -5
  44. package/dist/tools/burst.d.ts +1 -0
  45. package/dist/tools/burst.js +4 -6
  46. package/dist/tools/click.js +23 -28
  47. package/dist/tools/clipboard.js +31 -24
  48. package/dist/tools/navigate.d.ts +1 -1
  49. package/dist/tools/navigate.js +28 -10
  50. package/dist/tools/read.js +1 -1
  51. package/dist/tools/tabs.d.ts +10 -10
  52. package/dist/tools/tabs.js +47 -39
  53. package/dist/tools/type.js +2 -2
  54. package/docs/DOCUMENTATION.md +63 -6
  55. package/docs/architecture/blind-injection.md +20 -0
  56. package/docs/architecture/dual-injection.md +32 -0
  57. package/docs/architecture/overview.md +49 -0
  58. package/docs/architecture/security.md +35 -0
  59. package/docs/architecture/transport.md +37 -0
  60. package/docs/captcha-stealth/captcha-solver.md +75 -0
  61. package/docs/captcha-stealth/stealth-physics.md +60 -0
  62. package/docs/cli/cli-commands.md +44 -0
  63. package/docs/cli/cli-flags.md +20 -0
  64. package/docs/cli/cli-introduction.md +20 -0
  65. package/docs/cli/cli-scripting.md +32 -0
  66. package/docs/cli/g-ide/ide-antigravity.md +24 -0
  67. package/docs/cli/g-ide/ide-claude-code.md +24 -0
  68. package/docs/cli/g-ide/ide-claude-desktop.md +32 -0
  69. package/docs/cli/g-ide/ide-cursor.md +31 -0
  70. package/docs/cli/g-ide/ide-generic.md +34 -0
  71. package/docs/cli/g-ide/ide-opencode.md +24 -0
  72. package/docs/cli/g-ide/ide-vscode-copilot.md +24 -0
  73. package/docs/cli/g-ide/ide-windsurf.md +24 -0
  74. package/docs/configuration/config-locations.md +24 -0
  75. package/docs/configuration/configuration.md +73 -0
  76. package/docs/configuration/env-variables.md +19 -0
  77. package/docs/configuration/folders.md +54 -0
  78. package/docs/desktop-automation/electron-apps.md +162 -0
  79. package/docs/get-started/installation.md +47 -0
  80. package/docs/get-started/introduction.md +60 -0
  81. package/docs/get-started/quickstart.md +54 -0
  82. package/docs/get-started/setup-wizard.md +26 -0
  83. package/docs/skills-ecosystem/skills-overview.md +35 -0
  84. package/docs/skills-ecosystem/skills-recipes.md +36 -0
  85. package/docs/tools/tools.md +293 -0
  86. package/docs/troubleshooting/trouble-browser.md +27 -0
  87. package/docs/troubleshooting/trouble-config.md +20 -0
  88. package/docs/troubleshooting/trouble-loop.md +31 -0
  89. package/ext-dist/content.js +126 -101
  90. package/mcp.json +8 -15
  91. package/package.json +11 -5
  92. package/server.json +3 -3
  93. package/dist/tools/plan.d.ts +0 -39
  94. package/dist/tools/plan.js +0 -161
  95. package/docs/DEV_MODE.md +0 -68
  96. package/docs/PLUGINS.md +0 -538
  97. package/docs/TOOLS.md +0 -104
  98. package/docs/dev_plugin.md +0 -310
@@ -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 WeavetabManifestPermissions {
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 (e.g. "images/logo.svg" or "images/logo.png") */
103
+ /** Path to plugin logo */
86
104
  logo?: string;
87
- /** Path to plugin header banner (e.g. "images/banner.png" or "images/banner.webp") */
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
- /** Runtime compatibility requirements */
96
- compatibility?: WeavetabCompatibility;
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?: WeavetabManifestPermissions;
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: string;
166
+ version?: string;
114
167
  description?: string;
115
168
  tools?: WeavetabPluginTool[];
116
169
  overrides?: WeavetabToolOverride[];
117
170
  removeTools?: string[];
118
- onLoad?: () => Promise<void>;
119
- onUnload?: () => Promise<void>;
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
- onLoad?: () => Promise<void>;
133
- onUnload?: () => Promise<void>;
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
  }
@@ -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;
@@ -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
- try {
43
- const modulesDir = pluginDir || join(process.cwd(), "node_modules");
44
- if (!existsSync(modulesDir))
45
- return results;
46
- const entries = readdirSync(modulesDir, { withFileTypes: true });
47
- for (const entry of entries) {
48
- if (!entry.isDirectory())
49
- continue;
50
- if (entry.name.startsWith("@")) {
51
- // Scoped packages (@weavetab/plugin-xyz, @scope/xyz)
52
- const scopeDir = join(modulesDir, entry.name);
53
- try {
54
- const scopeEntries = readdirSync(scopeDir, { withFileTypes: true });
55
- for (const scopeEntry of scopeEntries) {
56
- if (!scopeEntry.isDirectory())
57
- continue;
58
- const info = inspectInstalledPackage(join(scopeDir, scopeEntry.name));
59
- if (info)
60
- results.push(info);
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
- // Not a valid plugin — skip silently
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
- try {
121
- const modulesDir = pluginDir || join(process.cwd(), "node_modules");
122
- if (!existsSync(modulesDir))
123
- return plugins;
124
- const entries = readdirSync(modulesDir, { withFileTypes: true });
125
- for (const entry of entries) {
126
- if (!entry.isDirectory())
127
- continue;
128
- if (entry.name.startsWith("@")) {
129
- // Scoped package directory (e.g. @weavetab, @microsoft, @mycompany)
130
- const scopeDir = join(modulesDir, entry.name);
131
- try {
132
- const scopeEntries = readdirSync(scopeDir, { withFileTypes: true });
133
- for (const scopeEntry of scopeEntries) {
134
- if (!scopeEntry.isDirectory())
135
- continue;
136
- const plugin = tryLoadPlugin(join(scopeDir, scopeEntry.name));
137
- if (plugin)
138
- plugins.push(plugin);
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
- catch { /* scope dir unreadable */ }
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
- else {
144
- // Top-level package — any name works: wt-github, my-cool-plugin, etc.
145
- const plugin = tryLoadPlugin(join(modulesDir, entry.name));
146
- if (plugin)
147
- plugins.push(plugin);
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
- // node_modules may not exist or be inaccessible
273
+ // Ignore
153
274
  }
154
- return plugins;
275
+ return null;
155
276
  }
@@ -1,6 +1,16 @@
1
1
  import { readFileSync, existsSync } from "node:fs";
2
2
  import { join, dirname } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
+ import { createRequire } from "node:module";
5
+ const _require = createRequire(import.meta.url);
6
+ function getVersion() {
7
+ try {
8
+ return _require("../../package.json").version || "2.4.0";
9
+ }
10
+ catch {
11
+ return "2.4.0";
12
+ }
13
+ }
4
14
  const __filename = fileURLToPath(import.meta.url);
5
15
  const __dirname = dirname(__filename);
6
16
  // ---------------------------------------------------------------------------
@@ -142,7 +152,7 @@ export async function checkNpmPackageExists(pkgName) {
142
152
  const res = await fetch(`https://registry.npmjs.org/${encodedName}`, {
143
153
  method: "HEAD",
144
154
  signal: AbortSignal.timeout(3000),
145
- headers: { "User-Agent": "Weavetab-CLI/2.5.0" },
155
+ headers: { "User-Agent": `Weavetab/${getVersion()}` },
146
156
  });
147
157
  return res.status === 200;
148
158
  }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Interface for isolated plugin file and key-value storage.
3
+ */
4
+ export interface IPluginStorage {
5
+ get<T = any>(key: string, defaultValue?: T): Promise<T | null>;
6
+ set<T = any>(key: string, value: T): Promise<void>;
7
+ delete(key: string): Promise<boolean>;
8
+ getJSON<T = any>(filename: string): Promise<T | null>;
9
+ setJSON<T = any>(filename: string, data: T): Promise<void>;
10
+ getPath(subpath?: string): string;
11
+ list(subpath?: string): Promise<string[]>;
12
+ }
13
+ /**
14
+ * Creates an isolated storage sandbox for a plugin under:
15
+ * ~/.weavetab/plugins/<plugin-name>/
16
+ */
17
+ export declare class PluginStorageManager implements IPluginStorage {
18
+ readonly pluginName: string;
19
+ private readonly pluginDir;
20
+ private readonly configPath;
21
+ private readonly kvStorePath;
22
+ private inMemoryKV;
23
+ constructor(pluginName: string);
24
+ /**
25
+ * Returns the absolute path to the plugin's isolated directory.
26
+ */
27
+ getDirectory(): string;
28
+ /**
29
+ * Returns the absolute path to the plugin's config.json.
30
+ */
31
+ getConfigPath(): string;
32
+ private ensureDir;
33
+ /**
34
+ * Loads the plugin's isolated config.json. If it does not exist and a defaultConfig is provided,
35
+ * writes the default config to ~/.weavetab/plugins/<plugin-name>/config.json.
36
+ */
37
+ loadConfig(defaultConfig?: Record<string, any>): Record<string, any>;
38
+ /**
39
+ * Saves updated configuration to ~/.weavetab/plugins/<plugin-name>/config.json.
40
+ */
41
+ saveConfig(configData: Record<string, any>): void;
42
+ private loadKV;
43
+ private persistKV;
44
+ get<T = any>(key: string, defaultValue?: T): Promise<T | null>;
45
+ set<T = any>(key: string, value: T): Promise<void>;
46
+ delete(key: string): Promise<boolean>;
47
+ getPath(subpath?: string): string;
48
+ getJSON<T = any>(filename: string): Promise<T | null>;
49
+ setJSON<T = any>(filename: string, data: T): Promise<void>;
50
+ list(subpath?: string): Promise<string[]>;
51
+ }