@sunshaobei/dsh-mcp-manager 0.0.0-stage → 0.4.0

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.
Files changed (144) hide show
  1. package/LICENSE +1 -0
  2. package/README.en.md +476 -0
  3. package/README.md +379 -2
  4. package/cordis.patch.yml +9 -0
  5. package/lib/THIRD-PARTY-LICENSES +43 -0
  6. package/lib/client/core/api.d.ts +42 -0
  7. package/lib/client/core/constants.d.ts +58 -0
  8. package/lib/client/core/current-session.d.ts +39 -0
  9. package/lib/client/core/dom.d.ts +19 -0
  10. package/lib/client/core/i18n.d.ts +12 -0
  11. package/lib/client/core/session.d.ts +27 -0
  12. package/lib/client/core/state.d.ts +120 -0
  13. package/lib/client/float/float.d.ts +126 -0
  14. package/lib/client/float/panel.d.ts +29 -0
  15. package/lib/client/float/quick-add.d.ts +38 -0
  16. package/lib/client/float/servers.d.ts +54 -0
  17. package/lib/client/index.d.ts +26 -0
  18. package/lib/client/locales.d.ts +123 -0
  19. package/lib/client/settings/settings-card.d.ts +47 -0
  20. package/lib/client.js +2568 -0
  21. package/lib/index.d.ts +86 -0
  22. package/lib/index.js +10035 -0
  23. package/lib/server/api/deps.d.ts +36 -0
  24. package/lib/server/api/impl/health/type.d.ts +12 -0
  25. package/lib/server/api/impl/service/index.d.ts +28 -0
  26. package/lib/server/api/interface.d.ts +27 -0
  27. package/lib/server/api/routes-controllers.d.ts +61 -0
  28. package/lib/server/api/routes-helpers.d.ts +8 -0
  29. package/lib/server/api/routes.d.ts +58 -0
  30. package/lib/server/catalog/deps.d.ts +51 -0
  31. package/lib/server/catalog/impl/cache-view/index.d.ts +48 -0
  32. package/lib/server/catalog/impl/digest/index.d.ts +19 -0
  33. package/lib/server/catalog/impl/directory/index.d.ts +168 -0
  34. package/lib/server/catalog/impl/entries/index.d.ts +129 -0
  35. package/lib/server/catalog/impl/entries/type.d.ts +19 -0
  36. package/lib/server/catalog/impl/history/index.d.ts +36 -0
  37. package/lib/server/catalog/impl/injection/index.d.ts +45 -0
  38. package/lib/server/catalog/impl/search/type.d.ts +59 -0
  39. package/lib/server/catalog/impl/service/index.d.ts +25 -0
  40. package/lib/server/catalog/interface.d.ts +45 -0
  41. package/lib/server/catalog/search.d.ts +108 -0
  42. package/lib/server/config/config-schema.d.ts +85 -0
  43. package/lib/server/config/impl/env/index.d.ts +24 -0
  44. package/lib/server/config/impl/env/policy.d.ts +54 -0
  45. package/lib/server/config/impl/model/type.d.ts +39 -0
  46. package/lib/server/config/impl/ui/type.d.ts +20 -0
  47. package/lib/server/config/import.d.ts +20 -0
  48. package/lib/server/config/interface.d.ts +15 -0
  49. package/lib/server/config/normalize.d.ts +24 -0
  50. package/lib/server/config/sources-sync.d.ts +32 -0
  51. package/lib/server/config/sources.d.ts +50 -0
  52. package/lib/server/connection/interface.d.ts +86 -0
  53. package/lib/server/connection/orchestrator/deps.d.ts +93 -0
  54. package/lib/server/connection/orchestrator/impl/service/index.d.ts +27 -0
  55. package/lib/server/connection/orchestrator/interface.d.ts +25 -0
  56. package/lib/server/connection/orchestrator/manager.d.ts +484 -0
  57. package/lib/server/connection/orchestrator/tool-names.d.ts +14 -0
  58. package/lib/server/connection/runtime/deps.d.ts +141 -0
  59. package/lib/server/connection/runtime/impl/middleware/type.d.ts +45 -0
  60. package/lib/server/connection/runtime/impl/service/index.d.ts +28 -0
  61. package/lib/server/connection/runtime/interface.d.ts +29 -0
  62. package/lib/server/connection/runtime/limits.d.ts +19 -0
  63. package/lib/server/connection/runtime/middleware.d.ts +194 -0
  64. package/lib/server/erasure/impl/guard/index.d.ts +35 -0
  65. package/lib/server/erasure/interface.d.ts +31 -0
  66. package/lib/server/inject/deps.d.ts +54 -0
  67. package/lib/server/inject/impl/call-timeout/index.d.ts +1 -0
  68. package/lib/server/inject/impl/image-admission/index.d.ts +81 -0
  69. package/lib/server/inject/impl/service/index.d.ts +25 -0
  70. package/lib/server/inject/interface.d.ts +27 -0
  71. package/lib/server/inject/middleware-register.d.ts +114 -0
  72. package/lib/server/pipeline/deps.d.ts +27 -0
  73. package/lib/server/pipeline/impl/args/index.d.ts +26 -0
  74. package/lib/server/pipeline/impl/authorize/index.d.ts +43 -0
  75. package/lib/server/pipeline/impl/msg/index.d.ts +8 -0
  76. package/lib/server/pipeline/impl/project/index.d.ts +43 -0
  77. package/lib/server/pipeline/impl/redact/index.d.ts +13 -0
  78. package/lib/server/pipeline/impl/service/index.d.ts +25 -0
  79. package/lib/server/pipeline/impl/timeout/index.d.ts +8 -0
  80. package/lib/server/pipeline/interface.d.ts +32 -0
  81. package/lib/server/servers/dispatch/deps.d.ts +100 -0
  82. package/lib/server/servers/dispatch/impl/call/index.d.ts +49 -0
  83. package/lib/server/servers/dispatch/impl/redact/index.d.ts +13 -0
  84. package/lib/server/servers/dispatch/interface.d.ts +18 -0
  85. package/lib/server/servers/lifecycle/deps.d.ts +62 -0
  86. package/lib/server/servers/lifecycle/impl/ledger/index.d.ts +52 -0
  87. package/lib/server/servers/lifecycle/impl/logs/index.d.ts +48 -0
  88. package/lib/server/servers/lifecycle/impl/mount/index.d.ts +83 -0
  89. package/lib/server/servers/lifecycle/impl/service/index.d.ts +26 -0
  90. package/lib/server/servers/lifecycle/impl/state/index.d.ts +35 -0
  91. package/lib/server/servers/lifecycle/impl/timeout/index.d.ts +39 -0
  92. package/lib/server/servers/lifecycle/interface.d.ts +56 -0
  93. package/lib/server/shared/compose.d.ts +38 -0
  94. package/lib/server/shared/constants.d.ts +62 -0
  95. package/lib/server/shared/file-io.d.ts +8 -0
  96. package/lib/server/shared/host-faces.d.ts +206 -0
  97. package/lib/server/shared/interface.d.ts +14 -0
  98. package/lib/server/shared/paths.d.ts +66 -0
  99. package/lib/server/shared/tool-names.d.ts +11 -0
  100. package/lib/server/stats/impl/collector.d.ts +68 -0
  101. package/lib/server/stats/impl/type.d.ts +41 -0
  102. package/lib/server/stats/interface.d.ts +13 -0
  103. package/lib/server/store/impl/middleware-state.d.ts +45 -0
  104. package/lib/server/store/impl/store.d.ts +43 -0
  105. package/lib/server/store/interface.d.ts +9 -0
  106. package/lib/server/upgrade/deps.d.ts +25 -0
  107. package/lib/server/upgrade/impl/chain/index.d.ts +2 -0
  108. package/lib/server/upgrade/impl/service/index.d.ts +9 -0
  109. package/lib/server/upgrade/impl/steps/index.d.ts +19 -0
  110. package/lib/server/upgrade/impl/steps/project-layout.d.ts +24 -0
  111. package/lib/server/upgrade/impl/steps/storage-layout.d.ts +17 -0
  112. package/lib/server/upgrade/impl/version/index.d.ts +7 -0
  113. package/lib/server/upgrade/interface.d.ts +21 -0
  114. package/lib/server/visibility/impl/mask/index.d.ts +7 -0
  115. package/lib/server/visibility/interface.d.ts +46 -0
  116. package/lib/server/workspace/impl/full-name/index.d.ts +24 -0
  117. package/lib/server/workspace/impl/root-resolution/index.d.ts +18 -0
  118. package/lib/server/workspace/impl/scope/index.d.ts +14 -0
  119. package/lib/server/workspace/impl/server-id/index.d.ts +17 -0
  120. package/lib/server/workspace/interface.d.ts +22 -0
  121. package/lib/shared/constants.d.ts +22 -0
  122. package/lib/shared/dto.d.ts +62 -0
  123. package/lib/shared/frames.d.ts +19 -0
  124. package/lib/shared/interface.d.ts +22 -0
  125. package/lib/shared/placement-math.d.ts +81 -0
  126. package/lib/shared/routes.d.ts +43 -0
  127. package/lib/shared/service.d.ts +85 -0
  128. package/lib/shared/status.d.ts +23 -0
  129. package/lib/vendor/shared/README.md +147 -0
  130. package/lib/vendor/shared/client/ensure-style.d.ts +23 -0
  131. package/lib/vendor/shared/client/i18n.d.ts +30 -0
  132. package/lib/vendor/shared/client/ui/index.d.ts +214 -0
  133. package/lib/vendor/shared/dsh-home.d.ts +26 -0
  134. package/lib/vendor/shared/host-utils.d.ts +82 -0
  135. package/lib/vendor/shared/loopback.d.ts +12 -0
  136. package/lib/vendor/shared/package.json +12 -0
  137. package/lib/vendor/shared/paths.d.ts +35 -0
  138. package/lib/vendor/shared/settings-namespace.d.ts +82 -0
  139. package/lib/vendor/shared/sse-hub.d.ts +75 -0
  140. package/lib/vendor/shared/upgrade-chain.d.ts +140 -0
  141. package/lib/vendor/shared/upgrade-tick.d.ts +17 -0
  142. package/locale/en.json +6 -0
  143. package/locale/zh.json +6 -0
  144. package/package.json +171 -4
