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
@@ -3,89 +3,61 @@ const chalk = require('chalk');
3
3
  const inquirer = require('inquirer');
4
4
  const { loadConfig } = require('../config/loader');
5
5
  const { normalizePlatformKey } = require('../shared/platforms');
6
+ const { getPlatformRegistry } = require('../platforms/runtime');
6
7
 
7
- /**
8
- * 获取当前类型的渠道服务
9
- */
10
- function getChannelServices(cliType) {
11
- const normalizedCliType = normalizePlatformKey(cliType || 'claude');
12
- if (normalizedCliType === 'claude') {
13
- const {
14
- getAllChannels,
15
- createChannel,
16
- updateChannel
17
- } = require('../server/services/channels');
18
- const { getProxyStatus } = require('../server/proxy-server');
19
- return { getAllChannels, createChannel, updateChannel, getProxyStatus };
20
- } else if (normalizedCliType === 'codex') {
21
- const {
22
- getChannels,
23
- createChannel,
24
- updateChannel
25
- } = require('../server/services/codex-channels');
26
- const { getCodexProxyStatus } = require('../server/codex-proxy-server');
27
- return {
28
- getAllChannels: () => {
29
- const result = getChannels();
30
- return Array.isArray(result?.channels) ? result.channels : [];
31
- },
32
- createChannel,
33
- updateChannel,
34
- getProxyStatus: getCodexProxyStatus
35
- };
36
- } else if (normalizedCliType === 'gemini') {
37
- const {
38
- getChannels,
39
- createChannel,
40
- updateChannel
41
- } = require('../server/services/gemini-channels');
42
- const { getGeminiProxyStatus } = require('../server/gemini-proxy-server');
43
- return {
44
- getAllChannels: () => {
45
- const result = getChannels();
46
- return Array.isArray(result?.channels) ? result.channels : [];
47
- },
48
- createChannel,
49
- updateChannel,
50
- getProxyStatus: getGeminiProxyStatus
51
- };
52
- } else if (normalizedCliType === 'opencode') {
53
- const {
54
- getChannels,
55
- createChannel,
56
- updateChannel
57
- } = require('../server/services/opencode-channels');
58
- const { getOpenCodeProxyStatus } = require('../server/opencode-proxy-server');
59
- return {
60
- getAllChannels: () => {
61
- const result = getChannels();
62
- return Array.isArray(result?.channels) ? result.channels : [];
63
- },
64
- createChannel,
65
- updateChannel,
66
- getProxyStatus: getOpenCodeProxyStatus
67
- };
68
- } else if (normalizedCliType === 'omp') {
69
- const {
70
- getChannels,
71
- createChannel,
72
- updateChannel
73
- } = require('../server/services/omp-channels');
74
- const { getOmpProxyStatus } = require('../server/omp-proxy-server');
75
- return {
76
- getAllChannels: () => {
77
- const result = getChannels();
78
- return Array.isArray(result?.channels) ? result.channels : [];
79
- },
80
- createChannel,
81
- updateChannel,
82
- getProxyStatus: getOmpProxyStatus,
83
- supportsCliCreate: false,
84
- managedProviderConfig: true
85
- };
8
+ function getPlatformRuntime() {
9
+ return require('../platforms/runtime').getPlatformRuntime();
10
+ }
11
+
12
+ function unwrapDriverResult(result) {
13
+ if (!result || typeof result !== 'object' || !result.status) {
14
+ return result;
15
+ }
16
+ if (result.status === 'ok') {
17
+ return result.data;
86
18
  }
19
+ throw result.cause instanceof Error
20
+ ? result.cause
21
+ : new Error(result.error || `Driver operation ${result.operation || 'unknown'} failed`);
22
+ }
87
23
 
88
- return null;
24
+ function getChannelServices(cliType, runtime = getPlatformRuntime()) {
25
+ const platform = normalizePlatformKey(cliType || 'claude');
26
+ const channelsDriver = runtime?.getDriver?.(platform, 'channels');
27
+ const proxyDriver = runtime?.getDriver?.(platform, 'proxy');
28
+ if (!channelsDriver && !proxyDriver) {
29
+ return null;
30
+ }
31
+
32
+ const channelMetadata = channelsDriver?.getCliMetadata?.() || {};
33
+ const proxyMetadata = proxyDriver?.getCliMetadata?.() || {};
34
+ const metadata = { ...channelMetadata, ...proxyMetadata };
35
+ const unwrapChannels = (operation, ...args) => {
36
+ if (typeof channelsDriver?.[operation] !== 'function') return null;
37
+ return unwrapDriverResult(channelsDriver[operation](...args));
38
+ };
39
+
40
+ return {
41
+ ...metadata,
42
+ getAllChannels: () => {
43
+ const value = unwrapChannels('list');
44
+ return Array.isArray(value) ? value : (value?.channels || []);
45
+ },
46
+ createChannel: input => unwrapChannels('create', input),
47
+ updateChannel: (id, patch) => unwrapChannels('update', id, patch),
48
+ activateChannel: id => unwrapChannels('applyNativeConfig', id),
49
+ getBestChannelForRestore: () => unwrapChannels('getBestChannelForRestore'),
50
+ formatChannelDetails: channel => channelsDriver?.formatCliChannelDetails?.(channel) || [],
51
+ getProxyStatus: () => proxyDriver ? unwrapDriverResult(proxyDriver.status()) : null,
52
+ startProxyServer: options => proxyDriver
53
+ ? unwrapDriverResult(proxyDriver.start(options))
54
+ : null,
55
+ stopProxyServer: options => proxyDriver
56
+ ? unwrapDriverResult(proxyDriver.stop(options))
57
+ : null,
58
+ channelsDriver,
59
+ proxyDriver
60
+ };
89
61
  }
90
62
 
91
63
  /**
@@ -106,19 +78,17 @@ async function handleChannelManagement() {
106
78
  return;
107
79
  }
108
80
 
109
- const channels = services.getAllChannels();
110
-
111
81
  if (channels.length === 0) {
112
82
  console.log(chalk.yellow('还没有添加任何渠道'));
113
- if (cliType === 'omp') {
114
- console.log(chalk.gray('提示: OMP 渠道请通过 Web UI 或 API 添加后,再在这里启停调度。\n'));
83
+ if (services.createUnavailableMessage) {
84
+ console.log(chalk.gray(`${services.createUnavailableMessage}\n`));
115
85
  } else if (services.supportsCliCreate === false || typeof services.createChannel !== 'function') {
116
86
  console.log(chalk.gray('提示: 当前平台不支持在 CLI 中添加渠道,请通过 Web UI 添加。\n'));
117
87
  } else {
118
88
  console.log(chalk.gray('提示: 使用主菜单的"添加渠道"功能来添加新渠道\n'));
119
89
  }
120
90
 
121
- const { action } = await inquirer.prompt([
91
+ await inquirer.prompt([
122
92
  {
123
93
  type: 'list',
124
94
  name: 'action',
@@ -156,11 +126,9 @@ async function handleAddChannel() {
156
126
  const services = getChannelServices(cliType);
157
127
 
158
128
  if (!services || services.supportsCliCreate === false || typeof services.createChannel !== 'function') {
159
- if (cliType === 'omp') {
160
- console.log(chalk.yellow('OMP 渠道创建/编辑需要使用 Web UI 或 API,以便配置 providerKey、providerApi、模型白名单等 OMP 字段。\n'));
161
- } else {
162
- console.log(chalk.red(`当前 CLI 类型 (${cliType}) 暂不支持添加渠道\n`));
163
- }
129
+ console.log(chalk.red(
130
+ `${services?.createUnavailableMessage || `当前 CLI 类型 (${cliType}) 暂不支持添加渠道`}\n`
131
+ ));
164
132
  await inquirer.prompt([{ type: 'input', name: 'continue', message: '按回车返回...' }]);
165
133
  return;
166
134
  }
@@ -210,76 +178,30 @@ async function handleAddChannel() {
210
178
  },
211
179
  ]);
212
180
 
213
- try {
214
- let channel;
215
-
216
- // Claude 类型的参数: (name, baseUrl, apiKey, websiteUrl, extraConfig)
217
- if (cliType === 'claude') {
218
- channel = services.createChannel(
219
- answers.name.trim(),
220
- answers.baseUrl.trim(),
221
- answers.apiKey.trim(),
222
- answers.websiteUrl.trim() || undefined
223
- );
224
- }
225
- // Codex 类型的参数: (name, providerKey, baseUrl, apiKey, wireApi, extraConfig)
226
- else if (cliType === 'codex') {
227
- // Codex 需要额外的 providerKey 和 wireApi 参数
228
- // 在这里简化为使用 name 作为 providerKey,wireApi 固定为 'responses'
229
- channel = services.createChannel(
230
- answers.name.trim(),
231
- answers.name.trim().toLowerCase().replace(/\s+/g, '-'), // 生成 providerKey
232
- answers.baseUrl.trim(),
233
- answers.apiKey.trim(),
234
- 'responses', // wireApi 固定值
235
- { websiteUrl: answers.websiteUrl.trim() || undefined }
236
- );
237
- }
238
- // Gemini 类型的参数: (name, baseUrl, apiKey, model, extraConfig)
239
- else if (cliType === 'gemini') {
240
- // Gemini 需要 model 参数
241
- const modelAnswer = await inquirer.prompt([
242
- {
243
- type: 'input',
244
- name: 'model',
245
- message: '模型名称 (默认: gemini-2.5-pro):',
246
- default: 'gemini-2.5-pro'
247
- }
248
- ]);
249
-
250
- channel = services.createChannel(
251
- answers.name.trim(),
252
- answers.baseUrl.trim(),
253
- answers.apiKey.trim(),
254
- modelAnswer.model.trim(),
255
- { websiteUrl: answers.websiteUrl.trim() || undefined }
256
- );
257
- } else if (cliType === 'opencode') {
258
- const extraAnswers = await inquirer.prompt([
259
- {
260
- type: 'input',
261
- name: 'wireApi',
262
- message: 'Wire API (默认: openai):',
263
- default: 'openai'
264
- },
265
- {
266
- type: 'input',
267
- name: 'model',
268
- message: '默认模型(可选,直接回车跳过):'
269
- }
270
- ]);
271
-
272
- channel = services.createChannel(
273
- answers.name.trim(),
274
- answers.baseUrl.trim(),
275
- answers.apiKey.trim(),
276
- {
277
- wireApi: extraAnswers.wireApi.trim() || 'openai',
278
- model: extraAnswers.model.trim() || null,
279
- websiteUrl: answers.websiteUrl.trim() || undefined
280
- }
281
- );
181
+ const extraQuestions = services.createQuestions || [];
182
+ const extraAnswers = extraQuestions.length > 0
183
+ ? await inquirer.prompt(extraQuestions)
184
+ : {};
185
+
186
+ const websiteUrl = answers.websiteUrl.trim() || undefined;
187
+ const createInput = {
188
+ ...services.createDefaults,
189
+ ...answers,
190
+ ...extraAnswers,
191
+ name: answers.name.trim(),
192
+ baseUrl: answers.baseUrl.trim(),
193
+ apiKey: answers.apiKey.trim(),
194
+ websiteUrl,
195
+ providerKey: answers.name.trim().toLowerCase().replace(/\s+/g, '-'),
196
+ extra: {
197
+ ...services.createDefaults,
198
+ ...extraAnswers,
199
+ websiteUrl
282
200
  }
201
+ };
202
+
203
+ try {
204
+ const channel = services.createChannel(createInput);
283
205
 
284
206
  console.log(chalk.green(`\n[OK] 渠道添加成功: ${channel.name}\n`));
285
207
  console.log(chalk.gray('提示: 使用"渠道管理"功能来启用此渠道\n'));
@@ -356,7 +278,6 @@ module.exports = {
356
278
  * 统一的多渠道选择处理
357
279
  */
358
280
  async function handleChannelToggle(channels, services, cliType) {
359
- const normalizedCliType = normalizePlatformKey(cliType || 'claude');
360
281
  const choices = channels.map(channel => {
361
282
  const enabled = channel.enabled !== false;
362
283
  const detailParts = [];
@@ -366,30 +287,7 @@ async function handleChannelToggle(channels, services, cliType) {
366
287
  detailParts.push(cleaned);
367
288
  }
368
289
 
369
- if (normalizedCliType === 'codex' && channel.providerKey) {
370
- detailParts.push(`provider ${channel.providerKey}`);
371
- }
372
-
373
- if (normalizedCliType === 'gemini' && channel.model) {
374
- detailParts.push(`model ${channel.model}`);
375
- }
376
-
377
- if (normalizedCliType === 'omp') {
378
- if (channel.providerKey) {
379
- detailParts.push(`provider ${channel.providerKey}`);
380
- }
381
- if (channel.model) {
382
- detailParts.push(`model ${channel.model}`);
383
- }
384
- const allowedModels = Array.isArray(channel.allowedModels) && channel.allowedModels.length > 0
385
- ? channel.allowedModels
386
- : channel.models;
387
- if (Array.isArray(allowedModels) && allowedModels.length > 0) {
388
- const preview = allowedModels.slice(0, 3).join(', ');
389
- const suffix = allowedModels.length > 3 ? ` +${allowedModels.length - 3}` : '';
390
- detailParts.push(`models ${preview}${suffix}`);
391
- }
392
- }
290
+ detailParts.push(...services.formatChannelDetails(channel));
393
291
 
394
292
  if (channel.maxConcurrency) {
395
293
  detailParts.push(`并发 ${channel.maxConcurrency}`);
@@ -564,13 +462,12 @@ function broadcastSchedulerSnapshot(source = 'claude') {
564
462
  }
565
463
 
566
464
  function buildSchedulerSources() {
567
- return [
568
- { key: 'claude', label: 'Claude (Claude Code)' },
569
- { key: 'codex', label: 'Codex (OpenAI)' },
570
- { key: 'gemini', label: 'Gemini' },
571
- { key: 'opencode', label: 'OpenCode' },
572
- { key: 'omp', label: 'OMP' }
573
- ];
465
+ return getPlatformRegistry()
466
+ .list({ enabledOnly: true })
467
+ .map(platform => ({
468
+ key: platform.key,
469
+ label: platform.label || platform.key
470
+ }));
574
471
  }
575
472
 
576
473
  async function handleLegacySwitch(channels, services) {
@@ -3,12 +3,17 @@ const chalk = require('chalk');
3
3
  const inquirer = require('inquirer');
4
4
  const { loadConfig, saveConfig } = require('../config/loader');
5
5
 
6
- const CLI_TYPES = {
7
- claude: { name: 'Claude Code', color: 'cyan' },
8
- codex: { name: 'Codex', color: 'green' },
9
- gemini: { name: 'Gemini', color: 'magenta' },
10
- opencode: { name: 'OpenCode', color: 'yellow' }
11
- };
6
+ function getCliTypes(registry) {
7
+ const { createPlatformCommandRegistry } = require('./platform-command-registry');
8
+ return Object.fromEntries(
9
+ createPlatformCommandRegistry({ registry }).list()
10
+ .filter(platform => platform.cliSelectable !== false)
11
+ .map(platform => [platform.key, {
12
+ name: platform.label || platform.title || platform.key,
13
+ color: platform.terminalColor || 'cyan'
14
+ }])
15
+ );
16
+ }
12
17
 
13
18
  /**
14
19
  * 切换CLI类型
@@ -20,13 +25,14 @@ async function handleSwitchCliType() {
20
25
  console.log(chalk.bold.cyan('╚=======================================╝\n'));
21
26
 
22
27
  const config = loadConfig();
28
+ const cliTypes = getCliTypes();
23
29
  const currentType = config.currentCliType || 'claude';
24
- const currentTypeInfo = CLI_TYPES[currentType] || CLI_TYPES.claude;
30
+ const currentTypeInfo = cliTypes[currentType] || cliTypes.claude || { name: currentType, color: 'cyan' };
25
31
 
26
32
  console.log(chalk.gray(`当前类型: ${currentTypeInfo.name}\n`));
27
33
 
28
34
  // 构建类型选项
29
- const choices = Object.entries(CLI_TYPES).map(([type, info]) => {
35
+ const choices = Object.entries(cliTypes).map(([type, info]) => {
30
36
  let name = '';
31
37
 
32
38
  // 如果是当前类型,添加[v]标记
@@ -37,7 +43,8 @@ async function handleSwitchCliType() {
37
43
  }
38
44
 
39
45
  // 类型名称
40
- name += chalk[info.color](info.name);
46
+ const colorize = typeof chalk[info.color] === 'function' ? chalk[info.color] : chalk.cyan;
47
+ name += colorize(info.name);
41
48
 
42
49
  return {
43
50
  name,
@@ -81,7 +88,7 @@ async function handleSwitchCliType() {
81
88
  config.currentCliType = selectedType;
82
89
  saveConfig(config);
83
90
 
84
- console.log(chalk.green(`\n[OK] 已切换到 ${CLI_TYPES[selectedType].name}\n`));
91
+ console.log(chalk.green(`\n[OK] 已切换到 ${cliTypes[selectedType].name}\n`));
85
92
  console.log(chalk.gray('下次启动时将使用新的类型\n'));
86
93
 
87
94
  await inquirer.prompt([
@@ -97,5 +104,6 @@ async function handleSwitchCliType() {
97
104
  }
98
105
 
99
106
  module.exports = {
100
- handleSwitchCliType
107
+ handleSwitchCliType,
108
+ _test: { getCliTypes }
101
109
  };
@@ -12,7 +12,7 @@ const {
12
12
  formatPortToolIssue
13
13
  } = require('../utils/port-helper');
14
14
  const { hasHostFlag } = require('../utils/cli-flags');
15
-
15
+ const { getPlatformRegistry, getPlatformRuntime } = require('../platforms/runtime');
16
16
  const PM2_APP_NAME = 'cc-tool';
17
17
  const STARTUP_LOG_FILE = 'cc-tool-out.log';
18
18
  const CURRENT_PM2_FORK_PATH = resolveCurrentPm2ForkPath();
@@ -254,15 +254,76 @@ function shouldStopPM2Process(status) {
254
254
  return !['stopped', 'errored', 'stopping', 'launching'].includes(String(status || '').toLowerCase());
255
255
  }
256
256
 
257
- function getManagedPorts(config = loadConfig()) {
258
- return [
259
- config.ports?.webUI || 19999,
260
- config.ports?.proxy || 20088,
261
- config.ports?.codexProxy || 20089,
262
- config.ports?.geminiProxy || 20090,
263
- config.ports?.opencodeProxy || 20091,
264
- config.ports?.ompProxy || 20092
265
- ].filter((port, index, list) => Number.isInteger(port) && port > 0 && list.indexOf(port) === index);
257
+ function listProxyPlatforms(registry = getPlatformRegistry()) {
258
+ if (!registry || typeof registry.list !== 'function') return [];
259
+ try {
260
+ return registry.list({ enabledOnly: true }).filter(platform => platform && platform.key);
261
+ } catch (_) {
262
+ return [];
263
+ }
264
+ }
265
+
266
+ function getPlatformPort(platform, config = {}) {
267
+ const configured = platform.portKey && config.ports?.[platform.portKey];
268
+ return configured || platform.defaultPort || null;
269
+ }
270
+
271
+ async function getProxyStatusEntries({ registry = getPlatformRegistry(), runtime = getPlatformRuntime(), config = loadConfig() } = {}) {
272
+ const entries = [];
273
+ for (const platform of listProxyPlatforms(registry)) {
274
+ const context = {
275
+ platform: platform.key,
276
+ capability: 'proxy',
277
+ operation: 'status'
278
+ };
279
+ try {
280
+ const driver = runtime && typeof runtime.getDriver === 'function'
281
+ ? runtime.getDriver(platform.key, 'proxy')
282
+ : null;
283
+ if (!driver || (typeof driver.status === 'string' && driver.status !== 'ok')) {
284
+ entries.push({ platform, state: { status: 'unsupported', ...context }, port: getPlatformPort(platform, config) });
285
+ continue;
286
+ }
287
+ if (typeof driver.status !== 'function') {
288
+ entries.push({ platform, state: { status: 'unsupported', ...context }, port: getPlatformPort(platform, config) });
289
+ continue;
290
+ }
291
+ const state = await Promise.resolve(driver.status());
292
+ if (state && typeof state.status === 'string' && state.status !== 'ok') {
293
+ entries.push({ platform, state: { ...state, ...context }, port: getPlatformPort(platform, config) });
294
+ } else {
295
+ entries.push({ platform, state: state || {}, port: getPlatformPort(platform, config) });
296
+ }
297
+ } catch (error) {
298
+ entries.push({
299
+ platform,
300
+ state: {
301
+ status: 'failed',
302
+ ...context,
303
+ error: error && error.message ? error.message : String(error)
304
+ },
305
+ port: getPlatformPort(platform, config)
306
+ });
307
+ }
308
+ }
309
+ return entries;
310
+ }
311
+
312
+ function getManagedPorts(config = loadConfig(), registry = getPlatformRegistry()) {
313
+ const configuredPorts = config.ports && typeof config.ports === 'object' ? config.ports : {};
314
+ const ports = [configuredPorts.webUI || 19999];
315
+ const configuredKeys = new Set(['webUI']);
316
+ for (const [key, port] of Object.entries(configuredPorts)) {
317
+ if (configuredKeys.has(key)) continue;
318
+ configuredKeys.add(key);
319
+ if (port) ports.push(port);
320
+ }
321
+ for (const platform of listProxyPlatforms(registry)) {
322
+ if (platform.portKey && configuredKeys.has(platform.portKey)) continue;
323
+ const port = getPlatformPort(platform, config);
324
+ if (port) ports.push(port);
325
+ }
326
+ return ports.filter((port, index, list) => Number.isInteger(port) && port > 0 && list.indexOf(port) === index);
266
327
  }
267
328
 
268
329
  async function cleanupManagedPorts(config = loadConfig(), options = {}) {
@@ -321,11 +382,18 @@ async function stopAllManagedInstances(existingProcess, config = loadConfig()) {
321
382
  }
322
383
 
323
384
  const cleanup = await cleanupManagedPorts(config, { timeoutMs: 3000 });
385
+ let lockCleanup = { removed: [], retained: [] };
386
+ try {
387
+ lockCleanup = require('../server/services/session-history-index').cleanupStaleInventoryLocks();
388
+ } catch (error) {
389
+ warnings.push(`清理 session-history 锁失败: ${error.message}`);
390
+ }
324
391
 
325
392
  return {
326
393
  hadPM2Process,
327
394
  pm2Status: status,
328
395
  cleanup,
396
+ lockCleanup,
329
397
  warnings
330
398
  };
331
399
  }
@@ -348,6 +416,9 @@ function printStopResult(result) {
348
416
  printPortToolIssue(cleanup.toolIssue);
349
417
  console.log(chalk.yellow('[TIP] 请检查是否有外部进程仍占用这些端口\n'));
350
418
  }
419
+ if (cleanup.lockCleanup?.removed?.length > 0) {
420
+ console.log(chalk.gray(`[CLEANUP] 已清理残留 session-history 锁: ${cleanup.lockCleanup.removed.length}`));
421
+ }
351
422
  warnings.forEach((warning) => {
352
423
  console.log(chalk.yellow(`[WARN] ${warning}`));
353
424
  });
@@ -409,6 +480,7 @@ function buildStartOptions(port, enableHost, enableHttps) {
409
480
  script: path.join(__dirname, '../index.js'),
410
481
  args: pmArgs,
411
482
  interpreter: 'node',
483
+ windowsHide: true,
412
484
  autorestart: true,
413
485
  kill_timeout: 5000,
414
486
  max_memory_restart: '500M',
@@ -654,30 +726,28 @@ async function handleStatus() {
654
726
  console.log(chalk.gray(' [ERROR] 状态: 未运行'));
655
727
  }
656
728
 
657
- // 代理服务状态(从运行时文件检测)
658
- ensureStorageDirMigrated();
659
- const claudeActive = fs.existsSync(PATHS.activeChannel.claude);
660
- const codexActive = fs.existsSync(PATHS.activeChannel.codex);
661
- const geminiActive = fs.existsSync(PATHS.activeChannel.gemini);
662
- const opencodeActive = fs.existsSync(PATHS.activeChannel.opencode);
663
- const ompActive = fs.existsSync(PATHS.activeChannel.omp);
729
+ // 代理服务状态由 Registry 中声明的 proxy driver 提供
730
+ const proxyStatuses = await getProxyStatusEntries({ config });
664
731
 
665
732
  console.log(chalk.bold('\n[PROXY] 代理服务:'));
666
-
667
- console.log(chalk.gray(' Claude: ') + (claudeActive ? chalk.green('[OK] 运行中') : chalk.gray('[STOP] 未启动')) +
668
- chalk.gray(` (http://localhost:${config.ports?.proxy || 20088})`));
669
-
670
- console.log(chalk.gray(' Codex: ') + (codexActive ? chalk.green('[OK] 运行中') : chalk.gray('[STOP] 未启动')) +
671
- chalk.gray(` (http://localhost:${config.ports?.codexProxy || 20089})`));
672
-
673
- console.log(chalk.gray(' Gemini: ') + (geminiActive ? chalk.green('[OK] 运行中') : chalk.gray('[STOP] 未启动')) +
674
- chalk.gray(` (http://localhost:${config.ports?.geminiProxy || 20090})`));
675
-
676
- console.log(chalk.gray(' OpenCode:') + (opencodeActive ? chalk.green('[OK] 运行中') : chalk.gray('[STOP] 未启动')) +
677
- chalk.gray(` (http://localhost:${config.ports?.opencodeProxy || 20091})`));
678
-
679
- console.log(chalk.gray(' OMP: ') + (ompActive ? chalk.green('[OK] 运行中') : chalk.gray('[STOP] 未启动')) +
680
- chalk.gray(` (http://localhost:${config.ports?.ompProxy || 20092})`));
733
+ for (const entry of proxyStatuses) {
734
+ const label = `${entry.platform.label || entry.platform.key}:`.padEnd(10);
735
+ const state = entry.state || {};
736
+ const port = state.port || entry.port || '-';
737
+ if (state.status === 'unsupported') {
738
+ console.log(chalk.gray(` ${label}${chalk.gray('[N/A] 不支持')}`));
739
+ continue;
740
+ }
741
+ if (state.status === 'failed') {
742
+ console.log(chalk.red(` ${label}${chalk.red('[ERROR] 查询失败')}`) +
743
+ chalk.gray(` (${state.error || 'unknown error'})`));
744
+ continue;
745
+ }
746
+ console.log(chalk.gray(` ${label}`) + (state.running
747
+ ? chalk.green('[OK] 运行中')
748
+ : chalk.gray('[STOP] 未启动')) +
749
+ chalk.gray(` (http://localhost:${port})`));
750
+ }
681
751
 
682
752
  console.log(chalk.bold('\n[TIP] 提示:'));
683
753
  console.log(chalk.gray(' • 代理服务通过 Web UI 界面控制'));
@@ -733,6 +803,8 @@ module.exports = {
733
803
  buildStartOptions,
734
804
  shouldTreatPortOwnershipAsReady,
735
805
  getManagedPorts,
806
+ getProxyStatusEntries,
807
+ listProxyPlatforms,
736
808
  shouldStopPM2Process
737
809
  }
738
810
  };
@@ -6,6 +6,8 @@ const { promisify } = require('util');
6
6
  const { loadConfig, getConfigFilePath } = require('../config/loader');
7
7
  const { PATHS, NATIVE_PATHS } = require('../config/paths');
8
8
  const { isPortInUse } = require('../utils/port-helper');
9
+ const { getPlatformCatalog } = require('../server/services/platform-catalog');
10
+ const { getPlatformContext } = require('../server/platform-context');
9
11
 
10
12
  const execAsync = promisify(exec);
11
13
 
@@ -176,21 +178,26 @@ async function checkPorts() {
176
178
  }
177
179
 
178
180
  function buildPortCheckMap(config = {}) {
179
- return {
180
- 'Web UI': config.ports?.webUI || 19999,
181
- 'Claude Proxy': config.ports?.proxy || 20088,
182
- 'Codex Proxy': config.ports?.codexProxy || 20089,
183
- 'Gemini Proxy': config.ports?.geminiProxy || 20090,
184
- 'OpenCode Proxy': config.ports?.opencodeProxy || 20091,
185
- 'OMP Proxy': config.ports?.ompProxy || 20092
186
- };
181
+ const catalog = getPlatformCatalog();
182
+ return Object.fromEntries(catalog.ports().map(entry => [
183
+ entry.platform ? `${entry.platform === 'claude' ? 'Claude' : (catalog.get(entry.platform)?.label || entry.platform)} Proxy` : entry.label,
184
+ config.ports?.[entry.key] || entry.defaultPort
185
+ ]));
187
186
  }
188
187
 
189
188
  /**
190
189
  * 检查 Claude Code 配置
191
190
  */
192
191
  async function checkClaudeConfig() {
193
- const settingsPath = NATIVE_PATHS.claude.settings;
192
+ let settingsPath = NATIVE_PATHS.claude.settings;
193
+ try {
194
+ const pathContext = getPlatformContext().registry.resolvePathContext('claude');
195
+ if (pathContext?.customized && pathContext.native?.settings) {
196
+ settingsPath = pathContext.native.settings;
197
+ }
198
+ } catch (_) {
199
+ // Keep the legacy path when the registry is unavailable during diagnostics.
200
+ }
194
201
  const exists = fs.existsSync(settingsPath);
195
202
 
196
203
  if (exists) {