coding-tool-x 3.8.3 → 3.8.5

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 (434) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +86 -15
  3. package/dist/web/assets/AgentsDrawer-BG6P3DX-.js +1 -0
  4. package/dist/web/assets/AgentsDrawer-Pw2k-ZAa.css +1 -0
  5. package/dist/web/assets/Analytics--z_FmL5P.js +1 -0
  6. package/dist/web/assets/Analytics-CyHhR126.css +1 -0
  7. package/dist/web/assets/AssetPathField-CrJtRKHy.js +1 -0
  8. package/dist/web/assets/ChatHistoryDrawer-B2qBQs9L.css +1 -0
  9. package/dist/web/assets/ChatHistoryDrawer-BiuEryJj.js +1 -0
  10. package/dist/web/assets/CollapseItem-BAYEsL_Q.js +1 -0
  11. package/dist/web/assets/CommandsDrawer-BZJxlPvV.css +1 -0
  12. package/dist/web/assets/CommandsDrawer-YJ-U7BXN.js +1 -0
  13. package/dist/web/assets/ConfigExportDrawer-Ce_hAknW.js +1 -0
  14. package/dist/web/assets/ConfigExportDrawer-dwruRKWr.css +1 -0
  15. package/dist/web/assets/ConfigTemplates-Bvr1NN6K.js +1 -0
  16. package/dist/web/assets/ConfigTemplatesDrawer-BfDi8yO3.css +1 -0
  17. package/dist/web/assets/ConfigTemplatesDrawer-BjIeHfF-.js +1 -0
  18. package/dist/web/assets/ConfigTemplatesPanel-AV00tm_D.js +1 -0
  19. package/dist/web/assets/ConfigTemplatesPanel-CWyPtk67.css +1 -0
  20. package/dist/web/assets/Divider-D28g1gwj.js +1 -0
  21. package/dist/web/assets/DrawerContent-ChXgZ1Xm.js +1 -0
  22. package/dist/web/assets/FavoritesDrawer-CG9eI9Ay.css +1 -0
  23. package/dist/web/assets/FavoritesDrawer-Cz95o4CD.js +1 -0
  24. package/dist/web/assets/GatewayConvertDrawer-CuNlf7AC.css +1 -0
  25. package/dist/web/assets/GatewayConvertDrawer-DLlbMz27.js +1 -0
  26. package/dist/web/assets/Home-CSuFuJ9Y.css +1 -0
  27. package/dist/web/assets/Home-D7CCOFQC.js +1 -0
  28. package/dist/web/assets/MarkdownEditor-qfVgSre_.css +1 -0
  29. package/dist/web/assets/MarkdownEditor-rEZ-c72L.js +1 -0
  30. package/dist/web/assets/McpDrawer-C0dnsZhQ.css +1 -0
  31. package/dist/web/assets/McpDrawer-DfoCyxU2.js +1 -0
  32. package/dist/web/assets/McpFormDrawer-BRbDcawn.css +1 -0
  33. package/dist/web/assets/McpFormDrawer-SKu8HNUR.js +1 -0
  34. package/dist/web/assets/PluginManager-BbeK-3rV.js +1 -0
  35. package/dist/web/assets/PluginsDrawer-CWVJ7yjS.js +1 -0
  36. package/dist/web/assets/PluginsDrawer-DM3QjprE.css +1 -0
  37. package/dist/web/assets/PluginsPanel-Cm2N3StG.js +1 -0
  38. package/dist/web/assets/PluginsPanel-D6ZqITcR.css +1 -0
  39. package/dist/web/assets/ProjectList-677RLZ8O.css +1 -0
  40. package/dist/web/assets/ProjectList-CsjnsNPv.js +1 -0
  41. package/dist/web/assets/PromptsDrawer-BIgiYCEQ.css +1 -0
  42. package/dist/web/assets/PromptsDrawer-EGd3djFa.js +1 -0
  43. package/dist/web/assets/RecentSessionsDrawer-CKFsOYDM.css +1 -0
  44. package/dist/web/assets/RecentSessionsDrawer-D1vtoLAs.js +1 -0
  45. package/dist/web/assets/SessionCard-BgLg49U3.js +1 -0
  46. package/dist/web/assets/SessionCard-aM2NHu6i.css +1 -0
  47. package/dist/web/assets/SessionList-BpYRz1cX.css +1 -0
  48. package/dist/web/assets/SessionList-TRiTklWk.js +1 -0
  49. package/dist/web/assets/SettingsDrawer-d5liXjHq.css +1 -0
  50. package/dist/web/assets/SettingsDrawer-rz2WejGY.js +1 -0
  51. package/dist/web/assets/SkillManager-BTjTUbHq.js +1 -0
  52. package/dist/web/assets/SkillManager-C0GCT2Kb.css +1 -0
  53. package/dist/web/assets/SkillsDrawer-BCNme-aJ.css +1 -0
  54. package/dist/web/assets/SkillsDrawer-pN0hbkK3.js +1 -0
  55. package/dist/web/assets/SkillsPanel-B3Nm5j32.css +1 -0
  56. package/dist/web/assets/SkillsPanel-gmBWEoiF.js +1 -0
  57. package/dist/web/assets/SpeedTestDrawer-Dc0WToJ_.css +1 -0
  58. package/dist/web/assets/SpeedTestDrawer-DcNzuVJk.js +1 -0
  59. package/dist/web/assets/Upload-v5rV0qTT.js +1 -0
  60. package/dist/web/assets/WorkspaceDrawer-CGnMu9p1.css +1 -0
  61. package/dist/web/assets/WorkspaceDrawer-DxmicM4U.js +1 -0
  62. package/dist/web/assets/WorkspaceManager-CJHCjARN.js +1 -0
  63. package/dist/web/assets/WorkspacePanel-D0-zHTHP.css +1 -0
  64. package/dist/web/assets/WorkspacePanel-D8_Zq-du.js +1 -0
  65. package/dist/web/assets/_plugin-vue_export-helper-BeLZJZij.js +1 -0
  66. package/dist/web/assets/client-UDwXBP9i.js +1 -0
  67. package/dist/web/assets/clipboard-B46yyoz_.js +1 -0
  68. package/dist/web/assets/headers-cf-QapUq.js +1 -0
  69. package/dist/web/assets/icons-B2Vg-IOC.js +1 -0
  70. package/dist/web/assets/index-C0EHRym0.css +1 -0
  71. package/dist/web/assets/index-DnEocV0M.js +2 -0
  72. package/dist/web/assets/light-Cp0t7v32.js +1 -0
  73. package/dist/web/assets/markdown-u082bbtj.js +1 -0
  74. package/dist/web/assets/platforms-CBkB45JP.js +1 -0
  75. package/dist/web/assets/platforms-FTpjxAT6.js +1 -0
  76. package/dist/web/assets/project-config-B2baPzYL.js +1 -0
  77. package/dist/web/assets/sessions-Cmy31slD.js +1 -0
  78. package/dist/web/assets/sessions-yC_oVVh8.js +1 -0
  79. package/dist/web/assets/skill-repo-input-B_fkGlUK.js +1 -0
  80. package/dist/web/assets/use-lock-html-scroll-DXIqmUW4.js +1 -0
  81. package/dist/web/assets/use-message-DoLM8C1C.js +1 -0
  82. package/dist/web/assets/useResponsiveDrawer-B3uojyvV.js +1 -0
  83. package/dist/web/assets/vendors-D0c_0KLP.js +1 -0
  84. package/dist/web/assets/vue-vendor-BqZbbXHA.js +1 -0
  85. package/dist/web/assets/workspaces-mjyPVGS8.js +1 -0
  86. package/dist/web/assets/xml-CgY3RMlX.js +1 -0
  87. package/dist/web/index.html +12 -8
  88. package/docs/native-log-memory-2026-09-16.md +43 -0
  89. package/docs/superpowers/plans/2026-08-26-configuration-driven-cli-platform.md +1578 -0
  90. package/docs/superpowers/plans/2026-08-30-project-scoped-config-control.md +1260 -0
  91. package/docs/superpowers/plans/2026-09-01-omp-skill-refresh-boundary.md +213 -0
  92. package/docs/superpowers/plans/2026-09-01-realtime-logs-panel.md +125 -0
  93. package/docs/superpowers/plans/2026-09-02-config-driven-platform-boundary.md +86 -0
  94. package/docs/superpowers/plans/2026-09-02-platform-implementation-drivers.md +508 -0
  95. package/docs/superpowers/plans/2026-09-03-cli-platform-correctness-boundary.md +760 -0
  96. package/docs/superpowers/plans/2026-09-03-dependency-security-remediation.md +133 -0
  97. package/docs/superpowers/plans/2026-09-04-model-catalog-pricing-sync.md +1078 -0
  98. package/docs/superpowers/plans/2026-09-05-codex-project-scan-500.md +128 -0
  99. package/docs/superpowers/plans/2026-09-06-project-skill-path-fix.md +97 -0
  100. package/docs/superpowers/specs/2026-08-26-configuration-driven-cli-platform-design.md +408 -0
  101. package/docs/superpowers/specs/2026-08-30-project-scoped-config-control-design.md +467 -0
  102. package/docs/superpowers/specs/2026-09-01-claude-session-content-repair-design.md +111 -0
  103. package/docs/superpowers/specs/2026-09-01-omp-skill-refresh-boundary-design.md +83 -0
  104. package/docs/superpowers/specs/2026-09-01-realtime-logs-panel-design.md +27 -0
  105. package/docs/superpowers/specs/2026-09-02-config-driven-platform-boundary-design.md +64 -0
  106. package/docs/superpowers/specs/2026-09-02-platform-implementation-driver-boundary-design.md +123 -0
  107. package/docs/superpowers/specs/2026-09-03-cli-platform-correctness-boundary-design.md +249 -0
  108. package/docs/superpowers/specs/2026-09-03-dependency-security-remediation-design.md +87 -0
  109. package/docs/superpowers/specs/2026-09-04-model-catalog-pricing-sync-design.md +301 -0
  110. package/docs/superpowers/specs/2026-09-05-codex-project-scan-500-design.md +24 -0
  111. package/package.json +13 -8
  112. package/src/commands/channels.js +88 -191
  113. package/src/commands/cli-type.js +19 -11
  114. package/src/commands/daemon.js +94 -32
  115. package/src/commands/doctor.js +16 -9
  116. package/src/commands/export-config.js +23 -5
  117. package/src/commands/list.js +130 -52
  118. package/src/commands/logs.js +44 -27
  119. package/src/commands/platform-command-registry.js +78 -0
  120. package/src/commands/plugin.js +11 -5
  121. package/src/commands/port-config.js +34 -52
  122. package/src/commands/proxy-control.js +207 -145
  123. package/src/commands/proxy.js +2 -2
  124. package/src/commands/resume.js +87 -91
  125. package/src/commands/search.js +137 -67
  126. package/src/commands/stats.js +30 -22
  127. package/src/commands/switch.js +1 -1
  128. package/src/commands/toggle-proxy.js +94 -111
  129. package/src/commands/ui.js +67 -6
  130. package/src/commands/workspace.js +90 -8
  131. package/src/config/default.js +6 -53
  132. package/src/config/loader.js +61 -24
  133. package/src/config/model-metadata.js +49 -59
  134. package/src/config/model-metadata.json +4095 -99
  135. package/src/config/paths.js +181 -8
  136. package/src/index.js +134 -71
  137. package/src/platforms/access.js +196 -0
  138. package/src/{server/services → platforms}/agents-service.js +354 -197
  139. package/src/platforms/channel-auth-registry.js +162 -0
  140. package/src/platforms/channel-auth-service.js +82 -0
  141. package/src/{server/services → platforms}/commands-service.js +195 -115
  142. package/src/platforms/driver-registry.js +59 -0
  143. package/src/platforms/drivers/claude/api-operations.js +62 -0
  144. package/src/{server/services/channels.js → platforms/drivers/claude/channels-implementation.js} +131 -33
  145. package/src/platforms/drivers/claude/channels.js +39 -0
  146. package/src/{server/services/config-sync-service.js → platforms/drivers/claude/config-sync.js} +29 -5
  147. package/src/{server/services → platforms/drivers/claude}/health-check.js +4 -4
  148. package/src/platforms/drivers/claude/health.js +22 -0
  149. package/src/platforms/drivers/claude/mcp.js +9 -0
  150. package/src/{server/services/settings-manager.js → platforms/drivers/claude/native-config-implementation.js} +13 -4
  151. package/src/platforms/drivers/claude/native-config.js +46 -0
  152. package/src/platforms/drivers/claude/native-logs.js +72 -0
  153. package/src/{server/services/claude-openai-gateway.js → platforms/drivers/claude/openai-gateway.js} +8 -65
  154. package/src/platforms/drivers/claude/project-config.js +21 -0
  155. package/src/platforms/drivers/claude/projects.js +62 -0
  156. package/src/platforms/drivers/claude/prompts.js +9 -0
  157. package/src/{server/proxy-server.js → platforms/drivers/claude/proxy-implementation.js} +41 -278
  158. package/src/platforms/drivers/claude/proxy.js +16 -0
  159. package/src/platforms/drivers/claude/resource-sync.js +9 -0
  160. package/src/platforms/drivers/claude/session-history-adapter.js +313 -0
  161. package/src/{server/services/sessions.js → platforms/drivers/claude/sessions-implementation.js} +145 -35
  162. package/src/platforms/drivers/claude/sessions.js +76 -0
  163. package/src/{server/services/claude-statistics-service.js → platforms/drivers/claude/statistics-implementation.js} +2 -2
  164. package/src/platforms/drivers/claude/statistics.js +16 -0
  165. package/src/{server/services/claude-wire.js → platforms/drivers/claude/wire.js} +1 -1
  166. package/src/platforms/drivers/codex/api-operations.js +9 -0
  167. package/src/{server/services/codex-channels.js → platforms/drivers/codex/channels-implementation.js} +149 -14
  168. package/src/platforms/drivers/codex/channels.js +45 -0
  169. package/src/{server/services/codex-config.js → platforms/drivers/codex/config.js} +12 -2
  170. package/src/{server/services/codex-env-manager.js → platforms/drivers/codex/env-manager.js} +1 -1
  171. package/src/platforms/drivers/codex/mcp.js +9 -0
  172. package/src/{server/services/codex-settings-manager.js → platforms/drivers/codex/native-config-implementation.js} +15 -6
  173. package/src/platforms/drivers/codex/native-config.js +47 -0
  174. package/src/platforms/drivers/codex/native-logs.js +166 -0
  175. package/src/platforms/drivers/codex/project-config.js +21 -0
  176. package/src/platforms/drivers/codex/projects.js +26 -0
  177. package/src/platforms/drivers/codex/prompts.js +9 -0
  178. package/src/{server/codex-proxy-server.js → platforms/drivers/codex/proxy-implementation.js} +30 -233
  179. package/src/platforms/drivers/codex/proxy.js +16 -0
  180. package/src/platforms/drivers/codex/resource-sync.js +9 -0
  181. package/src/{server/services/session-history-adapters/codex.js → platforms/drivers/codex/session-history-adapter.js} +37 -23
  182. package/src/{server/services/codex-sessions.js → platforms/drivers/codex/sessions-implementation.js} +81 -75
  183. package/src/platforms/drivers/codex/sessions.js +36 -0
  184. package/src/{server/services/codex-statistics-service.js → platforms/drivers/codex/statistics-implementation.js} +2 -2
  185. package/src/platforms/drivers/codex/statistics.js +16 -0
  186. package/src/platforms/drivers/gemini/api-operations.js +9 -0
  187. package/src/{server/services/gemini-channels.js → platforms/drivers/gemini/channels-implementation.js} +202 -37
  188. package/src/platforms/drivers/gemini/channels.js +39 -0
  189. package/src/{server/services/gemini-config.js → platforms/drivers/gemini/config.js} +12 -2
  190. package/src/platforms/drivers/gemini/mcp.js +9 -0
  191. package/src/{server/services/gemini-settings-manager.js → platforms/drivers/gemini/native-config-implementation.js} +14 -5
  192. package/src/platforms/drivers/gemini/native-config.js +39 -0
  193. package/src/platforms/drivers/gemini/native-logs.js +180 -0
  194. package/src/platforms/drivers/gemini/project-config.js +21 -0
  195. package/src/platforms/drivers/gemini/projects.js +26 -0
  196. package/src/platforms/drivers/gemini/prompts.js +9 -0
  197. package/src/{server/gemini-proxy-server.js → platforms/drivers/gemini/proxy-implementation.js} +36 -267
  198. package/src/platforms/drivers/gemini/proxy.js +16 -0
  199. package/src/platforms/drivers/gemini/resource-sync.js +9 -0
  200. package/src/{server/services/session-history-adapters/gemini.js → platforms/drivers/gemini/session-history-adapter.js} +70 -18
  201. package/src/{server/services/gemini-sessions.js → platforms/drivers/gemini/sessions-implementation.js} +53 -37
  202. package/src/platforms/drivers/gemini/sessions.js +47 -0
  203. package/src/{server/services/gemini-statistics-service.js → platforms/drivers/gemini/statistics-implementation.js} +2 -2
  204. package/src/platforms/drivers/gemini/statistics.js +16 -0
  205. package/src/{server/services/gemini-wire.js → platforms/drivers/gemini/wire.js} +1 -1
  206. package/src/platforms/drivers/generic-filesystem.js +166 -0
  207. package/src/platforms/drivers/generic-jsonl.js +141 -0
  208. package/src/platforms/drivers/generic-mcp.js +44 -0
  209. package/src/platforms/drivers/generic-openai-compatible.js +45 -0
  210. package/src/platforms/drivers/generic-prompt.js +41 -0
  211. package/src/platforms/drivers/legacy.js +677 -0
  212. package/src/platforms/drivers/native-config-snapshot.js +125 -0
  213. package/src/platforms/drivers/native-log-utils.js +1292 -0
  214. package/src/platforms/drivers/notification-hooks.js +578 -0
  215. package/src/{server/api/omp-config.js → platforms/drivers/omp/api-operations.js} +52 -64
  216. package/src/{server/services/omp-auth-provider-worker.js → platforms/drivers/omp/auth-provider-worker.js} +1 -1
  217. package/src/{server/services/omp-auth-providers.js → platforms/drivers/omp/auth-providers.js} +1 -1
  218. package/src/{server/services/omp-channels.js → platforms/drivers/omp/channels-implementation.js} +166 -39
  219. package/src/platforms/drivers/omp/channels.js +58 -0
  220. package/src/{server/services/omp-config.js → platforms/drivers/omp/config.js} +53 -13
  221. package/src/{server/services/omp-gateway-routing.js → platforms/drivers/omp/gateway-routing.js} +14 -7
  222. package/src/{server/services/omp-gateway.js → platforms/drivers/omp/gateway.js} +4 -262
  223. package/src/platforms/drivers/omp/mcp.js +9 -0
  224. package/src/{server/services/omp-settings-manager.js → platforms/drivers/omp/native-config-implementation.js} +75 -10
  225. package/src/platforms/drivers/omp/native-config.js +46 -0
  226. package/src/platforms/drivers/omp/native-logs.js +52 -0
  227. package/src/{server/services/omp-native-plugin-adapter.js → platforms/drivers/omp/native-plugin-adapter.js} +91 -4
  228. package/src/platforms/drivers/omp/project-config.js +29 -0
  229. package/src/platforms/drivers/omp/projects.js +20 -0
  230. package/src/platforms/drivers/omp/prompts.js +9 -0
  231. package/src/{server/omp-proxy-server.js → platforms/drivers/omp/proxy-implementation.js} +6 -15
  232. package/src/platforms/drivers/omp/proxy.js +16 -0
  233. package/src/platforms/drivers/omp/resource-sync.js +9 -0
  234. package/src/{server/services/session-history-adapters/omp.js → platforms/drivers/omp/session-history-adapter.js} +42 -18
  235. package/src/{server/services/omp-sessions.js → platforms/drivers/omp/sessions-implementation.js} +130 -94
  236. package/src/platforms/drivers/omp/sessions.js +36 -0
  237. package/src/{server/services/omp-skill-discovery.js → platforms/drivers/omp/skill-discovery.js} +159 -32
  238. package/src/{server/services/omp-skill-settings-service.js → platforms/drivers/omp/skill-settings.js} +1 -1
  239. package/src/{server/services/omp-statistics-service.js → platforms/drivers/omp/statistics-implementation.js} +3 -3
  240. package/src/platforms/drivers/omp/statistics.js +16 -0
  241. package/src/platforms/drivers/opencode/api-operations.js +9 -0
  242. package/src/{server/services/opencode-channels.js → platforms/drivers/opencode/channels-implementation.js} +28 -9
  243. package/src/platforms/drivers/opencode/channels.js +40 -0
  244. package/src/platforms/drivers/opencode/conversion.js +41 -0
  245. package/src/{server/services/opencode-gateway-converter.js → platforms/drivers/opencode/gateway-converter.js} +1 -1
  246. package/src/platforms/drivers/opencode/mcp.js +9 -0
  247. package/src/{server/services/opencode-settings-manager.js → platforms/drivers/opencode/native-config-implementation.js} +17 -6
  248. package/src/platforms/drivers/opencode/native-config.js +26 -0
  249. package/src/platforms/drivers/opencode/native-logs.js +133 -0
  250. package/src/platforms/drivers/opencode/project-config.js +98 -0
  251. package/src/platforms/drivers/opencode/projects.js +20 -0
  252. package/src/platforms/drivers/opencode/prompts.js +9 -0
  253. package/src/{server/opencode-proxy-server.js → platforms/drivers/opencode/proxy-implementation.js} +27 -362
  254. package/src/platforms/drivers/opencode/proxy.js +16 -0
  255. package/src/platforms/drivers/opencode/resource-sync.js +9 -0
  256. package/src/{server/services/opencode-sessions.js → platforms/drivers/opencode/sessions-implementation.js} +211 -64
  257. package/src/platforms/drivers/opencode/sessions.js +48 -0
  258. package/src/{server/services/opencode-statistics-service.js → platforms/drivers/opencode/statistics-implementation.js} +2 -2
  259. package/src/platforms/drivers/opencode/statistics.js +16 -0
  260. package/src/platforms/drivers/secure-file-driver.js +272 -0
  261. package/src/platforms/drivers/unsupported.js +7 -0
  262. package/src/platforms/manifest-schema.js +398 -0
  263. package/src/platforms/manifests/claude.json +489 -0
  264. package/src/platforms/manifests/codex.json +448 -0
  265. package/src/platforms/manifests/gemini.json +447 -0
  266. package/src/platforms/manifests/omp.json +496 -0
  267. package/src/platforms/manifests/opencode.json +422 -0
  268. package/src/{server/services → platforms}/native-oauth-adapters.js +87 -417
  269. package/src/{server/services → platforms}/notification-hooks.js +140 -1023
  270. package/src/platforms/oauth-credentials-service.js +506 -0
  271. package/src/platforms/path-resolver.js +179 -0
  272. package/src/platforms/platform-path-context.js +305 -0
  273. package/src/platforms/registry.js +248 -0
  274. package/src/platforms/remote-notification-providers.js +678 -0
  275. package/src/platforms/runtime.js +259 -0
  276. package/src/{server/services/session-history-adapters/index.js → platforms/session-history-adapters.js} +8 -1
  277. package/src/plugins/plugin-loader.js +2 -2
  278. package/src/plugins/plugin-manager.js +2 -2
  279. package/src/plugins/registry.js +27 -10
  280. package/src/reset-config.js +21 -5
  281. package/src/server/api/agents.js +46 -72
  282. package/src/server/api/commands.js +16 -6
  283. package/src/server/api/config-registry.js +206 -59
  284. package/src/server/api/config-sync.js +3 -2
  285. package/src/server/api/config-templates.js +20 -4
  286. package/src/server/api/config.js +111 -102
  287. package/src/server/api/convert.js +54 -178
  288. package/src/server/api/dashboard.js +168 -102
  289. package/src/server/api/hooks.js +1 -1
  290. package/src/server/api/mcp.js +224 -84
  291. package/src/server/api/platform-api-config.js +102 -0
  292. package/src/server/api/platform-route-factory.js +215 -0
  293. package/src/server/api/platform-routes.js +26 -0
  294. package/src/server/api/platforms.js +110 -0
  295. package/src/server/api/plugins.js +57 -57
  296. package/src/server/api/project-config.js +168 -0
  297. package/src/server/api/prompts.js +58 -45
  298. package/src/server/api/settings.js +55 -8
  299. package/src/server/api/skills.js +262 -133
  300. package/src/server/api/ui-config.js +2 -2
  301. package/src/server/api/validation-errors.js +18 -4
  302. package/src/server/api/workspaces.js +47 -12
  303. package/src/server/index.js +164 -213
  304. package/src/server/platform-context.js +81 -0
  305. package/src/server/services/channel-balance.js +11 -20
  306. package/src/server/services/channel-health.js +21 -6
  307. package/src/server/services/channel-scheduler.js +29 -39
  308. package/src/server/services/channel-sync-utils.js +23 -13
  309. package/src/server/services/config-export-service.js +350 -319
  310. package/src/server/services/config-registry-service.js +280 -54
  311. package/src/server/services/config-sync-manager.js +172 -944
  312. package/src/server/services/config-templates-service.js +419 -353
  313. package/src/server/services/control-manifest-store.js +206 -0
  314. package/src/server/services/dashboard-snapshot-worker.js +656 -100
  315. package/src/server/services/effective-control-service.js +557 -0
  316. package/src/server/services/local-resource-index.js +314 -0
  317. package/src/server/services/mcp-service.js +607 -977
  318. package/src/server/services/model-detector.js +15 -10
  319. package/src/server/services/native-log-channel-resolver.js +214 -0
  320. package/src/server/services/native-log-observer.js +291 -0
  321. package/src/server/services/network-access.js +1 -1
  322. package/src/server/services/platform-catalog.js +82 -0
  323. package/src/server/services/platform-resolution.js +32 -29
  324. package/src/server/services/plugins-service.js +376 -122
  325. package/src/server/services/project-config-service.js +550 -0
  326. package/src/server/services/project-path-validation.js +31 -0
  327. package/src/server/services/prompts-service.js +267 -233
  328. package/src/server/services/proxy-runtime.js +7 -4
  329. package/src/server/services/remote-credential-cache.js +43 -0
  330. package/src/server/services/repo-scanner-base.js +182 -75
  331. package/src/server/services/request-logger.js +7 -4
  332. package/src/server/services/session-history-index.js +780 -213
  333. package/src/server/services/session-history-worker.js +228 -33
  334. package/src/server/services/session-snapshot-worker.js +124 -34
  335. package/src/server/services/session-snapshots.js +4 -2
  336. package/src/server/services/skill-artifact-store.js +355 -0
  337. package/src/server/services/skill-format-adapters.js +65 -0
  338. package/src/server/services/skill-projection-service.js +323 -0
  339. package/src/server/services/skill-refresh-task-service.js +275 -0
  340. package/src/server/services/skill-service.js +2020 -677
  341. package/src/server/services/snapshot-cache.js +30 -8
  342. package/src/server/services/speed-test.js +8 -8
  343. package/src/server/services/sqlite-connection.js +19 -3
  344. package/src/server/services/statistics-service.js +240 -57
  345. package/src/server/services/ui-config.js +107 -76
  346. package/src/server/services/usage-log-utils.js +98 -0
  347. package/src/server/services/web-build.js +1 -1
  348. package/src/server/services/workspace-service.js +59 -29
  349. package/src/server/utils/pricing.js +10 -19
  350. package/src/server/websocket-server.js +153 -28
  351. package/src/{server/services/base → shared}/base-channel-service.js +156 -28
  352. package/src/shared/capability-driver.js +104 -0
  353. package/src/shared/channel-auth-policy.js +27 -0
  354. package/src/shared/channel-driver.js +161 -0
  355. package/src/shared/driver-factories/api.js +180 -0
  356. package/src/shared/driver-factories/mcp.js +244 -0
  357. package/src/shared/driver-factories/projects.js +121 -0
  358. package/src/shared/driver-factories/prompts.js +58 -0
  359. package/src/shared/driver-factories/resource-sync.js +375 -0
  360. package/src/shared/driver-result.js +78 -0
  361. package/src/shared/mcp-format.js +189 -0
  362. package/src/shared/platforms.js +104 -167
  363. package/src/shared/project-config.js +512 -0
  364. package/src/shared/proxy-driver.js +57 -0
  365. package/src/{server/services/base → shared}/response-usage-parser.js +8 -4
  366. package/src/ui/menu.js +91 -67
  367. package/src/utils/port-helper.js +35 -19
  368. package/src/utils/session.js +1 -1
  369. package/dist/web/assets/Analytics-COEUXmd4.js +0 -25
  370. package/dist/web/assets/Analytics-RNn1BUbG.css +0 -1
  371. package/dist/web/assets/ConfigTemplates-DLLkS53H.js +0 -1
  372. package/dist/web/assets/Home-DCEyLzwX.js +0 -1
  373. package/dist/web/assets/Home-DYVy4cGZ.css +0 -1
  374. package/dist/web/assets/PluginManager-Oe1ZHoMO.js +0 -1
  375. package/dist/web/assets/ProjectList-CWjuAFof.js +0 -1
  376. package/dist/web/assets/ProjectList-VeMNXfgO.css +0 -1
  377. package/dist/web/assets/SessionList-C4chCPxK.css +0 -1
  378. package/dist/web/assets/SessionList-v3ehGg9U.js +0 -1
  379. package/dist/web/assets/SkillManager-BsX8Qpj0.css +0 -1
  380. package/dist/web/assets/SkillManager-DUF_qP5S.js +0 -1
  381. package/dist/web/assets/WorkspaceManager-CjAWBosO.js +0 -1
  382. package/dist/web/assets/icons-nGdSKQn2.js +0 -1
  383. package/dist/web/assets/index-D5hyHPj7.js +0 -2
  384. package/dist/web/assets/index-bJRG3RXH.css +0 -1
  385. package/dist/web/assets/markdown-BNJfEgCA.js +0 -1
  386. package/dist/web/assets/markdown-BfC0goYb.css +0 -10
  387. package/dist/web/assets/naive-ui-BALVpMgd.js +0 -1
  388. package/dist/web/assets/vendors-DQA4Hw9v.js +0 -7
  389. package/dist/web/assets/vue-vendor-B5RqjEjQ.js +0 -45
  390. package/src/config/model-pricing.js +0 -35
  391. package/src/server/api/channels.js +0 -439
  392. package/src/server/api/claude-hooks.js +0 -56
  393. package/src/server/api/claude-statistics.js +0 -57
  394. package/src/server/api/codex-channels.js +0 -491
  395. package/src/server/api/codex-projects.js +0 -117
  396. package/src/server/api/codex-proxy.js +0 -293
  397. package/src/server/api/codex-sessions.js +0 -652
  398. package/src/server/api/codex-statistics.js +0 -57
  399. package/src/server/api/gemini-channels.js +0 -392
  400. package/src/server/api/gemini-projects.js +0 -103
  401. package/src/server/api/gemini-proxy.js +0 -231
  402. package/src/server/api/gemini-sessions.js +0 -550
  403. package/src/server/api/gemini-statistics.js +0 -57
  404. package/src/server/api/health-check.js +0 -31
  405. package/src/server/api/oauth-credentials.js +0 -198
  406. package/src/server/api/omp-channels.js +0 -575
  407. package/src/server/api/omp-projects.js +0 -88
  408. package/src/server/api/omp-proxy.js +0 -125
  409. package/src/server/api/omp-sessions.js +0 -386
  410. package/src/server/api/omp-statistics.js +0 -40
  411. package/src/server/api/opencode-channels.js +0 -471
  412. package/src/server/api/opencode-projects.js +0 -110
  413. package/src/server/api/opencode-proxy.js +0 -354
  414. package/src/server/api/opencode-sessions.js +0 -466
  415. package/src/server/api/opencode-statistics.js +0 -57
  416. package/src/server/api/projects.js +0 -120
  417. package/src/server/api/proxy.js +0 -330
  418. package/src/server/api/sessions.js +0 -810
  419. package/src/server/services/oauth-credentials-service.js +0 -945
  420. package/src/server/services/omp-session-log-observer.js +0 -138
  421. package/src/server/services/opencode-gateway-adapters.js +0 -43
  422. package/src/server/services/project-snapshots.js +0 -59
  423. package/src/server/services/proxy-log-helper.js +0 -298
  424. package/src/server/services/session-cache.js +0 -127
  425. package/src/server/services/session-converter.js +0 -582
  426. package/src/server/services/session-history-adapters/claude.js +0 -235
  427. /package/src/{server/services/claude-token-recovery.js → platforms/drivers/claude/token-recovery.js} +0 -0
  428. /package/src/{server/services/codex-parser.js → platforms/drivers/codex/parser.js} +0 -0
  429. /package/src/{server/services/codex-speed-test-template.json → platforms/drivers/codex/speed-test-template.json} +0 -0
  430. /package/src/{server/services/codex-wire.js → platforms/drivers/codex/wire.js} +0 -0
  431. /package/src/{server/services/omp-auth-gateway-client.js → platforms/drivers/omp/auth-gateway-client.js} +0 -0
  432. /package/src/{server/services/opencode-normalization.js → platforms/drivers/opencode/normalization.js} +0 -0
  433. /package/src/{server/services → shared}/config-artifact-paths.js +0 -0
  434. /package/src/{server/services/base → shared}/proxy-utils.js +0 -0