@@ -0,0 +1,36 @@
1
+ /**
2
+ * dsh-mcp-manager — api/deps.ts:API 层域的上对依赖声明(纯类型面,§3.1 规则 2)。
3
+ *
4
+ * 本域运行时能力消费**非零**(决策⑥ 以运行时能力消费为准):路由控制器取 workspace 域的
5
+ * scope 归一化 / 全名解析 / 工具名归一化,以及 config 域的 mcpServers JSON
6
+ * 解析(routes-controllers.ts:323)。宿主能力实测 0 命中(无 ctx/Context/logger/settings),
7
+ * 故只有两条 Port——比 §3.4/§3.5 表的预期窄得多,按实测收窄。
8
+ *
9
+ * **不为 `manager` 开口**(附录 G·G19 + D.3·37):本域对 `RoutesManager` 的 44 处直取走的是
10
+ * 结构参数类型(该类型 #767 W11b2a 起落位 connection/interface.ts)、**没有运行时能力 import
11
+ * 边**,而该门面一个 manager 能力都没导出——物理上开不出 Port。把 22 个成员命名成能力对象
12
+ * 不在本刀范围(附录 G·G19;D.3·37),归后续刀。
13
+ *
14
+ * 口径取「实际使用」而非「import 面」(附录 E.6 第 3 条的建议):端口成员 = 域内真实的消费点。
15
+ * 跨端契约常量 SCOPE_PROJECT / MIDDLEWARE_GLOBAL_ROOT 已按 §3.6 规则 6 迁 `src/shared/`
16
+ * (W3b),消费方直接从 `src/shared/interface.ts` 取,不在端口面内。
17
+ *
18
+ * **只许类型依赖**:本文件出现值 import 会被 verify-dir-imports 硬判红。域内取数一律经
19
+ * `impl/service` 的 `apiPorts.get()`,写入只由组合根在 `src/index.ts` 顶层完成。
20
+ */
21
+ import type * as workspaceApi from "../workspace/interface.js";
22
+ import type * as configModelApi from "../config/interface.js";
23
+ /** workspace 域给本域的能力面:scope 归一化 / 全名解析 / 工具名归一化。 */
24
+ export type WorkspacePort = Pick<typeof workspaceApi, "normalizeScope" | "parseFullServerName" | "normalizeToolName">;
25
+ /** config 域给本域的能力面:mcpServers JSON 导入解析。 */
26
+ export type ConfigModelPort = Pick<typeof configModelApi, "parseClaudeJson">;
27
+ /**
28
+ * 装配入参:本域依赖的全部外部。键集与组合根 `installApi` 的实参字面量由
29
+ * verify-dir-imports 的注入面对账强制**严格相等**(多一个键、少一个键都判红)。
30
+ */
31
+ export interface ApiDeps {
32
+ /** workspace 域:scope 归一化 / 全名解析 / 工具名归一化。 */
33
+ workspace: WorkspacePort;
34
+ /** config 域:mcpServers JSON 导入解析。 */
35
+ configModel: ConfigModelPort;
36
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * dsh-mcp-manager — server/api/impl/health/type.ts:服务器运行状态摘要类型落点(#767 W11b2a)。
3
+ *
4
+ * health/列表接口的返回形态;目录外消费经 api/interface.ts 门面转出。
5
+ */
6
+ /** 服务器运行状态摘要(health/列表接口返回)。 */
7
+ export interface ServerStatus {
8
+ state: string;
9
+ connectedAt?: number;
10
+ error?: string;
11
+ [key: string]: unknown;
12
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * dsh-mcp-manager — api/impl/service/index.ts:API 层域端口持有者(#767 B2a-wire W9)。
3
+ *
4
+ * 域内实现经 `apiPorts.get()` 取数,写入只发生在组合根入口顶层的 `installApi`
5
+ * (见 `src/index.ts`)。形态照 `catalog/impl/service/`、`pipeline/impl/service/`、
6
+ * `inject/impl/service/`、`connection/orchestrator/impl/service/` 与
7
+ * `connection/runtime/impl/service/`:模块级只有一个 `const` 实例、状态收进实例字段
8
+ * (I9),装配与复位只经方法(方法内赋值)。
9
+ */
10
+ import type { ApiDeps } from "../../deps.js";
11
+ /** API 层域端口表:装配一次、域内多处读。 */
12
+ declare class ApiPorts {
13
+ /** 未装配时保持 undefined——「没装配」与「装配了空实现」必须可区分(I6)。 */
14
+ private deps;
15
+ /** 装配:组合根递入静态模块引用;重复装配是编程错误,当场暴露。 */
16
+ install(deps: ApiDeps): void;
17
+ /** 卸载:只复位装配标记,本域没有别的活资源要释放。 */
18
+ release(): void;
19
+ /**
20
+ * 域内取数的唯一入口。未装配即抛错而不是回落默认值:默认值会让「组合根漏装」表现成行为
21
+ * 正常但参数不对(scope 归一化与 mcpServers 导入解析静默换实现),症状与本插件毫无字面关联。
22
+ * 只在路由 handler 内调用(不在模块求值期),故取到的一定是装配后的能力。
23
+ */
24
+ get(): ApiDeps;
25
+ }
26
+ /** 本域唯一的装配实例:类不外放,外部 new 不出第二份端口表。 */
27
+ export declare const apiPorts: ApiPorts;
28
+ export {};
@@ -0,0 +1,27 @@
1
+ /**
2
+ * dsh-mcp-manager — api/interface.ts:API 层门面(D10,#664 阶段 6)。
3
+ *
4
+ * API 层 = 路由装配(routes)+ 端点控制器(routes-controllers)+ 查询辅助
5
+ * (routes-helpers)。目录外模块**只能**从这里引用(verify-dir-imports
6
+ * 静态强制)。
7
+ *
8
+ * 本域**有对上依赖**(§3.1 规则 2;决策⑥ 以运行时能力消费为准):路由控制器取 workspace
9
+ * 域的 scope/全名/工具名/模式归一化与 config/model 子层的 mcpServers JSON 解析(`deps.ts`
10
+ * 的两条 Port)。域内实现一律经 `impl/service` 的端口持有者取数——W9 接线后 I2① 的
11
+ * `api|workspace` 与 `api|config/model` 两条值边消失,域间只剩类型边。对 `manager` 的
12
+ * 结构参数消费没有 import 边,不在本刀范围(附录 G·G19,归 W10)。
13
+ */
14
+ import type { ApiDeps } from "./deps.js";
15
+ /**
16
+ * 装配 API 层域:把组合根持有的两组端口写入域内注册表(见 impl/service)。组合根在
17
+ * `src/index.ts` 顶层调用,模块求值期即完成——两条 Port 全是静态模块引用,不需要宿主 ctx。
18
+ *
19
+ * 为什么不是 `export async function`:注入面对账(verify-dir-imports 的
20
+ * analyzeInjectionFaces)按 `export function installXxx(` 采点,`async` 前缀会让这条对账
21
+ * 静默失明(附录 G·G12)。本域装配是纯同步写标记,签名保持同步形态即可。
22
+ */
23
+ export declare function installApi(deps: ApiDeps): void;
24
+ /** 卸载 API 层域。只复位装配标记:本域读的是外部递进来的能力,没有活资源要释放。 */
25
+ export declare function releaseApi(): void;
26
+ export { ROUTES, queryParam, makeRoutes, makeEventsRoute, makeHealthRoute, uiConfigChangedFrame, broadcastFrame, SSE_HEARTBEAT_MS, SSE_PING_FRAME, } from "./routes.js";
27
+ export type { ServerStatus } from "./impl/health/type.js";
@@ -0,0 +1,61 @@
1
+ /**
2
+ * dsh-mcp-manager — 路由控制器工厂(#592 阶段二 Batch A:makeRoutes 消峰)。
3
+ *
4
+ * 每个控制器是「buildXxxRoute(manager, helpers) → WebRoute」形态的纯工厂:
5
+ * 拆分前 makeRoutes 单函数圈复杂度 84(全仓第一),复杂度全部来自内联在各
6
+ * 路由 handler 闭包中的方法分流与字段校验分支;拆分后 makeRoutes 退化为
7
+ * 数组装配(comp <= 15),每个控制器的分支彼此独立、可独立理解与测试。
8
+ *
9
+ * 行为约束(与拆分前逐字节等价):
10
+ * - 路由路径与围栏(方法白名单 / loopback 豁免)取自 shared/routes.ts 单点;
11
+ * - loopback 围栏与 405 分流顺序不变(#473 R2:config GET 豁免 loopback,
12
+ * 白名单外方法先于 loopback 直接 405);
13
+ * - JSON body 字节上限沿用 readJsonBody 的默认行为(历史上那个显式上限
14
+ * 常量 MAX_JSON_BODY_BYTES 已不存在)。
15
+ *
16
+ * 跨域取数一律经 apiPorts(见 deps.ts 与 impl/service),值为模块求值期绑定:
17
+ * installApi 在包入口顶层完成,handler 内的 get() 取到的一定是装配后的能力。
18
+ */
19
+ import type { WebRoute } from "@deepseek-ai/dsh-host-webserver";
20
+ import type { ServerResponse } from "node:http";
21
+ import type { RoutesManager } from "../connection/interface.js";
22
+ import type { ServerConfig } from "../config/interface.js";
23
+ import type { McpStore } from "../store/interface.js";
24
+ /** 控制器共享 helpers(原 makeRoutes 闭包三件套,提升为显式参数)。 */
25
+ export interface RouteHelpers {
26
+ handleError(res: ServerResponse, error: unknown): void;
27
+ scopeParam(url: URL): string;
28
+ maybeSession(url: URL): Promise<void>;
29
+ }
30
+ /** UI 配置读写(GET 豁免 loopback;写操作 loopback-only,未知顶层键 400)。 */
31
+ export declare function buildConfigRoute(manager: RoutesManager, helpers: RouteHelpers): WebRoute;
32
+ /** 服务器集合 CRUD:GET 快照(纯读)/ POST 添加 / PATCH 更新 / DELETE 删除。 */
33
+ export declare function buildServersRoute(manager: RoutesManager, helpers: RouteHelpers): WebRoute;
34
+ /** 会话切换(跟随会话的项目级 MCP)。 */
35
+ export declare function buildSessionRoute(manager: RoutesManager, helpers: RouteHelpers): WebRoute;
36
+ /** 切回前台可用性恢复(非强制,#1120):body 可选 { cwd?: string }。
37
+ * - 无 body(旧客户端):回落既有绑定,manager.resumeReconnect() 无参;
38
+ * - 有 cwd(含 ""):本次固定 root(""=明确无工作区→@global,不回落他人绑定);
39
+ * - 非对象(含数组,readJsonBodyOutcome 把数组归 json 需此处拒)/ cwd 非字符串 → 400
40
+ * 且不触发恢复。路径/围栏/方法分流不动。 */
41
+ export declare function buildResumeRoute(manager: RoutesManager, helpers: RouteHelpers): WebRoute;
42
+ /** POST /servers/connect:连接单服务器。 */
43
+ export declare function buildConnectRoute(manager: RoutesManager, helpers: RouteHelpers): WebRoute;
44
+ /** POST /servers/disconnect:断开单服务器。 */
45
+ export declare function buildDisconnectRoute(manager: RoutesManager, helpers: RouteHelpers): WebRoute;
46
+ /** POST /servers/reconnect:重连单服务器。 */
47
+ export declare function buildReconnectRoute(manager: RoutesManager, helpers: RouteHelpers): WebRoute;
48
+ /** 单台服务器的导入落点:在册且未开 overwrite → 记 skipped;在册开 overwrite → update;否则 add。 */
49
+ export declare function importOneServer(server: ServerConfig, store: McpStore, scope: string, overwrite: boolean, manager: RoutesManager, imported: string[], skipped: string[]): Promise<void>;
50
+ /** 导入 mcpServers JSON(同名 skip,overwrite=true 时更新)。 */
51
+ export declare function buildImportJsonRoute(manager: RoutesManager, helpers: RouteHelpers): WebRoute;
52
+ /** 路由一致性校验:@global 或当前会话项目 root 才可写(防跨空间串台)。放行返回 null。 */
53
+ export declare function workspaceMismatchError(root: string, sessionRoot: string | undefined, server: string): string | null;
54
+ /** 工具级禁用开关(PATCH;root 路由一致性校验防跨空间串台)。 */
55
+ export declare function buildToolDisableRoute(manager: RoutesManager, helpers: RouteHelpers): WebRoute;
56
+ /** 四来源全量状态(面板渲染面):路径 / 是否存在 / 原文 / 解析错误。 */
57
+ export declare function buildSourcesRoute(manager: RoutesManager, helpers: RouteHelpers): WebRoute;
58
+ /** 保存一个来源的 mcp.json(mkdir -p;随后互斥同步落运行面)。 */
59
+ export declare function buildSourceSaveRoute(manager: RoutesManager, helpers: RouteHelpers): WebRoute;
60
+ /** 显式改选同名生效来源(互斥裁决的记忆位),随后同步。 */
61
+ export declare function buildSourceActivateRoute(manager: RoutesManager, helpers: RouteHelpers): WebRoute;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * dsh-mcp-manager — 路由层共享辅助(#592 消峰拆分:routes ↔ controllers 公共面)。
3
+ *
4
+ * queryParam 从 routes.ts 提升至此,供 routes.ts 与 routes-controllers.ts
5
+ * 双向消费,避免 controllers 反向 import routes 造成装配层与实现层耦合。
6
+ */
7
+ /** 读取 URL 查询参数(缺失返回 undefined)。 */
8
+ export declare function queryParam(url: URL, name: string): string | undefined;
@@ -0,0 +1,58 @@
1
+ /**
2
+ * dsh-mcp-manager — HTTP 路由装配层(依赖 import 模块与仓库共享层)。
3
+ *
4
+ * /api/dsh-mcp/* 路由(loopback-only)供 web GUI 分级展示、快速接入、
5
+ * 导入 mcpServers JSON;events 为 SSE 状态推送通道。
6
+ * 例外:/api/dsh-mcp/config 是只读 UI 配置接口,允许非 loopback 访问,
7
+ * 便于远程页面读取 position/offset 等非敏感展示配置。
8
+ * 由 lib/index.js 组合根 re-export(ROUTES / makeRoutes)。
9
+ *
10
+ * #592 阶段二 Batch A 消峰:各端点 handler 已拆离至 routes-controllers.ts
11
+ * 的独立控制器工厂(原 makeRoutes 单函数圈复杂度 84 → 装配后 <= 15);
12
+ * 本文件只保留 ROUTES 常量、装配函数与 SSE/health 工厂(SSE/health 原本
13
+ * 即独立工厂形态,复杂度低,随装配层同文件保留)。
14
+ */
15
+ import type { SseHub } from "../../vendor/shared/sse-hub.js";
16
+ import { ROUTES } from "../../shared/interface.js";
17
+ import type { RoutesManager } from "../connection/interface.js";
18
+ import type { WebRoute } from "@deepseek-ai/dsh-host-webserver";
19
+ import type { ServerResponse } from "node:http";
20
+ import { queryParam } from "./routes-helpers.js";
21
+ export { ROUTES };
22
+ export { queryParam };
23
+ /** 组装 /api/dsh-mcp/* 路由。cwd 参数仅用于兼容旧调用(不再被路由使用)。 */
24
+ export declare function makeRoutes(manager: RoutesManager, _cwd?: string): WebRoute[];
25
+ /** 配置变更 SSE 帧:客户端收到后重新 GET /api/dsh-mcp/config 就地更新浮窗位置。 */
26
+ export declare function uiConfigChangedFrame(): string;
27
+ /**
28
+ * SSE 心跳间隔:30s data ping(对齐 dsh-notifier HEARTBEAT_MS 形态,#268)。
29
+ * 必须用 data 而非注释帧——注释帧既不触发客户端 onmessage 也不触发 onerror,
30
+ * 半开连接(移动端切后台系统冻结 JS 并静默掐断 TCP)时客户端零事件无法自愈;
31
+ * data 帧喂客户端 60s watchdog 使其能检测失活并关旧建新。
32
+ * #515 起心跳由共享 hub(shared/sse-hub.js)统一管理,本常量保留兼容导出。
33
+ */
34
+ export declare const SSE_HEARTBEAT_MS = 30000;
35
+ /** 心跳 ping 帧(内容固定,模块级缓存避免逐次序列化;#515 起 hub 内置同帧)。 */
36
+ export declare const SSE_PING_FRAME: string;
37
+ /**
38
+ * 向全部 SSE 连接写出一帧(#515 起收口到共享 hub;本函数保留兼容导出,
39
+ * 接受 hub 或旧 Set 形态)。判死/背压由 hub writeFrame 统一处理。
40
+ */
41
+ export declare function broadcastFrame(connections: SseHub | Set<ServerResponse> | undefined, frame: string): void;
42
+ /**
43
+ * 状态变化推送通道(SSE):浏览器端 EventSource 订阅,宿主状态变化时
44
+ * 广播 `{ type: "summary" }` 帧(内容不随帧传输,浏览器收到后自行拉取)。
45
+ *
46
+ * #515 起连接管理收口到共享 hub(shared/sse-hub.js):manager.sseHub 惰性创建
47
+ * (连接表 + 心跳 + stalled/maxAge 主动回收),本路由只负责入表与首帧——
48
+ * 取代旧裸 Set + per-connection 心跳(#268/#515,消除与 dsh-notifier 不对称)。
49
+ *
50
+ * 半开连接防护(#268,对齐 dsh-notifier):hub 每 30s 向全部连接写 data ping
51
+ * 心跳——移动端切后台系统冻结 JS 并静默掐断 TCP,两端均收不到 FIN/RST,
52
+ * 无心跳则客户端 watchdog 无失活信号可依。
53
+ */
54
+ export declare function makeEventsRoute(manager: RoutesManager, options?: {
55
+ heartbeatMs?: number;
56
+ }): WebRoute;
57
+ /** 健康检查:插件是否加载、服务器/连接/工具状态(诊断"插件没生效"的标准入口)。 */
58
+ export declare function makeHealthRoute(manager: RoutesManager): WebRoute;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * dsh-mcp-manager — catalog/deps.ts:目录域的上对依赖声明(纯类型面,§3.1 规则 2)。
3
+ *
4
+ * 本域运行时能力消费**非零**(决策⑥ 以运行时能力消费为准),按提供方分三组:store 取
5
+ * 磁盘目录缓存读面;connection 取目录 TTL 与装箱限额常量;workspace 取全名解析与工具名
6
+ * 归一。config 域实测 0 值引,不预置。
7
+ *
8
+ * **宿主能力(工具注册表读口、logger)不进端口**:它们是本域声明的**入参契约**——由
9
+ * connection 在调用点构造具体对象递入(同 `CatalogViewHost` 的口径,见下)。理由是这两条
10
+ * 能力每次调用都由调用方给:注册面视图取自 `host.ctx.tools.schemas()`、告警出口是
11
+ * `host.logger.warn`、目录落盘路径是 `host.catalogCachePath(root)`——它们是连接的宿主面
12
+ * 事实,本域只按值使用,不持有、不推导。端口只承载「本域真的要自己去取」的静态能力。
13
+ *
14
+ * CatalogViewHost 同理不进端口:它是本域声明的**入参契约**——connection 在调用点构造具体对象
15
+ * 递入 makeCatalogViewFor,不是本域向提供方 import 的能力面。
16
+ *
17
+ * 口径取「实际使用」而非「import 面」(附录 E.6 第 3 条的建议):端口成员 = 域内真实的消费点。
18
+ * 历史死导入(search 的 bareServerName)随 W1 删除后不再被引用,端口不为已不存在的符号开口;
19
+ * 跨端契约常量(SCOPE_PROJECT / MIDDLEWARE_GLOBAL_ROOT)与 ≥2 域消费的值常量
20
+ * (LIST_DEFAULT_TOOLS_PER_SERVER)已按 §3.6 规则 6 迁共享层,故不在端口面内(W3b)。
21
+ *
22
+ * **只许类型依赖**:本文件出现值 import 会被 verify-dir-imports 硬判红。三条域间值边
23
+ * (catalog|config/store、catalog|connection、catalog|workspace)已随 W4 接线消失——域内取数
24
+ * 一律经 `impl/service` 的 `catalogPorts.get()`,写入只由组合根在 `src/index.ts` 顶层完成。
25
+ */
26
+ import type * as storeApi from "../store/interface.js";
27
+ import type * as connectionRuntimeApi from "../connection/runtime/interface.js";
28
+ import type * as workspaceApi from "../workspace/interface.js";
29
+ /** store 域给本域的能力面:磁盘 last-good 目录缓存读面。 */
30
+ export type StorePort = Pick<typeof storeApi, "readCatalogServerFromDisk">;
31
+ /**
32
+ * connection 域给本域的能力面:目录 TTL 与装箱限额常量。
33
+ *
34
+ * 指向 runtime 子层门面:这 4 个常量的物理定义在 connection/runtime/limits.ts,而 connection
35
+ * 门面自 W10 起不再转发 runtime 的值面(那条转发边就是 I2① `connection|connection/runtime`)。
36
+ */
37
+ export type ConnectionPort = Pick<typeof connectionRuntimeApi, "CATALOG_TTL_MS" | "MAX_BYTES_PER_TOOL" | "MAX_TOOLS_PER_SERVER" | "MAX_TOTAL_CATALOG_BYTES">;
38
+ /** workspace 域给本域的能力面:全名解析与工具名归一。 */
39
+ export type WorkspacePort = Pick<typeof workspaceApi, "normalizedProjectRoot" | "fullServerName" | "parseFullServerName" | "normalizeToolName">;
40
+ /**
41
+ * 装配入参:本域依赖的全部外部。键集与组合根 `installCatalog` 的实参字面量由
42
+ * verify-dir-imports 的注入面对账强制**严格相等**(多一个键、少一个键都判红)。
43
+ */
44
+ export interface CatalogDeps {
45
+ /** store 域:磁盘 last-good 目录缓存读面。 */
46
+ store: StorePort;
47
+ /** connection 域:目录 TTL 与装箱限额常量。 */
48
+ connection: ConnectionPort;
49
+ /** workspace 域:全名解析与工具名归一。 */
50
+ workspace: WorkspacePort;
51
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * dsh-mcp-manager — catalog/impl/cache-view/index.ts:注入端目录缓存视图(#664 阶段 5)。
3
+ *
4
+ * 自 src/manager.ts 迁出(catalog 域):合成注入端目录缓存视图——以 B
5
+ * (supervisor 摘要缓存)为基底,逐服务器按「scope + 模式」用中间层目录摘要
6
+ * 覆盖(#569 修复核心)。diskCatalogSummaryCache(mtime 缓存)随迁,以工厂
7
+ * 闭包持有(防多实例共享串状态);宿主最小面 CatalogViewHost 解耦 manager
8
+ * 细节(C-DIR:catalogViewFor 私有缓存随迁,薄桥接)。
9
+ */
10
+ import type { McpMiddleware } from "../../../connection/runtime/interface.js";
11
+ import type { ServerConfig } from "../../../config/interface.js";
12
+ import type { CatalogCache } from "../entries/index.js";
13
+ /**
14
+ * 目录摘要缓存文件(连接成功时把工具描述摘要持久化于此;目录 digest 的稳定数据源)。
15
+ * 名字与权限的物理定义在 server/shared/paths.ts(I7 单源)。
16
+ */
17
+ export declare function catalogCacheFile(): string;
18
+ /** catalogViewFor 的宿主最小面(manager 提供;实例经读取器取——apply 完成后恒在,pre-step 窗口未装配时返回 undefined 走 B 兜底)。 */
19
+ export interface CatalogViewHost {
20
+ getCatalogCache(): CatalogCache;
21
+ getMiddleware(): McpMiddleware | undefined;
22
+ catalogCachePathFor(root: string): string;
23
+ }
24
+ /** 注入端目录缓存视图解析器(makeCatalogViewFor 产物)。 */
25
+ export type CatalogViewResolver = (cwd: string | undefined, servers: Map<string, {
26
+ server: ServerConfig;
27
+ scope: string;
28
+ }>) => Promise<CatalogCache>;
29
+ /**
30
+ * 合成注入端目录缓存视图(#569 修复核心):以 B(supervisor 摘要缓存)为基底,
31
+ * 逐服务器按「scope + 模式」用中间层目录摘要覆盖——修复「middleware 采集的
32
+ * 工具摘要注入端读不到」:
33
+ * - 用户手写 server.description 由 composeCatalogEntries 处理(优先级最高),
34
+ * 不在此视图内;
35
+ * - 本视图只负责注入端的 ②(中间层目录摘要)与 ③(B 缓存摘要)两级回退;
36
+ * - **判 middleware 实例**(apply 完成后恒在;实例缺失=pre-step 窗口尚未装配 → 全部走 B)。
37
+ *
38
+ * root 映射(单池后与单元表同口径):
39
+ * - scope=project → 该 cwd 归一化项目 root 的单元 → 无则磁盘 last-good;
40
+ * - scope=global → @global 单元/磁盘 → 仍无则保留 B。
41
+ * 严格按 scope 解析、不跨 scope 混配同名服务器(防不同配置被错配)。
42
+ * 单元不存在时读磁盘 last-good(带 mtime 缓存),**不主动 projectUnitFor**
43
+ * ——pre-step 无连接副作用。
44
+ *
45
+ * diskCatalogSummaryCache(root → server → { mtimeMs, summary })在闭包内
46
+ * 持有:防 pre-step 每轮重复读盘解析(目录文件最大 ~256KB,JSON parse 有成本)。
47
+ */
48
+ export declare function makeCatalogViewFor(host: CatalogViewHost): CatalogViewResolver;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * dsh-mcp-manager — catalog/impl/digest/index.ts:目录条目 digest(#664 阶段 5)。
3
+ *
4
+ * 自 src/catalog.ts 拆出(digest 域):sha256 摘要,**只含服务器集合(name),
5
+ * 不含描述文本**——工具描述不稳定(按需注册/动态描述),含描述会永远追不上
6
+ * 抖动触发替换注入;只含 name 后服务器增删才触发替换。
7
+ */
8
+ import type { CatalogEntry } from "../entries/index.js";
9
+ /**
10
+ * 目录条目 digest(sha256)——**只含服务器集合(name),不含描述文本**。
11
+ *
12
+ * 为什么:MCP 服务器的工具描述集合不稳定(按需注册/动态描述,实测 code-graph
13
+ * 等服务器不同时刻的摘要都不同)。若 digest 含描述文本,缓存摘要一更新就触发
14
+ * 替换注入——永远追不上抖动。digest 只含 name 后:
15
+ * - 服务器增删 → digest 变 → 原位替换(合理)
16
+ * - 描述/缓存更新 → digest 不变 → 本会话目录保持快照(静态能力地图语义,
17
+ * 与消息声明"仅描述能力、不代表当前连接状态"一致),跨会话才反映新缓存
18
+ */
19
+ export declare function digestCatalogEntries(entries: CatalogEntry[]): string;
@@ -0,0 +1,168 @@
1
+ import type { CatalogServer, CatalogTool } from "../entries/type.js";
2
+ /** 注册面视图:宿主 `ctx.tools.schemas()` 的返回形状(每次投影现取)。 */
3
+ export type SchemaView = ReadonlyArray<{
4
+ name?: unknown;
5
+ description?: unknown;
6
+ parameters?: unknown;
7
+ }>;
8
+ /** 投影入参:id 前缀(`mcp__<id>__`)源自连接层的账本键,其余为本域要用的宿主面事实。 */
9
+ export interface RegisteredProjectionInput {
10
+ root: string;
11
+ serverName: string;
12
+ id: string;
13
+ /** 注册面视图(连接层现取宿主注册表)。 */
14
+ schemas: SchemaView;
15
+ /**
16
+ * 落盘路径:连接层用 `host.catalogCachePath(root)` 算,本域不推路径。接 thunk 是为了
17
+ * 保住「路径求值失败 = 投影失败(unavailable 降级)」这条既有语义——调用点把求值推后
18
+ * 到这里,求值抛错才落在本层的 catch 内(先求值再传参会让异常逃出降级面)。
19
+ */
20
+ cachePath: () => string;
21
+ /** 凭据脱敏:本域不持服务器表,故这一层由调用方(连接层)闭包给出。 */
22
+ redact: (error: unknown) => string;
23
+ /** runtime 注入条目(内存态)不落盘(#413):判定同样归调用方。 */
24
+ isRuntimeServer: (name: string) => boolean;
25
+ /** 告警出口(连接层转 host.logger.warn)。 */
26
+ warn: (message: string) => void;
27
+ }
28
+ /**
29
+ * 每 root 的工具目录内存态 + last-good 磁盘读写 + 注册面投影。
30
+ *
31
+ * 「root 在册」由独立的 root 集合承载:只有 root 在册、且某服务器在册时读口才作答。这条区分
32
+ * 是必须的——「单元根本没有该 root」与「root 在册但没有该服务器」在消费点走的是不同分支
33
+ * (detail 报未发现,而 search/list 只是没有命中)。
34
+ */
35
+ declare class CatalogDirectory {
36
+ /** root →(裸 server 名 → 目录条目)。 */
37
+ private readonly byRoot;
38
+ /** 在册 root 集合:与 byRoot 同步维护,承载「单元存在但目录为空」这一可区分状态。 */
39
+ private readonly roots;
40
+ /**
41
+ * 失效账本:`root\0<裸 server 名>` 的脏标记集合(#1014 B7)。
42
+ *
43
+ * 为什么落在这里而不是连接层:本域是目录新鲜度的**唯一裁决点**,而「哪台脏了」是目录
44
+ * 事实。连接层只负责在变更发生时刻登记(markDirty)与在投影时消费,本域不依赖任何
45
+ * 上对能力——deps.ts 不动,域间值边保持 0。
46
+ *
47
+ * 为什么需要它:此前目录投影只按 TTL 短路(24h),tools/change / 重连 / 手工刷新 / 配置
48
+ * 变更全都没有落点,模型会一直看到旧 schema。TTL 是兜底上限,不是刷新策略。
49
+ */
50
+ private readonly dirty;
51
+ /** 读口:root 的整份目录;root 不在册 → undefined。域外只读,故给 ReadonlyMap。 */
52
+ serversFor(root: string): ReadonlyMap<string, CatalogServer> | undefined;
53
+ /** 读口:单服务器条目;root 或服务器不在册 → undefined。 */
54
+ entryFor(root: string, serverName: string): CatalogServer | undefined;
55
+ /** 读口:root 在册的服务器裸名表(A1 归因文案用)。 */
56
+ serverNamesFor(root: string): readonly string[];
57
+ /**
58
+ * 读口:该 id 前缀下是否已有注册工具——「已连上」的唯一正向证据(官方零状态 API)。
59
+ * 注册面视图按入参现给,本域不自己去取宿主注册表。
60
+ */
61
+ hasRegisteredTools(schemas: SchemaView, id: string): boolean;
62
+ /**
63
+ * 装载 root 的 last-good 目录缓存(缺失/损坏 → 空)。
64
+ *
65
+ * root 已在册即短路:单元创建是唯一载入点,重复读盘会拿磁盘那份**盖掉**内存里更新的投影
66
+ * (落盘是异步 fire-and-forget,内存态才是权威)。
67
+ */
68
+ ensureRootLoaded(root: string, cachePath: string): Promise<void>;
69
+ /** 注销 root 的内存目录(拆毁单元时调用);root 从未在册则无动作。 */
70
+ dropRoot(root: string): void;
71
+ /**
72
+ * 注销单个服务器的内存目录条目(remove/update 配置变更后调用;#392 遗留①)。
73
+ * @returns 是否真的删掉了条目——调用方据此决定要不要广播状态(与 `Map.delete` 同口径)。
74
+ */
75
+ dropServer(root: string, serverName: string): boolean;
76
+ /** 失效键:root 与裸名用 NUL 连接——两者都可能含 `/`,用路径分隔符会撞键。 */
77
+ private dirtyKey;
78
+ /**
79
+ * 登记失效(#1014 B7):投影短路前由调用点打上。幂等——同一条重复登记不叠加,
80
+ * 也不区分原因:失效只需要「要重投影」这一个事实。
81
+ */
82
+ markDirty(root: string, serverName: string): void;
83
+ /** 失效读口:显式失效压过 TTL 短路(这是本账本存在的全部意义)。 */
84
+ isDirty(root: string, serverName: string): boolean;
85
+ /**
86
+ * 消费失效:投影成功后清标记。
87
+ *
88
+ * 为什么只在成功后清:投影失败走 markUnavailable 降级,目录并不更新——此时保留标记,
89
+ * 下一次投影仍会绕过 TTL 短路重试;清掉就等于让一次失败把该服务器冻结 24h。
90
+ */
91
+ clearDirty(root: string, serverName: string): void;
92
+ /**
93
+ * 条目不可用(发现或落盘失败):带脱敏后的原因(unavailable 段),**并保留 last-good 工具表**。
94
+ *
95
+ * 为什么保留(#1014 §2「失败保留 last-good」):此前实现是把 tools 换成空表,等于一次落盘
96
+ * 失败就让该服务器在模型视野里「工具消失」。落盘失败是本地问题(路径不可写、目录不存在),
97
+ * 与远端工具清单无关——丢掉 last-good 会把一次瞬时失败放大成「这台服务器没有工具」。
98
+ * 目录读口本来就带 unavailable 段,消费者据此知道该快照不新鲜。
99
+ *
100
+ * discoveredAt 仍归零:它是「这份快照有多新」的事实位,归零让 isCatalogFresh 判不新鲜,
101
+ * 下一次投影(含脏标记在途)会重试。
102
+ */
103
+ markUnavailable(root: string, serverName: string, reason: string): void;
104
+ /**
105
+ * 注册面 → per-root 目录的投影(裁定 W + 裁定 K)。
106
+ *
107
+ * 官方注册名是 `mcp__<id>__<tool>`,去掉前缀即裸名;description 与 parameters 都在注册面
108
+ * 视图里,不需要第二份工具清单。fresh 短路:TTL 内不重投影(discover 惰性语义),且短路发生
109
+ * 在任何落盘动作之前。落盘失败(含路径求值失败)由本层 catch 收口成 unavailable 降级。
110
+ *
111
+ * 短路条件(#1014 B7):**无失效标记且** TTL 内才短路。显式失效压过 TTL——否则模型会
112
+ * 一直看着已删除的 schema,而 TTL 是 24h。
113
+ *
114
+ * 当前失效的生产者只有一个:tools/change 经 connection 域的 reprojectCatalogs 逐台 diff
115
+ * 后登记(connection/runtime/middleware.ts)。重连与配置变更只有在宿主顺带发出
116
+ * tools/change 时才间接生效;**手工刷新尚无失效落点**(刷新走的是纯读路径,不触发投影),
117
+ * 仍落在 TTL 短路内——该缺口记在 #1014,未在本批闭合。
118
+ */
119
+ projectRegisteredTools(input: RegisteredProjectionInput): Promise<void>;
120
+ /**
121
+ * #413:从封装定义(toolDefinitions)投影目录,替代远端 discover。
122
+ * 封装 execute 为调用方 JS 直呼(不经远端 MCP),目录数据源即调用方定义:
123
+ * name/description 直取;dsh-tools 的 parameters(ToolSchema)即 JSON Schema
124
+ * 形态,直接作 CatalogTool.inputSchema(与 supervisor 封装分支同口径)。
125
+ */
126
+ projectWrappedTools(input: {
127
+ root: string;
128
+ serverName: string;
129
+ definitions: readonly unknown[];
130
+ }): void;
131
+ /**
132
+ * 目录 last-good 持久化(空采集不写盘;public 供测试与外部触发)。
133
+ *
134
+ * 路径求值由**调用方**给(连接层用 host.catalogCachePath(root) 算),写盘失败只 warn;
135
+ * 路径求值失败按设计逃出本层、由投影的 catch 收口成 unavailable 降级。
136
+ */
137
+ persistRoot(root: string, opts: {
138
+ cachePath: () => string;
139
+ isRuntimeServer: (name: string) => boolean;
140
+ warn: (message: string) => void;
141
+ }): Promise<void>;
142
+ /**
143
+ * 从磁盘 last-good 目录缓存中清除单服务器条目(remove/update 后调用;#392 遗留①)。
144
+ * persistRoot 是全量覆盖且空采集不写盘——remove 后该 root 目录可能已空,若不显式
145
+ * 清盘,磁盘缓存仍残留已删服务器条目,插件重启/@global 单元重建时 ensureRootLoaded
146
+ * 把幽灵条目载回(ws_mcp_list 再次列出)。这里读现有缓存、删条目、写回;条目删空则
147
+ * 删除缓存文件。内存目录由调用方(dropMiddlewareConnection)先行删除。
148
+ */
149
+ removeRootEntry(root: string, serverName: string, opts: {
150
+ cachePath: string;
151
+ warn: (message: string) => void;
152
+ }): Promise<void>;
153
+ }
154
+ /** 目录域唯一的内存目录实例:类不外放,外部 new 不出第二份目录。 */
155
+ export declare const catalogDirectory: CatalogDirectory;
156
+ /** 目录新鲜判定:有条目、无 unavailable 段、且发现时间在 TTL 内
157
+ * (投影惰性重做专用;纯时间比较,无副作用)。 */
158
+ export declare function isCatalogFresh(catalog: CatalogServer | undefined): boolean;
159
+ /** 单服务器目录装箱(投影专用纯函数):按限额收敛工具清单——
160
+ * 工具数上限 MAX_TOOLS_PER_SERVER、单描述字节上限 MAX_BYTES_PER_TOOL(超限
161
+ * 截断)、累计字节上限 MAX_TOTAL_CATALOG_BYTES(超限即停)。行为与原
162
+ * discover 内联循环逐位一致(含 totalBytes 对截断后条目的计算口径)。 */
163
+ export declare function boundCatalogTools(tools: Iterable<{
164
+ name?: unknown;
165
+ description?: unknown;
166
+ inputSchema?: unknown;
167
+ }>): Map<string, CatalogTool>;
168
+ export {};