coding-tool-x 3.8.4 → 3.8.6

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 +15 -0
  2. package/README.md +86 -15
  3. package/dist/web/assets/AgentsDrawer-Db9wAyaZ.js +1 -0
  4. package/dist/web/assets/AgentsDrawer-Pw2k-ZAa.css +1 -0
  5. package/dist/web/assets/Analytics-C4gMzv7g.js +1 -0
  6. package/dist/web/assets/Analytics-CyHhR126.css +1 -0
  7. package/dist/web/assets/AssetPathField-Bd_nMFu3.js +1 -0
  8. package/dist/web/assets/ChatHistoryDrawer-B2qBQs9L.css +1 -0
  9. package/dist/web/assets/ChatHistoryDrawer-CwzJHyNP.js +1 -0
  10. package/dist/web/assets/CollapseItem-CTCfSiue.js +1 -0
  11. package/dist/web/assets/CommandsDrawer-BZJxlPvV.css +1 -0
  12. package/dist/web/assets/CommandsDrawer-vDghB1U9.js +1 -0
  13. package/dist/web/assets/ConfigExportDrawer-C-XDvlHc.js +1 -0
  14. package/dist/web/assets/ConfigExportDrawer-dwruRKWr.css +1 -0
  15. package/dist/web/assets/ConfigTemplates-CYY8XRED.js +1 -0
  16. package/dist/web/assets/ConfigTemplatesDrawer-BfDi8yO3.css +1 -0
  17. package/dist/web/assets/ConfigTemplatesDrawer-CbsJp76X.js +1 -0
  18. package/dist/web/assets/ConfigTemplatesPanel-CWyPtk67.css +1 -0
  19. package/dist/web/assets/ConfigTemplatesPanel-liMSTh7s.js +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-D6QSiYaL.js +1 -0
  24. package/dist/web/assets/GatewayConvertDrawer-CuNlf7AC.css +1 -0
  25. package/dist/web/assets/GatewayConvertDrawer-RvET6iNG.js +1 -0
  26. package/dist/web/assets/Home-CSuFuJ9Y.css +1 -0
  27. package/dist/web/assets/Home-P8eseZcf.js +1 -0
  28. package/dist/web/assets/MarkdownEditor-CYmLGYju.js +1 -0
  29. package/dist/web/assets/MarkdownEditor-qfVgSre_.css +1 -0
  30. package/dist/web/assets/McpDrawer-C0dnsZhQ.css +1 -0
  31. package/dist/web/assets/McpDrawer-Ch3bkKIq.js +1 -0
  32. package/dist/web/assets/McpFormDrawer-BRbDcawn.css +1 -0
  33. package/dist/web/assets/McpFormDrawer-BcTlgSpN.js +1 -0
  34. package/dist/web/assets/PluginManager-Dh2o0xkZ.js +1 -0
  35. package/dist/web/assets/PluginsDrawer-DM3QjprE.css +1 -0
  36. package/dist/web/assets/PluginsDrawer-deKQ5u3I.js +1 -0
  37. package/dist/web/assets/PluginsPanel-BofsgQgj.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-DhSWJHTA.js +1 -0
  41. package/dist/web/assets/PromptsDrawer-BIgiYCEQ.css +1 -0
  42. package/dist/web/assets/PromptsDrawer-Cfb2FeeU.js +1 -0
  43. package/dist/web/assets/RecentSessionsDrawer-3eXamJhr.js +1 -0
  44. package/dist/web/assets/RecentSessionsDrawer-CKFsOYDM.css +1 -0
  45. package/dist/web/assets/SessionCard-CW0eHURI.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-DQfyDGWU.js +1 -0
  49. package/dist/web/assets/SettingsDrawer-Dw5NUgHC.css +1 -0
  50. package/dist/web/assets/SettingsDrawer-u_vwUuPS.js +1 -0
  51. package/dist/web/assets/SkillManager-C0GCT2Kb.css +1 -0
  52. package/dist/web/assets/SkillManager-DHMprJLA.js +1 -0
  53. package/dist/web/assets/SkillsDrawer-BCNme-aJ.css +1 -0
  54. package/dist/web/assets/SkillsDrawer-CkF3BpyC.js +1 -0
  55. package/dist/web/assets/SkillsPanel-B3Nm5j32.css +1 -0
  56. package/dist/web/assets/SkillsPanel-DWE4pFGx.js +1 -0
  57. package/dist/web/assets/SpeedTestDrawer-BgfG1Ked.js +1 -0
  58. package/dist/web/assets/SpeedTestDrawer-Dc0WToJ_.css +1 -0
  59. package/dist/web/assets/Upload-CNwaGOws.js +1 -0
  60. package/dist/web/assets/WorkspaceDrawer-Br5CeCzL.js +1 -0
  61. package/dist/web/assets/WorkspaceDrawer-CGnMu9p1.css +1 -0
  62. package/dist/web/assets/WorkspaceManager-B2O8-qIw.js +1 -0
  63. package/dist/web/assets/WorkspacePanel-5oDKit3c.js +1 -0
  64. package/dist/web/assets/WorkspacePanel-D0-zHTHP.css +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-BMAUPq5K.js +2 -0
  71. package/dist/web/assets/index-Bi1MQHYX.css +1 -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-jbmqZuJs.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 +8 -4
  112. package/src/commands/channels.js +88 -191
  113. package/src/commands/cli-type.js +19 -11
  114. package/src/commands/daemon.js +104 -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 +165 -0
  140. package/src/platforms/channel-auth-service.js +107 -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 +98 -418
  269. package/src/{server/services → platforms}/notification-hooks.js +190 -1023
  270. package/src/platforms/oauth-credentials-service.js +635 -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 +133 -24
  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 +12 -2
  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 +876 -214
  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