@@ -5,8 +5,149 @@
5
5
  const express = require('express');
6
6
  const router = express.Router();
7
7
  const mcpService = require('../services/mcp-service');
8
- const MCP_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode', 'omp'];
9
- const MCP_EXPORT_FORMATS = ['json', 'claude', 'codex', 'gemini', 'opencode', 'omp'];
8
+ const { redactSecrets } = require('../../shared/project-config');
9
+
10
+ const SENSITIVE_URL_KEY = /^(?:token|secret|password|api[-_]?key|access[_-]?token|authorization|key)$/i;
11
+
12
+ function hasSensitiveUrl(value) {
13
+ if (typeof value !== 'string') return false;
14
+ try {
15
+ const parsed = new URL(value);
16
+ return Boolean(
17
+ parsed.username
18
+ || parsed.password
19
+ || [...parsed.searchParams.keys()].some(key => SENSITIVE_URL_KEY.test(key))
20
+ );
21
+ } catch {
22
+ return /\/\/[^/@\s:]+:[^@/\s]+@|[?&](?:token|secret|password|api[-_]?key|access[_-]?token|authorization|key)=/i.test(value);
23
+ }
24
+ }
25
+
26
+ function sanitizeMcpUrl(value) {
27
+ if (typeof value !== 'string') return value;
28
+ try {
29
+ const parsed = new URL(value);
30
+ parsed.username = '';
31
+ parsed.password = '';
32
+ for (const key of parsed.searchParams.keys()) {
33
+ if (SENSITIVE_URL_KEY.test(key)) parsed.searchParams.set(key, '[REDACTED]');
34
+ }
35
+ return parsed.toString();
36
+ } catch {
37
+ return value
38
+ .replace(/\/\/[^/@\s:]+:[^@/\s]+@/g, '//[REDACTED]@')
39
+ .replace(/([?&](?:token|secret|password|api[-_]?key|access[_-]?token|authorization|key)=)[^&\s]+/gi, '$1[REDACTED]');
40
+ }
41
+ }
42
+
43
+ const SENSITIVE_COMMAND_FLAG = /^(?:--?|\/)(?:token|secret|password|api[-_]?key|access[_-]?token|authorization|key|auth|credential|client[-_]?secret|bearer)$/i;
44
+
45
+ function sanitizeCommandLineValue(value) {
46
+ if (Array.isArray(value)) {
47
+ let redactNext = false;
48
+ return value.map(item => {
49
+ if (redactNext) {
50
+ redactNext = false;
51
+ return '[REDACTED]';
52
+ }
53
+ if (typeof item !== 'string') return '[REDACTED]';
54
+ if (SENSITIVE_COMMAND_FLAG.test(item)) {
55
+ redactNext = true;
56
+ return item;
57
+ }
58
+ const inline = item.match(/^((?:--?|\/)(?:token|secret|password|api[-_]?key|access[_-]?token|authorization|key|auth|credential|client[-_]?secret|bearer)=).*/i);
59
+ if (inline) return `${inline[1]}[REDACTED]`;
60
+ return sanitizeErrorMessage(item);
61
+ });
62
+ }
63
+ if (typeof value !== 'string') return value;
64
+ return sanitizeErrorMessage(value)
65
+ .replace(/((?:^|\s)(?:--?|\/)(?:token|secret|password|api[-_]?key|access[_-]?token|authorization|key|auth|credential|client[-_]?secret|bearer)(?:=|\s+))\S+/gi, '$1[REDACTED]');
66
+ }
67
+
68
+ function sanitizeMcpValue(value, key = '') {
69
+ if (/^url$/i.test(key)) return sanitizeMcpUrl(value);
70
+ if (/^(?:command|args)$/i.test(key)) return sanitizeCommandLineValue(value);
71
+ if (/(?:token|secret|password|api[-_]?key|access[_-]?token|authorization|headers?|env(?:ironment)?|envvars|experimentalenvironment|auth|oauth|credential|private[_-]?key)/i.test(key)) {
72
+ return '[REDACTED]';
73
+ }
74
+ if (typeof value === 'string') return sanitizeErrorMessage(value);
75
+ if (Array.isArray(value)) return value.map(item => sanitizeMcpValue(item));
76
+ if (!value || typeof value !== 'object') return value;
77
+ return Object.fromEntries(Object.entries(value).map(([childKey, childValue]) => [
78
+ childKey,
79
+ sanitizeMcpValue(childValue, childKey)
80
+ ]));
81
+ }
82
+ function sanitizeMcpServer(server) {
83
+ if (!server || typeof server !== 'object') return server;
84
+ const safe = sanitizeMcpValue(redactSecrets(server));
85
+ const env = server.server?.env;
86
+ const headers = server.server?.headers;
87
+ const url = server.server?.url;
88
+ return {
89
+ ...safe,
90
+ ...(env || headers || hasSensitiveUrl(url)
91
+ ? { hasSecret: true }
92
+ : {})
93
+ };
94
+ }
95
+
96
+ function sanitizeErrorMessage(value) {
97
+ return String(value || '')
98
+ .replace(/(https?:\/\/[^\s?]+)\?[^\s]+/gi, '$1?[REDACTED]')
99
+ .replace(/(Bearer\s+)[^\s]+/gi, '$1[REDACTED]')
100
+ .replace(/([?&](?:token|secret|password|api[-_]?key|access[_-]?token|authorization|key)=)[^&\s]+/gi, '$1[REDACTED]')
101
+ .replace(/((?:token|secret|password|api[-_]?key|access[_-]?token|authorization)\s*[:=]\s*)[^,\s}]+/gi, '$1[REDACTED]');
102
+ }
103
+ function sanitizeMcpServers(servers) {
104
+ if (!servers || typeof servers !== 'object' || Array.isArray(servers)) return {};
105
+ return Object.fromEntries(Object.entries(servers).map(([id, server]) => [id, sanitizeMcpServer(server)]));
106
+ }
107
+ const { resolveCapability, resolveOperation } = require('../../platforms/access');
108
+
109
+ function normalizePlatformKey(platform) {
110
+ return String(platform || '').trim().toLowerCase();
111
+ }
112
+
113
+ function resolveMcpPlatform(platform) {
114
+ try {
115
+ const resolved = resolveCapability(platform, 'mcp');
116
+ const driverId = resolved.manifest?.capabilities?.mcp || null;
117
+ return { key: resolved.key, driverId, driver: resolved.driver };
118
+ } catch (error) {
119
+ return { error };
120
+ }
121
+ }
122
+
123
+ function resolveMcpExportPlatform(platform) {
124
+ try {
125
+ const resolved = resolveOperation(platform, 'mcp', 'export');
126
+ return { key: resolved.key, driver: resolved.driver };
127
+ } catch (error) {
128
+ return { error };
129
+ }
130
+ }
131
+
132
+
133
+
134
+ function sendCapabilityError(res, error, fallbackStatus = 400) {
135
+ const status = ['not_found', 'unsupported'].includes(error?.code)
136
+ ? 404
137
+ : error?.code === 'invalid'
138
+ ? 400
139
+ : error?.code === 'failed'
140
+ ? 500
141
+ : (error?.status === 404 && error?.code ? 404 : fallbackStatus);
142
+ return res.status(status).json({
143
+ success: false,
144
+ error: sanitizeErrorMessage(error.message),
145
+ code: error.code || undefined,
146
+ platform: error.platform || undefined,
147
+ capability: error.capability || undefined,
148
+ operation: error.operation || undefined
149
+ });
150
+ }
10
151
 
