@weavetab/mcp 2.5.0-beta.0
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 +48 -0
- package/ARCHITECTURE.md +590 -0
- package/LICENSE +631 -0
- package/README.md +909 -0
- package/SECURITY.md +196 -0
- package/TROUBLESHOOTING.md +365 -0
- package/dist/anticaptcha/detector.d.ts +49 -0
- package/dist/anticaptcha/detector.js +465 -0
- package/dist/anticaptcha/integration.d.ts +36 -0
- package/dist/anticaptcha/integration.js +123 -0
- package/dist/anticaptcha/solver.d.ts +27 -0
- package/dist/anticaptcha/solver.js +552 -0
- package/dist/audit/logger.d.ts +13 -0
- package/dist/audit/logger.js +177 -0
- package/dist/audit/telemetry.d.ts +36 -0
- package/dist/audit/telemetry.js +162 -0
- package/dist/cdp/bridge.d.ts +16 -0
- package/dist/cdp/bridge.js +826 -0
- package/dist/cdp/confirm.d.ts +26 -0
- package/dist/cdp/confirm.js +79 -0
- package/dist/cdp/connector.d.ts +37 -0
- package/dist/cdp/connector.js +661 -0
- package/dist/cdp/extension.d.ts +36 -0
- package/dist/cdp/extension.js +118 -0
- package/dist/cdp/finder.d.ts +1 -0
- package/dist/cdp/finder.js +2 -0
- package/dist/cdp/helpers.d.ts +11 -0
- package/dist/cdp/helpers.js +195 -0
- package/dist/cdp/label_cache.d.ts +12 -0
- package/dist/cdp/label_cache.js +51 -0
- package/dist/cdp/lock.d.ts +33 -0
- package/dist/cdp/lock.js +206 -0
- package/dist/cdp/omni_broker.d.ts +17 -0
- package/dist/cdp/omni_broker.js +119 -0
- package/dist/cdp/profiles.d.ts +1 -0
- package/dist/cdp/profiles.js +87 -0
- package/dist/cdp/pruner.d.ts +41 -0
- package/dist/cdp/pruner.js +263 -0
- package/dist/cdp/ref_cache.d.ts +101 -0
- package/dist/cdp/ref_cache.js +234 -0
- package/dist/cdp/retry.d.ts +5 -0
- package/dist/cdp/retry.js +39 -0
- package/dist/cdp/retry_enhanced.d.ts +44 -0
- package/dist/cdp/retry_enhanced.js +102 -0
- package/dist/cdp/security.d.ts +50 -0
- package/dist/cdp/security.js +237 -0
- package/dist/cdp/verify.d.ts +39 -0
- package/dist/cdp/verify.js +143 -0
- package/dist/cdp/walker.d.ts +91 -0
- package/dist/cdp/walker.js +896 -0
- package/dist/cdp/walker.test.d.ts +2 -0
- package/dist/cdp/walker.test.js +172 -0
- package/dist/cdp/websocket_transport.d.ts +15 -0
- package/dist/cdp/websocket_transport.js +118 -0
- package/dist/cli/plugin.d.ts +9 -0
- package/dist/cli/plugin.js +1134 -0
- package/dist/cli/updater.d.ts +1 -0
- package/dist/cli/updater.js +72 -0
- package/dist/config/loader.d.ts +120 -0
- package/dist/config/loader.js +704 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1248 -0
- package/dist/intelligence/hints.d.ts +11 -0
- package/dist/intelligence/hints.js +39 -0
- package/dist/intelligence/intent.d.ts +33 -0
- package/dist/intelligence/intent.js +171 -0
- package/dist/intelligence/sessionMemory.d.ts +66 -0
- package/dist/intelligence/sessionMemory.js +284 -0
- package/dist/intelligence/sessionMemory.test.d.ts +2 -0
- package/dist/intelligence/sessionMemory.test.js +62 -0
- package/dist/intelligence/trail.d.ts +64 -0
- package/dist/intelligence/trail.js +233 -0
- package/dist/intelligence/trail.test.d.ts +2 -0
- package/dist/intelligence/trail.test.js +110 -0
- package/dist/intelligence/vision.d.ts +24 -0
- package/dist/intelligence/vision.js +151 -0
- package/dist/lifecycle/mission_manager.d.ts +16 -0
- package/dist/lifecycle/mission_manager.js +44 -0
- package/dist/overlay/bubble.d.ts +16 -0
- package/dist/overlay/bubble.js +37 -0
- package/dist/overlay/ghost.d.ts +35 -0
- package/dist/overlay/ghost.js +508 -0
- package/dist/overlay/injector.d.ts +22 -0
- package/dist/overlay/injector.js +198 -0
- package/dist/plugin/interface.d.ts +134 -0
- package/dist/plugin/interface.js +1 -0
- package/dist/plugin/loader.d.ts +18 -0
- package/dist/plugin/loader.js +155 -0
- package/dist/plugin/registry.d.ts +50 -0
- package/dist/plugin/registry.js +234 -0
- package/dist/security/blacklist.d.ts +24 -0
- package/dist/security/blacklist.js +80 -0
- package/dist/security/filter.d.ts +2 -0
- package/dist/security/filter.js +20 -0
- package/dist/security/guard.d.ts +8 -0
- package/dist/security/guard.js +35 -0
- package/dist/security/ratelimit.d.ts +11 -0
- package/dist/security/ratelimit.js +57 -0
- package/dist/security/secrets.d.ts +75 -0
- package/dist/security/secrets.js +365 -0
- package/dist/sensors/actionability.d.ts +81 -0
- package/dist/sensors/actionability.js +303 -0
- package/dist/sensors/guard.d.ts +25 -0
- package/dist/sensors/guard.js +168 -0
- package/dist/sensors/mutations.d.ts +23 -0
- package/dist/sensors/mutations.js +93 -0
- package/dist/sensors/network.d.ts +67 -0
- package/dist/sensors/network.js +193 -0
- package/dist/sensors/operation_monitor.d.ts +80 -0
- package/dist/sensors/operation_monitor.js +203 -0
- package/dist/sensors/pacing.d.ts +44 -0
- package/dist/sensors/pacing.js +111 -0
- package/dist/sensors/thoughts.d.ts +2 -0
- package/dist/sensors/thoughts.js +59 -0
- package/dist/sensors/wait.d.ts +12 -0
- package/dist/sensors/wait.js +44 -0
- package/dist/server.d.ts +7 -0
- package/dist/server.js +1687 -0
- package/dist/state/agent.d.ts +36 -0
- package/dist/state/agent.js +101 -0
- package/dist/state/cache.d.ts +16 -0
- package/dist/state/cache.js +92 -0
- package/dist/state/cache.test.d.ts +2 -0
- package/dist/state/cache.test.js +76 -0
- package/dist/state/delta.d.ts +71 -0
- package/dist/state/delta.js +234 -0
- package/dist/state/delta.test.d.ts +2 -0
- package/dist/state/delta.test.js +279 -0
- package/dist/state/groups.d.ts +15 -0
- package/dist/state/groups.js +80 -0
- package/dist/state/session.d.ts +30 -0
- package/dist/state/session.js +155 -0
- package/dist/tools/accessibility.d.ts +14 -0
- package/dist/tools/accessibility.js +32 -0
- package/dist/tools/ask.d.ts +14 -0
- package/dist/tools/ask.js +49 -0
- package/dist/tools/automation.d.ts +36 -0
- package/dist/tools/automation.js +218 -0
- package/dist/tools/browser_detect.d.ts +13 -0
- package/dist/tools/browser_detect.js +73 -0
- package/dist/tools/burst.d.ts +35 -0
- package/dist/tools/burst.js +344 -0
- package/dist/tools/canvas.d.ts +25 -0
- package/dist/tools/canvas.js +132 -0
- package/dist/tools/captcha.d.ts +38 -0
- package/dist/tools/captcha.js +209 -0
- package/dist/tools/click.d.ts +36 -0
- package/dist/tools/click.js +561 -0
- package/dist/tools/click_and_wait.d.ts +33 -0
- package/dist/tools/click_and_wait.js +149 -0
- package/dist/tools/clipboard.d.ts +29 -0
- package/dist/tools/clipboard.js +279 -0
- package/dist/tools/console.d.ts +21 -0
- package/dist/tools/console.js +107 -0
- package/dist/tools/cookies.d.ts +67 -0
- package/dist/tools/cookies.js +121 -0
- package/dist/tools/design_extract.d.ts +109 -0
- package/dist/tools/design_extract.js +641 -0
- package/dist/tools/dialog.d.ts +24 -0
- package/dist/tools/dialog.js +109 -0
- package/dist/tools/drag.d.ts +15 -0
- package/dist/tools/drag.js +29 -0
- package/dist/tools/emulation.d.ts +33 -0
- package/dist/tools/emulation.js +69 -0
- package/dist/tools/eval.d.ts +20 -0
- package/dist/tools/eval.js +92 -0
- package/dist/tools/fill.d.ts +41 -0
- package/dist/tools/fill.js +667 -0
- package/dist/tools/find.d.ts +30 -0
- package/dist/tools/find.js +60 -0
- package/dist/tools/geo.d.ts +19 -0
- package/dist/tools/geo.js +29 -0
- package/dist/tools/github/analyze.d.ts +4 -0
- package/dist/tools/github/analyze.js +113 -0
- package/dist/tools/github/cache.d.ts +21 -0
- package/dist/tools/github/cache.js +28 -0
- package/dist/tools/github/index.d.ts +4 -0
- package/dist/tools/github/index.js +4 -0
- package/dist/tools/github/issues.d.ts +9 -0
- package/dist/tools/github/issues.js +88 -0
- package/dist/tools/github/pr.d.ts +5 -0
- package/dist/tools/github/pr.js +88 -0
- package/dist/tools/github/rateLimiter.d.ts +4 -0
- package/dist/tools/github/rateLimiter.js +46 -0
- package/dist/tools/github/read.d.ts +34 -0
- package/dist/tools/github/read.js +74 -0
- package/dist/tools/github/repoContext.d.ts +20 -0
- package/dist/tools/github/repoContext.js +129 -0
- package/dist/tools/highlight.d.ts +20 -0
- package/dist/tools/highlight.js +140 -0
- package/dist/tools/hover.d.ts +19 -0
- package/dist/tools/hover.js +57 -0
- package/dist/tools/inspect.d.ts +17 -0
- package/dist/tools/inspect.js +125 -0
- package/dist/tools/integration.test.d.ts +2 -0
- package/dist/tools/integration.test.js +96 -0
- package/dist/tools/key.d.ts +25 -0
- package/dist/tools/key.js +173 -0
- package/dist/tools/macro_compiler.d.ts +7 -0
- package/dist/tools/macro_compiler.js +82 -0
- package/dist/tools/mouse.d.ts +21 -0
- package/dist/tools/mouse.js +62 -0
- package/dist/tools/navigate.d.ts +16 -0
- package/dist/tools/navigate.js +188 -0
- package/dist/tools/network_intercept.d.ts +17 -0
- package/dist/tools/network_intercept.js +151 -0
- package/dist/tools/office/index.d.ts +9 -0
- package/dist/tools/office/index.js +7 -0
- package/dist/tools/office/list.d.ts +15 -0
- package/dist/tools/office/list.js +41 -0
- package/dist/tools/office/parse.d.ts +20 -0
- package/dist/tools/office/parse.js +172 -0
- package/dist/tools/pdf.d.ts +22 -0
- package/dist/tools/pdf.js +42 -0
- package/dist/tools/performance.d.ts +12 -0
- package/dist/tools/performance.js +47 -0
- package/dist/tools/performance_test.d.ts +70 -0
- package/dist/tools/performance_test.js +128 -0
- package/dist/tools/plan.d.ts +39 -0
- package/dist/tools/plan.js +161 -0
- package/dist/tools/pointer.d.ts +35 -0
- package/dist/tools/pointer.js +522 -0
- package/dist/tools/read.d.ts +30 -0
- package/dist/tools/read.js +488 -0
- package/dist/tools/recording.d.ts +45 -0
- package/dist/tools/recording.js +124 -0
- package/dist/tools/response.d.ts +129 -0
- package/dist/tools/response.js +5 -0
- package/dist/tools/scrape.d.ts +24 -0
- package/dist/tools/scrape.js +140 -0
- package/dist/tools/screenshot.d.ts +40 -0
- package/dist/tools/screenshot.js +79 -0
- package/dist/tools/scroll.d.ts +23 -0
- package/dist/tools/scroll.js +196 -0
- package/dist/tools/select.d.ts +13 -0
- package/dist/tools/select.js +217 -0
- package/dist/tools/snapshot.d.ts +27 -0
- package/dist/tools/snapshot.js +99 -0
- package/dist/tools/storage.d.ts +18 -0
- package/dist/tools/storage.js +29 -0
- package/dist/tools/tabs.d.ts +77 -0
- package/dist/tools/tabs.js +342 -0
- package/dist/tools/thought.d.ts +16 -0
- package/dist/tools/thought.js +79 -0
- package/dist/tools/type.d.ts +65 -0
- package/dist/tools/type.js +1061 -0
- package/dist/tools/type_secret.d.ts +23 -0
- package/dist/tools/type_secret.js +229 -0
- package/dist/tools/upload.d.ts +19 -0
- package/dist/tools/upload.js +103 -0
- package/dist/tools/video_compiler.d.ts +24 -0
- package/dist/tools/video_compiler.js +199 -0
- package/dist/tools/viewport.d.ts +44 -0
- package/dist/tools/viewport.js +230 -0
- package/dist/tools/wait.d.ts +19 -0
- package/dist/tools/wait.js +202 -0
- package/dist/tools/wait_for.d.ts +23 -0
- package/dist/tools/wait_for.js +30 -0
- package/dist/tools/windows.d.ts +22 -0
- package/dist/tools/windows.js +57 -0
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.js +12 -0
- package/docs/DEV_MODE.md +68 -0
- package/docs/DOCUMENTATION.md +532 -0
- package/docs/PLUGINS.md +538 -0
- package/docs/TOOLS.md +104 -0
- package/docs/dev_plugin.md +310 -0
- package/ext-dist/background.js +467 -0
- package/ext-dist/content.js +3089 -0
- package/ext-dist/dashboard.html +583 -0
- package/ext-dist/hud/hud.js +73 -0
- package/ext-dist/hud/index.html +20 -0
- package/ext-dist/manifest.json +77 -0
- package/ext-dist/sidepanel/sidepanel.js +25 -0
- package/ext-dist/sidepanel.html +547 -0
- package/ext-dist/styles.css +218 -0
- package/icon.png +0 -0
- package/mcp.json +796 -0
- package/package.json +113 -0
- package/server.json +23 -0
- package/smithery.yaml +11 -0
|
@@ -0,0 +1,1134 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import pc from "picocolors";
|
|
3
|
+
import boxen from "boxen";
|
|
4
|
+
import ora from "ora";
|
|
5
|
+
import fs from "node:fs/promises";
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { pathToFileURL } from "node:url";
|
|
9
|
+
import { spawn } from "node:child_process";
|
|
10
|
+
import { getVerifiedPackages, resolvePluginTrust, resolvePluginCandidates, REGISTRY_URL, } from "../plugin/registry.js";
|
|
11
|
+
import { listInstalledPlugins, inspectInstalledPackage } from "../plugin/loader.js";
|
|
12
|
+
// Helper to run child process safely
|
|
13
|
+
function runCommand(cmd, cmdArgs, cwd = process.cwd()) {
|
|
14
|
+
return new Promise((resolve) => {
|
|
15
|
+
const isWin = process.platform === "win32";
|
|
16
|
+
const executable = isWin && !cmd.endsWith(".cmd") && !cmd.endsWith(".exe") ? `${cmd}.cmd` : cmd;
|
|
17
|
+
const child = spawn(executable, cmdArgs, {
|
|
18
|
+
cwd,
|
|
19
|
+
shell: isWin,
|
|
20
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
21
|
+
});
|
|
22
|
+
let stdout = "";
|
|
23
|
+
let stderr = "";
|
|
24
|
+
child.stdout?.on("data", (d) => { stdout += d.toString(); });
|
|
25
|
+
child.stderr?.on("data", (d) => { stderr += d.toString(); });
|
|
26
|
+
child.on("close", (code) => {
|
|
27
|
+
resolve({ code: code ?? 0, stdout, stderr });
|
|
28
|
+
});
|
|
29
|
+
child.on("error", (err) => {
|
|
30
|
+
resolve({ code: 1, stdout, stderr: err.message });
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function runNpmCommand(cmdArgs, cwd = process.cwd()) {
|
|
35
|
+
return runCommand("npm", cmdArgs, cwd);
|
|
36
|
+
}
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// 1. ADD / INSTALL PLUGIN WITH DISAMBIGUATION
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
export async function runPluginAdd(inputName, args = []) {
|
|
41
|
+
if (!inputName || inputName.startsWith("-")) {
|
|
42
|
+
console.log(pc.red("[ERROR] Please provide a package name to install."));
|
|
43
|
+
console.log(pc.dim("Usage: wt plugin add <packageName> [--official | --community] [--yes]"));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const isForce = args.includes("--yes") || args.includes("-y") || args.includes("--force");
|
|
47
|
+
const wantOfficial = args.includes("--official") || args.includes("-O");
|
|
48
|
+
const wantCommunity = args.includes("--community") || args.includes("-C");
|
|
49
|
+
const verifiedPackages = await getVerifiedPackages();
|
|
50
|
+
const resolveSpinner = ora(`Resolving package candidates for ${pc.cyan(inputName)}...`).start();
|
|
51
|
+
const candidates = await resolvePluginCandidates(inputName, verifiedPackages);
|
|
52
|
+
resolveSpinner.stop();
|
|
53
|
+
let selectedPkg = inputName;
|
|
54
|
+
let selectedCandidate;
|
|
55
|
+
// Disambiguation Flow
|
|
56
|
+
if (candidates.length > 1) {
|
|
57
|
+
if (wantOfficial) {
|
|
58
|
+
selectedCandidate = candidates.find((c) => c.isOfficialShortcut) || candidates[0];
|
|
59
|
+
selectedPkg = selectedCandidate.packageName;
|
|
60
|
+
}
|
|
61
|
+
else if (wantCommunity) {
|
|
62
|
+
selectedCandidate = candidates.find((c) => !c.isOfficialShortcut) || candidates[0];
|
|
63
|
+
selectedPkg = selectedCandidate.packageName;
|
|
64
|
+
}
|
|
65
|
+
else if (process.stdin.isTTY) {
|
|
66
|
+
console.log();
|
|
67
|
+
console.log(boxen(`${pc.bold(pc.cyan("Multiple plugins match"))} ${pc.yellow(`"${inputName}"`)}\n` +
|
|
68
|
+
pc.dim("Select which package you want to install:"), { padding: 1, borderStyle: "round", borderColor: "cyan" }));
|
|
69
|
+
const choice = await p.select({
|
|
70
|
+
message: "Choose package to install:",
|
|
71
|
+
options: candidates.map((c) => {
|
|
72
|
+
const tierTag = c.tier === "official"
|
|
73
|
+
? pc.bgBlue(pc.white(" Official "))
|
|
74
|
+
: c.tier === "verified"
|
|
75
|
+
? pc.bgGreen(pc.black(" Verified "))
|
|
76
|
+
: pc.bgYellow(pc.black(" Unverified "));
|
|
77
|
+
return {
|
|
78
|
+
value: c.packageName,
|
|
79
|
+
label: `${tierTag} ${pc.bold(c.packageName)}`,
|
|
80
|
+
hint: c.tooltip,
|
|
81
|
+
};
|
|
82
|
+
}),
|
|
83
|
+
});
|
|
84
|
+
if (p.isCancel(choice)) {
|
|
85
|
+
p.cancel("Installation aborted.");
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
selectedPkg = choice;
|
|
89
|
+
selectedCandidate = candidates.find((c) => c.packageName === selectedPkg);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
// Non-interactive fallback: Prefer official shortcut if available
|
|
93
|
+
selectedCandidate = candidates.find((c) => c.isOfficialShortcut) || candidates[0];
|
|
94
|
+
selectedPkg = selectedCandidate.packageName;
|
|
95
|
+
console.log(pc.dim(`[AUTO] Selected candidate: ${selectedPkg}`));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else if (candidates.length === 1) {
|
|
99
|
+
selectedCandidate = candidates[0];
|
|
100
|
+
selectedPkg = selectedCandidate.packageName;
|
|
101
|
+
}
|
|
102
|
+
const trust = selectedCandidate
|
|
103
|
+
? { tier: selectedCandidate.tier, label: selectedCandidate.label, badge: selectedCandidate.badge, tooltip: selectedCandidate.tooltip }
|
|
104
|
+
: resolvePluginTrust(selectedPkg, verifiedPackages);
|
|
105
|
+
console.log();
|
|
106
|
+
console.log(boxen(pc.bold(pc.cyan(" WEAVETAB PLUGIN INSTALL ")), { padding: 0, borderStyle: "round", borderColor: "cyan" }));
|
|
107
|
+
console.log();
|
|
108
|
+
// Trust Resolution & Security Guard
|
|
109
|
+
if (trust.tier === "official") {
|
|
110
|
+
console.log(`${pc.bgBlue(pc.white(" OFFICIAL "))} ${pc.green(selectedPkg)}`);
|
|
111
|
+
console.log(pc.dim(" Author: Weavetab Core Team"));
|
|
112
|
+
console.log(pc.dim(" Security: Audited and cryptographically trusted first-party package.\n"));
|
|
113
|
+
}
|
|
114
|
+
else if (trust.tier === "verified") {
|
|
115
|
+
console.log(`${pc.bgGreen(pc.black(" VERIFIED "))} ${pc.green(selectedPkg)}`);
|
|
116
|
+
console.log(pc.dim(" Status: Listed in Weavetab Community Verified Registry."));
|
|
117
|
+
console.log(pc.dim(" Security: Reviewed by Weavetab maintainers.\n"));
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
// Unverified Tier: Show prominent warning banner
|
|
121
|
+
const warningMessage = [
|
|
122
|
+
pc.bold(pc.yellow("⚠️ SECURITY WARNING: UNVERIFIED COMMUNITY PLUGIN")),
|
|
123
|
+
"",
|
|
124
|
+
`Package ${pc.bold(pc.yellow(`"${selectedPkg}"`))} is NOT verified by Weavetab.`,
|
|
125
|
+
pc.dim("It is an unvetted third-party package and may potentially contain:"),
|
|
126
|
+
` • ${pc.red("Arbitrary code execution")} during installation or runtime`,
|
|
127
|
+
` • ${pc.red("CDP browser control")} (evaluating scripts, stealing session cookies)`,
|
|
128
|
+
` • ${pc.red("Network or File System access")} (exfiltrating sensitive credentials)`,
|
|
129
|
+
"",
|
|
130
|
+
pc.bold(pc.yellow("Install and run this plugin strictly at your own risk!")),
|
|
131
|
+
].join("\n");
|
|
132
|
+
console.log(boxen(warningMessage, {
|
|
133
|
+
padding: 1,
|
|
134
|
+
borderStyle: "double",
|
|
135
|
+
borderColor: "yellow",
|
|
136
|
+
}));
|
|
137
|
+
console.log();
|
|
138
|
+
if (!isForce) {
|
|
139
|
+
if (!process.stdin.isTTY) {
|
|
140
|
+
console.log(pc.red("[ERROR] Unverified plugin cannot be installed in non-interactive mode without --yes flag."));
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const proceed = await p.confirm({
|
|
144
|
+
message: pc.yellow(`Do you want to proceed with installing unverified package "${selectedPkg}"?`),
|
|
145
|
+
initialValue: false,
|
|
146
|
+
});
|
|
147
|
+
if (p.isCancel(proceed) || !proceed) {
|
|
148
|
+
p.cancel("Installation aborted for safety.");
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Run npm install
|
|
154
|
+
const spinner = ora(`Installing ${pc.cyan(selectedPkg)} via npm...`).start();
|
|
155
|
+
const installResult = await runNpmCommand(["install", selectedPkg]);
|
|
156
|
+
if (installResult.code !== 0) {
|
|
157
|
+
spinner.fail(pc.red(`Failed to install ${selectedPkg}`));
|
|
158
|
+
if (installResult.stderr) {
|
|
159
|
+
console.error(pc.dim(installResult.stderr));
|
|
160
|
+
}
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
spinner.succeed(pc.green(`Successfully installed ${pc.bold(selectedPkg)}`));
|
|
164
|
+
// Inspect installed package for weavetab.json
|
|
165
|
+
const targetDir = path.join(process.cwd(), "node_modules", selectedPkg);
|
|
166
|
+
const installedInfo = inspectInstalledPackage(targetDir);
|
|
167
|
+
if (installedInfo && installedInfo.manifest) {
|
|
168
|
+
const manifest = installedInfo.manifest;
|
|
169
|
+
const tools = manifest.tools || [];
|
|
170
|
+
const permissions = manifest.permissions || {};
|
|
171
|
+
const infoLines = [
|
|
172
|
+
`${pc.bold("Plugin Details:")}`,
|
|
173
|
+
` ${pc.dim("Name:")} ${manifest.name || selectedPkg}`,
|
|
174
|
+
` ${pc.dim("Version:")} ${manifest.version || "1.0.0"}`,
|
|
175
|
+
` ${pc.dim("Author:")} ${manifest.author || "Community"}`,
|
|
176
|
+
` ${pc.dim("Trust Tier:")} ${trust.badge}`,
|
|
177
|
+
` ${pc.dim("Description:")} ${manifest.description || "No description provided"}`,
|
|
178
|
+
];
|
|
179
|
+
if (tools.length > 0) {
|
|
180
|
+
infoLines.push("", `${pc.bold(`Exposed Tools (${tools.length}):`)}`);
|
|
181
|
+
for (const tool of tools) {
|
|
182
|
+
infoLines.push(` • ${pc.cyan(tool.name)}: ${pc.dim(tool.description || "")}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const permsList = [];
|
|
186
|
+
if (permissions.network)
|
|
187
|
+
permsList.push(`Network (${permissions.network.reason || "unspecified"})`);
|
|
188
|
+
if (permissions.cdp)
|
|
189
|
+
permsList.push(`CDP (${permissions.cdp.reason || "unspecified"})`);
|
|
190
|
+
if (permissions.fs)
|
|
191
|
+
permsList.push(`FileSystem (${permissions.fs.reason || "unspecified"})`);
|
|
192
|
+
if (permissions.tools?.override?.length) {
|
|
193
|
+
permsList.push(`Tool Overrides: [${permissions.tools.override.join(", ")}] (${permissions.tools.reason || "unspecified"})`);
|
|
194
|
+
}
|
|
195
|
+
if (permissions.tools?.remove?.length) {
|
|
196
|
+
permsList.push(`Tool Removals: [${permissions.tools.remove.join(", ")}] (${permissions.tools.reason || "unspecified"})`);
|
|
197
|
+
}
|
|
198
|
+
if (permsList.length > 0) {
|
|
199
|
+
infoLines.push("", `${pc.bold("Requested Permissions & Modifications:")}`);
|
|
200
|
+
for (const perm of permsList) {
|
|
201
|
+
infoLines.push(` • ${pc.yellow(perm)}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
console.log();
|
|
205
|
+
console.log(boxen(infoLines.join("\n"), { padding: 1, borderStyle: "round", borderColor: "green" }));
|
|
206
|
+
console.log();
|
|
207
|
+
console.log(pc.green("✔ Plugin is ready to use! Restart Weavetab MCP server to activate changes.\n"));
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
console.log();
|
|
211
|
+
console.log(boxen(`${pc.yellow("⚠️ NOTE: No 'weavetab.json' manifest was detected inside this package.")}\n` +
|
|
212
|
+
pc.dim("Weavetab requires a valid weavetab.json manifest to automatically register tools.\n") +
|
|
213
|
+
pc.dim(`If you are the author, run ${pc.cyan("wt plugin init")} in the package directory to generate one.`), { padding: 1, borderStyle: "round", borderColor: "yellow" }));
|
|
214
|
+
console.log();
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
// 2. LIST INSTALLED PLUGINS
|
|
219
|
+
// ---------------------------------------------------------------------------
|
|
220
|
+
export async function runPluginList() {
|
|
221
|
+
const verifiedPackages = await getVerifiedPackages();
|
|
222
|
+
const plugins = listInstalledPlugins();
|
|
223
|
+
console.clear();
|
|
224
|
+
console.log(boxen(pc.bold(pc.cyan(" INSTALLED WEAVETAB PLUGINS ")), { padding: 0, borderStyle: "round", borderColor: "cyan" }));
|
|
225
|
+
console.log();
|
|
226
|
+
if (plugins.length === 0) {
|
|
227
|
+
console.log(pc.dim("No Weavetab plugins found in the local node_modules directory."));
|
|
228
|
+
console.log(pc.dim(`Install official or community plugins using: ${pc.cyan("wt plugin add <packageName>")}\n`));
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
console.log(pc.dim(`Found ${plugins.length} installed plugin(s):\n`));
|
|
232
|
+
for (const pInfo of plugins) {
|
|
233
|
+
const trust = resolvePluginTrust(pInfo.packageName, verifiedPackages);
|
|
234
|
+
const m = pInfo.manifest;
|
|
235
|
+
const toolCount = m.tools?.length ?? 0;
|
|
236
|
+
console.log(`${trust.badge} ${pc.bold(pc.cyan(pInfo.packageName))} ${pc.dim(`v${m.version || "1.0.0"}`)}`);
|
|
237
|
+
console.log(` ${pc.dim("Author:")} ${m.author || "Unknown"}`);
|
|
238
|
+
console.log(` ${pc.dim("Description:")} ${m.description || "(none)"}`);
|
|
239
|
+
console.log(` ${pc.dim("Tools:")} ${toolCount} tool(s) registered`);
|
|
240
|
+
if (m.tools && m.tools.length > 0) {
|
|
241
|
+
const toolNames = m.tools.map((t) => pc.green(t.name)).join(", ");
|
|
242
|
+
console.log(` ${pc.dim("Tool Names:")} ${toolNames}`);
|
|
243
|
+
}
|
|
244
|
+
console.log();
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
// ---------------------------------------------------------------------------
|
|
248
|
+
// 3. SEARCH PLUGINS (OFFICIAL & VERIFIED REGISTRY + NPM)
|
|
249
|
+
// ---------------------------------------------------------------------------
|
|
250
|
+
export async function runPluginSearch(query) {
|
|
251
|
+
const q = (query || "").toLowerCase().trim();
|
|
252
|
+
const spinner = ora(`Searching Weavetab plugin registry for "${pc.cyan(q || "*")}"...`).start();
|
|
253
|
+
const verifiedPackages = await getVerifiedPackages();
|
|
254
|
+
const results = [];
|
|
255
|
+
// 1. Search verified registry
|
|
256
|
+
for (const pkgName of verifiedPackages) {
|
|
257
|
+
if (!q || pkgName.toLowerCase().includes(q)) {
|
|
258
|
+
results.push({
|
|
259
|
+
name: pkgName,
|
|
260
|
+
tier: "verified",
|
|
261
|
+
badge: pc.bgGreen(pc.black(" Verified ")),
|
|
262
|
+
description: "Community plugin listed in verified registry",
|
|
263
|
+
author: "Community",
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
// 2. Query npm search
|
|
268
|
+
try {
|
|
269
|
+
const searchQuery = q ? `weavetab ${q}` : "weavetab plugin";
|
|
270
|
+
const res = await fetch(`https://registry.npmjs.org/-/v1/search?text=${encodeURIComponent(searchQuery)}&size=15`, {
|
|
271
|
+
signal: AbortSignal.timeout(3500),
|
|
272
|
+
headers: { "User-Agent": "Weavetab-CLI/2.5.0" },
|
|
273
|
+
});
|
|
274
|
+
if (res.ok) {
|
|
275
|
+
const data = (await res.json());
|
|
276
|
+
if (Array.isArray(data.objects)) {
|
|
277
|
+
for (const item of data.objects) {
|
|
278
|
+
const pkg = item.package;
|
|
279
|
+
if (!pkg || results.some((r) => r.name === pkg.name))
|
|
280
|
+
continue;
|
|
281
|
+
const trust = resolvePluginTrust(pkg.name, verifiedPackages);
|
|
282
|
+
results.push({
|
|
283
|
+
name: pkg.name,
|
|
284
|
+
tier: trust.tier,
|
|
285
|
+
badge: trust.tier === "official"
|
|
286
|
+
? pc.bgBlue(pc.white(" Official "))
|
|
287
|
+
: trust.tier === "verified"
|
|
288
|
+
? pc.bgGreen(pc.black(" Verified "))
|
|
289
|
+
: pc.bgYellow(pc.black(" Unverified ")),
|
|
290
|
+
description: pkg.description || "No description provided",
|
|
291
|
+
author: pkg.publisher?.username || pkg.author?.name || "Community",
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
catch { /* offline / network error */ }
|
|
298
|
+
spinner.stop();
|
|
299
|
+
console.log();
|
|
300
|
+
console.log(boxen(pc.bold(pc.cyan(` PLUGIN SEARCH RESULTS: "${q || "all"}" `)), { padding: 0, borderStyle: "round", borderColor: "cyan" }));
|
|
301
|
+
console.log();
|
|
302
|
+
if (results.length === 0) {
|
|
303
|
+
console.log(pc.yellow(`No plugins matching "${q}" found.`));
|
|
304
|
+
console.log(pc.dim(`Try browsing the registry or visit ${pc.cyan("https://weavetab.dev/plugins")}\n`));
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
console.log(pc.dim(`Found ${results.length} matching plugin(s):\n`));
|
|
308
|
+
for (const item of results) {
|
|
309
|
+
console.log(`${item.badge} ${pc.bold(pc.cyan(item.name))} ${pc.dim(`by ${item.author}`)}`);
|
|
310
|
+
console.log(` ${pc.dim(item.description)}`);
|
|
311
|
+
console.log(` ${pc.dim("Install:")} ${pc.green(`wt plugin add ${item.name}`)}`);
|
|
312
|
+
console.log();
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
// ---------------------------------------------------------------------------
|
|
316
|
+
// 4. BROWSE & ONE-CLICK INSTALL (TERMINAL MARKETPLACE)
|
|
317
|
+
// ---------------------------------------------------------------------------
|
|
318
|
+
export async function runPluginBrowse() {
|
|
319
|
+
console.clear();
|
|
320
|
+
p.intro(`${pc.bgCyan(pc.black(" Weavetab Plugins "))} ${pc.cyan("Interactive Plugin Explorer")}`);
|
|
321
|
+
const spinner = p.spinner();
|
|
322
|
+
spinner.start("Fetching plugin registry...");
|
|
323
|
+
const verifiedPackages = await getVerifiedPackages();
|
|
324
|
+
const installedList = listInstalledPlugins();
|
|
325
|
+
const installedSet = new Set(installedList.map((i) => i.packageName));
|
|
326
|
+
const items = [];
|
|
327
|
+
for (const pkg of verifiedPackages) {
|
|
328
|
+
const isInstalled = installedSet.has(pkg);
|
|
329
|
+
const status = isInstalled ? pc.green(" [Installed]") : "";
|
|
330
|
+
items.push({
|
|
331
|
+
value: pkg,
|
|
332
|
+
label: `${pc.bgGreen(pc.black(" Verified "))} ${pc.bold(pkg)}${status}`,
|
|
333
|
+
hint: isInstalled ? "Already installed. Select to reinstall or inspect." : "Verified community plugin. Press Enter to install.",
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
spinner.stop(`Found ${items.length} verified plugin(s) in registry.`);
|
|
337
|
+
if (items.length === 0) {
|
|
338
|
+
p.note("No plugins currently in registry.", "Weavetab Plugins");
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
items.push({
|
|
342
|
+
value: "__search__",
|
|
343
|
+
label: `${pc.cyan("🔍 Search for community plugins on npm...")}`,
|
|
344
|
+
hint: "Search for third-party community plugins",
|
|
345
|
+
});
|
|
346
|
+
const selected = await p.select({
|
|
347
|
+
message: "Select a plugin to install or inspect:",
|
|
348
|
+
options: items,
|
|
349
|
+
});
|
|
350
|
+
if (p.isCancel(selected)) {
|
|
351
|
+
p.cancel("Closed plugin explorer.");
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
if (selected === "__search__") {
|
|
355
|
+
const query = await p.text({
|
|
356
|
+
message: "Search query:",
|
|
357
|
+
placeholder: "e.g. vision, notion, search",
|
|
358
|
+
});
|
|
359
|
+
if (p.isCancel(query))
|
|
360
|
+
return;
|
|
361
|
+
await runPluginSearch(query);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
await runPluginAdd(selected);
|
|
365
|
+
}
|
|
366
|
+
// ---------------------------------------------------------------------------
|
|
367
|
+
// 5. REMOVE / UNINSTALL PLUGIN
|
|
368
|
+
// ---------------------------------------------------------------------------
|
|
369
|
+
export async function runPluginRemove(pkgName, args = []) {
|
|
370
|
+
if (!pkgName || pkgName.startsWith("-")) {
|
|
371
|
+
console.log(pc.red("[ERROR] Please specify the package name to remove."));
|
|
372
|
+
console.log(pc.dim("Usage: wt plugin remove <packageName>"));
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
const spinner = ora(`Uninstalling ${pc.cyan(pkgName)} via npm...`).start();
|
|
376
|
+
const result = await runNpmCommand(["uninstall", pkgName]);
|
|
377
|
+
if (result.code !== 0) {
|
|
378
|
+
spinner.fail(pc.red(`Failed to remove ${pkgName}`));
|
|
379
|
+
if (result.stderr)
|
|
380
|
+
console.error(pc.dim(result.stderr));
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
spinner.succeed(pc.green(`Successfully removed ${pc.bold(pkgName)}`));
|
|
384
|
+
console.log(pc.dim("The plugin and its tools have been uninstalled.\n"));
|
|
385
|
+
}
|
|
386
|
+
// ---------------------------------------------------------------------------
|
|
387
|
+
// 6. VERIFY / CHECK PLUGIN TRUST
|
|
388
|
+
// ---------------------------------------------------------------------------
|
|
389
|
+
export async function runPluginVerify(pkgName) {
|
|
390
|
+
if (!pkgName || pkgName.startsWith("-")) {
|
|
391
|
+
console.log(pc.red("[ERROR] Please specify a package name to verify."));
|
|
392
|
+
console.log(pc.dim("Usage: wt plugin verify <packageName>"));
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
const spinner = ora(`Checking trust status for ${pc.cyan(pkgName)}...`).start();
|
|
396
|
+
const verifiedPackages = await getVerifiedPackages();
|
|
397
|
+
spinner.stop();
|
|
398
|
+
const trust = resolvePluginTrust(pkgName, verifiedPackages);
|
|
399
|
+
console.log();
|
|
400
|
+
console.log(boxen(pc.bold(pc.cyan(` PLUGIN TRUST REPORT: ${pkgName} `)), { padding: 0, borderStyle: "round", borderColor: "cyan" }));
|
|
401
|
+
console.log();
|
|
402
|
+
console.log(` ${pc.dim("Package:")} ${pc.bold(pkgName)}`);
|
|
403
|
+
console.log(` ${pc.dim("Tier:")} ${trust.badge} (${trust.label})`);
|
|
404
|
+
console.log(` ${pc.dim("Details:")} ${trust.tooltip}`);
|
|
405
|
+
console.log();
|
|
406
|
+
if (trust.tier === "unverified") {
|
|
407
|
+
console.log(pc.yellow(" Want to get this package verified?"));
|
|
408
|
+
console.log(pc.dim(` Submit a verification PR to the registry at: ${pc.cyan(REGISTRY_URL)}\n`));
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
// ---------------------------------------------------------------------------
|
|
412
|
+
// 7. ENTERPRISE INIT: SCAFFOLD WEAVETAB PLUGIN
|
|
413
|
+
// ---------------------------------------------------------------------------
|
|
414
|
+
const DEFAULT_LOGO_SVG = `<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
415
|
+
<rect width="128" height="128" rx="28" fill="#0D1117"/>
|
|
416
|
+
<rect x="2" y="2" width="124" height="124" rx="26" stroke="#30363D" stroke-width="2"/>
|
|
417
|
+
<path d="M64 24L104 64L64 104L24 64L64 24Z" fill="url(#blue_grad)" stroke="#58A6FF" stroke-width="3"/>
|
|
418
|
+
<circle cx="64" cy="64" r="14" fill="#FFFFFF"/>
|
|
419
|
+
<defs>
|
|
420
|
+
<linearGradient id="blue_grad" x1="24" y1="24" x2="104" y2="104" gradientUnits="userSpaceOnUse">
|
|
421
|
+
<stop stop-color="#2F81F7"/>
|
|
422
|
+
<stop offset="1" stop-color="#1F6FEB"/>
|
|
423
|
+
</linearGradient>
|
|
424
|
+
</defs>
|
|
425
|
+
</svg>`;
|
|
426
|
+
const DEFAULT_BANNER_SVG = `<svg width="1200" height="400" viewBox="0 0 1200 400" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
427
|
+
<rect width="1200" height="400" fill="#0D1117"/>
|
|
428
|
+
<circle cx="600" cy="200" r="260" fill="#2F81F7" fill-opacity="0.12" filter="blur(80px)"/>
|
|
429
|
+
<path d="M600 80L720 200L600 320L480 200L600 80Z" fill="url(#grad)" stroke="#58A6FF" stroke-width="4"/>
|
|
430
|
+
<text x="600" y="360" text-anchor="middle" fill="#E6EDF3" font-family="-apple-system, BlinkMacSystemFont, Segoe UI, sans-serif" font-size="28" font-weight="600" letter-spacing="2">WEAVETAB MCP PLUGIN</text>
|
|
431
|
+
<defs>
|
|
432
|
+
<linearGradient id="grad" x1="480" y1="80" x2="720" y2="320" gradientUnits="userSpaceOnUse">
|
|
433
|
+
<stop stop-color="#388BFD"/>
|
|
434
|
+
<stop offset="1" stop-color="#1F6FEB"/>
|
|
435
|
+
</linearGradient>
|
|
436
|
+
</defs>
|
|
437
|
+
</svg>`;
|
|
438
|
+
export async function runPluginInit(args = []) {
|
|
439
|
+
const isYes = args.includes("--yes") || args.includes("-y");
|
|
440
|
+
const targetDirName = args.find((a) => !a.startsWith("-")) || "";
|
|
441
|
+
let targetDir = process.cwd();
|
|
442
|
+
if (targetDirName) {
|
|
443
|
+
targetDir = path.resolve(process.cwd(), targetDirName);
|
|
444
|
+
if (!existsSync(targetDir)) {
|
|
445
|
+
await fs.mkdir(targetDir, { recursive: true });
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
if (process.stdin.isTTY && !isYes) {
|
|
449
|
+
console.clear();
|
|
450
|
+
p.intro(`${pc.bgBlue(pc.white(" WEAVETAB "))} ${pc.bold(pc.cyan("Enterprise Plugin Generator"))}`);
|
|
451
|
+
}
|
|
452
|
+
let defaultName = path.basename(targetDir);
|
|
453
|
+
let defaultPkgName = defaultName.startsWith("wt-") ? defaultName : `wt-${defaultName}`;
|
|
454
|
+
let pkgName = defaultPkgName;
|
|
455
|
+
let displayName = defaultName.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
456
|
+
let description = "Weavetab Model Context Protocol (MCP) browser automation plugin.";
|
|
457
|
+
let authorName = "Developer";
|
|
458
|
+
let authorRole = "Maintainer";
|
|
459
|
+
let templateType = "cdp";
|
|
460
|
+
if (process.stdin.isTTY && !isYes) {
|
|
461
|
+
const enteredPkgName = await p.text({
|
|
462
|
+
message: "NPM Package Identifier (e.g. @org/wt-plugin or wt-plugin):",
|
|
463
|
+
defaultValue: defaultPkgName,
|
|
464
|
+
placeholder: defaultPkgName,
|
|
465
|
+
});
|
|
466
|
+
if (p.isCancel(enteredPkgName))
|
|
467
|
+
process.exit(0);
|
|
468
|
+
pkgName = enteredPkgName;
|
|
469
|
+
const enteredDisplayName = await p.text({
|
|
470
|
+
message: "Human-readable Display Name:",
|
|
471
|
+
defaultValue: displayName,
|
|
472
|
+
placeholder: displayName,
|
|
473
|
+
});
|
|
474
|
+
if (p.isCancel(enteredDisplayName))
|
|
475
|
+
process.exit(0);
|
|
476
|
+
displayName = enteredDisplayName;
|
|
477
|
+
const enteredDesc = await p.text({
|
|
478
|
+
message: "Description:",
|
|
479
|
+
defaultValue: description,
|
|
480
|
+
placeholder: description,
|
|
481
|
+
});
|
|
482
|
+
if (p.isCancel(enteredDesc))
|
|
483
|
+
process.exit(0);
|
|
484
|
+
description = enteredDesc;
|
|
485
|
+
const enteredAuthor = await p.text({
|
|
486
|
+
message: "Author Name or GitHub handle:",
|
|
487
|
+
defaultValue: authorName,
|
|
488
|
+
placeholder: authorName,
|
|
489
|
+
});
|
|
490
|
+
if (p.isCancel(enteredAuthor))
|
|
491
|
+
process.exit(0);
|
|
492
|
+
authorName = enteredAuthor;
|
|
493
|
+
const enteredRole = await p.text({
|
|
494
|
+
message: "Author Role (custom, e.g. Maintainer, Core Contributor, Creator):",
|
|
495
|
+
defaultValue: "Maintainer",
|
|
496
|
+
placeholder: "Maintainer",
|
|
497
|
+
validate: (val) => {
|
|
498
|
+
if (val && val.trim().length < 3)
|
|
499
|
+
return "Role must be at least 3 characters long.";
|
|
500
|
+
},
|
|
501
|
+
});
|
|
502
|
+
if (p.isCancel(enteredRole))
|
|
503
|
+
process.exit(0);
|
|
504
|
+
authorRole = enteredRole?.trim() || "Maintainer";
|
|
505
|
+
const selectedTemplate = await p.select({
|
|
506
|
+
message: "Select Plugin Architecture Template:",
|
|
507
|
+
options: [
|
|
508
|
+
{
|
|
509
|
+
value: "cdp",
|
|
510
|
+
label: `${pc.bold(pc.cyan("CDP Browser Automation"))} (Evaluates DOM, clicks elements, inspects tabs)`,
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
value: "api",
|
|
514
|
+
label: `${pc.bold(pc.green("External API Connector"))} (Exposes specialized MCP tools with JSON Schemas)`,
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
value: "minimal",
|
|
518
|
+
label: `${pc.bold(pc.gray("Minimal Starter"))} (Barebones TypeScript scaffold)`,
|
|
519
|
+
},
|
|
520
|
+
],
|
|
521
|
+
});
|
|
522
|
+
if (p.isCancel(selectedTemplate))
|
|
523
|
+
process.exit(0);
|
|
524
|
+
templateType = selectedTemplate;
|
|
525
|
+
}
|
|
526
|
+
const spinner = ora(`Scaffolding enterprise plugin in ${pc.cyan(targetDir)}...`).start();
|
|
527
|
+
// 1. Create directories
|
|
528
|
+
const srcDir = path.join(targetDir, "src");
|
|
529
|
+
const imgDir = path.join(targetDir, "images");
|
|
530
|
+
const testDir = path.join(targetDir, "test");
|
|
531
|
+
await fs.mkdir(srcDir, { recursive: true });
|
|
532
|
+
await fs.mkdir(imgDir, { recursive: true });
|
|
533
|
+
await fs.mkdir(testDir, { recursive: true });
|
|
534
|
+
// 2. Assets (Logo & Banner)
|
|
535
|
+
await fs.writeFile(path.join(imgDir, "logo.svg"), DEFAULT_LOGO_SVG, "utf-8");
|
|
536
|
+
await fs.writeFile(path.join(imgDir, "banner.svg"), DEFAULT_BANNER_SVG, "utf-8");
|
|
537
|
+
// 3. package.json
|
|
538
|
+
const packageJsonContent = {
|
|
539
|
+
name: pkgName,
|
|
540
|
+
version: "1.0.0",
|
|
541
|
+
description,
|
|
542
|
+
main: "./dist/index.js",
|
|
543
|
+
types: "./dist/index.d.ts",
|
|
544
|
+
type: "module",
|
|
545
|
+
files: ["dist", "images", "weavetab.json", "README.md"],
|
|
546
|
+
scripts: {
|
|
547
|
+
build: "tsc",
|
|
548
|
+
watch: "tsc -w",
|
|
549
|
+
prepare: "weavetab build",
|
|
550
|
+
test: "node --test test/plugin.test.js",
|
|
551
|
+
},
|
|
552
|
+
keywords: [
|
|
553
|
+
"weavetab",
|
|
554
|
+
"weavetab-plugin",
|
|
555
|
+
"mcp",
|
|
556
|
+
"model-context-protocol",
|
|
557
|
+
"browser-automation",
|
|
558
|
+
],
|
|
559
|
+
author: authorName,
|
|
560
|
+
license: "MIT",
|
|
561
|
+
devDependencies: {
|
|
562
|
+
typescript: "^5.7.2",
|
|
563
|
+
},
|
|
564
|
+
};
|
|
565
|
+
await fs.writeFile(path.join(targetDir, "package.json"), JSON.stringify(packageJsonContent, null, 2), "utf-8");
|
|
566
|
+
// 4. tsconfig.json
|
|
567
|
+
const tsConfigContent = {
|
|
568
|
+
compilerOptions: {
|
|
569
|
+
target: "ES2022",
|
|
570
|
+
module: "NodeNext",
|
|
571
|
+
moduleResolution: "NodeNext",
|
|
572
|
+
declaration: true,
|
|
573
|
+
declarationMap: true,
|
|
574
|
+
sourceMap: true,
|
|
575
|
+
outDir: "./dist",
|
|
576
|
+
rootDir: "./src",
|
|
577
|
+
strict: true,
|
|
578
|
+
esModuleInterop: true,
|
|
579
|
+
skipLibCheck: true,
|
|
580
|
+
},
|
|
581
|
+
include: ["src/**/*"],
|
|
582
|
+
};
|
|
583
|
+
await fs.writeFile(path.join(targetDir, "tsconfig.json"), JSON.stringify(tsConfigContent, null, 2), "utf-8");
|
|
584
|
+
// 5. .gitignore
|
|
585
|
+
const gitignoreContent = `node_modules/
|
|
586
|
+
dist/
|
|
587
|
+
*.log
|
|
588
|
+
.DS_Store
|
|
589
|
+
`;
|
|
590
|
+
await fs.writeFile(path.join(targetDir, ".gitignore"), gitignoreContent, "utf-8");
|
|
591
|
+
// 6. src/index.ts
|
|
592
|
+
const sanitizedFnName = pkgName.replace(/[^a-zA-Z0-9_]/g, "_").toLowerCase();
|
|
593
|
+
const sourceCode = `/**
|
|
594
|
+
* ${displayName}
|
|
595
|
+
* Model Context Protocol (MCP) Plugin for Weavetab
|
|
596
|
+
* Author: ${authorName}
|
|
597
|
+
* License: MIT
|
|
598
|
+
*/
|
|
599
|
+
|
|
600
|
+
export interface WeavetabSession {
|
|
601
|
+
evaluate<T = any>(script: string): Promise<T>;
|
|
602
|
+
click?(selector: string): Promise<any>;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
export interface PluginToolArgs {
|
|
606
|
+
query?: string;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
export interface WeavetabPluginTool {
|
|
610
|
+
name: string;
|
|
611
|
+
description: string;
|
|
612
|
+
schema?: Record<string, any>;
|
|
613
|
+
handler: (args: PluginToolArgs, session?: WeavetabSession, config?: any) => Promise<any>;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
export interface WeavetabPluginDefinition {
|
|
617
|
+
name: string;
|
|
618
|
+
version: string;
|
|
619
|
+
description: string;
|
|
620
|
+
tools: WeavetabPluginTool[];
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
export const plugin: WeavetabPluginDefinition = {
|
|
624
|
+
name: "${pkgName}",
|
|
625
|
+
version: "1.0.0",
|
|
626
|
+
description: "${description}",
|
|
627
|
+
tools: [
|
|
628
|
+
{
|
|
629
|
+
name: "${sanitizedFnName}",
|
|
630
|
+
description: "Inspects live DOM telemetry and executes actions on the active browser tab.",
|
|
631
|
+
schema: {
|
|
632
|
+
type: "object",
|
|
633
|
+
properties: {
|
|
634
|
+
query: {
|
|
635
|
+
type: "string",
|
|
636
|
+
description: "Optional query or CSS selector for DOM inspection."
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
},
|
|
640
|
+
handler: async (args: PluginToolArgs, session?: WeavetabSession) => {
|
|
641
|
+
if (!session || typeof session.evaluate !== "function") {
|
|
642
|
+
return {
|
|
643
|
+
status: "error",
|
|
644
|
+
error: "No active Chrome DevTools Protocol (CDP) session attached."
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
try {
|
|
649
|
+
const pageData = await session.evaluate(\`
|
|
650
|
+
(() => {
|
|
651
|
+
return {
|
|
652
|
+
title: document.title,
|
|
653
|
+
url: window.location.href,
|
|
654
|
+
elementsCount: document.querySelectorAll("*").length,
|
|
655
|
+
ready: document.readyState
|
|
656
|
+
};
|
|
657
|
+
})()
|
|
658
|
+
\`);
|
|
659
|
+
|
|
660
|
+
return {
|
|
661
|
+
status: "success",
|
|
662
|
+
data: pageData,
|
|
663
|
+
query: args?.query || null
|
|
664
|
+
};
|
|
665
|
+
} catch (err: any) {
|
|
666
|
+
return {
|
|
667
|
+
status: "error",
|
|
668
|
+
error: err.message || String(err)
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
]
|
|
674
|
+
};
|
|
675
|
+
|
|
676
|
+
export default plugin;
|
|
677
|
+
`;
|
|
678
|
+
await fs.writeFile(path.join(srcDir, "index.ts"), sourceCode, "utf-8");
|
|
679
|
+
// 7. weavetab.json
|
|
680
|
+
const authorHandle = authorName.replace(/^@/, "").trim();
|
|
681
|
+
const isGenericAuthor = ["author", "maintainer", "developer", "user"].includes(authorHandle.toLowerCase());
|
|
682
|
+
const authorGhUrl = authorName.startsWith("http")
|
|
683
|
+
? authorName
|
|
684
|
+
: (!isGenericAuthor && authorHandle && !authorHandle.includes(" "))
|
|
685
|
+
? `https://github.com/${authorHandle}`
|
|
686
|
+
: undefined;
|
|
687
|
+
const nodeMajor = process.versions.node ? process.versions.node.split(".")[0] : "18";
|
|
688
|
+
const weavetabManifest = {
|
|
689
|
+
$schema: "https://weavetab.dev/schema/plugin.json",
|
|
690
|
+
name: displayName,
|
|
691
|
+
version: "1.0.0",
|
|
692
|
+
description,
|
|
693
|
+
authors: [
|
|
694
|
+
{
|
|
695
|
+
name: authorHandle || "Author",
|
|
696
|
+
role: authorRole,
|
|
697
|
+
...(authorGhUrl ? { url: authorGhUrl } : {}),
|
|
698
|
+
},
|
|
699
|
+
],
|
|
700
|
+
logo: "images/logo.svg",
|
|
701
|
+
banner: "images/banner.svg",
|
|
702
|
+
license: "MIT",
|
|
703
|
+
tags: ["Automation", "Developer Tools", "MCP", "CDP"],
|
|
704
|
+
compatibility: {
|
|
705
|
+
weavetab: "^2.5.0",
|
|
706
|
+
node: `>=${nodeMajor}.0.0`,
|
|
707
|
+
},
|
|
708
|
+
permissions: {
|
|
709
|
+
network: {
|
|
710
|
+
domains: ["*"],
|
|
711
|
+
reason: "Required to inspect web tabs and telemetry.",
|
|
712
|
+
},
|
|
713
|
+
cdp: {
|
|
714
|
+
evaluate: true,
|
|
715
|
+
click: true,
|
|
716
|
+
reason: "Evaluates DOM scripts and queries tab metadata via Chrome DevTools Protocol.",
|
|
717
|
+
},
|
|
718
|
+
},
|
|
719
|
+
tools: [
|
|
720
|
+
{
|
|
721
|
+
name: sanitizedFnName,
|
|
722
|
+
description: `Inspects live DOM telemetry and executes actions on the active browser tab.`,
|
|
723
|
+
inputSchema: {
|
|
724
|
+
type: "object",
|
|
725
|
+
properties: {
|
|
726
|
+
query: {
|
|
727
|
+
type: "string",
|
|
728
|
+
description: "Optional query or CSS selector for DOM inspection.",
|
|
729
|
+
},
|
|
730
|
+
},
|
|
731
|
+
},
|
|
732
|
+
},
|
|
733
|
+
],
|
|
734
|
+
};
|
|
735
|
+
await fs.writeFile(path.join(targetDir, "weavetab.json"), JSON.stringify(weavetabManifest, null, 2), "utf-8");
|
|
736
|
+
// 8. README.md
|
|
737
|
+
const readmeContent = `# ${displayName}
|
|
738
|
+
|
|
739
|
+
> ${description}
|
|
740
|
+
|
|
741
|
+
An enterprise Model Context Protocol (MCP) browser extension for **Weavetab**.
|
|
742
|
+
|
|
743
|
+
## Installation
|
|
744
|
+
|
|
745
|
+
\`\`\`bash
|
|
746
|
+
npx weavetab add ${pkgName}
|
|
747
|
+
\`\`\`
|
|
748
|
+
|
|
749
|
+
## MCP Tools Exposed
|
|
750
|
+
|
|
751
|
+
| Tool Name | Description | Parameters |
|
|
752
|
+
| :--- | :--- | :--- |
|
|
753
|
+
| \`${sanitizedFnName}\` | Inspects live DOM telemetry and executes actions on active tab | \`query\` (*optional*) |
|
|
754
|
+
|
|
755
|
+
## Security & Permissions
|
|
756
|
+
|
|
757
|
+
- **Chrome DevTools Protocol (CDP)**: Evaluates DOM scripts on active tabs.
|
|
758
|
+
- **Network Scope**: Scoped to active browser session.
|
|
759
|
+
|
|
760
|
+
## Development & Build
|
|
761
|
+
|
|
762
|
+
\`\`\`bash
|
|
763
|
+
# 1. Install dependencies
|
|
764
|
+
npm install
|
|
765
|
+
|
|
766
|
+
# 2. Build and compile plugin manifest
|
|
767
|
+
npx weavetab build
|
|
768
|
+
|
|
769
|
+
# 3. Publish to NPM
|
|
770
|
+
npm publish --access public
|
|
771
|
+
\`\`\`
|
|
772
|
+
`;
|
|
773
|
+
await fs.writeFile(path.join(targetDir, "README.md"), readmeContent, "utf-8");
|
|
774
|
+
// 9. test/plugin.test.js
|
|
775
|
+
const testContent = `import test from "node:test";
|
|
776
|
+
import assert from "node:assert/strict";
|
|
777
|
+
|
|
778
|
+
test("plugin exports valid Weavetab MCP tools", async () => {
|
|
779
|
+
const { plugin } = await import("../dist/index.js");
|
|
780
|
+
assert.ok(plugin, "Plugin module exported");
|
|
781
|
+
assert.equal(typeof plugin.name, "string");
|
|
782
|
+
assert.ok(Array.isArray(plugin.tools), "Plugin exports tools array");
|
|
783
|
+
assert.ok(plugin.tools.length > 0, "Plugin defines at least 1 tool");
|
|
784
|
+
});
|
|
785
|
+
`;
|
|
786
|
+
await fs.writeFile(path.join(testDir, "plugin.test.js"), testContent, "utf-8");
|
|
787
|
+
spinner.succeed(pc.green(`Scaffolded ${pc.bold(displayName)} successfully!`));
|
|
788
|
+
console.log();
|
|
789
|
+
console.log(boxen(`${pc.bold(pc.cyan("✨ PLUGIN READY FOR DEVELOPMENT"))}\n\n` +
|
|
790
|
+
` ${pc.dim("Location:")} ${pc.bold(targetDir)}\n` +
|
|
791
|
+
` ${pc.dim("Package:")} ${pc.green(pkgName)}\n` +
|
|
792
|
+
` ${pc.dim("Tools:")} ${pc.yellow(sanitizedFnName)}\n\n` +
|
|
793
|
+
`${pc.bold("Next Steps:")}\n` +
|
|
794
|
+
` 1. ${pc.cyan("npm install")}\n` +
|
|
795
|
+
` 2. ${pc.cyan("npx weavetab build")} ${pc.dim("(Compiles TS & syncs weavetab.json)")}\n` +
|
|
796
|
+
` 3. ${pc.cyan("npm publish --access public")}`, { padding: 1, borderStyle: "round", borderColor: "cyan" }));
|
|
797
|
+
console.log();
|
|
798
|
+
}
|
|
799
|
+
// ---------------------------------------------------------------------------
|
|
800
|
+
// 8. ENTERPRISE BUILD: COMPILE & SYNC WEAVETAB.JSON
|
|
801
|
+
// ---------------------------------------------------------------------------
|
|
802
|
+
export async function runPluginBuild(args = []) {
|
|
803
|
+
const cwd = process.cwd();
|
|
804
|
+
const pkgPath = path.join(cwd, "package.json");
|
|
805
|
+
const manifestPath = path.join(cwd, "weavetab.json");
|
|
806
|
+
const tsConfigPath = path.join(cwd, "tsconfig.json");
|
|
807
|
+
console.log();
|
|
808
|
+
console.log(boxen(pc.bold(pc.cyan(" WEAVETAB PLUGIN COMPILER ")), { padding: 0, borderStyle: "round", borderColor: "cyan" }));
|
|
809
|
+
console.log();
|
|
810
|
+
if (!existsSync(pkgPath)) {
|
|
811
|
+
console.log(pc.red("[ERROR] No package.json found in current directory."));
|
|
812
|
+
console.log(pc.dim("Run 'wt init' to scaffold a new Weavetab plugin project."));
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
const pkgJson = JSON.parse(await fs.readFile(pkgPath, "utf-8"));
|
|
816
|
+
const isTypeScript = existsSync(tsConfigPath);
|
|
817
|
+
// 1. Compile TypeScript if applicable
|
|
818
|
+
const entryRel = pkgJson.main || (isTypeScript ? "dist/index.js" : "index.js");
|
|
819
|
+
const entryAbs = path.join(cwd, entryRel);
|
|
820
|
+
if (isTypeScript) {
|
|
821
|
+
const compileSpinner = ora("Compiling TypeScript plugin sources...").start();
|
|
822
|
+
const localTsc = path.join(cwd, "node_modules", ".bin", process.platform === "win32" ? "tsc.cmd" : "tsc");
|
|
823
|
+
let compiled = false;
|
|
824
|
+
let lastError = "";
|
|
825
|
+
if (existsSync(localTsc)) {
|
|
826
|
+
const res = await runCommand(localTsc, [], cwd);
|
|
827
|
+
if (res.code === 0)
|
|
828
|
+
compiled = true;
|
|
829
|
+
else
|
|
830
|
+
lastError = res.stderr || res.stdout;
|
|
831
|
+
}
|
|
832
|
+
if (!compiled) {
|
|
833
|
+
const npmRes = await runNpmCommand(["run", "build"], cwd);
|
|
834
|
+
if (npmRes.code === 0)
|
|
835
|
+
compiled = true;
|
|
836
|
+
else
|
|
837
|
+
lastError = npmRes.stderr || npmRes.stdout;
|
|
838
|
+
}
|
|
839
|
+
if (!compiled) {
|
|
840
|
+
const npxRes = await runCommand("npx", ["-p", "typescript", "tsc"], cwd);
|
|
841
|
+
if (npxRes.code === 0)
|
|
842
|
+
compiled = true;
|
|
843
|
+
else
|
|
844
|
+
lastError = npxRes.stderr || npxRes.stdout;
|
|
845
|
+
}
|
|
846
|
+
if (compiled || existsSync(entryAbs)) {
|
|
847
|
+
compileSpinner.succeed(pc.green("TypeScript compiled successfully."));
|
|
848
|
+
}
|
|
849
|
+
else {
|
|
850
|
+
compileSpinner.fail(pc.red("TypeScript compilation failed."));
|
|
851
|
+
if (lastError)
|
|
852
|
+
console.error(pc.dim(lastError));
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
// 2. Load compiled plugin module
|
|
857
|
+
const inspectSpinner = ora("Introspecting exported MCP tools and schemas...").start();
|
|
858
|
+
if (!existsSync(entryAbs)) {
|
|
859
|
+
inspectSpinner.fail(pc.red(`Main entrypoint not found: ${entryRel}`));
|
|
860
|
+
console.log(pc.dim("Ensure 'main' in package.json points to a valid compiled JavaScript file."));
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
let exportedTools = [];
|
|
864
|
+
try {
|
|
865
|
+
const fileUrl = pathToFileURL(entryAbs).href;
|
|
866
|
+
const mod = await import(`${fileUrl}?t=${Date.now()}`);
|
|
867
|
+
const pluginInstance = mod.default || mod.plugin || mod;
|
|
868
|
+
if (pluginInstance && Array.isArray(pluginInstance.tools)) {
|
|
869
|
+
exportedTools = pluginInstance.tools.map((t) => {
|
|
870
|
+
const inputSchema = t.inputSchema || t.schema || {
|
|
871
|
+
type: "object",
|
|
872
|
+
properties: {},
|
|
873
|
+
};
|
|
874
|
+
return {
|
|
875
|
+
name: t.name,
|
|
876
|
+
description: t.description || "",
|
|
877
|
+
inputSchema,
|
|
878
|
+
};
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
inspectSpinner.succeed(`Discovered ${pc.cyan(String(exportedTools.length))} MCP tool(s).`);
|
|
882
|
+
}
|
|
883
|
+
catch (err) {
|
|
884
|
+
inspectSpinner.warn(pc.yellow(`Dynamic introspection note: ${err.message}`));
|
|
885
|
+
}
|
|
886
|
+
// 3. Read or create weavetab.json
|
|
887
|
+
let manifest = {
|
|
888
|
+
$schema: "https://weavetab.dev/schema/plugin.json",
|
|
889
|
+
name: pkgJson.name || path.basename(cwd),
|
|
890
|
+
version: pkgJson.version || "1.0.0",
|
|
891
|
+
description: pkgJson.description || "",
|
|
892
|
+
logo: "images/logo.svg",
|
|
893
|
+
banner: "images/banner.svg",
|
|
894
|
+
license: pkgJson.license || "MIT",
|
|
895
|
+
tags: ["MCP", "Automation"],
|
|
896
|
+
};
|
|
897
|
+
if (existsSync(manifestPath)) {
|
|
898
|
+
try {
|
|
899
|
+
manifest = JSON.parse(await fs.readFile(manifestPath, "utf-8"));
|
|
900
|
+
}
|
|
901
|
+
catch {
|
|
902
|
+
console.log(pc.yellow("Warning: Existing weavetab.json was malformed, rebuilding cleanly."));
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
// 4. Synchronize metadata & tools
|
|
906
|
+
manifest.$schema = "https://weavetab.dev/schema/plugin.json";
|
|
907
|
+
manifest.version = pkgJson.version || manifest.version || "1.0.0";
|
|
908
|
+
if (!manifest.description && pkgJson.description) {
|
|
909
|
+
manifest.description = pkgJson.description;
|
|
910
|
+
}
|
|
911
|
+
// Sync authors
|
|
912
|
+
if (!manifest.authors && !manifest.author) {
|
|
913
|
+
manifest.authors = [
|
|
914
|
+
{
|
|
915
|
+
name: pkgJson.author || "Maintainer",
|
|
916
|
+
role: "Maintainer",
|
|
917
|
+
},
|
|
918
|
+
];
|
|
919
|
+
}
|
|
920
|
+
// Sync compatibility
|
|
921
|
+
if (!manifest.compatibility) {
|
|
922
|
+
const nodeMajor = process.versions.node ? process.versions.node.split(".")[0] : "18";
|
|
923
|
+
manifest.compatibility = {
|
|
924
|
+
weavetab: "^2.5.0",
|
|
925
|
+
node: pkgJson.engines?.node || `>=${nodeMajor}.0.0`,
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
// Sync tools array with introspected definitions
|
|
929
|
+
if (exportedTools.length > 0) {
|
|
930
|
+
manifest.tools = exportedTools;
|
|
931
|
+
}
|
|
932
|
+
// Ensure default permissions exist if empty
|
|
933
|
+
if (!manifest.permissions) {
|
|
934
|
+
manifest.permissions = {
|
|
935
|
+
cdp: {
|
|
936
|
+
evaluate: true,
|
|
937
|
+
reason: "Evaluates DOM telemetry and executes automated user actions.",
|
|
938
|
+
},
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
// 5. Comprehensive Manifest Validation & Audit Rules
|
|
942
|
+
const errors = [];
|
|
943
|
+
const warnings = [];
|
|
944
|
+
// Name validation
|
|
945
|
+
if (!manifest.name || typeof manifest.name !== "string" || manifest.name.trim().length === 0) {
|
|
946
|
+
errors.push("Plugin 'name' is required and cannot be empty.");
|
|
947
|
+
}
|
|
948
|
+
else if (["my-plugin", "plugin-starter", "plugin-template", "example"].includes(manifest.name.toLowerCase())) {
|
|
949
|
+
warnings.push(`Plugin name '${manifest.name}' appears to be a placeholder. Consider setting a descriptive name.`);
|
|
950
|
+
}
|
|
951
|
+
// Version semver validation
|
|
952
|
+
const semverRegex = /^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$/;
|
|
953
|
+
if (!manifest.version || !semverRegex.test(manifest.version)) {
|
|
954
|
+
errors.push(`Plugin version '${manifest.version}' is invalid. Must follow Semantic Versioning (e.g. 1.0.0).`);
|
|
955
|
+
}
|
|
956
|
+
// Description validation
|
|
957
|
+
if (!manifest.description || manifest.description.trim().length < 10) {
|
|
958
|
+
warnings.push("Plugin 'description' is missing or very short. Provide a clear summary for users on the registry.");
|
|
959
|
+
}
|
|
960
|
+
// Authors validation
|
|
961
|
+
const authorsList = Array.isArray(manifest.authors)
|
|
962
|
+
? manifest.authors
|
|
963
|
+
: manifest.author
|
|
964
|
+
? (Array.isArray(manifest.author) ? manifest.author : [manifest.author])
|
|
965
|
+
: [];
|
|
966
|
+
if (authorsList.length === 0) {
|
|
967
|
+
errors.push("At least one author must be declared in 'authors' or package.json.");
|
|
968
|
+
}
|
|
969
|
+
else {
|
|
970
|
+
for (const a of authorsList) {
|
|
971
|
+
if (typeof a === "object" && a !== null) {
|
|
972
|
+
if (!a.name || String(a.name).trim().length < 2) {
|
|
973
|
+
errors.push(`Author name '${a.name || ""}' must be at least 2 characters.`);
|
|
974
|
+
}
|
|
975
|
+
if (a.role !== undefined && a.role !== null) {
|
|
976
|
+
const roleStr = String(a.role).trim();
|
|
977
|
+
if (roleStr.length < 3) {
|
|
978
|
+
errors.push(`Invalid author role for '${a.name || "author"}': "${roleStr}". Role must be at least 3 characters (e.g. 'Creator', 'Maintainer', 'Lead Architect').`);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
if (a.url) {
|
|
982
|
+
try {
|
|
983
|
+
const parsedUrl = new URL(a.url);
|
|
984
|
+
if (!["http:", "https:"].includes(parsedUrl.protocol)) {
|
|
985
|
+
errors.push(`Author URL '${a.url}' must use HTTP or HTTPS protocol.`);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
catch {
|
|
989
|
+
errors.push(`Author URL '${a.url}' is not a valid URL.`);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
// Tools validation
|
|
996
|
+
if (!manifest.tools || manifest.tools.length === 0) {
|
|
997
|
+
warnings.push("No MCP tools declared or discovered. The plugin will not expose any capabilities to AI agents.");
|
|
998
|
+
}
|
|
999
|
+
else {
|
|
1000
|
+
for (const tool of manifest.tools) {
|
|
1001
|
+
if (!tool.name || !/^[a-zA-Z0-9_-]+$/.test(tool.name)) {
|
|
1002
|
+
errors.push(`Tool name '${tool.name}' is invalid. Must only contain letters, numbers, underscores, or hyphens.`);
|
|
1003
|
+
}
|
|
1004
|
+
if (!tool.description || tool.description.trim().length < 5) {
|
|
1005
|
+
warnings.push(`Tool '${tool.name}' has a very short description. Write a clear description so AI agents understand its purpose.`);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
// Permissions validation
|
|
1010
|
+
if (manifest.permissions?.network?.domains?.includes("*")) {
|
|
1011
|
+
warnings.push("Wildcard domain access ('*') requested in network permissions. Narrowing to specific domains improves security.");
|
|
1012
|
+
}
|
|
1013
|
+
if (manifest.permissions?.cdp && (!manifest.permissions.cdp.reason || manifest.permissions.cdp.reason.trim().length < 5)) {
|
|
1014
|
+
warnings.push("Missing justification in permissions.cdp.reason. Explain why Chrome DevTools Protocol access is required.");
|
|
1015
|
+
}
|
|
1016
|
+
// Asset files validation
|
|
1017
|
+
if (manifest.logo && typeof manifest.logo === "string") {
|
|
1018
|
+
const logoPath = path.resolve(cwd, manifest.logo);
|
|
1019
|
+
if (!existsSync(logoPath)) {
|
|
1020
|
+
warnings.push(`Referenced logo asset '${manifest.logo}' was not found at '${path.relative(cwd, logoPath)}'.`);
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
if (manifest.banner && typeof manifest.banner === "string") {
|
|
1024
|
+
const bannerPath = path.resolve(cwd, manifest.banner);
|
|
1025
|
+
if (!existsSync(bannerPath)) {
|
|
1026
|
+
warnings.push(`Referenced banner asset '${manifest.banner}' was not found at '${path.relative(cwd, bannerPath)}'.`);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
// Print Warnings
|
|
1030
|
+
if (warnings.length > 0) {
|
|
1031
|
+
console.log();
|
|
1032
|
+
console.log(pc.yellow(pc.bold("⚠ WEAVETAB BUILD WARNINGS:")));
|
|
1033
|
+
for (const w of warnings) {
|
|
1034
|
+
console.log(` ${pc.yellow("•")} ${w}`);
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
// Print Errors and abort if any
|
|
1038
|
+
if (errors.length > 0) {
|
|
1039
|
+
console.log();
|
|
1040
|
+
console.log(pc.red(pc.bold("✖ WEAVETAB BUILD FAILED WITH ERRORS:")));
|
|
1041
|
+
for (const e of errors) {
|
|
1042
|
+
console.log(` ${pc.red("•")} ${e}`);
|
|
1043
|
+
}
|
|
1044
|
+
console.log();
|
|
1045
|
+
process.exit(1);
|
|
1046
|
+
}
|
|
1047
|
+
// 6. Write back synced weavetab.json
|
|
1048
|
+
await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
1049
|
+
// 6. Print Summary Report
|
|
1050
|
+
const declaredToolsList = manifest.tools?.map((t) => ` • ${pc.bold(pc.cyan(t.name))} - ${pc.dim(t.description)}`).join("\n") || pc.dim(" (None declared)");
|
|
1051
|
+
const perms = [];
|
|
1052
|
+
if (manifest.permissions?.cdp)
|
|
1053
|
+
perms.push("CDP (Browser Automation)");
|
|
1054
|
+
if (manifest.permissions?.network)
|
|
1055
|
+
perms.push(`Network (${manifest.permissions.network.domains?.join(", ") || "Active Tab"})`);
|
|
1056
|
+
if (manifest.permissions?.fs)
|
|
1057
|
+
perms.push("Local Filesystem");
|
|
1058
|
+
console.log(boxen(`${pc.bold(pc.green("✔ WEAVETAB PLUGIN BUILD COMPLETED"))}\n\n` +
|
|
1059
|
+
` ${pc.dim("Package:")} ${pc.bold(pkgJson.name || manifest.name)} (v${manifest.version})\n` +
|
|
1060
|
+
` ${pc.dim("Entrypoint:")} ${pc.cyan(entryRel)}\n` +
|
|
1061
|
+
` ${pc.dim("Permissions:")} ${pc.yellow(perms.join(", ") || "Standard")}\n` +
|
|
1062
|
+
` ${pc.dim("Manifest:")} ${pc.green("weavetab.json (Synced & Validated)")}\n\n` +
|
|
1063
|
+
`${pc.bold("Exposed MCP Tools:")}\n${declaredToolsList}\n\n` +
|
|
1064
|
+
`${pc.dim("Ready to publish to NPM:")}\n` +
|
|
1065
|
+
` ${pc.bold(pc.cyan("npm publish --access public"))}`, { padding: 1, borderStyle: "round", borderColor: "green" }));
|
|
1066
|
+
console.log();
|
|
1067
|
+
}
|
|
1068
|
+
// ---------------------------------------------------------------------------
|
|
1069
|
+
// 9. MAIN CLI ROUTER FOR PLUGINS
|
|
1070
|
+
// ---------------------------------------------------------------------------
|
|
1071
|
+
export async function runPluginCli(args) {
|
|
1072
|
+
const subcmd = args[0]?.toLowerCase();
|
|
1073
|
+
if (!subcmd || subcmd === "--help" || subcmd === "-h" || subcmd === "help") {
|
|
1074
|
+
console.log();
|
|
1075
|
+
console.log(boxen(pc.bold(pc.cyan(" WEAVETAB PLUGIN MANAGER ")), { padding: 0, borderStyle: "round", borderColor: "cyan" }));
|
|
1076
|
+
console.log();
|
|
1077
|
+
console.log(pc.bold("USAGE"));
|
|
1078
|
+
console.log(` wt plugin <command> [options]`);
|
|
1079
|
+
console.log();
|
|
1080
|
+
console.log(pc.bold("DEVELOPER COMMANDS"));
|
|
1081
|
+
console.log(` ${pc.cyan("init [name]")} Scaffold a new enterprise TypeScript Weavetab plugin`);
|
|
1082
|
+
console.log(` ${pc.cyan("build")} Compile TypeScript & synchronize weavetab.json manifest`);
|
|
1083
|
+
console.log(` ${pc.cyan("manifest")} Alias for 'build' to validate & sync manifest`);
|
|
1084
|
+
console.log();
|
|
1085
|
+
console.log(pc.bold("USER COMMANDS"));
|
|
1086
|
+
console.log(` ${pc.cyan("add <pkg>")} Install a plugin (resolves official @weavetab/ & community)`);
|
|
1087
|
+
console.log(` ${pc.cyan("browse")} Interactive terminal explorer to browse & 1-click install`);
|
|
1088
|
+
console.log(` ${pc.cyan("search [query]")} Search plugins across official registry & npm`);
|
|
1089
|
+
console.log(` ${pc.cyan("list")} List all installed plugins and their registered tools`);
|
|
1090
|
+
console.log(` ${pc.cyan("remove <pkg>")} Uninstall a plugin from local environment`);
|
|
1091
|
+
console.log(` ${pc.cyan("verify <pkg>")} Check trust tier and registry verification status`);
|
|
1092
|
+
console.log();
|
|
1093
|
+
console.log(pc.bold("OPTIONS (for 'add')"));
|
|
1094
|
+
console.log(` ${pc.cyan("--official, -O")} Prefer official @weavetab/plugin-<name>`);
|
|
1095
|
+
console.log(` ${pc.cyan("--community, -C")} Prefer community package`);
|
|
1096
|
+
console.log(` ${pc.cyan("--yes, -y")} Bypass interactive warning prompt for unverified plugins`);
|
|
1097
|
+
console.log();
|
|
1098
|
+
return;
|
|
1099
|
+
}
|
|
1100
|
+
if (subcmd === "init" || subcmd === "create" || subcmd === "new") {
|
|
1101
|
+
await runPluginInit(args.slice(1));
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
1104
|
+
if (subcmd === "build" || subcmd === "manifest" || subcmd === "sync") {
|
|
1105
|
+
await runPluginBuild(args.slice(1));
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1108
|
+
if (subcmd === "add" || subcmd === "install" || subcmd === "i") {
|
|
1109
|
+
await runPluginAdd(args[1], args.slice(2));
|
|
1110
|
+
return;
|
|
1111
|
+
}
|
|
1112
|
+
if (subcmd === "browse" || subcmd === "explore") {
|
|
1113
|
+
await runPluginBrowse();
|
|
1114
|
+
return;
|
|
1115
|
+
}
|
|
1116
|
+
if (subcmd === "search" || subcmd === "find") {
|
|
1117
|
+
await runPluginSearch(args[1]);
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
if (subcmd === "list" || subcmd === "ls") {
|
|
1121
|
+
await runPluginList();
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
if (subcmd === "remove" || subcmd === "rm" || subcmd === "uninstall") {
|
|
1125
|
+
await runPluginRemove(args[1], args.slice(2));
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1128
|
+
if (subcmd === "verify" || subcmd === "check") {
|
|
1129
|
+
await runPluginVerify(args[1]);
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
console.log(pc.red(`[ERROR] Unknown plugin subcommand: "${subcmd}"`));
|
|
1133
|
+
console.log(pc.dim("Run 'wt plugin --help' to see available commands."));
|
|
1134
|
+
}
|