@kwonye/mcpx 0.1.93 → 0.1.95
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/adapters/claude-desktop.d.ts.map +1 -1
- package/dist/adapters/claude.d.ts +2 -1
- package/dist/adapters/claude.d.ts.map +1 -1
- package/dist/adapters/cline.d.ts +3 -1
- package/dist/adapters/cline.d.ts.map +1 -1
- package/dist/adapters/codex.d.ts +2 -1
- package/dist/adapters/codex.d.ts.map +1 -1
- package/dist/adapters/cursor.d.ts +2 -1
- package/dist/adapters/cursor.d.ts.map +1 -1
- package/dist/adapters/hermes.d.ts +11 -0
- package/dist/adapters/hermes.d.ts.map +1 -0
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/kiro.d.ts.map +1 -1
- package/dist/adapters/openclaw.d.ts +11 -0
- package/dist/adapters/openclaw.d.ts.map +1 -0
- package/dist/adapters/opencode.d.ts +2 -1
- package/dist/adapters/opencode.d.ts.map +1 -1
- package/dist/adapters/qwen.d.ts.map +1 -1
- package/dist/adapters/utils/index.d.ts +18 -4
- package/dist/adapters/utils/index.d.ts.map +1 -1
- package/dist/adapters/vscode.d.ts +2 -1
- package/dist/adapters/vscode.d.ts.map +1 -1
- package/dist/{cli-y7ba5qvr.js → cli-28fsxhe3.js} +430 -156
- package/dist/{cli-qvkpwy9w.js → cli-njtbb11m.js} +138 -43
- package/dist/{cli-8vxva0cb.js → cli-tk8bggg5.js} +81 -8
- package/dist/{cli-np0wwh6m.js → cli-v27bt25d.js} +157 -11
- package/dist/{cli-kwyp9ywh.js → cli-wxw1t3rr.js} +941 -303
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +388 -77
- package/dist/compat/hermes.d.ts +7 -0
- package/dist/compat/hermes.d.ts.map +1 -0
- package/dist/compat/index.d.ts.map +1 -1
- package/dist/compat/openclaw.d.ts +7 -0
- package/dist/compat/openclaw.d.ts.map +1 -0
- package/dist/{config-2s5nsm2g.js → config-5p08cb0c.js} +6 -2
- package/dist/core/add-command.d.ts.map +1 -1
- package/dist/core/config-repair.d.ts.map +1 -1
- package/dist/core/config-store.d.ts +33 -0
- package/dist/core/config-store.d.ts.map +1 -0
- package/dist/core/config.d.ts +13 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/daemon.d.ts +5 -1
- package/dist/core/daemon.d.ts.map +1 -1
- package/dist/core/errors.d.ts +18 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/index.d.ts +6 -3
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +30 -6
- package/dist/core/managed-index-lock.d.ts +2 -0
- package/dist/core/managed-index-lock.d.ts.map +1 -0
- package/dist/core/oauth.d.ts.map +1 -1
- package/dist/core/plugin-host.d.ts +2 -0
- package/dist/core/plugin-host.d.ts.map +1 -0
- package/dist/core/plugin-manager.d.ts +19 -6
- 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 +6 -2
- package/dist/core/plugin-projections.d.ts.map +1 -1
- package/dist/core/registry.d.ts +4 -1
- package/dist/core/registry.d.ts.map +1 -1
- package/dist/core/secrets.d.ts.map +1 -1
- package/dist/core/server-auth.d.ts.map +1 -1
- package/dist/core/skill-projections.d.ts +3 -0
- package/dist/core/skill-projections.d.ts.map +1 -0
- package/dist/core/status.d.ts +1 -0
- package/dist/core/status.d.ts.map +1 -1
- package/dist/core/sync.d.ts +19 -2
- package/dist/core/sync.d.ts.map +1 -1
- package/dist/core/update-manager.d.ts.map +1 -1
- package/dist/gateway/server.d.ts +4 -1
- package/dist/gateway/server.d.ts.map +1 -1
- package/dist/plugin-host-954a7tf7.js +129 -0
- package/dist/{plugin-manager-1sc4b6am.js → plugin-manager-m8ar2271.js} +12 -4
- package/dist/{registry-mztfy0cz.js → registry-n12thgf2.js} +1 -1
- package/dist/types.d.ts +5 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/package.json +3 -2
- package/dist/cli-m2645syd.js +0 -2199
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
|
+
persistSyncState,
|
|
2
3
|
syncAllClients
|
|
3
|
-
} from "./cli-
|
|
4
|
+
} from "./cli-wxw1t3rr.js";
|
|
4
5
|
import {
|
|
5
6
|
loadConfig,
|
|
6
|
-
loadMergedConfig,
|
|
7
7
|
saveConfig
|
|
8
|
-
} from "./cli-
|
|
8
|
+
} from "./cli-v27bt25d.js";
|
|
9
9
|
import {
|
|
10
10
|
SecretsManager
|
|
11
|
-
} from "./cli-
|
|
11
|
+
} from "./cli-tk8bggg5.js";
|
|
12
12
|
import {
|
|
13
13
|
ensureDir,
|
|
14
14
|
getConfigPath,
|
|
@@ -468,6 +468,10 @@ var HOOKS_JSON_REL = path4.join("hooks", "hooks.json");
|
|
|
468
468
|
var SKILLS_DIR = "skills";
|
|
469
469
|
var COMMANDS_DIR = "commands";
|
|
470
470
|
var AGENTS_DIR = "agents";
|
|
471
|
+
function sanitizeComponentId(id) {
|
|
472
|
+
const stripped = id.replace(/[\/\\]/g, "_").replace(/^\.+/, "");
|
|
473
|
+
return stripped.length > 0 ? stripped : "unnamed";
|
|
474
|
+
}
|
|
471
475
|
function readManifest(pluginRoot) {
|
|
472
476
|
const manifestPath = path4.join(pluginRoot, PLUGIN_JSON_REL);
|
|
473
477
|
if (!fs4.existsSync(manifestPath)) {
|
|
@@ -528,7 +532,7 @@ function discoverSkills(pluginRoot) {
|
|
|
528
532
|
const content = fs4.readFileSync(skillPath, "utf8");
|
|
529
533
|
const { frontmatter } = parseFrontmatter(content);
|
|
530
534
|
results.push({
|
|
531
|
-
id: frontmatter.name ?? path4.basename(entry.name, ".md"),
|
|
535
|
+
id: sanitizeComponentId(frontmatter.name ?? path4.basename(entry.name, ".md")),
|
|
532
536
|
type: "skills",
|
|
533
537
|
path: skillPath,
|
|
534
538
|
description: frontmatter.description
|
|
@@ -541,7 +545,7 @@ function discoverSkills(pluginRoot) {
|
|
|
541
545
|
const content = fs4.readFileSync(skillMdPath, "utf8");
|
|
542
546
|
const { frontmatter } = parseFrontmatter(content);
|
|
543
547
|
results.push({
|
|
544
|
-
id: frontmatter.name ?? entry.name,
|
|
548
|
+
id: sanitizeComponentId(frontmatter.name ?? entry.name),
|
|
545
549
|
type: "skills",
|
|
546
550
|
path: skillMdPath,
|
|
547
551
|
description: frontmatter.description
|
|
@@ -564,7 +568,7 @@ function discoverCommands(pluginRoot) {
|
|
|
564
568
|
const content = fs4.readFileSync(cmdPath, "utf8");
|
|
565
569
|
const { frontmatter } = parseFrontmatter(content);
|
|
566
570
|
results.push({
|
|
567
|
-
id: frontmatter.name ?? path4.basename(entry.name, ".md"),
|
|
571
|
+
id: sanitizeComponentId(frontmatter.name ?? path4.basename(entry.name, ".md")),
|
|
568
572
|
type: "commands",
|
|
569
573
|
path: cmdPath,
|
|
570
574
|
description: frontmatter.description
|
|
@@ -587,7 +591,7 @@ function discoverAgents(pluginRoot) {
|
|
|
587
591
|
const content = fs4.readFileSync(agentPath, "utf8");
|
|
588
592
|
const { frontmatter } = parseFrontmatter(content);
|
|
589
593
|
results.push({
|
|
590
|
-
id: frontmatter.name ?? path4.basename(entry.name, ".md"),
|
|
594
|
+
id: sanitizeComponentId(frontmatter.name ?? path4.basename(entry.name, ".md")),
|
|
591
595
|
type: "agents",
|
|
592
596
|
path: agentPath,
|
|
593
597
|
description: frontmatter.description
|
|
@@ -644,6 +648,9 @@ function discoverComponents(pluginRoot) {
|
|
|
644
648
|
function hasManifest(pluginRoot) {
|
|
645
649
|
return fs4.existsSync(path4.join(pluginRoot, PLUGIN_JSON_REL));
|
|
646
650
|
}
|
|
651
|
+
function resolvePluginVars(value, pluginRoot, dataDir) {
|
|
652
|
+
return value.replace(/\$\{CLAUDE_PLUGIN_ROOT\}/g, pluginRoot).replace(/\$\{DATA\}/g, dataDir).replace(/\$\{CLAUDE_PROJECT_DIR\}/g, pluginRoot);
|
|
653
|
+
}
|
|
647
654
|
|
|
648
655
|
// src/core/plugin-manager.ts
|
|
649
656
|
class PluginManager {
|
|
@@ -691,11 +698,18 @@ class PluginManager {
|
|
|
691
698
|
if (config.plugins[pluginId]) {
|
|
692
699
|
throw new Error(`Plugin ${pluginId} is already installed`);
|
|
693
700
|
}
|
|
701
|
+
const serverNames = info.components.mcpServers.map((s) => `${pluginName}__${s.id}`);
|
|
702
|
+
for (const existing of Object.values(config.plugins)) {
|
|
703
|
+
for (const sn of serverNames) {
|
|
704
|
+
if (existing.serverNames.includes(sn)) {
|
|
705
|
+
throw new Error(`Server name collision: "${sn}" is already claimed by plugin "${existing.name}". Use --name to specify a different plugin name.`);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
}
|
|
694
709
|
const dataDir = path5.join(getPluginDataRoot(), pluginId);
|
|
695
710
|
ensureDir(dataDir);
|
|
696
711
|
const enabled = options?.enabled ?? true;
|
|
697
712
|
const discovered = info.components;
|
|
698
|
-
const serverNames = discovered.mcpServers.map((s) => `${pluginName}__${s.id}`);
|
|
699
713
|
const plugin = {
|
|
700
714
|
id: pluginId,
|
|
701
715
|
name: pluginName,
|
|
@@ -727,17 +741,17 @@ class PluginManager {
|
|
|
727
741
|
const approved = plugin.approvals?.mcpServers !== false;
|
|
728
742
|
config.servers[nsName] = {
|
|
729
743
|
transport: "stdio",
|
|
730
|
-
command:
|
|
731
|
-
args: mcp.
|
|
732
|
-
env: mcp.env,
|
|
733
|
-
cwd: mcp.cwd || plugin.root,
|
|
744
|
+
command: "npx",
|
|
745
|
+
args: ["-y", "@kwonye/mcpx@latest", "plugin-host", pluginId, mcp.id],
|
|
734
746
|
enabled: enabled && approved
|
|
735
747
|
};
|
|
736
748
|
}
|
|
737
749
|
saveConfig(config, this.configPath);
|
|
738
750
|
this.lifecycle.initData(pluginId);
|
|
739
|
-
const
|
|
740
|
-
syncAllClients(
|
|
751
|
+
const syncConfig = loadConfig(this.configPath);
|
|
752
|
+
const summary = syncAllClients(syncConfig, this.secrets);
|
|
753
|
+
persistSyncState(summary, syncConfig);
|
|
754
|
+
saveConfig(syncConfig, this.configPath);
|
|
741
755
|
return plugin;
|
|
742
756
|
}
|
|
743
757
|
async updatePlugin(pluginId) {
|
|
@@ -762,18 +776,18 @@ class PluginManager {
|
|
|
762
776
|
const nsName = `${pluginName}__${mcp.id}`;
|
|
763
777
|
config.servers[nsName] = {
|
|
764
778
|
transport: "stdio",
|
|
765
|
-
command:
|
|
766
|
-
args: mcp.
|
|
767
|
-
env: mcp.env,
|
|
768
|
-
cwd: mcp.cwd || current.root,
|
|
779
|
+
command: "npx",
|
|
780
|
+
args: ["-y", "@kwonye/mcpx@latest", "plugin-host", pluginId, mcp.id],
|
|
769
781
|
enabled: current.enabled && approved
|
|
770
782
|
};
|
|
771
783
|
}
|
|
772
784
|
current.approvals = {};
|
|
773
785
|
config.plugins[pluginId] = current;
|
|
774
786
|
saveConfig(config, this.configPath);
|
|
775
|
-
const
|
|
776
|
-
syncAllClients(
|
|
787
|
+
const syncConfig = loadConfig(this.configPath);
|
|
788
|
+
const summary = syncAllClients(syncConfig, this.secrets);
|
|
789
|
+
persistSyncState(summary, syncConfig);
|
|
790
|
+
saveConfig(syncConfig, this.configPath);
|
|
777
791
|
return current;
|
|
778
792
|
}
|
|
779
793
|
async uninstallPlugin(pluginId, keepData = false) {
|
|
@@ -791,8 +805,10 @@ class PluginManager {
|
|
|
791
805
|
if (!keepData) {
|
|
792
806
|
await this.lifecycle.removeData(pluginId);
|
|
793
807
|
}
|
|
794
|
-
const
|
|
795
|
-
syncAllClients(
|
|
808
|
+
const syncConfig = loadConfig(this.configPath);
|
|
809
|
+
const summary = syncAllClients(syncConfig, this.secrets);
|
|
810
|
+
persistSyncState(summary, syncConfig);
|
|
811
|
+
saveConfig(syncConfig, this.configPath);
|
|
796
812
|
}
|
|
797
813
|
async enablePlugin(pluginId) {
|
|
798
814
|
const config = loadConfig(this.configPath);
|
|
@@ -803,8 +819,10 @@ class PluginManager {
|
|
|
803
819
|
throw new Error(`Plugin ${pluginId} not found`);
|
|
804
820
|
plugin.enabled = true;
|
|
805
821
|
saveConfig(config, this.configPath);
|
|
806
|
-
const
|
|
807
|
-
syncAllClients(
|
|
822
|
+
const syncConfig = loadConfig(this.configPath);
|
|
823
|
+
const summary = syncAllClients(syncConfig, this.secrets);
|
|
824
|
+
persistSyncState(summary, syncConfig);
|
|
825
|
+
saveConfig(syncConfig, this.configPath);
|
|
808
826
|
}
|
|
809
827
|
async disablePlugin(pluginId) {
|
|
810
828
|
const config = loadConfig(this.configPath);
|
|
@@ -815,8 +833,10 @@ class PluginManager {
|
|
|
815
833
|
throw new Error(`Plugin ${pluginId} not found`);
|
|
816
834
|
plugin.enabled = false;
|
|
817
835
|
saveConfig(config, this.configPath);
|
|
818
|
-
const
|
|
819
|
-
syncAllClients(
|
|
836
|
+
const syncConfig = loadConfig(this.configPath);
|
|
837
|
+
const summary = syncAllClients(syncConfig, this.secrets);
|
|
838
|
+
persistSyncState(summary, syncConfig);
|
|
839
|
+
saveConfig(syncConfig, this.configPath);
|
|
820
840
|
}
|
|
821
841
|
async approveComponent(pluginId, component) {
|
|
822
842
|
const config = loadConfig(this.configPath);
|
|
@@ -836,8 +856,10 @@ class PluginManager {
|
|
|
836
856
|
}
|
|
837
857
|
}
|
|
838
858
|
saveConfig(config, this.configPath);
|
|
839
|
-
const
|
|
840
|
-
syncAllClients(
|
|
859
|
+
const syncConfig = loadConfig(this.configPath);
|
|
860
|
+
const summary = syncAllClients(syncConfig, this.secrets);
|
|
861
|
+
persistSyncState(summary, syncConfig);
|
|
862
|
+
saveConfig(syncConfig, this.configPath);
|
|
841
863
|
}
|
|
842
864
|
async getPluginStatus(pluginId) {
|
|
843
865
|
const config = loadConfig(this.configPath);
|
|
@@ -865,30 +887,62 @@ async function installPlugin(source, options) {
|
|
|
865
887
|
const manager = new PluginManager;
|
|
866
888
|
return manager.installPlugin(source, options);
|
|
867
889
|
}
|
|
868
|
-
async function updatePlugin(
|
|
890
|
+
async function updatePlugin(nameOrId) {
|
|
869
891
|
const manager = new PluginManager;
|
|
870
|
-
|
|
892
|
+
const config = loadConfig(manager["configPath"]);
|
|
893
|
+
const id = resolvePluginId(config, nameOrId);
|
|
894
|
+
return manager.updatePlugin(id);
|
|
871
895
|
}
|
|
872
|
-
async function uninstallPlugin(
|
|
896
|
+
async function uninstallPlugin(nameOrId, keepData) {
|
|
873
897
|
const manager = new PluginManager;
|
|
874
|
-
|
|
898
|
+
const config = loadConfig(manager["configPath"]);
|
|
899
|
+
const id = resolvePluginId(config, nameOrId);
|
|
900
|
+
await manager.uninstallPlugin(id, keepData);
|
|
875
901
|
}
|
|
876
|
-
async function enablePlugin(
|
|
902
|
+
async function enablePlugin(nameOrId) {
|
|
877
903
|
const manager = new PluginManager;
|
|
878
|
-
|
|
904
|
+
const config = loadConfig(manager["configPath"]);
|
|
905
|
+
const id = resolvePluginId(config, nameOrId);
|
|
906
|
+
await manager.enablePlugin(id);
|
|
879
907
|
}
|
|
880
|
-
async function disablePlugin(
|
|
908
|
+
async function disablePlugin(nameOrId) {
|
|
881
909
|
const manager = new PluginManager;
|
|
882
|
-
|
|
910
|
+
const config = loadConfig(manager["configPath"]);
|
|
911
|
+
const id = resolvePluginId(config, nameOrId);
|
|
912
|
+
await manager.disablePlugin(id);
|
|
913
|
+
}
|
|
914
|
+
async function setPluginProjectOverride(nameOrId, projectPath, override) {
|
|
915
|
+
const config = loadConfig();
|
|
916
|
+
const id = resolvePluginId(config, nameOrId);
|
|
917
|
+
const plugin = config.plugins?.[id];
|
|
918
|
+
if (!plugin)
|
|
919
|
+
throw new Error(`Plugin ${id} not found`);
|
|
920
|
+
if (!config.projects?.[projectPath]) {
|
|
921
|
+
throw new Error(`Project "${projectPath}" is not registered. Run "mcpx project init" there first.`);
|
|
922
|
+
}
|
|
923
|
+
if (!plugin.projectOverrides)
|
|
924
|
+
plugin.projectOverrides = {};
|
|
925
|
+
plugin.projectOverrides[projectPath] = { ...plugin.projectOverrides[projectPath], ...override };
|
|
926
|
+
saveConfig(config);
|
|
927
|
+
const secrets = new SecretsManager;
|
|
928
|
+
const syncConfig = loadConfig();
|
|
929
|
+
const summary = syncAllClients(syncConfig, secrets);
|
|
930
|
+
persistSyncState(summary, syncConfig);
|
|
931
|
+
saveConfig(syncConfig);
|
|
883
932
|
}
|
|
884
|
-
async function approvePluginComponent(
|
|
933
|
+
async function approvePluginComponent(nameOrId, component) {
|
|
885
934
|
const manager = new PluginManager;
|
|
886
|
-
|
|
935
|
+
const config = loadConfig(manager["configPath"]);
|
|
936
|
+
const id = resolvePluginId(config, nameOrId);
|
|
937
|
+
await manager.approveComponent(id, component);
|
|
887
938
|
}
|
|
888
|
-
async function getPluginStatus(
|
|
939
|
+
async function getPluginStatus(nameOrId) {
|
|
889
940
|
const manager = new PluginManager;
|
|
890
|
-
if (
|
|
891
|
-
|
|
941
|
+
if (nameOrId) {
|
|
942
|
+
const config = loadConfig(manager["configPath"]);
|
|
943
|
+
const id = resolvePluginId(config, nameOrId);
|
|
944
|
+
return manager.getPluginStatus(id);
|
|
945
|
+
}
|
|
892
946
|
const plugins = await manager.listPlugins();
|
|
893
947
|
return plugins[0] || null;
|
|
894
948
|
}
|
|
@@ -896,9 +950,50 @@ async function listPlugins() {
|
|
|
896
950
|
const manager = new PluginManager;
|
|
897
951
|
return manager.listPlugins();
|
|
898
952
|
}
|
|
953
|
+
function resolvePluginId(config, nameOrId) {
|
|
954
|
+
const plugins = config.plugins ?? {};
|
|
955
|
+
if (plugins[nameOrId])
|
|
956
|
+
return nameOrId;
|
|
957
|
+
const matches = Object.keys(plugins).filter((id) => plugins[id].name === nameOrId);
|
|
958
|
+
if (matches.length === 1)
|
|
959
|
+
return matches[0];
|
|
960
|
+
if (matches.length === 0) {
|
|
961
|
+
throw new Error(`Plugin "${nameOrId}" not found. Available: ${Object.values(plugins).map((p) => `${p.name} (${p.id})`).join(", ") || "none"}`);
|
|
962
|
+
}
|
|
963
|
+
throw new Error(`Multiple plugins match "${nameOrId}": ${matches.join(", ")}. Use the full id (<name>@<sha8>).`);
|
|
964
|
+
}
|
|
965
|
+
async function pluginConfigSet(nameOrId, key, value, projectPath) {
|
|
966
|
+
const manager = new PluginManager;
|
|
967
|
+
const config = loadConfig(manager["configPath"]);
|
|
968
|
+
const id = resolvePluginId(config, nameOrId);
|
|
969
|
+
const plugin = config.plugins?.[id];
|
|
970
|
+
if (!plugin)
|
|
971
|
+
throw new Error(`Plugin ${id} not found`);
|
|
972
|
+
if (projectPath) {
|
|
973
|
+
if (!plugin.projectOverrides)
|
|
974
|
+
plugin.projectOverrides = {};
|
|
975
|
+
if (!plugin.projectOverrides[projectPath])
|
|
976
|
+
plugin.projectOverrides[projectPath] = {};
|
|
977
|
+
if (!plugin.projectOverrides[projectPath].config)
|
|
978
|
+
plugin.projectOverrides[projectPath].config = {};
|
|
979
|
+
plugin.projectOverrides[projectPath].config[key] = value;
|
|
980
|
+
} else {
|
|
981
|
+
if (!plugin.config)
|
|
982
|
+
plugin.config = {};
|
|
983
|
+
plugin.config[key] = value;
|
|
984
|
+
}
|
|
985
|
+
saveConfig(config, manager["configPath"]);
|
|
986
|
+
}
|
|
987
|
+
async function pluginSync() {
|
|
988
|
+
const config = loadConfig();
|
|
989
|
+
const secrets = new SecretsManager;
|
|
990
|
+
const summary = syncAllClients(config, secrets);
|
|
991
|
+
persistSyncState(summary, config);
|
|
992
|
+
saveConfig(config);
|
|
993
|
+
}
|
|
899
994
|
async function preparePlugin(name) {
|
|
900
995
|
const manager = new PluginManager;
|
|
901
996
|
await manager.preparePlugin(name);
|
|
902
997
|
}
|
|
903
998
|
|
|
904
|
-
export { PluginCache, PluginDataManager, PluginLifecycle, parseSource, readManifest, discoverComponents, hasManifest, PluginManager, inspectPlugin, installPlugin, updatePlugin, uninstallPlugin, enablePlugin, disablePlugin, approvePluginComponent, getPluginStatus, listPlugins, preparePlugin };
|
|
999
|
+
export { PluginCache, PluginDataManager, PluginLifecycle, parseSource, readManifest, discoverComponents, hasManifest, resolvePluginVars, PluginManager, inspectPlugin, installPlugin, updatePlugin, uninstallPlugin, enablePlugin, disablePlugin, setPluginProjectOverride, approvePluginComponent, getPluginStatus, listPlugins, resolvePluginId, pluginConfigSet, pluginSync, preparePlugin };
|
|
@@ -32,6 +32,13 @@ function removeServer(config, name, force = false) {
|
|
|
32
32
|
throw new Error(`Server "${name}" does not exist.`);
|
|
33
33
|
}
|
|
34
34
|
delete config.servers[name];
|
|
35
|
+
if (config.projects) {
|
|
36
|
+
for (const project of Object.values(config.projects)) {
|
|
37
|
+
if (project.disabledServers) {
|
|
38
|
+
project.disabledServers = project.disabledServers.filter((s) => s !== name);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
35
42
|
}
|
|
36
43
|
function updateServer(config, name, spec) {
|
|
37
44
|
validateServerName(name);
|
|
@@ -66,14 +73,18 @@ function ensureGatewayToken(config, secrets) {
|
|
|
66
73
|
try {
|
|
67
74
|
const fileValue = fs.readFileSync(tokenPath, "utf8").trim();
|
|
68
75
|
if (fileValue) {
|
|
69
|
-
secrets.setSecret(secretName, fileValue);
|
|
70
76
|
return fileValue;
|
|
71
77
|
}
|
|
72
78
|
} catch {}
|
|
79
|
+
const storeValue = secrets.getSecret(secretName);
|
|
80
|
+
if (storeValue) {
|
|
81
|
+
ensureParentDir(tokenPath);
|
|
82
|
+
fs.writeFileSync(tokenPath, storeValue, { mode: 384 });
|
|
83
|
+
return storeValue;
|
|
84
|
+
}
|
|
73
85
|
const token = crypto.randomBytes(32).toString("base64url");
|
|
74
86
|
ensureParentDir(tokenPath);
|
|
75
87
|
fs.writeFileSync(tokenPath, token, { mode: 384 });
|
|
76
|
-
secrets.setSecret(secretName, token);
|
|
77
88
|
return token;
|
|
78
89
|
}
|
|
79
90
|
function rotateGatewayToken(config, secrets) {
|
|
@@ -82,7 +93,7 @@ function rotateGatewayToken(config, secrets) {
|
|
|
82
93
|
const tokenPath = getGatewayTokenPath(secretName);
|
|
83
94
|
ensureParentDir(tokenPath);
|
|
84
95
|
fs.writeFileSync(tokenPath, token, { mode: 384 });
|
|
85
|
-
secrets.
|
|
96
|
+
secrets.removeSecret(secretName);
|
|
86
97
|
return token;
|
|
87
98
|
}
|
|
88
99
|
function registerProject(globalConfig, projectPath, name) {
|
|
@@ -111,20 +122,82 @@ function setProjectServerEnabled(globalConfig, projectPath, serverName, enabled)
|
|
|
111
122
|
if (!project) {
|
|
112
123
|
throw new Error(`Project "${resolvedPath}" is not registered.`);
|
|
113
124
|
}
|
|
125
|
+
const serverSpec = globalConfig.servers[serverName];
|
|
126
|
+
if (!serverSpec) {
|
|
127
|
+
throw new Error(`Server "${serverName}" does not exist.`);
|
|
128
|
+
}
|
|
114
129
|
if (!project.disabledServers) {
|
|
115
130
|
project.disabledServers = [];
|
|
116
131
|
}
|
|
117
132
|
if (enabled) {
|
|
118
133
|
project.disabledServers = project.disabledServers.filter((s) => s !== serverName);
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
globalConfig.servers[serverName] = { ...serverSpec, enabled: true };
|
|
134
|
+
if (!isServerEnabled(serverSpec)) {
|
|
135
|
+
return { effective: false, reason: "globally_disabled" };
|
|
122
136
|
}
|
|
123
137
|
} else {
|
|
124
138
|
if (!project.disabledServers.includes(serverName)) {
|
|
125
139
|
project.disabledServers.push(serverName);
|
|
126
140
|
}
|
|
127
141
|
}
|
|
142
|
+
return { effective: true };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// src/core/errors.ts
|
|
146
|
+
class UpstreamError extends Error {
|
|
147
|
+
code;
|
|
148
|
+
upstream;
|
|
149
|
+
status;
|
|
150
|
+
wwwAuthenticate;
|
|
151
|
+
constructor(upstream, code, message, options) {
|
|
152
|
+
super(message);
|
|
153
|
+
this.name = "UpstreamError";
|
|
154
|
+
this.code = code;
|
|
155
|
+
this.upstream = upstream;
|
|
156
|
+
this.status = options?.status;
|
|
157
|
+
this.wwwAuthenticate = options?.wwwAuthenticate;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
class SecretNotFoundError extends Error {
|
|
162
|
+
code = "secret_missing";
|
|
163
|
+
secretName;
|
|
164
|
+
constructor(secretName) {
|
|
165
|
+
super(`Secret not found: ${secretName}. Run: mcpx secret set ${secretName}`);
|
|
166
|
+
this.name = "SecretNotFoundError";
|
|
167
|
+
this.secretName = secretName;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
var TIMEOUT_RE = /timed out after \d+ms/;
|
|
171
|
+
var UNREACHABLE_PATTERNS = [
|
|
172
|
+
/fetch failed/i,
|
|
173
|
+
/ECONNREFUSED/,
|
|
174
|
+
/ENOTFOUND/,
|
|
175
|
+
/EAI_AGAIN/,
|
|
176
|
+
/socket hang up/,
|
|
177
|
+
/ENOENT/,
|
|
178
|
+
/connect econnrefused/i
|
|
179
|
+
];
|
|
180
|
+
function classifyUpstreamError(upstream, error, wwwAuthenticate) {
|
|
181
|
+
if (error instanceof UpstreamError)
|
|
182
|
+
return error;
|
|
183
|
+
if (error instanceof SecretNotFoundError) {
|
|
184
|
+
return new UpstreamError(upstream, "secret_missing", error.message);
|
|
185
|
+
}
|
|
186
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
187
|
+
const maybeHttpError = error;
|
|
188
|
+
if (maybeHttpError?.code && (maybeHttpError.code === 401 || maybeHttpError.code === 403)) {
|
|
189
|
+
return new UpstreamError(upstream, "auth_required", message, {
|
|
190
|
+
status: maybeHttpError.code,
|
|
191
|
+
wwwAuthenticate
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
if (TIMEOUT_RE.test(message)) {
|
|
195
|
+
return new UpstreamError(upstream, "timeout", message);
|
|
196
|
+
}
|
|
197
|
+
if (UNREACHABLE_PATTERNS.some((p) => p.test(message))) {
|
|
198
|
+
return new UpstreamError(upstream, "unreachable", message);
|
|
199
|
+
}
|
|
200
|
+
return new UpstreamError(upstream, "upstream_error", message);
|
|
128
201
|
}
|
|
129
202
|
|
|
130
203
|
// src/core/secrets.ts
|
|
@@ -240,7 +313,7 @@ class SecretsManager {
|
|
|
240
313
|
return value;
|
|
241
314
|
const secret = this.getSecret(secretName);
|
|
242
315
|
if (!secret)
|
|
243
|
-
throw new
|
|
316
|
+
throw new SecretNotFoundError(secretName);
|
|
244
317
|
return secret;
|
|
245
318
|
}
|
|
246
319
|
rotateLocalToken(secretName = "local_gateway_token") {
|
|
@@ -260,4 +333,4 @@ function readSecretValueFromStdin() {
|
|
|
260
333
|
return content;
|
|
261
334
|
}
|
|
262
335
|
|
|
263
|
-
export { SecretsManager, readSecretValueFromStdin, validateServerName, addServer, removeServer, updateServer, setServerEnabled, getGatewayTokenSecretName, ensureGatewayToken, rotateGatewayToken, registerProject, unregisterProject, setProjectServerEnabled };
|
|
336
|
+
export { UpstreamError, SecretNotFoundError, classifyUpstreamError, SecretsManager, readSecretValueFromStdin, validateServerName, addServer, removeServer, updateServer, setServerEnabled, getGatewayTokenSecretName, ensureGatewayToken, rotateGatewayToken, registerProject, unregisterProject, setProjectServerEnabled };
|