11
152
  /**
12
153
  * GET /api/mcp/servers
@@ -17,13 +158,13 @@ router.get('/servers', (req, res) => {
17
158
  const servers = mcpService.getAllServers();
18
159
  res.json({
19
160
  success: true,
20
- servers
161
+ servers: sanitizeMcpServers(servers)
21
162
  });
22
163
  } catch (error) {
23
- console.error('[MCP API] Get servers failed:', error);
164
+ console.error('[MCP API] Get servers failed:', sanitizeErrorMessage(error.message || error));
24
165
  res.status(500).json({
25
166
  success: false,
26
- error: error.message
167
+ error: sanitizeErrorMessage(error.message)
27
168
  });
28
169
  }
29
170
  });
@@ -43,13 +184,13 @@ router.get('/servers/:id', (req, res) => {
43
184
  }
44
185
  res.json({
45
186
  success: true,
46
- server
187
+ server: sanitizeMcpServer(server)
47
188
  });
48
189
  } catch (error) {
49
- console.error('[MCP API] Get server failed:', error);
50
- res.status(500).json({
190
+ console.error('[MCP API] Get server failed:', sanitizeErrorMessage(error.message || error));
191
+ res.status(400).json({
51
192
  success: false,
52
- error: error.message
193
+ error: sanitizeErrorMessage(error.message)
53
194
  });
54
195
  }
55
196
  });
@@ -79,13 +220,13 @@ router.post('/servers', async (req, res) => {
79
220
  const result = await mcpService.saveServer(server);
80
221
  res.json({
81
222
  success: true,
82
- server: result
223
+ server: sanitizeMcpServer(result)
83
224
  });
84
225
  } catch (error) {
85
- console.error('[MCP API] Save server failed:', error);
226
+ console.error('[MCP API] Save server failed:', sanitizeErrorMessage(error.message || error));
86
227
  res.status(400).json({
87
228
  success: false,
88
- error: error.message
229
+ error: sanitizeErrorMessage(error.message)
89
230
  });
90
231
  }
91
232
  });
@@ -107,10 +248,10 @@ router.delete('/servers/:id', async (req, res) => {
107
248
  success: true
108
249
  });
109
250
  } catch (error) {
110
- console.error('[MCP API] Delete server failed:', error);
111
- res.status(500).json({
251
+ console.error('[MCP API] Delete server failed:', sanitizeErrorMessage(error.message || error));
252
+ res.status(400).json({
112
253
  success: false,
113
- error: error.message
254
+ error: sanitizeErrorMessage(error.message)
114
255
  });
115
256
  }
116
257
  });
@@ -140,13 +281,13 @@ router.post('/servers/:id/toggle', async (req, res) => {
140
281
  const server = await mcpService.toggleServerApp(req.params.id, app, enabled);
141
282
  res.json({
142
283
  success: true,
143
- server
284
+ server: sanitizeMcpServer(server)
144
285
  });
145
286
  } catch (error) {
146
- console.error('[MCP API] Toggle server failed:', error);
287
+ console.error('[MCP API] Toggle server failed:', sanitizeErrorMessage(error.message || error));
147
288
  res.status(400).json({
148
289
  success: false,
149
- error: error.message
290
+ error: sanitizeErrorMessage(error.message)
150
291
  });
151
292
  }
152
293
  });
@@ -163,10 +304,10 @@ router.get('/presets', (req, res) => {
163
304
  presets
164
305
  });
165
306
  } catch (error) {
166
- console.error('[MCP API] Get presets failed:', error);
307
+ console.error('[MCP API] Get presets failed:', sanitizeErrorMessage(error.message || error));
167
308
  res.status(500).json({
168
309
  success: false,
169
- error: error.message
310
+ error: sanitizeErrorMessage(error.message)
170
311
  });
171
312
  }
172
313
  });
@@ -177,29 +318,20 @@ router.get('/presets', (req, res) => {
177
318
  */