@@ -0,0 +1,168 @@
1
+ 'use strict';
2
+
3
+ const express = require('express');
4
+ const { ProjectConfigService } = require('../services/project-config-service');
5
+ const { resolvePlatform } = require('../../platforms/access');
6
+ const { sendApiError } = require('./validation-errors');
7
+
8
+ const router = express.Router();
9
+ const projectConfigService = new ProjectConfigService();
10
+
11
+ function requireProjectPath(source = {}) {
12
+ const projectPath = source.projectPath;
13
+ if (typeof projectPath !== 'string' || !projectPath.trim()) {
14
+ throw new Error('projectPath is required');
15
+ }
16
+ return projectPath.trim();
17
+ }
18
+
19
+ function requirePlatform(source = {}) {
20
+ return resolvePlatform(source.platform, { fallback: 'claude' }).key;
21
+ }
22
+
23
+ function queryInput(req) {
24
+ return { ...req.query, ...(req.body && typeof req.body === 'object' ? req.body : {}) };
25
+ }
26
+
27
+ router.get('/', async (req, res) => {
28
+ try {
29
+ const input = queryInput(req);
30
+ const projectPath = requireProjectPath(input);
31
+ const platform = requirePlatform(input);
32
+ const snapshot = await projectConfigService.getSnapshot(projectPath, platform);
33
+ res.json(snapshot);
34
+ } catch (error) {
35
+ console.error('[Project Config API] Get snapshot failed:', error);
36
+ sendApiError(res, error);
37
+ }
38
+ });
39
+
40
+ router.get('/skills', async (req, res) => {
41
+ try {
42
+ const input = queryInput(req);
43
+ const projectPath = requireProjectPath(input);
44
+ const platform = requirePlatform(input);
45
+ const skills = await projectConfigService.listProjectSkills(projectPath, platform);
46
+ res.json({ success: true, projectPath, platform, skills });
47
+ } catch (error) {
48
+ console.error('[Project Config API] List project Skills failed:', error);
49
+ sendApiError(res, error);
50
+ }
51
+ });
52
+
53
+ router.put('/skills/toggle', async (req, res) => {
54
+ try {
55
+ const projectPath = requireProjectPath(req.body);
56
+ const platform = requirePlatform(req.body);
57
+ if (!req.body.controlKey) throw new Error('Skill controlKey is required');
58
+ const result = await projectConfigService.setProjectSkillEnabled(
59
+ projectPath,
60
+ platform,
61
+ req.body.controlKey,
62
+ req.body.enabled
63
+ );
64
+ res.json({ success: true, projectPath, platform, ...result });
65
+ } catch (error) {
66
+ console.error('[Project Config API] Toggle project Skill failed:', error);
67
+ sendApiError(res, error);
68
+ }
69
+ });
70
+
71
+ router.get('/instruction', async (req, res) => {
72
+ try {
73
+ const input = queryInput(req);
74
+ const projectPath = requireProjectPath(input);
75
+ const platform = requirePlatform(input);
76
+ const instruction = await projectConfigService.readInstruction(projectPath, platform);
77
+ res.json({ success: true, projectPath, platform, instruction });
78
+ } catch (error) {
79
+ console.error('[Project Config API] Read instruction failed:', error);
80
+ sendApiError(res, error);
81
+ }
82
+ });
83
+
84
+ router.put('/instruction', async (req, res) => {
85
+ try {
86
+ const projectPath = requireProjectPath(req.body);
87
+ const platform = requirePlatform(req.body);
88
+ if (typeof req.body.content !== 'string') throw new Error('Instruction content must be a string');
89
+ const instruction = await projectConfigService.writeInstruction(projectPath, platform, req.body.content);
90
+ res.json({ success: true, projectPath, platform, instruction });
91
+ } catch (error) {
92
+ console.error('[Project Config API] Write instruction failed:', error);
93
+ sendApiError(res, error);
94
+ }
95
+ });
96
+
97
+ router.delete('/instruction', async (req, res) => {
98
+ try {
99
+ const input = queryInput(req);
100
+ const projectPath = requireProjectPath(input);
101
+ const platform = requirePlatform(input);
102
+ const instruction = await projectConfigService.deleteInstruction(projectPath, platform);
103
+ res.json({ success: true, projectPath, platform, instruction });
104
+ } catch (error) {
105
+ console.error('[Project Config API] Delete instruction failed:', error);
106
+ sendApiError(res, error);
107
+ }
108
+ });
109
+
110
+ router.get('/mcp', async (req, res) => {
111
+ try {
112
+ const input = queryInput(req);
113
+ const projectPath = requireProjectPath(input);
114
+ const platform = requirePlatform(input);
115
+ const mcp = await projectConfigService.listProjectMcp(projectPath, platform);
116
+ res.json({ success: true, projectPath, platform, mcp });
117
+ } catch (error) {
118
+ console.error('[Project Config API] List project MCP failed:', error);
119
+ sendApiError(res, error);
120
+ }
121
+ });
122
+
123
+ router.put('/mcp/:id', async (req, res) => {
124
+ try {
125
+ const projectPath = requireProjectPath(req.body);
126
+ const platform = requirePlatform(req.body);
127
+ if (!req.body.server || typeof req.body.server !== 'object' || Array.isArray(req.body.server)) {
128
+ throw new Error('MCP server spec must be an object');
129
+ }
130
+ const result = await projectConfigService.upsertProjectMcp(
131
+ projectPath,
132
+ platform,
133
+ req.params.id,
134
+ req.body.server
135
+ );
136
+ res.json({ success: result.success !== false, projectPath, platform, server: result });
137
+ } catch (error) {
138
+ console.error('[Project Config API] Upsert project MCP failed:', error);
139
+ sendApiError(res, error);
140
+ }
141
+ });
142
+
143
+ router.delete('/mcp/:id', async (req, res) => {
144
+ try {
145
+ const input = queryInput(req);
146
+ const projectPath = requireProjectPath(input);
147
+ const platform = requirePlatform(input);
148
+ const result = await projectConfigService.removeProjectMcp(projectPath, platform, req.params.id);
149
+ res.json({ success: result.success !== false, projectPath, platform, server: result });
150
+ } catch (error) {
151
+ console.error('[Project Config API] Remove project MCP failed:', error);
152
+ sendApiError(res, error);
153
+ }
154
+ });
155
+
156
+ router.post('/mcp/:id/test', async (req, res) => {
157
+ try {
158
+ const projectPath = requireProjectPath(req.body);
159
+ const platform = requirePlatform(req.body);
160
+ const result = await projectConfigService.testProjectMcp(projectPath, platform, req.params.id);
161
+ res.json({ success: true, projectPath, platform, result });
162
+ } catch (error) {
163
+ console.error('[Project Config API] Test project MCP failed:', error);
164
+ sendApiError(res, error, error.code === 'MCP_DISABLED' ? 403 : 500);
165
+ }
166
+ });
167
+
168
+ module.exports = router;
@@ -5,7 +5,45 @@
5
5
  const express = require('express');
