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/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
- return parseDocument(text);
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) doc.contents = toNode([]);
920
+ if (doc.contents === null) {
921
+ doc.contents = toNode([]);
922
+ doc.contents.flow = false;
923
+ }
918
924
  return doc.contents;
919
925
  }
920
- function mcpRows(doc) {
921
- return (rowSeq(doc).items ?? []).filter((item) => item.get("name") === MCP_PLUGIN);
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 mcpRows(doc).map((item) => {
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 seq = rowSeq(doc);
961
- const existing = inputId !== "" ? mcpRows(doc).find((item) => item.get("id") === inputId) : void 0;
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 = new Set(
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) seq.add(node);
987
- else seq.items[seq.items.indexOf(existing)] = node;
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
- const item = mcpRows(doc).find((row) => row.get("id") === id);
994
- if (item === void 0) return false;
995
- if (disabled) item.set("disabled", true);
996
- else item.delete("disabled");
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
- const item = mcpRows(doc).find((row) => row.get("id") === id);
1003
- if (item === void 0) return false;
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
- seq.items.splice(seq.items.indexOf(item), 1);
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
  }