178
319
  router.post('/import/:platform', async (req, res) => {
179
320
  try {
180
- const { platform } = req.params;
321
+ const resolved = resolveMcpPlatform(req.params.platform);
322
+ if (resolved.error) return sendCapabilityError(res, resolved.error, 404);
181
323
 
182
- if (!MCP_PLATFORMS.includes(platform)) {
183
- return res.status(400).json({
184
- success: false,
185
- error: `无效的平台: ${platform}`
186
- });
187
- }
188
-
189
- const count = await mcpService.importFromPlatform(platform);
324
+ const count = await mcpService.importFromPlatform(resolved.key);
190
325
  res.json({
191
326
  success: true,
192
327
  imported: count,
193
328
  message: count > 0
194
- ? `成功从 ${platform} 导入 ${count} 个 MCP 服务器`
195
- : `${platform} 没有可导入的 MCP 服务器`
329
+ ? `成功从 ${resolved.key} 导入 ${count} 个 MCP 服务器`
330
+ : `${resolved.key} 没有可导入的 MCP 服务器`
196
331
  });
197
332
  } catch (error) {
198
- console.error('[MCP API] Import failed:', error);
199
- res.status(500).json({
200
- success: false,
201
- error: error.message
202
- });
333
+ console.error('[MCP API] Import failed:', sanitizeErrorMessage(error.message || error));
334
+ return sendCapabilityError(res, error, 500);
203
335
  }
204
336
  });