6
6
  const router = express.Router();
7
7
  const promptsService = require('../services/prompts-service');
8
- const PROMPT_FILE_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode'];
8
+ const { getPlatformContext } = require('../platform-context');
9
+
10
+ function normalizePlatformKey(platform) {
11
+ return String(platform || '').trim().toLowerCase();
12
+ }
13
+
14
+ function createCapabilityError(platform, code) {
15
+ const key = normalizePlatformKey(platform);
16
+ const error = new Error(code === 'not_found'
17
+ ? `无效的平台: ${key}`
18
+ : `平台 ${key} 未声明 prompts capability`);
19
+ error.status = 404;
20
+ error.code = code;
21
+ error.platform = key;
22
+ error.capability = 'prompts';
23
+ return error;
24
+ }
25
+
26
+ function resolvePromptPlatform(platform) {
27
+ const key = normalizePlatformKey(platform);
28
+ const registry = getPlatformContext().registry;
29
+ if (!registry.resolve(key)) return { error: createCapabilityError(key, 'not_found') };
30
+ const driverId = registry.getCapability(key, 'prompts');
31
+ if (!driverId || driverId === 'unsupported') {
32
+ return { error: createCapabilityError(key, 'unsupported') };
33
+ }
34
+ return { key, driverId };
35
+ }
36
+
37
+ function sendCapabilityError(res, error, fallbackStatus = 400) {
38
+ const status = error?.status === 404 && error?.code ? 404 : fallbackStatus;
39
+ return res.status(status).json({
40
+ success: false,
41
+ error: error.message,
42
+ code: error.code || undefined,
43
+ platform: error.platform || undefined,
44
+ capability: error.capability || undefined
45
+ });
46
+ }
9
47
 
