@kwonye/mcpx 0.1.99 → 0.1.101
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/dist/{cli-28g7x9rn.js → cli-6wh8qx2y.js} +9 -3
- package/dist/{cli-dy7x0zrg.js → cli-6zq2fd6h.js} +1 -1
- package/dist/{cli-b0xrqsjc.js → cli-9k3wmag6.js} +286 -363
- package/dist/{cli-ct7asw78.js → cli-aawpvhhe.js} +13 -10
- package/dist/{cli-1ewrsfj4.js → cli-hh7rthhx.js} +4 -1
- package/dist/{cli-7x2n23em.js → cli-qzzjkt0q.js} +1132 -723
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +352 -88
- package/dist/{config-yx5m60fv.js → config-fbgyqmv1.js} +2 -2
- package/dist/core/auth-probe.d.ts +6 -1
- package/dist/core/auth-probe.d.ts.map +1 -1
- package/dist/core/daemon.d.ts +1 -0
- package/dist/core/daemon.d.ts.map +1 -1
- package/dist/core/identifiers.d.ts +3 -0
- package/dist/core/identifiers.d.ts.map +1 -0
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +22 -6
- package/dist/core/marketplace.d.ts.map +1 -1
- package/dist/core/oauth.d.ts +77 -2
- package/dist/core/oauth.d.ts.map +1 -1
- package/dist/core/paths.d.ts +1 -0
- package/dist/core/paths.d.ts.map +1 -1
- package/dist/core/plugin-cache.d.ts.map +1 -1
- package/dist/core/plugin-host.d.ts.map +1 -1
- package/dist/core/plugin-lifecycle.d.ts.map +1 -1
- package/dist/core/plugin-manager.d.ts.map +1 -1
- package/dist/core/plugin-parse.d.ts.map +1 -1
- package/dist/core/plugin-projections.d.ts.map +1 -1
- package/dist/core/plugin-source.d.ts.map +1 -1
- package/dist/core/secrets.d.ts.map +1 -1
- package/dist/core/skill-projections.d.ts.map +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/update-manager.d.ts.map +1 -1
- package/dist/gateway/server.d.ts.map +1 -1
- package/dist/{marketplace-pwabb3ea.js → marketplace-zem0m4mw.js} +5 -5
- package/dist/{plugin-host-9xcpnehq.js → plugin-host-5jmyt0td.js} +13 -39
- package/dist/{plugin-manager-19q55n69.js → plugin-manager-rw05c2x3.js} +4 -4
- package/dist/{registry-yvbxcaq6.js → registry-04kfpjh0.js} +2 -2
- package/package.json +1 -1
|
@@ -3,11 +3,11 @@ import {
|
|
|
3
3
|
loadProjectConfig,
|
|
4
4
|
saveConfig,
|
|
5
5
|
saveProjectConfig
|
|
6
|
-
} from "./cli-
|
|
6
|
+
} from "./cli-6zq2fd6h.js";
|
|
7
7
|
import {
|
|
8
8
|
SecretsManager,
|
|
9
9
|
ensureGatewayToken
|
|
10
|
-
} from "./cli-
|
|
10
|
+
} from "./cli-hh7rthhx.js";
|
|
11
11
|
import {
|
|
12
12
|
ensureDir,
|
|
13
13
|
findProjectConfigPath,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
readJsonFile,
|
|
21
21
|
sha256,
|
|
22
22
|
writeJsonAtomic
|
|
23
|
-
} from "./cli-
|
|
23
|
+
} from "./cli-6wh8qx2y.js";
|
|
24
24
|
import {
|
|
25
25
|
__require,
|
|
26
26
|
isServerEnabled
|
|
@@ -33,8 +33,10 @@ import path21 from "node:path";
|
|
|
33
33
|
// src/core/plugin-cache.ts
|
|
34
34
|
import fs from "node:fs";
|
|
35
35
|
import path from "node:path";
|
|
36
|
-
import {
|
|
36
|
+
import { execFile } from "node:child_process";
|
|
37
|
+
import { promisify } from "node:util";
|
|
37
38
|
var TMP_PREFIX = "mcpx-acquire-";
|
|
39
|
+
var execFileAsync = promisify(execFile);
|
|
38
40
|
|
|
39
41
|
class PluginCache {
|
|
40
42
|
cacheRoot;
|
|
@@ -55,24 +57,18 @@ class PluginCache {
|
|
|
55
57
|
async resolveSha(source) {
|
|
56
58
|
const srcDir = this.sourceDir(source);
|
|
57
59
|
if (source.type === "github") {
|
|
58
|
-
const repo = source.original.split("@")[0];
|
|
60
|
+
const repo = source.original.replace(/^github\.com\//, "").split("@")[0];
|
|
59
61
|
const ref = source.ref || "HEAD";
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
timeout: 30000
|
|
63
|
-
});
|
|
64
|
-
const sha = output.split("\t")[0]?.trim();
|
|
62
|
+
const { stdout } = await execFileAsync("git", ["ls-remote", `https://github.com/${repo}.git`, ref], { timeout: 30000 });
|
|
63
|
+
const sha = stdout.split("\t")[0]?.trim();
|
|
65
64
|
if (!sha)
|
|
66
65
|
throw new Error(`Could not resolve ref ${ref} for ${repo}`);
|
|
67
66
|
return sha;
|
|
68
67
|
}
|
|
69
68
|
if (source.type === "git" || source.type === "git-subdir") {
|
|
70
69
|
const ref = source.ref || "HEAD";
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
timeout: 30000
|
|
74
|
-
});
|
|
75
|
-
const sha = output.split("\t")[0]?.trim();
|
|
70
|
+
const { stdout } = await execFileAsync("git", ["ls-remote", source.original, ref], { timeout: 30000 });
|
|
71
|
+
const sha = stdout.split("\t")[0]?.trim();
|
|
76
72
|
if (!sha)
|
|
77
73
|
throw new Error(`Could not resolve ref ${ref} for ${source.original}`);
|
|
78
74
|
return sha;
|
|
@@ -99,23 +95,22 @@ class PluginCache {
|
|
|
99
95
|
}
|
|
100
96
|
ensureDir(srcDir);
|
|
101
97
|
if (source.type === "github" || source.type === "git" || source.type === "git-subdir") {
|
|
102
|
-
const remote = source.type === "github" ? `https://github.com/${source.original.split("@")[0]}.git` : source.original;
|
|
98
|
+
const remote = source.type === "github" ? `https://github.com/${source.original.replace(/^github\.com\//, "").split("@")[0]}.git` : source.original;
|
|
103
99
|
const target = source.resolvedSha || source.ref || "HEAD";
|
|
104
100
|
const tmpDir = fs.mkdtempSync(path.join(this.cacheRoot, TMP_PREFIX));
|
|
105
101
|
try {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
102
|
+
await execFileAsync("git", ["init"], { cwd: tmpDir, timeout: 1e4 });
|
|
103
|
+
await execFileAsync("git", ["remote", "add", "origin", remote], { cwd: tmpDir, timeout: 1e4 });
|
|
104
|
+
await execFileAsync("git", ["fetch", "--depth", "1", "origin", target], {
|
|
109
105
|
cwd: tmpDir,
|
|
110
|
-
stdio: "pipe",
|
|
111
106
|
timeout: 60000
|
|
112
107
|
});
|
|
113
|
-
|
|
114
|
-
const
|
|
108
|
+
await execFileAsync("git", ["checkout", "--detach", "FETCH_HEAD"], { cwd: tmpDir, timeout: 1e4 });
|
|
109
|
+
const { stdout } = await execFileAsync("git", ["rev-parse", "HEAD"], {
|
|
115
110
|
cwd: tmpDir,
|
|
116
|
-
encoding: "utf8",
|
|
117
111
|
timeout: 1e4
|
|
118
|
-
})
|
|
112
|
+
});
|
|
113
|
+
const actualSha = stdout.trim();
|
|
119
114
|
fs.rmSync(path.join(tmpDir, ".git"), { recursive: true, force: true });
|
|
120
115
|
const finalDest = this.shaDir(srcDir, actualSha);
|
|
121
116
|
if (fs.existsSync(finalDest)) {
|
|
@@ -130,10 +125,21 @@ class PluginCache {
|
|
|
130
125
|
if (!subdir.startsWith(path.resolve(tmpDir) + path.sep) || !fs.existsSync(subdir)) {
|
|
131
126
|
throw new Error(`Plugin subdirectory not found: ${source.path}`);
|
|
132
127
|
}
|
|
133
|
-
|
|
128
|
+
try {
|
|
129
|
+
fs.renameSync(subdir, finalDest);
|
|
130
|
+
} catch (error) {
|
|
131
|
+
if (!fs.existsSync(finalDest))
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
134
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
135
135
|
} else {
|
|
136
|
-
|
|
136
|
+
try {
|
|
137
|
+
fs.renameSync(tmpDir, finalDest);
|
|
138
|
+
} catch (error) {
|
|
139
|
+
if (!fs.existsSync(finalDest))
|
|
140
|
+
throw error;
|
|
141
|
+
return { source: source.original, name: pluginName, sha: actualSha, root: finalDest };
|
|
142
|
+
}
|
|
137
143
|
}
|
|
138
144
|
this.updateRef(srcDir, actualSha);
|
|
139
145
|
return { source: source.original, name: pluginName, sha: actualSha, root: finalDest };
|
|
@@ -357,7 +363,10 @@ class PluginDataManager {
|
|
|
357
363
|
// src/core/plugin-lifecycle.ts
|
|
358
364
|
import fs3 from "node:fs";
|
|
359
365
|
import path3 from "node:path";
|
|
360
|
-
import {
|
|
366
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
367
|
+
import { promisify as promisify2 } from "node:util";
|
|
368
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
369
|
+
|
|
361
370
|
class PluginLifecycle {
|
|
362
371
|
dataManager;
|
|
363
372
|
constructor() {
|
|
@@ -373,9 +382,8 @@ class PluginLifecycle {
|
|
|
373
382
|
const pkg = JSON.parse(fs3.readFileSync(pkgJsonPath, "utf8"));
|
|
374
383
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
375
384
|
if (Object.keys(deps).length > 0) {
|
|
376
|
-
|
|
385
|
+
await execFileAsync2("npm", ["install", "--production", "--ignore-scripts"], {
|
|
377
386
|
cwd: pluginRoot,
|
|
378
|
-
stdio: "pipe",
|
|
379
387
|
timeout: 120000
|
|
380
388
|
});
|
|
381
389
|
}
|
|
@@ -384,8 +392,7 @@ class PluginLifecycle {
|
|
|
384
392
|
const reqPath = path3.join(pluginRoot, "requirements.txt");
|
|
385
393
|
if (fs3.existsSync(reqPath)) {
|
|
386
394
|
try {
|
|
387
|
-
|
|
388
|
-
stdio: "pipe",
|
|
395
|
+
await execFileAsync2("pip", ["install", "-r", reqPath], {
|
|
389
396
|
timeout: 120000
|
|
390
397
|
});
|
|
391
398
|
} catch {}
|
|
@@ -450,10 +457,7 @@ function parseSource(source) {
|
|
|
450
457
|
original: source
|
|
451
458
|
};
|
|
452
459
|
}
|
|
453
|
-
|
|
454
|
-
type: "git",
|
|
455
|
-
original: source
|
|
456
|
-
};
|
|
460
|
+
throw new Error(`Unrecognized plugin source: ${source}`);
|
|
457
461
|
}
|
|
458
462
|
|
|
459
463
|
// src/core/plugin-parse.ts
|
|
@@ -462,7 +466,8 @@ import path4 from "node:path";
|
|
|
462
466
|
import { z } from "zod";
|
|
463
467
|
var CLAUDE_PLUGIN_JSON_REL = path4.join(".claude-plugin", "plugin.json");
|
|
464
468
|
var CODEX_PLUGIN_JSON_REL = path4.join(".codex-plugin", "plugin.json");
|
|
465
|
-
var
|
|
469
|
+
var AGENT_PLUGIN_JSON_REL = "plugin.json";
|
|
470
|
+
var MCP_JSON_RELS = ["mcp.json", ".mcp.json"];
|
|
466
471
|
var HOOKS_JSON_REL = path4.join("hooks", "hooks.json");
|
|
467
472
|
var SKILLS_DIR = "skills";
|
|
468
473
|
var COMMANDS_DIR = "commands";
|
|
@@ -498,7 +503,7 @@ var manifestSchema = z.object({
|
|
|
498
503
|
}).passthrough().optional()
|
|
499
504
|
}).passthrough();
|
|
500
505
|
function manifestPath(pluginRoot) {
|
|
501
|
-
for (const rel of [CLAUDE_PLUGIN_JSON_REL, CODEX_PLUGIN_JSON_REL]) {
|
|
506
|
+
for (const rel of [AGENT_PLUGIN_JSON_REL, CLAUDE_PLUGIN_JSON_REL, CODEX_PLUGIN_JSON_REL]) {
|
|
502
507
|
const candidate = path4.join(pluginRoot, rel);
|
|
503
508
|
if (fs4.existsSync(candidate))
|
|
504
509
|
return candidate;
|
|
@@ -687,7 +692,7 @@ function discoverHooks(pluginRoot, raw) {
|
|
|
687
692
|
}
|
|
688
693
|
function discoverMcpServers(pluginRoot, raw) {
|
|
689
694
|
const declared = typeof raw?.mcpServers === "string" ? raw.mcpServers : undefined;
|
|
690
|
-
const mcpPath = declared ? resolveWithinRoot(pluginRoot, declared) : path4.join(pluginRoot,
|
|
695
|
+
const mcpPath = declared ? resolveWithinRoot(pluginRoot, declared) : MCP_JSON_RELS.map((rel) => path4.join(pluginRoot, rel)).find(fs4.existsSync) ?? null;
|
|
691
696
|
if (!mcpPath || !fs4.existsSync(mcpPath))
|
|
692
697
|
return [];
|
|
693
698
|
try {
|
|
@@ -724,7 +729,7 @@ function hasManifest(pluginRoot) {
|
|
|
724
729
|
return manifestPath(pluginRoot) !== null;
|
|
725
730
|
}
|
|
726
731
|
function resolvePluginVars(value, pluginRoot, dataDir) {
|
|
727
|
-
return value.replace(/\$\{CLAUDE_PLUGIN_ROOT\}/g, pluginRoot).replace(/\$\{DATA\}/g, dataDir).replace(/\$\{CLAUDE_PROJECT_DIR\}/g, pluginRoot);
|
|
732
|
+
return value.replace(/\$\{CLAUDE_PLUGIN_ROOT\}/g, pluginRoot).replace(/\$\{CLAUDE_PLUGIN_DATA\}/g, dataDir).replace(/\$\{MCPX_PLUGIN_ROOT\}/g, pluginRoot).replace(/\$\{MCPX_PLUGIN_DATA\}/g, dataDir).replace(/\$\{DATA\}/g, dataDir).replace(/\$\{CLAUDE_PROJECT_DIR\}/g, pluginRoot);
|
|
728
733
|
}
|
|
729
734
|
|
|
730
735
|
// src/core/config-store.ts
|
|
@@ -997,7 +1002,7 @@ function detectManagedEntryDrift(managedIndex, clientId, entryName, existingValu
|
|
|
997
1002
|
// src/adapters/claude.ts
|
|
998
1003
|
import fs8 from "node:fs";
|
|
999
1004
|
import path8 from "node:path";
|
|
1000
|
-
import { z as
|
|
1005
|
+
import { z as z4 } from "zod";
|
|
1001
1006
|
|
|
1002
1007
|
// src/core/plugin-projections.ts
|
|
1003
1008
|
import fs6 from "node:fs";
|
|
@@ -1021,6 +1026,17 @@ function saveOwnership(targetDir, manifest) {
|
|
|
1021
1026
|
}
|
|
1022
1027
|
function recordOwned(targetDir, pluginId, paths) {
|
|
1023
1028
|
const manifest = loadOwnership(targetDir);
|
|
1029
|
+
const previous = manifest.plugins[pluginId]?.paths ?? [];
|
|
1030
|
+
const next = new Set(paths);
|
|
1031
|
+
for (const stale of previous) {
|
|
1032
|
+
if (next.has(stale))
|
|
1033
|
+
continue;
|
|
1034
|
+
const stalePath = path6.join(targetDir, stale);
|
|
1035
|
+
try {
|
|
1036
|
+
assertWithinBase(targetDir, stalePath);
|
|
1037
|
+
fs6.rmSync(stalePath, { recursive: true, force: true });
|
|
1038
|
+
} catch {}
|
|
1039
|
+
}
|
|
1024
1040
|
manifest.plugins[pluginId] = {
|
|
1025
1041
|
pluginId,
|
|
1026
1042
|
paths,
|
|
@@ -1043,6 +1059,7 @@ function prunePluginProjections(targetDir, pluginId) {
|
|
|
1043
1059
|
for (const p of paths) {
|
|
1044
1060
|
const fullPath = path6.join(targetDir, p);
|
|
1045
1061
|
try {
|
|
1062
|
+
assertWithinBase(targetDir, fullPath);
|
|
1046
1063
|
if (fs6.existsSync(fullPath)) {
|
|
1047
1064
|
fs6.rmSync(fullPath, { recursive: true, force: true });
|
|
1048
1065
|
}
|
|
@@ -1099,6 +1116,7 @@ function syncClaude(plugins) {
|
|
|
1099
1116
|
continue;
|
|
1100
1117
|
}
|
|
1101
1118
|
const targetDir = path6.join(targetBase, plugin.pluginName);
|
|
1119
|
+
assertWithinBase(targetBase, targetDir);
|
|
1102
1120
|
ensureDir(targetDir);
|
|
1103
1121
|
const mcpJsonSrc = path6.join(plugin.pluginRoot, ".mcp.json");
|
|
1104
1122
|
const mcpJsonDest = path6.join(targetDir, ".mcp.json");
|
|
@@ -1125,226 +1143,70 @@ function syncClaudeDesktop(plugins) {
|
|
|
1125
1143
|
unsupported
|
|
1126
1144
|
};
|
|
1127
1145
|
}
|
|
1128
|
-
function
|
|
1129
|
-
const targetBase = path6.join(homeDir(), ".codex", "skills");
|
|
1146
|
+
function syncNamespacedComponents(config, plugins) {
|
|
1130
1147
|
const projectedDirs = [];
|
|
1131
|
-
const unsupported = ["hooks", "agents"];
|
|
1132
1148
|
const errors = [];
|
|
1133
|
-
|
|
1134
|
-
if (!
|
|
1135
|
-
|
|
1136
|
-
if (plugin.components.skills
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
for (const skill of plugin.skills) {
|
|
1140
|
-
try {
|
|
1141
|
-
const targetDir = path6.join(targetBase, nsName(plugin.pluginName, skill.id));
|
|
1142
|
-
assertWithinBase(targetBase, targetDir);
|
|
1143
|
-
ensureDir(targetDir);
|
|
1144
|
-
copyFileOrDir(skill.path, path6.join(targetDir, "SKILL.md"));
|
|
1145
|
-
owned.push(nsName(plugin.pluginName, skill.id));
|
|
1146
|
-
projectedDirs.push(targetDir);
|
|
1147
|
-
} catch (error) {
|
|
1148
|
-
errors.push(`${plugin.pluginName}/${skill.id}: ${error.message}`);
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
recordOwned(targetBase, plugin.pluginId, owned);
|
|
1152
|
-
}
|
|
1153
|
-
}
|
|
1154
|
-
return {
|
|
1155
|
-
clientId: "codex",
|
|
1156
|
-
status: "SYNCED",
|
|
1157
|
-
projectedDirs,
|
|
1158
|
-
unsupported,
|
|
1159
|
-
...errors.length > 0 ? { error: errors.join("; ") } : {}
|
|
1160
|
-
};
|
|
1161
|
-
}
|
|
1162
|
-
function syncCursor(plugins) {
|
|
1163
|
-
const skillsBase = path6.join(homeDir(), ".cursor", "skills");
|
|
1164
|
-
const commandsBase = path6.join(homeDir(), ".cursor", "commands");
|
|
1165
|
-
const projectedDirs = [];
|
|
1166
|
-
const unsupported = ["hooks", "agents"];
|
|
1167
|
-
const errors = [];
|
|
1168
|
-
for (const plugin of plugins) {
|
|
1169
|
-
if (!plugin.enabled)
|
|
1170
|
-
continue;
|
|
1171
|
-
const ownedSkills = [];
|
|
1172
|
-
const ownedCommands = [];
|
|
1173
|
-
if (plugin.components.skills && isComponentApproved(plugin, "skills")) {
|
|
1174
|
-
ensureDir(skillsBase);
|
|
1175
|
-
for (const skill of plugin.skills) {
|
|
1176
|
-
try {
|
|
1177
|
-
const targetDir = path6.join(skillsBase, nsName(plugin.pluginName, skill.id));
|
|
1178
|
-
assertWithinBase(skillsBase, targetDir);
|
|
1179
|
-
ensureDir(targetDir);
|
|
1180
|
-
copyFileOrDir(skill.path, path6.join(targetDir, "SKILL.md"));
|
|
1181
|
-
ownedSkills.push(nsName(plugin.pluginName, skill.id));
|
|
1182
|
-
projectedDirs.push(targetDir);
|
|
1183
|
-
} catch (error) {
|
|
1184
|
-
errors.push(`${plugin.pluginName}/${skill.id}: ${error.message}`);
|
|
1185
|
-
}
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
if (plugin.components.commands && isComponentApproved(plugin, "commands")) {
|
|
1189
|
-
ensureDir(commandsBase);
|
|
1190
|
-
for (const cmd of plugin.commands) {
|
|
1191
|
-
try {
|
|
1192
|
-
const targetPath = path6.join(commandsBase, `${nsName(plugin.pluginName, cmd.id)}.md`);
|
|
1193
|
-
assertWithinBase(commandsBase, targetPath);
|
|
1194
|
-
copyFileOrDir(cmd.path, targetPath);
|
|
1195
|
-
ownedCommands.push(`${nsName(plugin.pluginName, cmd.id)}.md`);
|
|
1196
|
-
projectedDirs.push(targetPath);
|
|
1197
|
-
} catch (error) {
|
|
1198
|
-
errors.push(`${plugin.pluginName}/${cmd.id}: ${error.message}`);
|
|
1199
|
-
}
|
|
1149
|
+
const syncSkills = (plugin) => {
|
|
1150
|
+
if (!config.skillsBase)
|
|
1151
|
+
return;
|
|
1152
|
+
if (!plugin.components.skills || !isComponentApproved(plugin, "skills")) {
|
|
1153
|
+
if (fs6.existsSync(config.skillsBase)) {
|
|
1154
|
+
recordOwned(config.skillsBase, plugin.pluginId, []);
|
|
1200
1155
|
}
|
|
1156
|
+
return;
|
|
1201
1157
|
}
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
}
|
|
1215
|
-
function syncVsCode(plugins) {
|
|
1216
|
-
const skillsBase = path6.join(homeDir(), ".copilot", "skills");
|
|
1217
|
-
const projectedDirs = [];
|
|
1218
|
-
const unsupported = ["hooks", "agents"];
|
|
1219
|
-
const errors = [];
|
|
1220
|
-
for (const plugin of plugins) {
|
|
1221
|
-
if (!plugin.enabled)
|
|
1222
|
-
continue;
|
|
1223
|
-
if (plugin.components.skills && isComponentApproved(plugin, "skills")) {
|
|
1224
|
-
ensureDir(skillsBase);
|
|
1225
|
-
const owned = [];
|
|
1226
|
-
for (const skill of plugin.skills) {
|
|
1227
|
-
try {
|
|
1228
|
-
const targetDir = path6.join(skillsBase, nsName(plugin.pluginName, skill.id));
|
|
1229
|
-
assertWithinBase(skillsBase, targetDir);
|
|
1230
|
-
ensureDir(targetDir);
|
|
1231
|
-
copyFileOrDir(skill.path, path6.join(targetDir, "SKILL.md"));
|
|
1232
|
-
owned.push(nsName(plugin.pluginName, skill.id));
|
|
1233
|
-
projectedDirs.push(targetDir);
|
|
1234
|
-
} catch (error) {
|
|
1235
|
-
errors.push(`${plugin.pluginName}/${skill.id}: ${error.message}`);
|
|
1236
|
-
}
|
|
1158
|
+
ensureDir(config.skillsBase);
|
|
1159
|
+
const owned = [];
|
|
1160
|
+
for (const skill of plugin.skills) {
|
|
1161
|
+
try {
|
|
1162
|
+
const name = nsName(plugin.pluginName, skill.id);
|
|
1163
|
+
const targetDir = path6.join(config.skillsBase, name);
|
|
1164
|
+
assertWithinBase(config.skillsBase, targetDir);
|
|
1165
|
+
ensureDir(targetDir);
|
|
1166
|
+
copyFileOrDir(skill.path, path6.join(targetDir, "SKILL.md"));
|
|
1167
|
+
owned.push(name);
|
|
1168
|
+
projectedDirs.push(targetDir);
|
|
1169
|
+
} catch (error) {
|
|
1170
|
+
errors.push(`${plugin.pluginName}/${skill.id}: ${error.message}`);
|
|
1237
1171
|
}
|
|
1238
|
-
recordOwned(skillsBase, plugin.pluginId, owned);
|
|
1239
1172
|
}
|
|
1240
|
-
|
|
1241
|
-
return {
|
|
1242
|
-
clientId: "vscode",
|
|
1243
|
-
status: "SYNCED",
|
|
1244
|
-
projectedDirs,
|
|
1245
|
-
unsupported,
|
|
1246
|
-
...errors.length > 0 ? { error: errors.join("; ") } : {}
|
|
1173
|
+
recordOwned(config.skillsBase, plugin.pluginId, owned);
|
|
1247
1174
|
};
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
for (const plugin of plugins) {
|
|
1255
|
-
if (!plugin.enabled)
|
|
1256
|
-
continue;
|
|
1257
|
-
if (plugin.components.skills && isComponentApproved(plugin, "skills")) {
|
|
1258
|
-
ensureDir(targetBase);
|
|
1259
|
-
const owned = [];
|
|
1260
|
-
for (const skill of plugin.skills) {
|
|
1261
|
-
try {
|
|
1262
|
-
const targetDir = path6.join(targetBase, nsName(plugin.pluginName, skill.id));
|
|
1263
|
-
assertWithinBase(targetBase, targetDir);
|
|
1264
|
-
ensureDir(targetDir);
|
|
1265
|
-
copyFileOrDir(skill.path, path6.join(targetDir, "SKILL.md"));
|
|
1266
|
-
owned.push(nsName(plugin.pluginName, skill.id));
|
|
1267
|
-
projectedDirs.push(targetDir);
|
|
1268
|
-
} catch (error) {
|
|
1269
|
-
errors.push(`${plugin.pluginName}/${skill.id}: ${error.message}`);
|
|
1270
|
-
}
|
|
1175
|
+
const syncCommands = (plugin) => {
|
|
1176
|
+
if (!config.commandsBase)
|
|
1177
|
+
return;
|
|
1178
|
+
if (!plugin.components.commands || !isComponentApproved(plugin, "commands")) {
|
|
1179
|
+
if (fs6.existsSync(config.commandsBase)) {
|
|
1180
|
+
recordOwned(config.commandsBase, plugin.pluginId, []);
|
|
1271
1181
|
}
|
|
1272
|
-
|
|
1182
|
+
return;
|
|
1273
1183
|
}
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
const projectedDirs = [];
|
|
1286
|
-
const unsupported = ["hooks", "agents", "commands"];
|
|
1287
|
-
const errors = [];
|
|
1288
|
-
for (const plugin of plugins) {
|
|
1289
|
-
if (!plugin.enabled)
|
|
1290
|
-
continue;
|
|
1291
|
-
if (plugin.components.skills && isComponentApproved(plugin, "skills")) {
|
|
1292
|
-
ensureDir(targetBase);
|
|
1293
|
-
const owned = [];
|
|
1294
|
-
for (const skill of plugin.skills) {
|
|
1295
|
-
try {
|
|
1296
|
-
const targetDir = path6.join(targetBase, nsName(plugin.pluginName, skill.id));
|
|
1297
|
-
assertWithinBase(targetBase, targetDir);
|
|
1298
|
-
ensureDir(targetDir);
|
|
1299
|
-
copyFileOrDir(skill.path, path6.join(targetDir, "SKILL.md"));
|
|
1300
|
-
owned.push(nsName(plugin.pluginName, skill.id));
|
|
1301
|
-
projectedDirs.push(targetDir);
|
|
1302
|
-
} catch (error) {
|
|
1303
|
-
errors.push(`${plugin.pluginName}/${skill.id}: ${error.message}`);
|
|
1304
|
-
}
|
|
1184
|
+
ensureDir(config.commandsBase);
|
|
1185
|
+
const owned = [];
|
|
1186
|
+
for (const command of plugin.commands) {
|
|
1187
|
+
try {
|
|
1188
|
+
const targetPath = path6.join(config.commandsBase, `${nsName(plugin.pluginName, command.id)}.md`);
|
|
1189
|
+
assertWithinBase(config.commandsBase, targetPath);
|
|
1190
|
+
copyFileOrDir(command.path, targetPath);
|
|
1191
|
+
owned.push(path6.basename(targetPath));
|
|
1192
|
+
projectedDirs.push(targetPath);
|
|
1193
|
+
} catch (error) {
|
|
1194
|
+
errors.push(`${plugin.pluginName}/${command.id}: ${error.message}`);
|
|
1305
1195
|
}
|
|
1306
|
-
recordOwned(targetBase, plugin.pluginId, owned);
|
|
1307
1196
|
}
|
|
1308
|
-
|
|
1309
|
-
return {
|
|
1310
|
-
clientId: "cline",
|
|
1311
|
-
status: "SYNCED",
|
|
1312
|
-
projectedDirs,
|
|
1313
|
-
unsupported,
|
|
1314
|
-
...errors.length > 0 ? { error: errors.join("; ") } : {}
|
|
1197
|
+
recordOwned(config.commandsBase, plugin.pluginId, owned);
|
|
1315
1198
|
};
|
|
1316
|
-
}
|
|
1317
|
-
function syncKiro(plugins) {
|
|
1318
|
-
const targetBase = path6.join(homeDir(), ".kiro", "skills");
|
|
1319
|
-
const projectedDirs = [];
|
|
1320
|
-
const unsupported = ["hooks", "agents", "commands"];
|
|
1321
|
-
const errors = [];
|
|
1322
1199
|
for (const plugin of plugins) {
|
|
1323
1200
|
if (!plugin.enabled)
|
|
1324
1201
|
continue;
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
const owned = [];
|
|
1328
|
-
for (const skill of plugin.skills) {
|
|
1329
|
-
try {
|
|
1330
|
-
const targetDir = path6.join(targetBase, nsName(plugin.pluginName, skill.id));
|
|
1331
|
-
assertWithinBase(targetBase, targetDir);
|
|
1332
|
-
ensureDir(targetDir);
|
|
1333
|
-
copyFileOrDir(skill.path, path6.join(targetDir, "SKILL.md"));
|
|
1334
|
-
owned.push(nsName(plugin.pluginName, skill.id));
|
|
1335
|
-
projectedDirs.push(targetDir);
|
|
1336
|
-
} catch (error) {
|
|
1337
|
-
errors.push(`${plugin.pluginName}/${skill.id}: ${error.message}`);
|
|
1338
|
-
}
|
|
1339
|
-
}
|
|
1340
|
-
recordOwned(targetBase, plugin.pluginId, owned);
|
|
1341
|
-
}
|
|
1202
|
+
syncSkills(plugin);
|
|
1203
|
+
syncCommands(plugin);
|
|
1342
1204
|
}
|
|
1343
1205
|
return {
|
|
1344
|
-
clientId:
|
|
1206
|
+
clientId: config.clientId,
|
|
1345
1207
|
status: "SYNCED",
|
|
1346
1208
|
projectedDirs,
|
|
1347
|
-
unsupported,
|
|
1209
|
+
unsupported: config.unsupported,
|
|
1348
1210
|
...errors.length > 0 ? { error: errors.join("; ") } : {}
|
|
1349
1211
|
};
|
|
1350
1212
|
}
|
|
@@ -1361,12 +1223,12 @@ function syncPluginsToClient(clientId, plugins) {
|
|
|
1361
1223
|
const syncMap = {
|
|
1362
1224
|
claude: syncClaude,
|
|
1363
1225
|
"claude-desktop": syncClaudeDesktop,
|
|
1364
|
-
codex:
|
|
1365
|
-
cursor:
|
|
1366
|
-
vscode:
|
|
1367
|
-
qwen:
|
|
1368
|
-
cline:
|
|
1369
|
-
kiro:
|
|
1226
|
+
codex: (plugins2) => syncNamespacedComponents({ clientId: "codex", skillsBase: path6.join(homeDir(), ".codex", "skills"), unsupported: ["hooks", "agents"] }, plugins2),
|
|
1227
|
+
cursor: (plugins2) => syncNamespacedComponents({ clientId: "cursor", skillsBase: path6.join(homeDir(), ".cursor", "skills"), commandsBase: path6.join(homeDir(), ".cursor", "commands"), unsupported: ["hooks", "agents"] }, plugins2),
|
|
1228
|
+
vscode: (plugins2) => syncNamespacedComponents({ clientId: "vscode", skillsBase: path6.join(homeDir(), ".copilot", "skills"), unsupported: ["hooks", "agents"] }, plugins2),
|
|
1229
|
+
qwen: (plugins2) => syncNamespacedComponents({ clientId: "qwen", skillsBase: path6.join(homeDir(), ".qwen", "skills"), unsupported: ["hooks", "agents", "commands"] }, plugins2),
|
|
1230
|
+
cline: (plugins2) => syncNamespacedComponents({ clientId: "cline", skillsBase: path6.join(homeDir(), ".config", "cline", "skills"), unsupported: ["hooks", "agents", "commands"] }, plugins2),
|
|
1231
|
+
kiro: (plugins2) => syncNamespacedComponents({ clientId: "kiro", skillsBase: path6.join(homeDir(), ".kiro", "skills"), unsupported: ["hooks", "agents", "commands"] }, plugins2),
|
|
1370
1232
|
opencode: syncOpenCode
|
|
1371
1233
|
};
|
|
1372
1234
|
const syncFn = syncMap[clientId];
|
|
@@ -1377,7 +1239,7 @@ function syncPluginsToClient(clientId, plugins) {
|
|
|
1377
1239
|
claude: path6.join(homeDir(), ".claude", "skills"),
|
|
1378
1240
|
"claude-desktop": "",
|
|
1379
1241
|
codex: path6.join(homeDir(), ".codex", "skills"),
|
|
1380
|
-
cursor: path6.join(homeDir(), ".cursor"),
|
|
1242
|
+
cursor: path6.join(homeDir(), ".cursor", "skills"),
|
|
1381
1243
|
vscode: path6.join(homeDir(), ".copilot", "skills"),
|
|
1382
1244
|
qwen: path6.join(homeDir(), ".qwen", "skills"),
|
|
1383
1245
|
cline: path6.join(homeDir(), ".config", "cline", "skills"),
|
|
@@ -1388,6 +1250,9 @@ function syncPluginsToClient(clientId, plugins) {
|
|
|
1388
1250
|
if (target) {
|
|
1389
1251
|
const enabledIds = plugins.filter((p) => p.enabled).map((p) => p.pluginId);
|
|
1390
1252
|
pruneUnlistedPlugins(target, enabledIds);
|
|
1253
|
+
if (clientId === "cursor") {
|
|
1254
|
+
pruneUnlistedPlugins(path6.join(homeDir(), ".cursor", "commands"), enabledIds);
|
|
1255
|
+
}
|
|
1391
1256
|
}
|
|
1392
1257
|
return syncFn(plugins);
|
|
1393
1258
|
}
|
|
@@ -1427,18 +1292,53 @@ function copyFileOrDir(src, dest, errors) {
|
|
|
1427
1292
|
// src/core/skill-projections.ts
|
|
1428
1293
|
import fs7 from "node:fs";
|
|
1429
1294
|
import path7 from "node:path";
|
|
1295
|
+
import { z as z3 } from "zod";
|
|
1296
|
+
|
|
1297
|
+
// src/core/identifiers.ts
|
|
1298
|
+
import { z as z2 } from "zod";
|
|
1299
|
+
var skillIdSchema = z2.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/);
|
|
1300
|
+
function validateSkillId(id) {
|
|
1301
|
+
const result = skillIdSchema.safeParse(id);
|
|
1302
|
+
if (!result.success) {
|
|
1303
|
+
throw new Error(`Invalid skill id "${id}". Use letters, numbers, dots, underscores, and hyphens.`);
|
|
1304
|
+
}
|
|
1305
|
+
return id;
|
|
1306
|
+
}
|
|
1307
|
+
function normalizePluginName(name) {
|
|
1308
|
+
const normalized = name.replace(/[\\/]/g, "_").replace(/^\.+/, "").replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
1309
|
+
if (!normalized) {
|
|
1310
|
+
throw new Error(`Invalid plugin name "${name}"`);
|
|
1311
|
+
}
|
|
1312
|
+
return normalized;
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
// src/core/skill-projections.ts
|
|
1430
1316
|
var OWNERSHIP_MANIFEST2 = "mcpx-skills.json";
|
|
1317
|
+
var ownershipSchema = z3.object({
|
|
1318
|
+
version: z3.literal(1),
|
|
1319
|
+
paths: z3.array(z3.string())
|
|
1320
|
+
});
|
|
1321
|
+
function assertWithinBase2(base, target) {
|
|
1322
|
+
const resolvedBase = path7.resolve(base);
|
|
1323
|
+
const resolvedTarget = path7.resolve(target);
|
|
1324
|
+
if (resolvedTarget !== resolvedBase && !resolvedTarget.startsWith(resolvedBase + path7.sep)) {
|
|
1325
|
+
throw new Error(`Refusing to access path outside projection base: ${target}`);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1431
1328
|
function loadOwnership2(targetDir) {
|
|
1432
1329
|
const manifestPath2 = path7.join(targetDir, OWNERSHIP_MANIFEST2);
|
|
1433
1330
|
if (!fs7.existsSync(manifestPath2)) {
|
|
1434
1331
|
return { version: 1, paths: [] };
|
|
1435
1332
|
}
|
|
1436
1333
|
try {
|
|
1437
|
-
|
|
1334
|
+
const parsed = ownershipSchema.safeParse(JSON.parse(fs7.readFileSync(manifestPath2, "utf8")));
|
|
1335
|
+
if (parsed.success)
|
|
1336
|
+
return parsed.data;
|
|
1438
1337
|
} catch {
|
|
1439
1338
|
console.error(`[mcpx] Warning: corrupt ownership manifest at ${manifestPath2}, resetting`);
|
|
1440
1339
|
return { version: 1, paths: [] };
|
|
1441
1340
|
}
|
|
1341
|
+
return { version: 1, paths: [] };
|
|
1442
1342
|
}
|
|
1443
1343
|
function saveOwnership2(targetDir, manifest) {
|
|
1444
1344
|
ensureDir(targetDir);
|
|
@@ -1451,11 +1351,13 @@ function projectSkillsToDir(targetDir, skills, layout) {
|
|
|
1451
1351
|
if (!newPaths.has(ownedPath)) {
|
|
1452
1352
|
const fullPath = path7.join(targetDir, ownedPath);
|
|
1453
1353
|
try {
|
|
1354
|
+
assertWithinBase2(targetDir, fullPath);
|
|
1454
1355
|
if (fs7.existsSync(fullPath)) {
|
|
1455
1356
|
fs7.rmSync(fullPath, { recursive: true, force: true });
|
|
1456
1357
|
}
|
|
1457
1358
|
if (layout === "dir") {
|
|
1458
1359
|
const dirPath = path7.join(targetDir, path7.dirname(ownedPath));
|
|
1360
|
+
assertWithinBase2(targetDir, dirPath);
|
|
1459
1361
|
try {
|
|
1460
1362
|
const remaining = fs7.readdirSync(dirPath);
|
|
1461
1363
|
if (remaining.length === 0) {
|
|
@@ -1469,7 +1371,9 @@ function projectSkillsToDir(targetDir, skills, layout) {
|
|
|
1469
1371
|
}
|
|
1470
1372
|
}
|
|
1471
1373
|
for (const skill of skills) {
|
|
1374
|
+
validateSkillId(skill.id);
|
|
1472
1375
|
const filePath = layout === "dir" ? path7.join(targetDir, skill.id, "SKILL.md") : path7.join(targetDir, `${skill.id}.md`);
|
|
1376
|
+
assertWithinBase2(targetDir, filePath);
|
|
1473
1377
|
ensureDir(path7.dirname(filePath));
|
|
1474
1378
|
fs7.writeFileSync(filePath, skill.content, "utf-8");
|
|
1475
1379
|
}
|
|
@@ -1493,16 +1397,16 @@ function syncProjectDisabledArray(project, managedNames, disabledForThisProject)
|
|
|
1493
1397
|
delete project.disabledMcpServers;
|
|
1494
1398
|
}
|
|
1495
1399
|
}
|
|
1496
|
-
var stringMapSchema =
|
|
1497
|
-
var claudeEntrySchema =
|
|
1498
|
-
type:
|
|
1499
|
-
url:
|
|
1400
|
+
var stringMapSchema = z4.record(z4.string(), z4.string());
|
|
1401
|
+
var claudeEntrySchema = z4.object({
|
|
1402
|
+
type: z4.string().optional(),
|
|
1403
|
+
url: z4.string().min(1).optional(),
|
|
1500
1404
|
headers: stringMapSchema.optional(),
|
|
1501
|
-
command:
|
|
1502
|
-
args:
|
|
1405
|
+
command: z4.string().min(1).optional(),
|
|
1406
|
+
args: z4.array(z4.string()).optional(),
|
|
1503
1407
|
env: stringMapSchema.optional(),
|
|
1504
|
-
cwd:
|
|
1505
|
-
disabled:
|
|
1408
|
+
cwd: z4.string().min(1).optional(),
|
|
1409
|
+
disabled: z4.boolean().optional()
|
|
1506
1410
|
}).passthrough();
|
|
1507
1411
|
|
|
1508
1412
|
class ClaudeAdapter {
|
|
@@ -1643,29 +1547,29 @@ class ClaudeAdapter {
|
|
|
1643
1547
|
}
|
|
1644
1548
|
|
|
1645
1549
|
// src/adapters/claude-desktop.ts
|
|
1646
|
-
import { execFileSync
|
|
1550
|
+
import { execFileSync } from "node:child_process";
|
|
1647
1551
|
import path9 from "node:path";
|
|
1648
|
-
import { z as
|
|
1552
|
+
import { z as z5 } from "zod";
|
|
1649
1553
|
|
|
1650
1554
|
// src/version.ts
|
|
1651
1555
|
var APP_VERSION = "0.1.3";
|
|
1652
1556
|
|
|
1653
1557
|
// src/adapters/claude-desktop.ts
|
|
1654
|
-
var stringMapSchema2 =
|
|
1655
|
-
var claudeDesktopEntrySchema =
|
|
1656
|
-
type:
|
|
1657
|
-
url:
|
|
1558
|
+
var stringMapSchema2 = z5.record(z5.string(), z5.string());
|
|
1559
|
+
var claudeDesktopEntrySchema = z5.object({
|
|
1560
|
+
type: z5.string().optional(),
|
|
1561
|
+
url: z5.string().min(1).optional(),
|
|
1658
1562
|
headers: stringMapSchema2.optional(),
|
|
1659
|
-
command:
|
|
1660
|
-
args:
|
|
1563
|
+
command: z5.string().min(1).optional(),
|
|
1564
|
+
args: z5.array(z5.string()).optional(),
|
|
1661
1565
|
env: stringMapSchema2.optional(),
|
|
1662
|
-
cwd:
|
|
1663
|
-
disabled:
|
|
1566
|
+
cwd: z5.string().min(1).optional(),
|
|
1567
|
+
disabled: z5.boolean().optional()
|
|
1664
1568
|
}).passthrough();
|
|
1665
1569
|
function buildProxyEntry(entry) {
|
|
1666
1570
|
const upstreamName = entry.name.replace(/ \(mcpx\)$/, "");
|
|
1667
1571
|
try {
|
|
1668
|
-
const mcpxPath =
|
|
1572
|
+
const mcpxPath = execFileSync("which", ["mcpx"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
1669
1573
|
if (mcpxPath)
|
|
1670
1574
|
return { command: mcpxPath, args: ["proxy", upstreamName] };
|
|
1671
1575
|
} catch {}
|
|
@@ -1793,22 +1697,22 @@ class ClaudeDesktopAdapter {
|
|
|
1793
1697
|
import path10 from "node:path";
|
|
1794
1698
|
import fs9 from "node:fs";
|
|
1795
1699
|
import { parse, stringify } from "@iarna/toml";
|
|
1796
|
-
import { z as
|
|
1797
|
-
var stringMapSchema3 =
|
|
1700
|
+
import { z as z6 } from "zod";
|
|
1701
|
+
var stringMapSchema3 = z6.preprocess((value) => {
|
|
1798
1702
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1799
1703
|
return value;
|
|
1800
1704
|
}
|
|
1801
1705
|
return Object.fromEntries(Object.entries(value));
|
|
1802
|
-
},
|
|
1803
|
-
var codexEntrySchema =
|
|
1804
|
-
enabled:
|
|
1805
|
-
url:
|
|
1706
|
+
}, z6.record(z6.string(), z6.string()));
|
|
1707
|
+
var codexEntrySchema = z6.object({
|
|
1708
|
+
enabled: z6.boolean().optional(),
|
|
1709
|
+
url: z6.string().min(1).optional(),
|
|
1806
1710
|
headers: stringMapSchema3.optional(),
|
|
1807
1711
|
http_headers: stringMapSchema3.optional(),
|
|
1808
|
-
command:
|
|
1809
|
-
args:
|
|
1712
|
+
command: z6.string().min(1).optional(),
|
|
1713
|
+
args: z6.array(z6.string()).optional(),
|
|
1810
1714
|
env: stringMapSchema3.optional(),
|
|
1811
|
-
cwd:
|
|
1715
|
+
cwd: z6.string().min(1).optional()
|
|
1812
1716
|
}).passthrough();
|
|
1813
1717
|
|
|
1814
1718
|
class CodexAdapter {
|
|
@@ -1923,17 +1827,17 @@ class CodexAdapter {
|
|
|
1923
1827
|
|
|
1924
1828
|
// src/adapters/cursor.ts
|
|
1925
1829
|
import path11 from "node:path";
|
|
1926
|
-
import { z as
|
|
1927
|
-
var stringMapSchema4 =
|
|
1928
|
-
var cursorEntrySchema =
|
|
1929
|
-
type:
|
|
1930
|
-
url:
|
|
1830
|
+
import { z as z7 } from "zod";
|
|
1831
|
+
var stringMapSchema4 = z7.record(z7.string(), z7.string());
|
|
1832
|
+
var cursorEntrySchema = z7.object({
|
|
1833
|
+
type: z7.string().optional(),
|
|
1834
|
+
url: z7.string().min(1).optional(),
|
|
1931
1835
|
headers: stringMapSchema4.optional(),
|
|
1932
|
-
command:
|
|
1933
|
-
args:
|
|
1836
|
+
command: z7.string().min(1).optional(),
|
|
1837
|
+
args: z7.array(z7.string()).optional(),
|
|
1934
1838
|
env: stringMapSchema4.optional(),
|
|
1935
|
-
cwd:
|
|
1936
|
-
disabled:
|
|
1839
|
+
cwd: z7.string().min(1).optional(),
|
|
1840
|
+
disabled: z7.boolean().optional()
|
|
1937
1841
|
}).passthrough();
|
|
1938
1842
|
|
|
1939
1843
|
class CursorAdapter {
|
|
@@ -2054,19 +1958,19 @@ class CursorAdapter {
|
|
|
2054
1958
|
// src/adapters/cline.ts
|
|
2055
1959
|
import path12 from "node:path";
|
|
2056
1960
|
import fs10 from "node:fs";
|
|
2057
|
-
import { z as
|
|
1961
|
+
import { z as z8 } from "zod";
|
|
2058
1962
|
var HTTP_TRANSPORT_TYPES = new Set(["http", "sse", "streamableHttp"]);
|
|
2059
|
-
var stringMapSchema5 =
|
|
2060
|
-
var clineEntrySchema =
|
|
2061
|
-
type:
|
|
2062
|
-
transportType:
|
|
2063
|
-
url:
|
|
1963
|
+
var stringMapSchema5 = z8.record(z8.string(), z8.string());
|
|
1964
|
+
var clineEntrySchema = z8.object({
|
|
1965
|
+
type: z8.string().optional(),
|
|
1966
|
+
transportType: z8.string().optional(),
|
|
1967
|
+
url: z8.string().min(1).optional(),
|
|
2064
1968
|
headers: stringMapSchema5.optional(),
|
|
2065
|
-
command:
|
|
2066
|
-
args:
|
|
1969
|
+
command: z8.string().min(1).optional(),
|
|
1970
|
+
args: z8.array(z8.string()).optional(),
|
|
2067
1971
|
env: stringMapSchema5.optional(),
|
|
2068
|
-
cwd:
|
|
2069
|
-
disabled:
|
|
1972
|
+
cwd: z8.string().min(1).optional(),
|
|
1973
|
+
disabled: z8.boolean().optional()
|
|
2070
1974
|
}).passthrough();
|
|
2071
1975
|
function getCandidatePaths() {
|
|
2072
1976
|
if (process.platform === "linux") {
|
|
@@ -2215,17 +2119,17 @@ class ClineAdapter {
|
|
|
2215
2119
|
|
|
2216
2120
|
// src/adapters/kiro.ts
|
|
2217
2121
|
import path13 from "node:path";
|
|
2218
|
-
import { z as
|
|
2219
|
-
var stringMapSchema6 =
|
|
2220
|
-
var kiroEntrySchema =
|
|
2221
|
-
type:
|
|
2222
|
-
url:
|
|
2122
|
+
import { z as z9 } from "zod";
|
|
2123
|
+
var stringMapSchema6 = z9.record(z9.string(), z9.string());
|
|
2124
|
+
var kiroEntrySchema = z9.object({
|
|
2125
|
+
type: z9.string().optional(),
|
|
2126
|
+
url: z9.string().min(1).optional(),
|
|
2223
2127
|
headers: stringMapSchema6.optional(),
|
|
2224
|
-
command:
|
|
2225
|
-
args:
|
|
2128
|
+
command: z9.string().min(1).optional(),
|
|
2129
|
+
args: z9.array(z9.string()).optional(),
|
|
2226
2130
|
env: stringMapSchema6.optional(),
|
|
2227
|
-
cwd:
|
|
2228
|
-
disabled:
|
|
2131
|
+
cwd: z9.string().min(1).optional(),
|
|
2132
|
+
disabled: z9.boolean().optional()
|
|
2229
2133
|
}).passthrough();
|
|
2230
2134
|
|
|
2231
2135
|
class KiroAdapter {
|
|
@@ -2342,18 +2246,18 @@ class KiroAdapter {
|
|
|
2342
2246
|
|
|
2343
2247
|
// src/adapters/opencode.ts
|
|
2344
2248
|
import path14 from "node:path";
|
|
2345
|
-
import { z as
|
|
2346
|
-
var stringMapSchema7 =
|
|
2347
|
-
var openCodeEntrySchema =
|
|
2348
|
-
type:
|
|
2349
|
-
url:
|
|
2249
|
+
import { z as z10 } from "zod";
|
|
2250
|
+
var stringMapSchema7 = z10.record(z10.string(), z10.string());
|
|
2251
|
+
var openCodeEntrySchema = z10.object({
|
|
2252
|
+
type: z10.string().optional(),
|
|
2253
|
+
url: z10.string().min(1).optional(),
|
|
2350
2254
|
headers: stringMapSchema7.optional(),
|
|
2351
|
-
command:
|
|
2352
|
-
args:
|
|
2255
|
+
command: z10.union([z10.string().min(1), z10.array(z10.string())]).optional(),
|
|
2256
|
+
args: z10.array(z10.string()).optional(),
|
|
2353
2257
|
env: stringMapSchema7.optional(),
|
|
2354
2258
|
environment: stringMapSchema7.optional(),
|
|
2355
|
-
cwd:
|
|
2356
|
-
enabled:
|
|
2259
|
+
cwd: z10.string().min(1).optional(),
|
|
2260
|
+
enabled: z10.boolean().optional()
|
|
2357
2261
|
}).passthrough();
|
|
2358
2262
|
|
|
2359
2263
|
class OpenCodeAdapter {
|
|
@@ -2480,17 +2384,17 @@ class OpenCodeAdapter {
|
|
|
2480
2384
|
|
|
2481
2385
|
// src/adapters/vscode.ts
|
|
2482
2386
|
import path15 from "node:path";
|
|
2483
|
-
import { z as
|
|
2484
|
-
var stringMapSchema8 =
|
|
2485
|
-
var vscodeEntrySchema =
|
|
2486
|
-
type:
|
|
2487
|
-
url:
|
|
2387
|
+
import { z as z11 } from "zod";
|
|
2388
|
+
var stringMapSchema8 = z11.record(z11.string(), z11.string());
|
|
2389
|
+
var vscodeEntrySchema = z11.object({
|
|
2390
|
+
type: z11.string().optional(),
|
|
2391
|
+
url: z11.string().min(1).optional(),
|
|
2488
2392
|
headers: stringMapSchema8.optional(),
|
|
2489
|
-
command:
|
|
2490
|
-
args:
|
|
2393
|
+
command: z11.string().min(1).optional(),
|
|
2394
|
+
args: z11.array(z11.string()).optional(),
|
|
2491
2395
|
env: stringMapSchema8.optional(),
|
|
2492
|
-
cwd:
|
|
2493
|
-
disabled:
|
|
2396
|
+
cwd: z11.string().min(1).optional(),
|
|
2397
|
+
disabled: z11.boolean().optional()
|
|
2494
2398
|
}).passthrough();
|
|
2495
2399
|
|
|
2496
2400
|
class VsCodeAdapter {
|
|
@@ -2616,17 +2520,17 @@ class VsCodeAdapter {
|
|
|
2616
2520
|
|
|
2617
2521
|
// src/adapters/qwen.ts
|
|
2618
2522
|
import path16 from "node:path";
|
|
2619
|
-
import { z as
|
|
2620
|
-
var stringMapSchema9 =
|
|
2621
|
-
var qwenEntrySchema =
|
|
2622
|
-
httpUrl:
|
|
2623
|
-
url:
|
|
2523
|
+
import { z as z12 } from "zod";
|
|
2524
|
+
var stringMapSchema9 = z12.record(z12.string(), z12.string());
|
|
2525
|
+
var qwenEntrySchema = z12.object({
|
|
2526
|
+
httpUrl: z12.string().min(1).optional(),
|
|
2527
|
+
url: z12.string().min(1).optional(),
|
|
2624
2528
|
headers: stringMapSchema9.optional(),
|
|
2625
|
-
command:
|
|
2626
|
-
args:
|
|
2529
|
+
command: z12.string().min(1).optional(),
|
|
2530
|
+
args: z12.array(z12.string()).optional(),
|
|
2627
2531
|
env: stringMapSchema9.optional(),
|
|
2628
|
-
cwd:
|
|
2629
|
-
disabled:
|
|
2532
|
+
cwd: z12.string().min(1).optional(),
|
|
2533
|
+
disabled: z12.boolean().optional()
|
|
2630
2534
|
}).passthrough();
|
|
2631
2535
|
|
|
2632
2536
|
class QwenAdapter {
|
|
@@ -2757,18 +2661,18 @@ class QwenAdapter {
|
|
|
2757
2661
|
// src/adapters/openclaw.ts
|
|
2758
2662
|
import fs11 from "node:fs";
|
|
2759
2663
|
import path17 from "node:path";
|
|
2760
|
-
import { z as
|
|
2761
|
-
var stringMapSchema10 =
|
|
2762
|
-
var openclawEntrySchema =
|
|
2763
|
-
url:
|
|
2764
|
-
transport:
|
|
2664
|
+
import { z as z13 } from "zod";
|
|
2665
|
+
var stringMapSchema10 = z13.record(z13.string(), z13.string());
|
|
2666
|
+
var openclawEntrySchema = z13.object({
|
|
2667
|
+
url: z13.string().min(1).optional(),
|
|
2668
|
+
transport: z13.string().optional(),
|
|
2765
2669
|
headers: stringMapSchema10.optional(),
|
|
2766
|
-
command:
|
|
2767
|
-
args:
|
|
2670
|
+
command: z13.string().min(1).optional(),
|
|
2671
|
+
args: z13.array(z13.string()).optional(),
|
|
2768
2672
|
env: stringMapSchema10.optional(),
|
|
2769
|
-
cwd:
|
|
2770
|
-
workingDirectory:
|
|
2771
|
-
auth:
|
|
2673
|
+
cwd: z13.string().min(1).optional(),
|
|
2674
|
+
workingDirectory: z13.string().min(1).optional(),
|
|
2675
|
+
auth: z13.unknown().optional()
|
|
2772
2676
|
}).passthrough();
|
|
2773
2677
|
|
|
2774
2678
|
class OpenClawAdapter {
|
|
@@ -2908,18 +2812,18 @@ class OpenClawAdapter {
|
|
|
2908
2812
|
import fs12 from "node:fs";
|
|
2909
2813
|
import path18 from "node:path";
|
|
2910
2814
|
import YAML from "yaml";
|
|
2911
|
-
import { z as
|
|
2912
|
-
var stringMapSchema11 =
|
|
2913
|
-
var hermesEntrySchema =
|
|
2914
|
-
url:
|
|
2815
|
+
import { z as z14 } from "zod";
|
|
2816
|
+
var stringMapSchema11 = z14.record(z14.string(), z14.string());
|
|
2817
|
+
var hermesEntrySchema = z14.object({
|
|
2818
|
+
url: z14.string().min(1).optional(),
|
|
2915
2819
|
headers: stringMapSchema11.optional(),
|
|
2916
|
-
command:
|
|
2917
|
-
args:
|
|
2820
|
+
command: z14.string().min(1).optional(),
|
|
2821
|
+
args: z14.array(z14.string()).optional(),
|
|
2918
2822
|
env: stringMapSchema11.optional(),
|
|
2919
|
-
auth:
|
|
2920
|
-
enabled:
|
|
2921
|
-
timeout:
|
|
2922
|
-
tools:
|
|
2823
|
+
auth: z14.unknown().optional(),
|
|
2824
|
+
enabled: z14.boolean().optional(),
|
|
2825
|
+
timeout: z14.number().optional(),
|
|
2826
|
+
tools: z14.array(z14.string()).optional()
|
|
2923
2827
|
}).passthrough();
|
|
2924
2828
|
|
|
2925
2829
|
class HermesAdapter {
|
|
@@ -3084,13 +2988,18 @@ function listSkills() {
|
|
|
3084
2988
|
for (const file of files) {
|
|
3085
2989
|
if (file.endsWith(".md")) {
|
|
3086
2990
|
const id = path19.basename(file, ".md");
|
|
3087
|
-
|
|
3088
|
-
|
|
2991
|
+
try {
|
|
2992
|
+
const content = fs13.readFileSync(path19.join(skillsDir, file), "utf-8");
|
|
2993
|
+
skills.push({ id, content });
|
|
2994
|
+
} catch {
|
|
2995
|
+
console.error(`[mcpx] Warning: could not read skill ${file}`);
|
|
2996
|
+
}
|
|
3089
2997
|
}
|
|
3090
2998
|
}
|
|
3091
2999
|
return skills;
|
|
3092
3000
|
}
|
|
3093
3001
|
function getSkill(id) {
|
|
3002
|
+
validateSkillId(id);
|
|
3094
3003
|
const skillsDir = getSkillsDir();
|
|
3095
3004
|
const filePath = path19.join(skillsDir, `${id}.md`);
|
|
3096
3005
|
if (!fs13.existsSync(filePath)) {
|
|
@@ -3100,12 +3009,14 @@ function getSkill(id) {
|
|
|
3100
3009
|
return { id, content };
|
|
3101
3010
|
}
|
|
3102
3011
|
function saveSkill(id, content) {
|
|
3012
|
+
validateSkillId(id);
|
|
3103
3013
|
const skillsDir = getSkillsDir();
|
|
3104
3014
|
ensureDir(skillsDir);
|
|
3105
3015
|
const filePath = path19.join(skillsDir, `${id}.md`);
|
|
3106
3016
|
fs13.writeFileSync(filePath, content, "utf-8");
|
|
3107
3017
|
}
|
|
3108
3018
|
function deleteSkill(id) {
|
|
3019
|
+
validateSkillId(id);
|
|
3109
3020
|
const skillsDir = getSkillsDir();
|
|
3110
3021
|
const filePath = path19.join(skillsDir, `${id}.md`);
|
|
3111
3022
|
if (fs13.existsSync(filePath)) {
|
|
@@ -3464,7 +3375,7 @@ class PluginManager {
|
|
|
3464
3375
|
}
|
|
3465
3376
|
async installResolvedPlugin(source, options, sourceLabel = source.original) {
|
|
3466
3377
|
const info = await this.inspectResolvedSource(source, sourceLabel);
|
|
3467
|
-
const pluginName = options?.name || info.manifest?.name || path21.basename(info.root);
|
|
3378
|
+
const pluginName = normalizePluginName(options?.name || info.manifest?.name || path21.basename(info.root));
|
|
3468
3379
|
const pluginId = `${pluginName}@${info.sha.slice(0, 8)}`;
|
|
3469
3380
|
const dataDir = path21.join(getPluginDataRoot(), pluginId);
|
|
3470
3381
|
ensureDir(dataDir);
|
|
@@ -3518,7 +3429,7 @@ class PluginManager {
|
|
|
3518
3429
|
for (const mcp of discovered.mcpServers) {
|
|
3519
3430
|
const nsName2 = `${pluginName}__${mcp.id}`;
|
|
3520
3431
|
const approved = plugin.approvals?.mcpServers !== false;
|
|
3521
|
-
config.servers[nsName2] = mcp.transport === "http" && mcp.url ? { transport: "http", url: mcp.url, enabled: enabled && approved } : {
|
|
3432
|
+
config.servers[nsName2] = mcp.transport === "http" && mcp.url ? { transport: "http", url: new URL(mcp.url).toString(), enabled: enabled && approved } : {
|
|
3522
3433
|
transport: "stdio",
|
|
3523
3434
|
command: "npx",
|
|
3524
3435
|
args: ["-y", "@kwonye/mcpx@latest", "plugin-host", pluginId, mcp.id],
|
|
@@ -3549,7 +3460,7 @@ class PluginManager {
|
|
|
3549
3460
|
const currentSnapshot = loadConfig(this.configPath).plugins?.[pluginId];
|
|
3550
3461
|
if (!currentSnapshot)
|
|
3551
3462
|
throw new Error(`Plugin ${pluginId} not found`);
|
|
3552
|
-
const pluginName = info.manifest?.name || currentSnapshot.name;
|
|
3463
|
+
const pluginName = normalizePluginName(info.manifest?.name || currentSnapshot.name);
|
|
3553
3464
|
let updatedPlugin;
|
|
3554
3465
|
await mutateConfig((config) => {
|
|
3555
3466
|
if (!config.plugins)
|
|
@@ -3565,17 +3476,18 @@ class PluginManager {
|
|
|
3565
3476
|
current.root = info.root;
|
|
3566
3477
|
current.discovered = info.components;
|
|
3567
3478
|
current.serverNames = info.components.mcpServers.map((s) => `${pluginName}__${s.id}`);
|
|
3568
|
-
const
|
|
3479
|
+
const requiresApproval = !current.marketplace && info.components.mcpServers.length > 0;
|
|
3480
|
+
const approved = !requiresApproval && current.approvals?.mcpServers !== false;
|
|
3569
3481
|
for (const mcp of info.components.mcpServers) {
|
|
3570
3482
|
const nsName2 = `${pluginName}__${mcp.id}`;
|
|
3571
|
-
config.servers[nsName2] = mcp.transport === "http" && mcp.url ? { transport: "http", url: mcp.url, enabled: current.enabled && approved } : {
|
|
3483
|
+
config.servers[nsName2] = mcp.transport === "http" && mcp.url ? { transport: "http", url: new URL(mcp.url).toString(), enabled: current.enabled && approved } : {
|
|
3572
3484
|
transport: "stdio",
|
|
3573
3485
|
command: "npx",
|
|
3574
3486
|
args: ["-y", "@kwonye/mcpx@latest", "plugin-host", pluginId, mcp.id],
|
|
3575
3487
|
enabled: current.enabled && approved
|
|
3576
3488
|
};
|
|
3577
3489
|
}
|
|
3578
|
-
current.approvals = {};
|
|
3490
|
+
current.approvals = requiresApproval ? { mcpServers: false } : {};
|
|
3579
3491
|
if (sourceFingerprint && current.marketplace)
|
|
3580
3492
|
current.marketplace.sourceFingerprint = sourceFingerprint;
|
|
3581
3493
|
delete current.updateError;
|
|
@@ -3620,6 +3532,11 @@ class PluginManager {
|
|
|
3620
3532
|
if (!plugin)
|
|
3621
3533
|
throw new Error(`Plugin ${pluginId} not found`);
|
|
3622
3534
|
plugin.enabled = true;
|
|
3535
|
+
for (const name of plugin.serverNames) {
|
|
3536
|
+
const server = config.servers[name];
|
|
3537
|
+
if (server)
|
|
3538
|
+
server.enabled = plugin.approvals?.mcpServers !== false;
|
|
3539
|
+
}
|
|
3623
3540
|
}, this.configPath);
|
|
3624
3541
|
const syncConfig = loadConfig(this.configPath);
|
|
3625
3542
|
const summary = syncAllClients(syncConfig, this.secrets);
|
|
@@ -3635,6 +3552,11 @@ class PluginManager {
|
|
|
3635
3552
|
if (!plugin)
|
|
3636
3553
|
throw new Error(`Plugin ${pluginId} not found`);
|
|
3637
3554
|
plugin.enabled = false;
|
|
3555
|
+
for (const name of plugin.serverNames) {
|
|
3556
|
+
const server = config.servers[name];
|
|
3557
|
+
if (server)
|
|
3558
|
+
server.enabled = false;
|
|
3559
|
+
}
|
|
3638
3560
|
}, this.configPath);
|
|
3639
3561
|
const syncConfig = loadConfig(this.configPath);
|
|
3640
3562
|
const summary = syncAllClients(syncConfig, this.secrets);
|
|
@@ -3678,10 +3600,11 @@ class PluginManager {
|
|
|
3678
3600
|
const config = loadConfig(this.configPath);
|
|
3679
3601
|
if (!config.plugins)
|
|
3680
3602
|
throw new Error(`Plugin ${pluginId} not found`);
|
|
3681
|
-
const
|
|
3603
|
+
const resolvedId = resolvePluginId(config, pluginId);
|
|
3604
|
+
const plugin = config.plugins[resolvedId];
|
|
3682
3605
|
if (!plugin)
|
|
3683
3606
|
throw new Error(`Plugin ${pluginId} not found`);
|
|
3684
|
-
await this.lifecycle.prepareDependencies(
|
|
3607
|
+
await this.lifecycle.prepareDependencies(resolvedId, plugin.root);
|
|
3685
3608
|
}
|
|
3686
3609
|
}
|
|
3687
3610
|
async function inspectPlugin(source) {
|
|
@@ -3697,7 +3620,7 @@ async function updatePlugin(nameOrId) {
|
|
|
3697
3620
|
const config = loadConfig(manager["configPath"]);
|
|
3698
3621
|
const id = resolvePluginId(config, nameOrId);
|
|
3699
3622
|
if (config.plugins?.[id]?.marketplace) {
|
|
3700
|
-
const { updateMarketplaceInstalledPlugin } = await import("./marketplace-
|
|
3623
|
+
const { updateMarketplaceInstalledPlugin } = await import("./marketplace-zem0m4mw.js");
|
|
3701
3624
|
return updateMarketplaceInstalledPlugin(id);
|
|
3702
3625
|
}
|
|
3703
3626
|
return manager.updatePlugin(id);
|