205
337
 
@@ -215,10 +347,10 @@ router.get('/stats', (req, res) => {
215
347
  stats
216
348
  });
217
349
  } catch (error) {
218
- console.error('[MCP API] Get stats failed:', error);
350
+ console.error('[MCP API] Get stats failed:', sanitizeErrorMessage(error.message || error));
219
351
  res.status(500).json({
220
352
  success: false,
221
- error: error.message
353
+ error: sanitizeErrorMessage(error.message)
222
354
  });
223
355
  }
224
356
  });
@@ -240,12 +372,13 @@ router.post('/servers/:id/test', async (req, res) => {
240
372
  result
241
373
  });
242
374
  } catch (error) {
243
- console.error('[MCP API] Test server failed:', error);
244
- res.status(500).json({
375
+ console.error('[MCP API] Test server failed:', sanitizeErrorMessage(error.message || error));
376
+ const safeError = sanitizeErrorMessage(error.message || error);
377
+ res.status(error.code === 'MCP_DISABLED' ? 403 : 500).json({
245
378
  success: false,
246
- error: error.message,
247
- message: error.message,
248
- hint: error?.data?.hint || null
379
+ error: safeError,
380
+ message: safeError,
381
+ hint: sanitizeMcpValue(error?.data?.hint)
249
382
  });
250
383
  }
251
384
  });
