dsh-plugin-capabilities 0.3.7 → 0.3.9

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
@@ -667,14 +667,14 @@ function stringArray(value) {
667
667
  const out = value.filter((entry) => typeof entry === "string");
668
668
  return out.length > 0 ? out : void 0;
669
669
  }
670
- function mapClaudeEntry(name2, entry) {
670
+ function mapMcpServersEntry(agent, name2, entry) {
671
671
  if (typeof entry !== "object" || entry === null) return null;
672
672
  const record = entry;
673
673
  const type = typeof record.type === "string" ? record.type : "stdio";
674
674
  if (type === "stdio" || type === "stdio" && record.command !== void 0) {
675
675
  if (typeof record.command !== "string" || record.command === "") return null;
676
676
  return {
677
- agent: "claude-code",
677
+ agent,
678
678
  name: name2,
679
679
  transport: "stdio",
680
680
  command: record.command,
@@ -685,7 +685,7 @@ function mapClaudeEntry(name2, entry) {
685
685
  if (type === "http" || type === "streamable-http") {
686
686
  if (typeof record.url !== "string" || record.url === "") return null;
687
687
  return {
688
- agent: "claude-code",
688
+ agent,
689
689
  name: name2,
690
690
  transport: "streamable-http",
691
691
  url: record.url,
@@ -708,11 +708,57 @@ function scanClaudeMcp(home = homedir()) {
708
708
  }
709
709
  const out = [];
710
710
  for (const [name2, entry] of Object.entries(merged)) {
711
- const mapped = mapClaudeEntry(name2, entry);
711
+ const mapped = mapMcpServersEntry("claude-code", name2, entry);
712
712
  if (mapped !== null) out.push(mapped);
713
713
  }
714
714
  return out;
715
715
  }
716
+ function scanCursorMcp(home = homedir()) {
717
+ const file = join(home, ".cursor", "mcp.json");
718
+ if (!existsSync(file)) return [];
719
+ let parsed;
720
+ try {
721
+ parsed = JSON.parse(readFileSync(file, "utf8"));
722
+ } catch {
723
+ return [];
724
+ }
725
+ if (typeof parsed.mcpServers !== "object" || parsed.mcpServers === null) return [];
726
+ const out = [];
727
+ for (const [name2, entry] of Object.entries(parsed.mcpServers)) {
728
+ const mapped = mapMcpServersEntry("cursor", name2, entry);
729
+ if (mapped !== null) out.push(mapped);
730
+ }
731
+ return out;
732
+ }
733
+ function scanGeminiMcp(home = homedir()) {
734
+ const file = join(home, ".gemini", "settings.json");
735
+ if (!existsSync(file)) return [];
736
+ let parsed;
737
+ try {
738
+ parsed = JSON.parse(readFileSync(file, "utf8"));
739
+ } catch {
740
+ return [];
741
+ }
742
+ if (typeof parsed.mcpServers !== "object" || parsed.mcpServers === null) return [];
743
+ const out = [];
744
+ for (const [name2, entry] of Object.entries(parsed.mcpServers)) {
745
+ if (typeof entry !== "object" || entry === null) continue;
746
+ const record = entry;
747
+ if (typeof record.command === "string" && record.command !== "") {
748
+ out.push({
749
+ agent: "gemini",
750
+ name: name2,
751
+ transport: "stdio",
752
+ command: record.command,
753
+ args: stringArray(record.args),
754
+ env: stringEntries(record.env)
755
+ });
756
+ } else if (typeof record.httpUrl === "string" && record.httpUrl !== "") {
757
+ out.push({ agent: "gemini", name: name2, transport: "streamable-http", url: record.httpUrl });
758
+ }
759
+ }
760
+ return out;
761
+ }
716
762
  function scanCodexMcp(home = homedir()) {
717
763
  const file = join(home, ".codex", "config.toml");
718
764
  if (!existsSync(file)) return [];
@@ -745,7 +791,7 @@ function scanCodexMcp(home = homedir()) {
745
791
  }
746
792
  function scanAllMcp(home = homedir()) {
747
793
  const seen = /* @__PURE__ */ new Set();
748
- return [...scanClaudeMcp(home), ...scanCodexMcp(home)].filter((server) => {
794
+ return [...scanClaudeMcp(home), ...scanCodexMcp(home), ...scanCursorMcp(home), ...scanGeminiMcp(home)].filter((server) => {
749
795
  const key = `${server.agent}/${server.name}`;
750
796
  if (seen.has(key)) return false;
751
797
  seen.add(key);
@@ -764,9 +810,11 @@ function argvProfile(argv = process.argv) {
764
810
  if (flag !== -1 && flag + 1 < argv.length && !argv[flag + 1].startsWith("-")) return argv[flag + 1];
765
811
  return void 0;
766
812
  }
813
+ function dshHomeDir(dshHome = process.env.DSH_HOME) {
814
+ return dshHome ?? join2(homedir2(), ".dsh");
815
+ }
767
816
  function profileDir(profile, dshHome = process.env.DSH_HOME) {
768
- const home = dshHome ?? join2(homedir2(), ".dsh");
769
- return join2(home, "profiles", profile);
817
+ return join2(dshHomeDir(dshHome), "profiles", profile);
770
818
  }
771
819
 
772
820
  // src/routes.ts
@@ -1437,8 +1485,8 @@ function assertPatchParses(path, doc) {
1437
1485
  `\u914D\u7F6E\u6587\u4EF6 ${path} \u5B58\u5728 YAML \u8BED\u6CD5\u9519\u8BEF\uFF08${at}${total}\uFF09\uFF0C\u672A\u5199\u5165\u4EFB\u4F55\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u590D\u8BE5\u6587\u4EF6\u540E\u91CD\u8BD5 / YAML syntax error in the patch file (${at}${total}); nothing was written \u2014 fix it and retry`
1438
1486
  );
1439
1487
  }
1440
- function loadPatch(profileDirPath) {
1441
- const path = join9(profileDirPath, "cordis.patch.yml");
1488
+ function loadPatch(dirPath) {
1489
+ const path = join9(dirPath, "cordis.patch.yml");
1442
1490
  const text = existsSync6(path) ? readFileSync6(path, "utf8") : "[]";
1443
1491
  const doc = parseDocument(text);
1444
1492
  assertPatchParses(path, doc);
@@ -1446,9 +1494,9 @@ function loadPatch(profileDirPath) {
1446
1494
  if (contents !== null && contents.flow === true && contents.items.length === 0) contents.flow = false;
1447
1495
  return doc;
1448
1496
  }
1449
- function savePatch(profileDirPath, doc) {
1450
- mkdirSync5(profileDirPath, { recursive: true });
1451
- writeFileSync4(join9(profileDirPath, "cordis.patch.yml"), String(doc), "utf8");
1497
+ function savePatch(dirPath, doc) {
1498
+ mkdirSync5(dirPath, { recursive: true });
1499
+ writeFileSync4(join9(dirPath, "cordis.patch.yml"), String(doc), "utf8");
1452
1500
  }
1453
1501
  function toNode(value) {
1454
1502
  return new Document(value).contents;
@@ -1532,10 +1580,36 @@ function takenIds(doc) {
1532
1580
  }
1533
1581
  return taken;
1534
1582
  }
1535
- function listMcp(profileDirPath) {
1536
- const doc = loadPatch(profileDirPath);
1583
+ function listMcp(dirPath) {
1584
+ const doc = loadPatch(dirPath);
1537
1585
  return mcpRowItems(doc).map(({ node }) => rowToMcp(doc, node));
1538
1586
  }
1587
+ function listMcpScoped(profileDirPath, dshHomePath) {
1588
+ let globalRows = [];
1589
+ let globalError;
1590
+ if (existsSync6(join9(dshHomePath, "cordis.patch.yml"))) {
1591
+ try {
1592
+ globalRows = listMcp(dshHomePath);
1593
+ } catch (error) {
1594
+ globalError = error instanceof Error ? error.message : String(error);
1595
+ }
1596
+ }
1597
+ const globalIds = new Set(globalRows.map((row) => row.id).filter((id) => id !== ""));
1598
+ return {
1599
+ servers: [
1600
+ ...globalRows.map((row) => ({ ...row, scope: "global" })),
1601
+ ...listMcp(profileDirPath).map((row) => ({
1602
+ ...row,
1603
+ scope: "profile",
1604
+ ...globalIds.has(row.id) ? { shadowed: true } : {}
1605
+ }))
1606
+ ],
1607
+ ...globalError !== void 0 ? { globalError } : {}
1608
+ };
1609
+ }
1610
+ function mcpScopeDir(scope, profileDirPath, dshHomePath) {
1611
+ return scope === "global" ? dshHomePath : profileDirPath;
1612
+ }
1539
1613
  function validateMcpInput(input) {
1540
1614
  if (!SERVER_NAME_RE.test(input.serverName)) return "serverName must be 1-32 chars of A-Z a-z 0-9 _ -";
1541
1615
  const id = input.id ?? "";
@@ -1547,9 +1621,9 @@ function validateMcpInput(input) {
1547
1621
  }
1548
1622
  return null;
1549
1623
  }
1550
- function upsertMcp(profileDirPath, input) {
1624
+ function upsertMcp(dirPath, input) {
1551
1625
  const inputId = input.id ?? "";
1552
- const doc = loadPatch(profileDirPath);
1626
+ const doc = loadPatch(dirPath);
1553
1627
  const list = managedInsert(doc);
1554
1628
  const existing = inputId !== "" ? mcpRowItems(doc).find(({ node: node2 }) => String(node2.get("id") ?? "") === inputId) : void 0;
1555
1629
  let id = inputId !== "" ? inputId : `mcp-${input.serverName}`;
@@ -1581,21 +1655,21 @@ function upsertMcp(profileDirPath, input) {
1581
1655
  } else {
1582
1656
  rowSeq(doc).items.splice(rowSeq(doc).items.indexOf(existing.node), 1, node);
1583
1657
  }
1584
- savePatch(profileDirPath, doc);
1658
+ savePatch(dirPath, doc);
1585
1659
  return id;
1586
1660
  }
1587
- function setMcpDisabled(profileDirPath, id, disabled) {
1588
- const doc = loadPatch(profileDirPath);
1661
+ function setMcpDisabled(dirPath, id, disabled) {
1662
+ const doc = loadPatch(dirPath);
1589
1663
  managedInsert(doc);
1590
1664
  const hit = mcpRowItems(doc).find(({ node }) => String(node.get("id") ?? "") === id);
1591
1665
  if (hit === void 0) return false;
1592
1666
  if (disabled) hit.node.set("disabled", true);
1593
1667
  else hit.node.delete("disabled");
1594
- savePatch(profileDirPath, doc);
1668
+ savePatch(dirPath, doc);
1595
1669
  return true;
1596
1670
  }
1597
- function removeMcp(profileDirPath, id) {
1598
- const doc = loadPatch(profileDirPath);
1671
+ function removeMcp(dirPath, id) {
1672
+ const doc = loadPatch(dirPath);
1599
1673
  managedInsert(doc);
1600
1674
  const hit = mcpRowItems(doc).find(({ node }) => String(node.get("id") ?? "") === id);
1601
1675
  if (hit === void 0 || hit.list === void 0) return false;
@@ -1605,9 +1679,59 @@ function removeMcp(profileDirPath, id) {
1605
1679
  if (owner !== void 0 && hit.list.items.length === 0 && owner.items.length === 1) {
1606
1680
  seq.items.splice(seq.items.indexOf(owner), 1);
1607
1681
  }
1608
- savePatch(profileDirPath, doc);
1682
+ savePatch(dirPath, doc);
1609
1683
  return true;
1610
1684
  }
1685
+ function mcpRowToInput(row) {
1686
+ return {
1687
+ id: "",
1688
+ serverName: row.serverName,
1689
+ transport: row.transport,
1690
+ disabled: row.disabled,
1691
+ ...row.transport === "stdio" ? {
1692
+ command: row.command,
1693
+ ...row.args !== void 0 ? { args: row.args } : {},
1694
+ ...row.env !== void 0 ? { env: row.env } : {},
1695
+ ...row.cwd !== void 0 ? { cwd: row.cwd } : {}
1696
+ } : {
1697
+ url: row.url,
1698
+ ...row.headers !== void 0 ? { headers: row.headers } : {}
1699
+ }
1700
+ };
1701
+ }
1702
+ function resolveCommandOnPath(command, pathEnv, platform = process.platform) {
1703
+ if (command.includes("/") || command.includes("\\")) return existsSync6(command);
1704
+ const exts = platform === "win32" ? ["", ".com", ".exe", ".bat", ".cmd"] : [""];
1705
+ const separator = platform === "win32" ? ";" : ":";
1706
+ for (const rawDir of pathEnv.split(separator)) {
1707
+ const dir = rawDir.trim().replace(/^"|"$/g, "");
1708
+ if (dir === "") continue;
1709
+ for (const ext of exts) {
1710
+ if (existsSync6(join9(dir, command + ext))) return true;
1711
+ }
1712
+ }
1713
+ return false;
1714
+ }
1715
+ async function checkMcpRow(row, options = {}) {
1716
+ const pathEnv = options.pathEnv ?? process.env.PATH ?? "";
1717
+ if (row.transport === "stdio") {
1718
+ const command = row.command ?? "";
1719
+ if (command === "") return { ok: false, detail: "row has no command" };
1720
+ return resolveCommandOnPath(command, pathEnv, options.platform) ? { ok: true, detail: command } : { ok: false, detail: `${command} not found on PATH` };
1721
+ }
1722
+ if (row.url === void 0 || !/^https?:\/\//.test(row.url)) return { ok: false, detail: "row has no http url" };
1723
+ try {
1724
+ const response = await fetch(row.url, {
1725
+ headers: { accept: "application/json, text/event-stream" },
1726
+ signal: AbortSignal.timeout(options.timeoutMs ?? 5e3)
1727
+ });
1728
+ return { ok: true, detail: `HTTP ${response.status}` };
1729
+ } catch (error) {
1730
+ const message = error instanceof Error ? error.message : String(error);
1731
+ const cause = error.cause?.code;
1732
+ return { ok: false, detail: cause !== void 0 ? String(cause) : message };
1733
+ }
1734
+ }
1611
1735
 
1612
1736
  // src/routes.ts
1613
1737
  function customSkillWritable(dir, dshHome) {
@@ -1633,6 +1757,9 @@ function toSkillRow(skill, dshHome) {
1633
1757
  function toRootView(entry) {
1634
1758
  return { ...entry, live: entry.roots.every((root) => rootExists(root)) };
1635
1759
  }
1760
+ function readScope(value) {
1761
+ return value === "global" ? "global" : "profile";
1762
+ }
1636
1763
  function mountCapabilitiesRoutes(host, config) {
1637
1764
  const disposers = [
1638
1765
  host.webServer.register({
@@ -1949,7 +2076,7 @@ function mountCapabilitiesRoutes(host, config) {
1949
2076
  sendJson(response, 502, { error: "market index unavailable (offline?)" });
1950
2077
  return;
1951
2078
  }
1952
- const existing = new Set(listMcp(config.profileDirPath).map((row) => row.serverName));
2079
+ const existing = new Set(listMcpScoped(config.profileDirPath, config.dshHomePath).servers.map((row) => row.serverName));
1953
2080
  sendJson(response, 200, {
1954
2081
  source: index.source,
1955
2082
  servers: (index.servers ?? []).map((server) => ({ ...server, installed: existing.has(server.id) }))
@@ -2008,7 +2135,7 @@ function mountCapabilitiesRoutes(host, config) {
2008
2135
  sendJson(response, 404, { error: "server not found in the market index" });
2009
2136
  return;
2010
2137
  }
2011
- if (listMcp(config.profileDirPath).some((row) => row.serverName === server.id)) {
2138
+ if (listMcpScoped(config.profileDirPath, config.dshHomePath).servers.some((row) => row.serverName === server.id)) {
2012
2139
  sendJson(response, 409, { error: "already installed" });
2013
2140
  return;
2014
2141
  }
@@ -2023,8 +2150,9 @@ function mountCapabilitiesRoutes(host, config) {
2023
2150
  sendJson(response, 400, { error: invalid });
2024
2151
  return;
2025
2152
  }
2026
- const id = upsertMcp(config.profileDirPath, input);
2027
- sendJson(response, 200, { ok: true, id, restartNeeded: true });
2153
+ const scope = readScope(body.scope);
2154
+ const id = upsertMcp(mcpScopeDir(scope, config.profileDirPath, config.dshHomePath), input);
2155
+ sendJson(response, 200, { ok: true, id, scope, restartNeeded: true });
2028
2156
  } catch (error) {
2029
2157
  sendJson(response, 500, { error: error instanceof Error ? error.message : String(error) });
2030
2158
  }
@@ -2039,7 +2167,12 @@ function mountCapabilitiesRoutes(host, config) {
2039
2167
  response.end();
2040
2168
  return;
2041
2169
  }
2042
- sendJson(response, 200, { servers: listMcp(config.profileDirPath), restartNeeded: true });
2170
+ const listing = listMcpScoped(config.profileDirPath, config.dshHomePath);
2171
+ sendJson(response, 200, {
2172
+ servers: listing.servers,
2173
+ ...listing.globalError !== void 0 ? { globalError: listing.globalError } : {},
2174
+ restartNeeded: true
2175
+ });
2043
2176
  }
2044
2177
  }),
2045
2178
  host.webServer.register({
@@ -2062,8 +2195,9 @@ function mountCapabilitiesRoutes(host, config) {
2062
2195
  sendJson(response, 400, { error: invalid });
2063
2196
  return;
2064
2197
  }
2065
- const id = upsertMcp(config.profileDirPath, input);
2066
- sendJson(response, 200, { ok: true, id, restartNeeded: true });
2198
+ const scope = readScope(input.scope);
2199
+ const id = upsertMcp(mcpScopeDir(scope, config.profileDirPath, config.dshHomePath), input);
2200
+ sendJson(response, 200, { ok: true, id, scope, restartNeeded: true });
2067
2201
  } catch (error) {
2068
2202
  sendJson(response, 500, { error: error instanceof Error ? error.message : String(error) });
2069
2203
  }
@@ -2088,7 +2222,8 @@ function mountCapabilitiesRoutes(host, config) {
2088
2222
  sendJson(response, 400, { error: "id and disabled are required" });
2089
2223
  return;
2090
2224
  }
2091
- const ok = setMcpDisabled(config.profileDirPath, body.id, body.disabled);
2225
+ const dir = mcpScopeDir(readScope(body.scope), config.profileDirPath, config.dshHomePath);
2226
+ const ok = setMcpDisabled(dir, body.id, body.disabled);
2092
2227
  sendJson(response, ok ? 200 : 404, ok ? { ok: true, restartNeeded: true } : { error: "server row not found" });
2093
2228
  } catch (error) {
2094
2229
  sendJson(response, 500, { error: error instanceof Error ? error.message : String(error) });
@@ -2114,13 +2249,84 @@ function mountCapabilitiesRoutes(host, config) {
2114
2249
  sendJson(response, 400, { error: "id is required" });
2115
2250
  return;
2116
2251
  }
2117
- const ok = removeMcp(config.profileDirPath, body.id);
2252
+ const dir = mcpScopeDir(readScope(body.scope), config.profileDirPath, config.dshHomePath);
2253
+ const ok = removeMcp(dir, body.id);
2118
2254
  sendJson(response, ok ? 200 : 404, ok ? { ok: true, restartNeeded: true } : { error: "server row not found" });
2119
2255
  } catch (error) {
2120
2256
  sendJson(response, 500, { error: error instanceof Error ? error.message : String(error) });
2121
2257
  }
2122
2258
  }
2123
2259
  }),
2260
+ host.webServer.register({
2261
+ kind: "exact",
2262
+ path: "/dsh-plugin-capabilities/mcp/check",
2263
+ handler: async (request, response) => {
2264
+ if (request.method !== "POST") {
2265
+ response.writeHead(405, { allow: "POST" });
2266
+ response.end();
2267
+ return;
2268
+ }
2269
+ if (!sameOrigin(request)) {
2270
+ sendJson(response, 403, { error: "untrusted origin" });
2271
+ return;
2272
+ }
2273
+ try {
2274
+ const body = await readJsonBody(request);
2275
+ if (typeof body.id !== "string") {
2276
+ sendJson(response, 400, { error: "id is required" });
2277
+ return;
2278
+ }
2279
+ const dir = mcpScopeDir(readScope(body.scope), config.profileDirPath, config.dshHomePath);
2280
+ const row = listMcp(dir).find((item) => item.id === body.id);
2281
+ if (row === void 0) {
2282
+ sendJson(response, 404, { error: "server row not found" });
2283
+ return;
2284
+ }
2285
+ sendJson(response, 200, await checkMcpRow(row));
2286
+ } catch (error) {
2287
+ sendJson(response, 500, { error: error instanceof Error ? error.message : String(error) });
2288
+ }
2289
+ }
2290
+ }),
2291
+ host.webServer.register({
2292
+ kind: "exact",
2293
+ path: "/dsh-plugin-capabilities/mcp/copy",
2294
+ handler: async (request, response) => {
2295
+ if (request.method !== "POST") {
2296
+ response.writeHead(405, { allow: "POST" });
2297
+ response.end();
2298
+ return;
2299
+ }
2300
+ if (!sameOrigin(request)) {
2301
+ sendJson(response, 403, { error: "untrusted origin" });
2302
+ return;
2303
+ }
2304
+ try {
2305
+ const body = await readJsonBody(request);
2306
+ if (typeof body.id !== "string") {
2307
+ sendJson(response, 400, { error: "id is required" });
2308
+ return;
2309
+ }
2310
+ const scope = readScope(body.scope);
2311
+ const toScope = readScope(body.toScope);
2312
+ const sourceRow = listMcp(mcpScopeDir(scope, config.profileDirPath, config.dshHomePath)).find((item) => item.id === body.id);
2313
+ if (sourceRow === void 0) {
2314
+ sendJson(response, 404, { error: "server row not found" });
2315
+ return;
2316
+ }
2317
+ const input = mcpRowToInput(sourceRow);
2318
+ const invalid = validateMcpInput(input);
2319
+ if (invalid !== null) {
2320
+ sendJson(response, 400, { error: invalid });
2321
+ return;
2322
+ }
2323
+ const id = upsertMcp(mcpScopeDir(toScope, config.profileDirPath, config.dshHomePath), input);
2324
+ sendJson(response, 200, { ok: true, id, scope: toScope, restartNeeded: true });
2325
+ } catch (error) {
2326
+ sendJson(response, 500, { error: error instanceof Error ? error.message : String(error) });
2327
+ }
2328
+ }
2329
+ }),
2124
2330
  host.webServer.register({
2125
2331
  kind: "exact",
2126
2332
  path: "/dsh-plugin-capabilities/import/scan",
@@ -2133,8 +2339,9 @@ function mountCapabilitiesRoutes(host, config) {
2133
2339
  try {
2134
2340
  sendJson(response, 200, {
2135
2341
  servers: scanAllMcp(),
2136
- // Profile serverNames, so the browser can grey out existing ones.
2137
- existing: listMcp(config.profileDirPath).map((row) => row.serverName)
2342
+ // ServerNames present in either patch layer, so the browser can
2343
+ // grey out existing ones.
2344
+ existing: listMcpScoped(config.profileDirPath, config.dshHomePath).servers.map((row) => row.serverName)
2138
2345
  });
2139
2346
  } catch (error) {
2140
2347
  sendJson(response, 500, { error: error instanceof Error ? error.message : String(error) });
@@ -2162,9 +2369,8 @@ function mountCapabilitiesRoutes(host, config) {
2162
2369
  const results = [];
2163
2370
  for (const server of scanAllMcp()) {
2164
2371
  if (!wanted.has(`${server.agent}/${server.name}`)) continue;
2165
- const existing = listMcp(config.profileDirPath).some((row) => row.serverName === server.name);
2166
- if (existing) {
2167
- results.push({ name: server.name, ok: false, error: "already in profile" });
2372
+ if (listMcpScoped(config.profileDirPath, config.dshHomePath).servers.some((row) => row.serverName === server.name)) {
2373
+ results.push({ name: server.name, ok: false, error: "already configured" });
2168
2374
  continue;
2169
2375
  }
2170
2376
  const input = {
@@ -2178,7 +2384,7 @@ function mountCapabilitiesRoutes(host, config) {
2178
2384
  results.push({ name: server.name, ok: false, error: invalid });
2179
2385
  continue;
2180
2386
  }
2181
- upsertMcp(config.profileDirPath, input);
2387
+ upsertMcp(mcpScopeDir(readScope(body.scope), config.profileDirPath, config.dshHomePath), input);
2182
2388
  results.push({ name: server.name, ok: true });
2183
2389
  }
2184
2390
  sendJson(response, 200, { ok: results.every((item) => item.ok), results, restartNeeded: true });
@@ -2261,6 +2467,7 @@ function apply(ctx, config) {
2261
2467
  ctx.effect(
2262
2468
  () => mountCapabilitiesRoutes(hostCtx, {
2263
2469
  profileDirPath: profileDir(profile),
2470
+ dshHomePath: dshHomeDir(),
2264
2471
  remountProvider
2265
2472
  }),
2266
2473
  "dsh-plugin-capabilities: http routes"