dsh-plugin-capabilities 0.1.2 → 0.1.4
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/README.md +2 -2
- package/lib/client.js +77 -22
- package/lib/client.js.map +2 -2
- package/lib/index.js +99 -34
- package/lib/index.js.map +3 -3
- package/package.json +15 -3
- package/src/client/McpTab.tsx +81 -29
- package/src/client/css.ts +9 -0
- package/src/client/locales.ts +2 -0
- package/src/mcp.test.ts +42 -0
- package/src/mcp.ts +132 -38
package/lib/index.js
CHANGED
|
@@ -904,7 +904,10 @@ var SERVER_NAME_RE = /^[A-Za-z0-9_-]{1,32}$/;
|
|
|
904
904
|
function loadPatch(profileDirPath) {
|
|
905
905
|
const path = join4(profileDirPath, "cordis.patch.yml");
|
|
906
906
|
const text = existsSync3(path) ? readFileSync2(path, "utf8") : "[]";
|
|
907
|
-
|
|
907
|
+
const doc = parseDocument(text);
|
|
908
|
+
const contents = doc.contents;
|
|
909
|
+
if (contents !== null && contents.flow === true && contents.items.length === 0) contents.flow = false;
|
|
910
|
+
return doc;
|
|
908
911
|
}
|
|
909
912
|
function savePatch(profileDirPath, doc) {
|
|
910
913
|
mkdirSync2(profileDirPath, { recursive: true });
|
|
@@ -914,34 +917,87 @@ function toNode(value) {
|
|
|
914
917
|
return new Document(value).contents;
|
|
915
918
|
}
|
|
916
919
|
function rowSeq(doc) {
|
|
917
|
-
if (doc.contents === null)
|
|
920
|
+
if (doc.contents === null) {
|
|
921
|
+
doc.contents = toNode([]);
|
|
922
|
+
doc.contents.flow = false;
|
|
923
|
+
}
|
|
918
924
|
return doc.contents;
|
|
919
925
|
}
|
|
920
|
-
function
|
|
921
|
-
return
|
|
926
|
+
function isSeqNode(value) {
|
|
927
|
+
return typeof value === "object" && value !== null && Array.isArray(value.items);
|
|
928
|
+
}
|
|
929
|
+
function insertListOf(item) {
|
|
930
|
+
if (item.has("id")) return void 0;
|
|
931
|
+
const node = item.get("insert");
|
|
932
|
+
return isSeqNode(node) ? node : void 0;
|
|
933
|
+
}
|
|
934
|
+
function mcpRowItems(doc) {
|
|
935
|
+
const found = [];
|
|
936
|
+
for (const item of rowSeq(doc).items ?? []) {
|
|
937
|
+
if (item.get("name") === MCP_PLUGIN) found.push({ node: item });
|
|
938
|
+
const list = insertListOf(item);
|
|
939
|
+
for (const row of list?.items ?? []) {
|
|
940
|
+
if (row.get("name") === MCP_PLUGIN) found.push({ node: row, list });
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
return found;
|
|
944
|
+
}
|
|
945
|
+
function rowToMcp(doc, item) {
|
|
946
|
+
const configNode = item.get("config");
|
|
947
|
+
const plain = typeof configNode === "object" && configNode !== null && typeof configNode.toJS === "function" ? configNode.toJS(doc) : {};
|
|
948
|
+
return {
|
|
949
|
+
id: String(item.get("id") ?? ""),
|
|
950
|
+
serverName: String(plain.serverName ?? ""),
|
|
951
|
+
transport: plain.transport === "streamable-http" ? "streamable-http" : "stdio",
|
|
952
|
+
disabled: item.get("disabled") === true,
|
|
953
|
+
...typeof plain.command === "string" && plain.command !== "" ? { command: plain.command } : {},
|
|
954
|
+
...Array.isArray(plain.args) ? { args: plain.args.map(String) } : {},
|
|
955
|
+
...isStringMap(plain.env) ? { env: plain.env } : {},
|
|
956
|
+
...typeof plain.cwd === "string" && plain.cwd !== "" ? { cwd: plain.cwd } : {},
|
|
957
|
+
...typeof plain.url === "string" && plain.url !== "" ? { url: plain.url } : {},
|
|
958
|
+
...isStringMap(plain.headers) ? { headers: plain.headers } : {}
|
|
959
|
+
};
|
|
922
960
|
}
|
|
923
961
|
function isStringMap(value) {
|
|
924
962
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
925
963
|
return Object.values(value).every((entry) => typeof entry === "string");
|
|
926
964
|
}
|
|
965
|
+
function managedInsert(doc) {
|
|
966
|
+
const seq = rowSeq(doc);
|
|
967
|
+
const bare = [];
|
|
968
|
+
let target;
|
|
969
|
+
for (const item of seq.items ?? []) {
|
|
970
|
+
if (item.get("name") === MCP_PLUGIN) bare.push(item);
|
|
971
|
+
const list = insertListOf(item);
|
|
972
|
+
if (list !== void 0 && list.items.some((row) => row.get("name") === MCP_PLUGIN)) target ??= list;
|
|
973
|
+
}
|
|
974
|
+
if (target === void 0) {
|
|
975
|
+
const entry = toNode({ insert: [] });
|
|
976
|
+
seq.add(entry);
|
|
977
|
+
target = entry.get("insert");
|
|
978
|
+
target.flow = false;
|
|
979
|
+
}
|
|
980
|
+
for (const row of bare) {
|
|
981
|
+
seq.items.splice(seq.items.indexOf(row), 1);
|
|
982
|
+
target.add(row);
|
|
983
|
+
}
|
|
984
|
+
return target;
|
|
985
|
+
}
|
|
986
|
+
function takenIds(doc) {
|
|
987
|
+
const taken = /* @__PURE__ */ new Set();
|
|
988
|
+
for (const item of rowSeq(doc).items ?? []) {
|
|
989
|
+
const id = String(item.get("id") ?? "");
|
|
990
|
+
if (id !== "") taken.add(id);
|
|
991
|
+
for (const row of insertListOf(item)?.items ?? []) {
|
|
992
|
+
const rowId = String(row.get("id") ?? "");
|
|
993
|
+
if (rowId !== "") taken.add(rowId);
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
return taken;
|
|
997
|
+
}
|
|
927
998
|
function listMcp(profileDirPath) {
|
|
928
999
|
const doc = loadPatch(profileDirPath);
|
|
929
|
-
return
|
|
930
|
-
const configNode = item.get("config");
|
|
931
|
-
const plain = typeof configNode === "object" && configNode !== null && typeof configNode.toJS === "function" ? configNode.toJS(doc) : {};
|
|
932
|
-
return {
|
|
933
|
-
id: String(item.get("id") ?? ""),
|
|
934
|
-
serverName: String(plain.serverName ?? ""),
|
|
935
|
-
transport: plain.transport === "streamable-http" ? "streamable-http" : "stdio",
|
|
936
|
-
disabled: item.get("disabled") === true,
|
|
937
|
-
...typeof plain.command === "string" && plain.command !== "" ? { command: plain.command } : {},
|
|
938
|
-
...Array.isArray(plain.args) ? { args: plain.args.map(String) } : {},
|
|
939
|
-
...isStringMap(plain.env) ? { env: plain.env } : {},
|
|
940
|
-
...typeof plain.cwd === "string" && plain.cwd !== "" ? { cwd: plain.cwd } : {},
|
|
941
|
-
...typeof plain.url === "string" && plain.url !== "" ? { url: plain.url } : {},
|
|
942
|
-
...isStringMap(plain.headers) ? { headers: plain.headers } : {}
|
|
943
|
-
};
|
|
944
|
-
});
|
|
1000
|
+
return mcpRowItems(doc).map(({ node }) => rowToMcp(doc, node));
|
|
945
1001
|
}
|
|
946
1002
|
function validateMcpInput(input) {
|
|
947
1003
|
if (!SERVER_NAME_RE.test(input.serverName)) return "serverName must be 1-32 chars of A-Z a-z 0-9 _ -";
|
|
@@ -957,13 +1013,11 @@ function validateMcpInput(input) {
|
|
|
957
1013
|
function upsertMcp(profileDirPath, input) {
|
|
958
1014
|
const inputId = input.id ?? "";
|
|
959
1015
|
const doc = loadPatch(profileDirPath);
|
|
960
|
-
const
|
|
961
|
-
const existing = inputId !== "" ?
|
|
1016
|
+
const list = managedInsert(doc);
|
|
1017
|
+
const existing = inputId !== "" ? mcpRowItems(doc).find(({ node: node2 }) => String(node2.get("id") ?? "") === inputId) : void 0;
|
|
962
1018
|
let id = inputId !== "" ? inputId : `mcp-${input.serverName}`;
|
|
963
1019
|
if (existing === void 0) {
|
|
964
|
-
const taken =
|
|
965
|
-
(seq.items ?? []).map((item) => String(item.get("id") ?? "")).filter((id2) => id2 !== "")
|
|
966
|
-
);
|
|
1020
|
+
const taken = takenIds(doc);
|
|
967
1021
|
let suffix = 2;
|
|
968
1022
|
while (taken.has(id)) id = `mcp-${input.serverName}-${suffix++}`;
|
|
969
1023
|
}
|
|
@@ -983,26 +1037,37 @@ function upsertMcp(profileDirPath, input) {
|
|
|
983
1037
|
const row = { id, name: MCP_PLUGIN, config };
|
|
984
1038
|
if (input.disabled === true) row.disabled = true;
|
|
985
1039
|
const node = toNode(row);
|
|
986
|
-
if (existing === void 0)
|
|
987
|
-
|
|
1040
|
+
if (existing === void 0) {
|
|
1041
|
+
list.add(node);
|
|
1042
|
+
} else if (existing.list !== void 0) {
|
|
1043
|
+
existing.list.items.splice(existing.list.items.indexOf(existing.node), 1, node);
|
|
1044
|
+
} else {
|
|
1045
|
+
rowSeq(doc).items.splice(rowSeq(doc).items.indexOf(existing.node), 1, node);
|
|
1046
|
+
}
|
|
988
1047
|
savePatch(profileDirPath, doc);
|
|
989
1048
|
return id;
|
|
990
1049
|
}
|
|
991
1050
|
function setMcpDisabled(profileDirPath, id, disabled) {
|
|
992
1051
|
const doc = loadPatch(profileDirPath);
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
if (
|
|
996
|
-
|
|
1052
|
+
managedInsert(doc);
|
|
1053
|
+
const hit = mcpRowItems(doc).find(({ node }) => String(node.get("id") ?? "") === id);
|
|
1054
|
+
if (hit === void 0) return false;
|
|
1055
|
+
if (disabled) hit.node.set("disabled", true);
|
|
1056
|
+
else hit.node.delete("disabled");
|
|
997
1057
|
savePatch(profileDirPath, doc);
|
|
998
1058
|
return true;
|
|
999
1059
|
}
|
|
1000
1060
|
function removeMcp(profileDirPath, id) {
|
|
1001
1061
|
const doc = loadPatch(profileDirPath);
|
|
1002
|
-
|
|
1003
|
-
|
|
1062
|
+
managedInsert(doc);
|
|
1063
|
+
const hit = mcpRowItems(doc).find(({ node }) => String(node.get("id") ?? "") === id);
|
|
1064
|
+
if (hit === void 0 || hit.list === void 0) return false;
|
|
1065
|
+
hit.list.items.splice(hit.list.items.indexOf(hit.node), 1);
|
|
1004
1066
|
const seq = rowSeq(doc);
|
|
1005
|
-
|
|
1067
|
+
const owner = (seq.items ?? []).find((item) => insertListOf(item) === hit.list);
|
|
1068
|
+
if (owner !== void 0 && hit.list.items.length === 0 && owner.items.length === 1) {
|
|
1069
|
+
seq.items.splice(seq.items.indexOf(owner), 1);
|
|
1070
|
+
}
|
|
1006
1071
|
savePatch(profileDirPath, doc);
|
|
1007
1072
|
return true;
|
|
1008
1073
|
}
|