@@ -268,13 +401,14 @@ router.post('/servers/order', (req, res) => {
268
401
  const servers = mcpService.updateServerOrder(serverIds);
269
402
  res.json({
270
403
  success: true,
271
- servers
404
+ servers: sanitizeMcpServers(servers)
272
405
  });
273
406
  } catch (error) {
274
- console.error('[MCP API] Update order failed:', error);
275
- res.status(500).json({
407
+ console.error('[MCP API] Update order failed:', sanitizeErrorMessage(error.message || error));
408
+ const safeError = sanitizeErrorMessage(error.message || error);
409
+ res.status(/invalid MCP server ID/i.test(safeError) ? 400 : 500).json({
276
410
  success: false,
277
- error: error.message
411
+ error: safeError
278
412
  });
279
413
  }
280
414
  });
@@ -285,13 +419,19 @@ router.post('/servers/order', (req, res) => {
285
419
  */
286
420
  router.get('/export', (req, res) => {
287
421
  try {
288
- const format = req.query.format || 'json';
289
-
290
- if (!MCP_EXPORT_FORMATS.includes(format)) {
291
- return res.status(400).json({
292
- success: false,
293
- error: `无效的导出格式: ${format}`
294
- });
422
+ const format = normalizePlatformKey(req.query.format || 'json');
423
+
424
+ if (format !== 'json') {
425
+ const resolved = resolveMcpExportPlatform(format);
426
+ if (resolved.error) {
427
+ if (resolved.error.code === 'not_found') {
428
+ return res.status(400).json({
429
+ success: false,
430
+ error: `无效的导出格式: ${format}`
431
+ });
432
+ }
433
+ return sendCapabilityError(res, resolved.error, 404);
434
+ }
295
435
  }
296
436
 
297
437
  const result = mcpService.exportServers(format);
@@ -300,11 +440,8 @@ router.get('/export', (req, res) => {
300
440
  ...result
301
441
  });
302
442
  } catch (error) {
303
- console.error('[MCP API] Export failed:', error);
304
- res.status(500).json({
305
- success: false,
306
- error: error.message
307
- });
443
+ console.error('[MCP API] Export failed:', sanitizeErrorMessage(error.message || error));
444
+ return sendCapabilityError(res, error, 500);
308
445
  }
309
446
  });
310
447
 
@@ -314,26 +451,29 @@ router.get('/export', (req, res) => {
314
451
  */
315
452
  router.get('/export/download', (req, res) => {
316
453
  try {
317
- const format = req.query.format || 'json';
318
-
319
- if (!MCP_EXPORT_FORMATS.includes(format)) {
320
- return res.status(400).json({
321
- success: false,
322
- error: `无效的导出格式: ${format}`
323
- });
454
+ const format = normalizePlatformKey(req.query.format || 'json');
455
+
456
+ if (format !== 'json') {
457
+ const resolved = resolveMcpExportPlatform(format);
458
+ if (resolved.error) {
459
+ if (resolved.error.code === 'not_found') {
460
+ return res.status(400).json({
461
+ success: false,
462
+ error: `无效的导出格式: ${format}`
463
+ });
464
+ }
465
+ return sendCapabilityError(res, resolved.error, 404);
466
+ }
324
467
  }
325
468
 
326
469
  const result = mcpService.exportServers(format);
327
470
 
328
- res.setHeader('Content-Type', format === 'codex' ? 'application/toml' : 'application/json');
471
+ res.setHeader('Content-Type', result.contentType || 'application/octet-stream');
329
472
  res.setHeader('Content-Disposition', `attachment; filename="${result.filename}"`);
330
473
  res.send(result.content);
331
474
  } catch (error) {
332
- console.error('[MCP API] Export download failed:', error);
333
- res.status(500).json({
334
- success: false,
335
- error: error.message
336
- });
475
+ console.error('[MCP API] Export download failed:', sanitizeErrorMessage(error.message || error));
476
+ return sendCapabilityError(res, error, 500);
337
477
  }
338
478
  });
339
479
 
@@ -348,20 +488,20 @@ router.get('/servers/:id/tools', async (req, res) => {
348
488
  if (result.status === 'error') {
349
489
  return res.status(502).json({
350
490
  success: false,
351
- error: result.error || '获取工具列表失败',
352
- message: result.error || '获取工具列表失败',
353
- hint: result.hint || null,
491
+ error: sanitizeErrorMessage(result.error || '获取工具列表失败'),
492
+ message: sanitizeErrorMessage(result.error || '获取工具列表失败'),
493
+ hint: sanitizeMcpValue(result.hint || null),
354
494
  duration: result.duration,
355
495
  tools: []
356
496
  });
357
497
  }
358
498
  res.json({ success: true, ...result });
359
499
  } catch (err) {
360
- res.status(404).json({
500
+ res.status(err.code === 'MCP_DISABLED' ? 403 : 404).json({
361
501
  success: false,
362
- error: err.message,
363
- message: err.message,
364
- hint: err?.data?.hint || null
502
+ error: sanitizeErrorMessage(err.message),
503
+ message: sanitizeErrorMessage(err.message),
504
+ hint: sanitizeMcpValue(err?.data?.hint || null)
365
505
  });
366
506
  }
367
507
  });
@@ -382,11 +522,11 @@ router.post('/servers/:id/tools/test', async (req, res) => {
382
522
  const result = await mcpService.callServerTool(id, toolName, args || {});
383
523
  res.json({ success: true, ...result });
384
524
  } catch (err) {
385
- res.status(500).json({
525
+ res.status(err.code === 'MCP_DISABLED' ? 403 : 500).json({
386
526
  success: false,
387
- error: err.message,
388
- message: err.message,
389
- hint: err?.data?.hint || null
527
+ error: sanitizeErrorMessage(err.message),
528
+ message: sanitizeErrorMessage(err.message),
529
+ hint: sanitizeMcpValue(err?.data?.hint || null)
390
530
  });
391
531
  }
392
532
  });
@@ -416,7 +556,7 @@ router.get('/servers/:id/info', async (req, res) => {
416
556
  } : {}
417
557
  });
418
558
  } catch (err) {
419
- res.status(500).json({ success: false, error: err.message });
559
+ res.status(500).json({ success: false, error: sanitizeErrorMessage(err.message) });
420
560
  }
421
561
  });
422
562
 
@@ -0,0 +1,102 @@
1
+ 'use strict';
2
+
3
+ function cloneObject(value) {
4
+ return value && typeof value === 'object' && !Array.isArray(value) ? { ...value } : {};
5
+ }
6
+
7
+ function normalizeQuery(query) {
8
+ return cloneObject(query);
9
+ }
10
+
11
+ function normalizeBody(body) {
12
+ return body === undefined ? undefined : body;
13
+ }
14
+
15
+ function requestContext(request, manifest, route, config) {
16
+ const params = { ...request.params };
17
+ delete params.platform;
18
+ if (params.projectHash && !params.projectName) {
19
+ params.projectName = params.projectHash;
20
+ delete params.projectHash;
21
+ }
22
+ return {
23
+ platform: manifest.key,
24
+ manifest,
25
+ route,
26
+ config,
27
+ params,
28
+ query: normalizeQuery(request.query),
29
+ body: normalizeBody(request.body),
30
+ remoteAddress: request.ip || request.socket?.remoteAddress
31
+ };
32
+ }
33
+
34
+ function projectPayload(data, context) {
35
+ if (Array.isArray(data)) {
36
+ return { projects: data, currentProject: null, meta: {} };
37
+ }
38
+ const payload = cloneObject(data);
39
+ if (!Array.isArray(payload.projects)) payload.projects = [];
40
+ if (!Object.prototype.hasOwnProperty.call(payload, 'currentProject')) payload.currentProject = null;
41
+ if (!payload.meta || typeof payload.meta !== 'object' || Array.isArray(payload.meta)) payload.meta = {};
42
+ return payload;
43
+ }
44
+
45
+ function sessionPayload(data) {
46
+ if (Array.isArray(data)) return { sessions: data, meta: {} };
47
+ const payload = cloneObject(data);
48
+ if (!Array.isArray(payload.sessions)) payload.sessions = [];
49
+ if (!payload.meta || typeof payload.meta !== 'object' || Array.isArray(payload.meta)) payload.meta = {};
50
+ return payload;
51
+ }
52
+
53
+ const requestCodecs = Object.freeze({
54
+ default: requestContext,
55
+ 'projects-list': requestContext,
56
+ 'sessions-list': requestContext,
57
+ json: requestContext
58
+ });
59
+
60
+ const responseCodecs = Object.freeze({
61
+ default: value => value,
62
+ 'projects-list': projectPayload,
63
+ projects: projectPayload,
64
+ 'sessions-list': sessionPayload,
65
+ sessions: sessionPayload
66
+ });
67
+
68
+ function hasRequestCodec(codec) {
69
+ return typeof codec === 'function' || typeof requestCodecs[codec] === 'function';
70
+ }
71
+
72
+ function hasResponseCodec(codec) {
73
+ return typeof codec === 'function' || typeof responseCodecs[codec] === 'function';
74
+ }
75
+
76
+
77
+ function applyRequestCodec(request, manifest, route, config) {
78
+ const codec = typeof route.request === 'function' ? route.request : requestCodecs[route.request] || requestCodecs.default;
79
+ return codec(request, manifest, route, config);
80
+ }
81
+
82
+ function applyResponseCodec(value, manifest, route, context) {
83
+ const codec = typeof route.response === 'function' ? route.response : responseCodecs[route.response] || responseCodecs.default;
84
+ return codec(value, manifest, route, context);
85
+ }
86
+
87
+ function emptyPayload(route) {
88
+ if (route.response === 'projects-list' || route.response === 'projects') return { projects: [], currentProject: null, meta: {} };
89
+ if (route.response === 'sessions-list' || route.response === 'sessions') return { sessions: [], meta: {} };
90
+ return {};
91
+ }
92
+
93
+ module.exports = {
94
+ requestCodecs,
95
+ responseCodecs,
96
+ hasRequestCodec,
97
+ hasResponseCodec,
98
+ applyRequestCodec,
99
+ applyResponseCodec,
100
+ emptyPayload,
101
+ createRequestContext: requestContext
102
+ };