10
48
  /**
11
49
  * GET /api/prompts/presets
@@ -77,7 +115,7 @@ router.get('/presets/:id', (req, res) => {
77
115
  * POST /api/prompts/presets
78
116
  * 添加或更新预设
79
117
  */
80
- router.post('/presets', (req, res) => {
118
+ router.post('/presets', async (req, res) => {
81
119
  try {
82
120
  const preset = req.body;
83
121
 
@@ -95,17 +133,14 @@ router.post('/presets', (req, res) => {
95
133
  });
96
134
  }
97
135
 
98
- const result = promptsService.savePreset(preset);
136
+ const result = await promptsService.savePreset(preset);
99
137
  res.json({
100
138
  success: true,
101
139
  preset: result
102
140
  });
103
141
  } catch (error) {
104
142
  console.error('[Prompts API] Save preset failed:', error);
105
- res.status(400).json({
106
- success: false,
107
- error: error.message
108
- });
143
+ return sendCapabilityError(res, error, 400);
109
144
  }
110
145
  });
111
146
 
@@ -180,49 +215,36 @@ router.post('/deactivate', async (req, res) => {
180
215
  * GET /api/prompts/platform-status
181
216
  * 获取各平台提示词状态
182
217
  */
183
- router.get('/platform-status', (req, res) => {
218
+ router.get('/platform-status', async (req, res) => {
184
219
  try {
185
- const status = promptsService.getPlatformStatus();
220
+ const status = await promptsService.getPlatformStatus();
186
221
  res.json({
187
222
  success: true,
188
223
  status
189
224
  });
190
225
  } catch (error) {
191
226
  console.error('[Prompts API] Get platform status failed:', error);
192
- res.status(500).json({
193
- success: false,
194
- error: error.message
195
- });
227
+ return sendCapabilityError(res, error, 500);
196
228
  }
197
229
  });
198
-
199
230
  /**
200
231
  * GET /api/prompts/platform/:platform
201
232
  * 读取指定平台的提示词
202
233
  */
203
- router.get('/platform/:platform', (req, res) => {
234
+ router.get('/platform/:platform', async (req, res) => {
204
235
  try {
205
- const { platform } = req.params;
206
-
207
- if (!PROMPT_FILE_PLATFORMS.includes(platform)) {
208
- return res.status(400).json({
209
- success: false,
210
- error: `无效的平台: ${platform}`
211
- });
212
- }
236
+ const resolved = resolvePromptPlatform(req.params.platform);
237
+ if (resolved.error) return sendCapabilityError(res, resolved.error, 404);
213
238
 
214
- const content = promptsService.readPlatformPrompt(platform);
239
+ const content = await promptsService.readPlatformPrompt(resolved.key);
215
240
  res.json({
216
241
  success: true,
217
- platform,
242
+ platform: resolved.key,
218
243
  content
219
244
  });
220
245
  } catch (error) {
221
246
  console.error('[Prompts API] Read platform prompt failed:', error);
222
- res.status(500).json({
223
- success: false,
224
- error: error.message
225
- });
247
+ return sendCapabilityError(res, error, 500);
226
248
  }
227
249
  });
228
250
 
@@ -230,30 +252,21 @@ router.get('/platform/:platform', (req, res) => {
230
252
  * POST /api/prompts/import/:platform
231
253
  * 从指定平台导入提示词
232
254
  */
233
- router.post('/import/:platform', (req, res) => {
255
+ router.post('/import/:platform', async (req, res) => {
234
256
  try {
235
- const { platform } = req.params;
236
- const { name } = req.body;
257
+ const resolved = resolvePromptPlatform(req.params.platform);
258
+ if (resolved.error) return sendCapabilityError(res, resolved.error, 404);
237
259
 
238
- if (!PROMPT_FILE_PLATFORMS.includes(platform)) {
239
- return res.status(400).json({
240
- success: false,
241
- error: `无效的平台: ${platform}`
242
- });
243
- }
244
-
245
- const preset = promptsService.importFromPlatform(platform, name);
260
+ const { name } = req.body;
261
+ const preset = await promptsService.importFromPlatform(resolved.key, name);
246
262
  res.json({
247
263
  success: true,
248
264
  preset,
249
- message: `成功从 ${platform} 导入提示词`
265
+ message: `成功从 ${resolved.key} 导入提示词`
250
266
  });
251
267
  } catch (error) {
252
268
  console.error('[Prompts API] Import failed:', error);
253
- res.status(400).json({
254
- success: false,
255
- error: error.message
256
- });
269
+ return sendCapabilityError(res, error, 500);
257
270
  }
258
271
  });
259
272
 
@@ -3,9 +3,11 @@ const router = express.Router();
3
3
  const {
4
4
  MODEL_METADATA,
5
5
  METADATA_LAST_UPDATED,
6
- getDefaultSpeedTestModels,
7
- saveDefaultSpeedTestModels
6
+ METADATA_SOURCE,
7
+ getModelIdsByToolType,
8
+ getDefaultSpeedTestModels
8
9
  } = require('../../config/model-metadata');
10
+ const { getPlatformCatalog } = require('../services/platform-catalog');
9
11
  const { loadConfig, saveConfig } = require('../../config/loader');
10
12
  const {
11
13
  MODEL_SCHEMA_VERSION,
@@ -34,6 +36,35 @@ function normalizeDefinitions(value) {
34
36
  return isPlainObject(value) ? value : {};
35
37
  }
36
38
 
39
+ function normalizeSpeedTestModels(value) {
40
+ if (!isPlainObject(value)) return {};
41
+ return Object.fromEntries(
42
+ Object.entries(value)
43
+ .map(([key, modelId]) => [String(key).trim(), typeof modelId === 'string' ? modelId.trim() : ''])
44
+ .filter(([key, modelId]) => key && modelId)
45
+ );
46
+ }
47
+
48
+ function getModelPlatformEntries(catalog = getPlatformCatalog()) {
49
+ return catalog.list({ capability: 'channels' }).map(manifest => ({
50
+ key: manifest.key,
51
+ catalogKey: manifest.modelConfig?.catalogKey || manifest.key
52
+ }));
53
+ }
54
+
55
+ function resolveDefaultSpeedTestModels(config = {}, catalog = getPlatformCatalog()) {
56
+ const configured = normalizeSpeedTestModels(config.defaultSpeedTestModels);
57
+ const staticDefaults = normalizeSpeedTestModels(getDefaultSpeedTestModels());
58
+ return Object.fromEntries(getModelPlatformEntries(catalog).map(({ key, catalogKey }) => [
59
+ key,
60
+ configured[key]
61
+ || configured[catalogKey]
62
+ || staticDefaults[catalogKey]
63
+ || staticDefaults[key]
64
+ || null
65
+ ]));
66
+ }
67
+
37
68
  function buildResolvedModelSettings(definitions, overrides) {
38
69
  const ids = new Set([
39
70
  ...Object.keys(MODEL_METADATA),
@@ -62,7 +93,12 @@ function handleGetModelSettings(req, res) {
62
93
  const config = loadConfig();
63
94
  const overrides = config.modelMetadataOverrides || {};
64
95
  const definitions = normalizeDefinitions(config.modelDefinitions);
65
- const defaultSpeedTestModels = getDefaultSpeedTestModels();
96
+ const catalog = getPlatformCatalog();
97
+ const platformEntries = getModelPlatformEntries(catalog);
98
+ const defaultSpeedTestModels = resolveDefaultSpeedTestModels(config, catalog);
99
+ const toolModels = Object.fromEntries(
100
+ platformEntries.map(({ key, catalogKey }) => [key, getModelIdsByToolType(catalogKey)])
101
+ );
66
102
 
67
103
  // Build merged table: built-in + user overrides
68
104
  const merged = {};
@@ -91,6 +127,8 @@ function handleGetModelSettings(req, res) {
91
127
  warnings: resolved.warnings,
92
128
  builtinModelIds: Object.keys(MODEL_METADATA),
93
129
  lastUpdated: METADATA_LAST_UPDATED,
130
+ metadataSource: METADATA_SOURCE,
131
+ toolModels,
94
132
  defaultSpeedTestModels
95
133
  });
96
134
  } catch (error) {
@@ -110,6 +148,9 @@ function handleSaveModelSettings(req, res) {
110
148
  if (overrides !== undefined && (typeof overrides !== 'object' || overrides === null || Array.isArray(overrides))) {
111
149
  return res.status(400).json({ error: 'overrides must be an object' });
112
150
  }
151
+ if (defaultSpeedTestModels !== undefined && !isPlainObject(defaultSpeedTestModels)) {
152
+ return res.status(400).json({ error: 'defaultSpeedTestModels must be an object' });
153
+ }
113
154
 
114
155
  // Validate each override entry
115
156
  if (overrides && typeof overrides === 'object') {
@@ -121,7 +162,7 @@ function handleSaveModelSettings(req, res) {
121
162
  return res.status(400).json({ error: `${modelId}: metadata must be an object` });
122
163
  }
123
164
  if (meta.limit !== undefined) {
124
- if (typeof meta.limit !== 'object') {
165
+ if (!isPlainObject(meta.limit)) {
125
166
  return res.status(400).json({ error: `${modelId}: limit must be an object` });
126
167
  }
127
168
  if (meta.limit.context !== undefined && (typeof meta.limit.context !== 'number' || meta.limit.context <= 0)) {
@@ -132,10 +173,10 @@ function handleSaveModelSettings(req, res) {
132
173
  }
133
174
  }
134
175
  if (meta.pricing !== undefined) {
135
- if (typeof meta.pricing !== 'object') {
176
+ if (!isPlainObject(meta.pricing)) {
136
177
  return res.status(400).json({ error: `${modelId}: pricing must be an object` });
137
178
  }
138
- for (const field of ['input', 'output']) {
179
+ for (const field of ['input', 'output', 'cacheCreation', 'cacheRead']) {
139
180
  if (meta.pricing[field] !== undefined && (typeof meta.pricing[field] !== 'number' || meta.pricing[field] < 0)) {
140
181
  return res.status(400).json({ error: `${modelId}: pricing.${field} must be a non-negative number` });
141
182
  }
@@ -166,6 +207,12 @@ function handleSaveModelSettings(req, res) {
166
207
  }
167
208
 
168
209
  const config = loadConfig();
210
+ const catalog = getPlatformCatalog();
211
+ const persistedDefaultSpeedTestModels = {
212
+ ...normalizeSpeedTestModels(getDefaultSpeedTestModels()),
213
+ ...normalizeSpeedTestModels(config.defaultSpeedTestModels),
214
+ ...normalizeSpeedTestModels(defaultSpeedTestModels)
215
+ };
169
216
  const newConfig = {
170
217
  ...config,
171
218
  modelMetadataOverrides: overrides && typeof overrides === 'object'
@@ -174,17 +221,17 @@ function handleSaveModelSettings(req, res) {
174
221
  modelDefinitions: normalizedDefinitions === undefined
175
222
  ? (config.modelDefinitions || {})
176
223
  : normalizedDefinitions,
224
+ defaultSpeedTestModels: persistedDefaultSpeedTestModels,
177
225
  modelMetadataSchemaVersion: MODEL_SCHEMA_VERSION
178
226
  };
179
227
  saveConfig(newConfig);
180
- const persistedDefaultSpeedTestModels = saveDefaultSpeedTestModels(defaultSpeedTestModels);
181
228
 
182
229
  res.json({
183
230
  success: true,
184
231
  schemaVersion: MODEL_SCHEMA_VERSION,
185
232
  overrides: redactSensitiveFields(newConfig.modelMetadataOverrides),
186
233
  definitions: redactSensitiveFields(newConfig.modelDefinitions),
187
- defaultSpeedTestModels: persistedDefaultSpeedTestModels
234
+ defaultSpeedTestModels: resolveDefaultSpeedTestModels(newConfig, catalog)
188
235
  });
189
236
  } catch (error) {
190
237
  console.error('Error saving model metadata:', error);