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
@@ -5,6 +5,7 @@ const fs = require('fs');
5
5
  const path = require('path');
6
6
  const { execFileSync } = require('child_process');
7
7
  const workspaceService = require('../services/workspace-service');
8
+ const { resolveKnownProjectPath } = require('../services/project-path-validation');
8
9
 
9
10
  function normalizeBranchName(branchName) {
10
11
  if (typeof branchName !== 'string') {
@@ -51,6 +52,7 @@ function validateBranchMode(branchMode) {
51
52
  return { valid: true, normalized };
52
53
  }
53
54
 
55
+
54
56
  /**
55
57
  * GET /api/workspaces
56
58
  * 获取所有工作区列表
@@ -75,7 +77,7 @@ router.get('/', (req, res) => {
75
77
  * 读取工作区中的文件内容(仅限 CLAUDE.md 等配置文件)
76
78
  * Query: path=文件路径
77
79
  */
78
- router.get('/read-file', (req, res) => {
80
+ router.get('/read-file', async (req, res) => {
79
81
  try {
80
82
  const filePath = req.query.path;
81
83
 
@@ -97,19 +99,52 @@ router.get('/read-file', (req, res) => {
97
99
  });
98
100
  }
99
101
 
100
- if (!fs.existsSync(filePath)) {
101
- return res.status(404).json({
102
+ const canonicalPath = await resolveKnownProjectPath(filePath);
103
+ if (!canonicalPath || !allowedFiles.includes(path.basename(canonicalPath))) {
104
+ return res.status(403).json({
102
105
  success: false,
103
- message: '文件不存在'
106
+ message: '仅允许读取已登记项目或工作区中的配置文件'
104
107
  });
105
108
  }
106
109
 
107
- const content = fs.readFileSync(filePath, 'utf8');
108
-
109
- res.json({
110
- success: true,
111
- content: content
112
- });
110
+ let fileDescriptor;
111
+ try {
112
+ const noFollow = fs.constants.O_NOFOLLOW || 0;
113
+ fileDescriptor = fs.openSync(canonicalPath, fs.constants.O_RDONLY | noFollow);
114
+ const openedStat = fs.fstatSync(fileDescriptor);
115
+ const revalidatedPath = await resolveKnownProjectPath(canonicalPath);
116
+ const pathStat = fs.statSync(canonicalPath);
117
+ const sameFile = openedStat.dev === pathStat.dev && openedStat.ino === pathStat.ino;
118
+ if (revalidatedPath !== canonicalPath || !sameFile) {
119
+ return res.status(403).json({
120
+ success: false,
121
+ message: '仅允许读取已登记项目或工作区中的配置文件'
122
+ });
123
+ }
124
+ if (!openedStat.isFile()) {
125
+ return res.status(403).json({
126
+ success: false,
127
+ message: '仅允许读取配置文件'
128
+ });
129
+ }
130
+ const content = fs.readFileSync(fileDescriptor, 'utf8');
131
+ return res.json({
132
+ success: true,
133
+ content
134
+ });
135
+ } catch (error) {
136
+ if (error.code === 'ENOENT') {
137
+ return res.status(404).json({
138
+ success: false,
139
+ message: '文件不存在'
140
+ });
141
+ }
142
+ throw error;
143
+ } finally {
144
+ if (fileDescriptor !== undefined) {
145
+ fs.closeSync(fileDescriptor);
146
+ }
147
+ }
113
148
  } catch (error) {
114
149
  res.status(500).json({
115
150
  success: false,
@@ -217,7 +252,7 @@ router.get('/:id', (req, res, next) => {
217
252
  * }]
218
253
  * }
219
254
  */
220
- router.post('/', (req, res) => {
255
+ router.post('/', async (req, res) => {
221
256
  try {
222
257
  const { name, description, baseDir, projects, configTemplateId } = req.body;
223
258
 
@@ -280,7 +315,7 @@ router.post('/', (req, res) => {
280
315
  }
281
316
  }
282
317
 
283
- const workspace = workspaceService.createWorkspace({
318
+ const workspace = await workspaceService.createWorkspace({
284
319
  name,
285
320
  description,
286
321
  baseDir,
@@ -4,7 +4,12 @@ const express = require('express');
4
4
  const path = require('path');
5
5
  const chalk = require('chalk');
6
6
  const { loadConfig } = require('../config/loader');
7
- const { PATHS, ensureStorageDirMigrated } = require('../config/paths');
7
+ const {
8
+ PATHS,
9
+ ensureStorageDirMigrated,
10
+ getPlatformStatePath,
11
+ persistOmpAgentPath
12
+ } = require('../config/paths');
8
13
  const { startWebSocketServer: attachWebSocketServer } = require('./websocket-server');
9
14
  const {
10
15
  isPortInUse,
@@ -13,24 +18,19 @@ const {
13
18
  getPortToolIssue,
14
19
  formatPortToolIssue
15
20
  } = require('../utils/port-helper');
16
- const { isProxyConfig } = require('./services/settings-manager');
17
- const {
18
- isProxyConfig: isCodexProxyConfig,
19
- setProxyConfig: setCodexProxyConfig
20
- } = require('./services/codex-settings-manager');
21
- const { setProxyConfig: setOpenCodeProxyConfig } = require('./services/opencode-settings-manager');
22
- const { startProxyServer } = require('./proxy-server');
23
- const { startCodexProxyServer } = require('./codex-proxy-server');
24
- const { startGeminiProxyServer } = require('./gemini-proxy-server');
25
- const { startOpenCodeProxyServer, collectProxyModelList } = require('./opencode-proxy-server');
26
- const { startOmpProxyServer } = require('./omp-proxy-server');
27
21
  const {
28
22
  createRemoteMutationGuard,
29
- isRemoteMutationAllowedByEnv
23
+ isRemoteMutationAllowedByEnv,
24
+ createSameOriginGuard
30
25
  } = require('./services/network-access');
31
26
  const { createApiRequestLogger } = require('./services/request-logger');
32
27
  const { inspectWebBuildState, ensureWebDistReady } = require('./services/web-build');
33
28
  const { ensureHttpsCredentials } = require('./services/https-cert');
29
+ const notificationHooks = require('../platforms/notification-hooks');
30
+ const {
31
+ prepareNativeCliLogObserver,
32
+ startNativeCliLogObserver
33
+ } = require('./services/native-log-observer');
34
34
 
35
35
  function getInquirer() {
36
36
  return require('inquirer');
@@ -46,6 +46,18 @@ function isInteractivePortConflictMode(options = {}) {
46
46
  return Boolean(process.stdin && process.stdin.isTTY && process.stdout && process.stdout.isTTY);
47
47
  }
48
48
 
49
+ function traceStartupMemory(stage) {
50
+ if (process.env.CC_TOOL_MEMORY_TRACE !== '1') return;
51
+ const usage = process.memoryUsage();
52
+ console.log(`[MEM] ${stage} ${JSON.stringify({
53
+ rss: usage.rss,
54
+ heapUsed: usage.heapUsed,
55
+ heapTotal: usage.heapTotal,
56
+ external: usage.external,
57
+ arrayBuffers: usage.arrayBuffers
58
+ })}`);
59
+ }
60
+
49
61
  function printPortConflictHelp(port) {
50
62
  console.log(chalk.yellow('\n[TIP] 解决方案:'));
51
63
  console.log(chalk.gray(' 1. 运行 ctx 命令,选择"配置端口"修改端口'));
@@ -67,6 +79,9 @@ function printPortToolIssue(issue = getPortToolIssue()) {
67
79
  async function startServer(port, host = '127.0.0.1', options = {}) {
68
80
  ensureStorageDirMigrated();
69
81
  const config = loadConfig();
82
+ if (typeof persistOmpAgentPath === 'function') {
83
+ persistOmpAgentPath();
84
+ }
70
85
  // 使用配置的端口,如果没有传入参数
71
86
  if (!port) {
72
87
  port = config.ports?.webUI || 19999;
@@ -182,62 +197,41 @@ async function startServer(port, host = '127.0.0.1', options = {}) {
182
197
  }));
183
198
 
184
199
  }
200
+ app.use(['/api/skills', '/api/project-config', '/api/mcp', '/api/config-registry'], createSameOriginGuard({
201
+ enabled: true,
202
+ message: 'Skill/MCP/project configuration requires same-origin access.'
203
+ }));
185
204
 
186
- // API Routes
187
- app.use('/api/projects', require('./api/projects')(config));
188
- app.use('/api/sessions', require('./api/sessions')(config));
189
-
190
- // Codex API Routes
191
- app.use('/api/codex/projects', require('./api/codex-projects')(config));
192
- app.use('/api/codex/sessions', require('./api/codex-sessions')(config));
193
- app.use('/api/codex/channels', require('./api/codex-channels')(config));
194
-
195
- // Gemini API Routes
196
- app.use('/api/gemini/projects', require('./api/gemini-projects')(config));
197
- app.use('/api/gemini/sessions', require('./api/gemini-sessions')(config));
198
- app.use('/api/gemini/channels', require('./api/gemini-channels')(config));
199
- app.use('/api/gemini/proxy', require('./api/gemini-proxy'));
200
-
201
- // OpenCode API Routes
202
- app.use('/api/opencode/projects', require('./api/opencode-projects')(config));
203
- app.use('/api/opencode/sessions', require('./api/opencode-sessions')(config));
204
- app.use('/api/opencode/channels', require('./api/opencode-channels')(config));
205
- app.use('/api/opencode/proxy', require('./api/opencode-proxy'));
206
- app.use('/api/opencode/statistics', require('./api/opencode-statistics'));
207
-
208
- // OMP API Routes
209
- app.use('/api/omp/projects', require('./api/omp-projects')(config));
210
- app.use('/api/omp/sessions', require('./api/omp-sessions')(config));
211
- app.use('/api/omp/channels', require('./api/omp-channels')(config));
212
- app.use('/api/omp/proxy', require('./api/omp-proxy'));
213
- app.use('/api/omp/statistics', require('./api/omp-statistics'));
214
- app.use('/api/omp/config', require('./api/omp-config'));
205
+ // Keep the legacy /api/hooks endpoint ahead of Claude's rootAlias /hooks route.
206
+ app.use('/api/hooks', require('./api/hooks'));
207
+
208
+ const { getPlatformContext } = require('./platform-context');
209
+ const platformContext = getPlatformContext({ dependencies: { config } });
210
+ app.use('/api/platforms', require('./api/platforms')({
211
+ ...platformContext,
212
+ config
213
+ }));
214
+ app.use('/api', require('./api/platform-routes')({
215
+ ...platformContext,
216
+ config
217
+ }));
215
218
 
216
219
  app.use('/api/aliases', require('./api/aliases')());
217
220
  app.use('/api/favorites', require('./api/favorites'));
218
221
  app.use('/api/ui-config', require('./api/ui-config'));
219
222
  app.use('/api/channel-balances', require('./api/channel-balances'));
220
223
  app.use('/api/security', require('./api/security'));
221
- app.use('/api/channels', require('./api/channels'));
222
- app.use('/api/proxy', require('./api/proxy'));
223
- app.use('/api/codex/proxy', require('./api/codex-proxy'));
224
224
  app.use('/api/settings', require('./api/settings'));
225
225
  app.use('/api/config', require('./api/config'));
226
226
  app.use('/api/convert', require('./api/convert'));
227
227
  app.use('/api/statistics', require('./api/statistics'));
228
- app.use('/api/claude/statistics', require('./api/claude-statistics'));
229
- app.use('/api/codex/statistics', require('./api/codex-statistics'));
230
- app.use('/api/gemini/statistics', require('./api/gemini-statistics'));
231
228
  app.use('/api/pm2-autostart', require('./api/pm2-autostart')());
232
229
  app.use('/api/dashboard', require('./api/dashboard'));
233
230
  app.use('/api/mcp', require('./api/mcp'));
234
231
  app.use('/api/prompts', require('./api/prompts'));
235
232
  app.use('/api/env', require('./api/env'));
236
233
  app.use('/api/skills', require('./api/skills'));
237
- const claudeHooks = require('./api/claude-hooks');
238
- const notificationHooks = require('./services/notification-hooks');
239
- app.use('/api/claude/hooks', claudeHooks);
240
- app.use('/api/hooks', require('./api/hooks'));
234
+ app.use('/api/project-config', require('./api/project-config'));
241
235
 
242
236
  // 初始化 Claude hooks 默认配置(自动开启任务完成通知)
243
237
  notificationHooks.initDefaultHooks();
@@ -256,7 +250,6 @@ async function startServer(port, host = '127.0.0.1', options = {}) {
256
250
 
257
251
  // 配置导出/导入 API
258
252
  app.use('/api/config-export', require('./api/config-export'));
259
- app.use('/api/oauth-credentials', require('./api/oauth-credentials'));
260
253
 
261
254
  // 配置同步 API
262
255
  app.use('/api/config-sync', require('./api/config-sync'));
@@ -264,8 +257,6 @@ async function startServer(port, host = '127.0.0.1', options = {}) {
264
257
  // 配置注册表 API (集中管理 skills/commands/agents/plugins 的启用/禁用)
265
258
  app.use('/api/config-registry', require('./api/config-registry'));
266
259
 
267
- // 健康检查 API
268
- app.use('/api/health-check', require('./api/health-check')(config));
269
260
 
270
261
  // Serve static files in production
271
262
  const distPath = path.join(__dirname, '../../dist/web');
@@ -322,6 +313,14 @@ async function startServer(port, host = '127.0.0.1', options = {}) {
322
313
 
323
314
  // 附加 WebSocket 服务器到同一个端口
324
315
  attachWebSocketServer(server, { host });
316
+ const nativeCliLogs = config.nativeCliLogs || {};
317
+ const nativeLogOptions = {
318
+ enabled: nativeCliLogs.enabled !== false,
319
+ intervalSeconds: Number.isInteger(nativeCliLogs.intervalSeconds) ? nativeCliLogs.intervalSeconds : 5
320
+ };
321
+ traceStartupMemory('http-ready');
322
+ prepareNativeCliLogObserver(nativeLogOptions);
323
+ traceStartupMemory('log-baseline-ready');
325
324
  console.log(` ${wsProtocol}://localhost:${port}/ws\n`);
326
325
 
327
326
  if (useHttps) {
@@ -330,188 +329,140 @@ async function startServer(port, host = '127.0.0.1', options = {}) {
330
329
 
331
330
  if (host === '0.0.0.0') {
332
331
  if (allowRemoteMutation) {
333
- console.log(chalk.yellow(' [WARN] LAN 远程写操作已启用(默认行为)'));
334
- console.log(chalk.gray(' 如需禁止,请设置 CC_TOOL_ALLOW_REMOTE_WRITE=false'));
332
+ console.log(chalk.yellow(' [WARN] LAN 远程写操作已启用(显式授权)'));
333
+ console.log(chalk.gray(' 默认远程写操作关闭;如需关闭显式授权,请移除 CC_TOOL_ALLOW_REMOTE_WRITE=true'));
335
334
  } else {
336
- console.log(chalk.yellow(' [LOCK] 已启用 LAN 安全保护:远程写操作被 CC_TOOL_ALLOW_REMOTE_WRITE=false 禁止'));
335
+ console.log(chalk.yellow(' [LOCK] 已启用 LAN 安全保护:远程写操作默认关闭'));
337
336
  }
338
337
  }
339
338
  // 自动恢复代理状态
340
- autoRestoreProxies();
341
-
342
- // 延迟执行健康检查,避免阻塞启动
343
- setTimeout(() => performStartupHealthCheck(), 2000);
344
-
345
- return server;
339
+ try {
340
+ await autoRestoreProxies({ ...platformContext, config });
341
+ traceStartupMemory('proxy-restore-complete');
342
+ await performStartupHealthCheck({ config });
343
+ traceStartupMemory('health-check-complete');
344
+ return server;
345
+ } finally {
346
+ try {
347
+ startNativeCliLogObserver({ pollImmediately: true });
348
+ } catch (error) {
349
+ console.error(chalk.yellow(`[WARN] 原生日志轮询启动失败: ${error.message}`));
350
+ }
351
+ traceStartupMemory('native-log-poll-started');
352
+ }
346
353
  }
347
354
 
348
355
  // 自动恢复代理状态
349
- function autoRestoreProxies() {
350
- const config = loadConfig();
351
- const fs = require('fs');
352
-
353
- // 检查 Claude 代理状态文件
354
- const claudeActiveFile = PATHS.activeChannel.claude;
355
- if (fs.existsSync(claudeActiveFile)) {
356
- console.log(chalk.cyan('\n[SYNC] 检测到 Claude 代理状态文件,正在自动启动...'));
357
- const proxyPort = config.ports?.proxy || 20088;
358
- startProxyServer(proxyPort)
359
- .then(() => {
360
- console.log(chalk.green(`[OK] Claude 代理已自动启动,端口: ${proxyPort}`));
361
- })
362
- .catch((err) => {
363
- console.error(chalk.red(`[ERROR] Claude 代理启动失败: ${err.message}`));
364
- });
365
- }
366
-
367
- // 检查 Codex 代理状态文件
368
- const codexActiveFile = PATHS.activeChannel.codex;
369
- if (fs.existsSync(codexActiveFile)) {
370
- console.log(chalk.cyan('\n[SYNC] 检测到 Codex 代理状态文件,正在自动启动...'));
371
- const codexProxyPort = config.ports?.codexProxy || 20089;
372
- startCodexProxyServer(codexProxyPort)
373
- .then((result) => {
374
- const port = result?.port || codexProxyPort;
375
- console.log(chalk.green(`[OK] Codex 代理已自动启动,端口: ${port}`));
376
-
377
- // 重启后重新写入 cc-proxy 配置与环境变量,避免缺少 provider/env 导致报错
378
- try {
379
- const cfgResult = setCodexProxyConfig(port);
380
- if (cfgResult?.success) {
381
- console.log(chalk.gray(' 已同步 codex config.toml 与 CC_PROXY_KEY'));
382
- }
383
- } catch (err) {
384
- console.error(chalk.red(`[ERROR] Codex 代理配置同步失败: ${err.message}`));
385
- }
386
- })
387
- .catch((err) => {
388
- console.error(chalk.red(`[ERROR] Codex 代理启动失败: ${err.message}`));
389
- });
390
- }
391
-
392
- // 检查 Gemini 代理状态文件
393
- const geminiActiveFile = PATHS.activeChannel.gemini;
394
- if (fs.existsSync(geminiActiveFile)) {
395
- console.log(chalk.cyan('\n[SYNC] 检测到 Gemini 代理状态文件,正在自动启动...'));
396
- const geminiProxyPort = config.ports?.geminiProxy || 20090;
397
- startGeminiProxyServer(geminiProxyPort)
398
- .then((result) => {
399
- if (result.success) {
400
- console.log(chalk.green(`[OK] Gemini 代理已自动启动,端口: ${result.port}`));
401
- } else {
402
- console.error(chalk.red(`[ERROR] Gemini 代理启动失败: ${result.error || 'Unknown error'}`));
403
- }
404
- })
405
- .catch((err) => {
406
- console.error(chalk.red(`[ERROR] Gemini 代理启动失败: ${err.message}`));
407
- });
408
- } else {
409
- console.log(chalk.gray('\n[TIP] 提示: 如需使用 Gemini 代理,请在前端界面激活 Gemini 渠道'));
410
- }
411
-
412
- // 检查 OpenCode 代理状态文件
413
- const opencodeActiveFile = PATHS.activeChannel.opencode;
414
- if (fs.existsSync(opencodeActiveFile)) {
415
- console.log(chalk.cyan('\n[SYNC] 检测到 OpenCode 代理状态文件,正在自动启动...'));
416
- const opencodeProxyPort = config.ports?.opencodeProxy || 20091;
417
- startOpenCodeProxyServer(opencodeProxyPort)
418
- .then(async (result) => {
419
- if (result.success) {
420
- console.log(chalk.green(`[OK] OpenCode 代理已自动启动,端口: ${result.port}`));
421
- try {
422
- const { getEnabledChannels: getEnabledOpenCodeChannels } = require('./services/opencode-channels');
423
- const enabledChs = getEnabledOpenCodeChannels();
424
- const allModels = [];
425
- const seen = new Set();
426
- enabledChs.forEach((ch) => {
427
- [ch.model, ch.speedTestModel].forEach((m) => {
428
- if (typeof m === 'string' && m.trim() && !seen.has(m.trim().toLowerCase())) {
429
- seen.add(m.trim().toLowerCase());
430
- allModels.push(m.trim());
431
- }
432
- });
433
- });
434
- const detectedModels = await collectProxyModelList(enabledChs, { useCacheOnly: true });
435
- if (Array.isArray(detectedModels)) {
436
- detectedModels.forEach((m) => {
437
- if (typeof m === 'string' && m.trim() && !seen.has(m.trim().toLowerCase())) {
438
- seen.add(m.trim().toLowerCase());
439
- allModels.push(m.trim());
440
- }
441
- });
442
- }
443
- const firstChannel = enabledChs[0];
444
- const activeModel = firstChannel && (firstChannel.model || firstChannel.speedTestModel) || null;
445
- const cfgResult = setOpenCodeProxyConfig(result.port, { model: activeModel, models: allModels });
446
- if (cfgResult?.success) {
447
- console.log(chalk.gray(' 已同步 OpenCode 配置文件'));
448
- }
449
- } catch (err) {
450
- console.error(chalk.red(`[ERROR] OpenCode 代理配置同步失败: ${err.message}`));
451
- }
452
- } else {
453
- console.error(chalk.red(`[ERROR] OpenCode 代理启动失败: ${result.error || 'Unknown error'}`));
454
- }
455
- })
456
- .catch((err) => {
457
- console.error(chalk.red(`[ERROR] OpenCode 代理启动失败: ${err.message}`));
458
- });
459
- }
460
-
461
- // 检查 OMP 受管 provider 配置状态文件
462
- const ompActiveFile = PATHS.activeChannel.omp;
463
- if (fs.existsSync(ompActiveFile)) {
464
- console.log(chalk.cyan('\n[SYNC] 检测到 OMP 受管渠道状态文件,正在自动恢复...'));
465
- startOmpProxyServer({ preserveStartTime: true })
466
- .then((result) => {
467
- if (result.success) {
468
- console.log(chalk.green(`[OK] OMP 动态网关已自动恢复: http://127.0.0.1:${result.port}`));
469
- } else {
470
- console.error(chalk.red(`[ERROR] OMP 动态网关恢复失败: ${result.error || 'Unknown error'}`));
471
- }
472
- })
473
- .catch((err) => {
474
- console.error(chalk.red(`[ERROR] OMP 动态网关恢复失败: ${err.message}`));
475
- });
356
+ async function autoRestoreProxies({ registry, runtime, config, fsImpl = require('fs') } = {}) {
357
+ const resolvedRegistry = registry || require('../platforms/runtime').getPlatformRegistry();
358
+ const resolvedRuntime = runtime || require('../platforms/runtime').getPlatformRuntime();
359
+ const resolvedConfig = config || loadConfig();
360
+ const platforms = typeof resolvedRegistry.list === 'function'
361
+ ? resolvedRegistry.list({ enabledOnly: true })
362
+ : [];
363
+
364
+ const summary = { attempted: 0, restored: 0, failed: 0 };
365
+ for (const platform of platforms) {
366
+ const key = platform && platform.key;
367
+ const markerPath = key && getPlatformStatePath('activeChannel', key);
368
+ if (!key || !markerPath || !fsImpl.existsSync(markerPath)) continue;
369
+
370
+ let driver;
371
+ try {
372
+ driver = resolvedRuntime.getDriver(key, 'proxy');
373
+ } catch (error) {
374
+ console.error(chalk.red(`[ERROR] ${platform.label || key} 代理恢复失败: ${error.message}`));
375
+ continue;
376
+ }
377
+ if (!driver || typeof driver.restoreOnBoot !== 'function') continue;
378
+ summary.attempted += 1;
379
+
380
+ const serviceLabel = platform.proxyLabels?.serviceLabel || `${platform.label || key} 代理服务`;
381
+ console.log(chalk.cyan(`\n[SYNC] 检测到 ${serviceLabel} 状态文件,正在自动恢复...`));
382
+ try {
383
+ const result = await driver.restoreOnBoot({ config: resolvedConfig });
384
+ if (!result || result.status === 'ok') {
385
+ const port = result?.port;
386
+ const suffix = port
387
+ ? platform.proxyMode === 'managed'
388
+ ? `: http://127.0.0.1:${port}`
389
+ : `,端口: ${port}`
390
+ : '';
391
+ const action = platform.proxyMode === 'managed' ? '动态网关已自动恢复' : '代理已自动启动';
392
+ console.log(chalk.green(`[OK] ${platform.label || key} ${action}${suffix}`));
393
+ summary.restored += 1;
394
+ } else {
395
+ summary.failed += 1;
396
+ console.error(chalk.red(`[ERROR] ${platform.label || key} 代理恢复失败: ${result.error || result.status}`));
397
+ }
398
+ } catch (error) {
399
+ summary.failed += 1;
400
+ console.error(chalk.red(`[ERROR] ${platform.label || key} 代理恢复失败: ${error.message}`));
401
+ }
476
402
  }
403
+ return summary;
477
404
  }
478
405
 
479
406
  // 启动时执行健康检查
480
- async function performStartupHealthCheck() {
481
- const { healthCheckAllProjects } = require('./services/health-check');
482
- const { getProjects } = require('./services/sessions');
483
-
407
+ async function performStartupHealthCheck({ config = loadConfig() } = {}) {
484
408
  try {
485
409
  console.log(chalk.cyan('\n[SEARCH] 正在进行启动健康检查...'));
486
410
 
487
- // 获取所有项目
488
- const config = loadConfig();
489
- const projects = await getProjects(config);
490
-
491
- if (projects.length === 0) {
492
- console.log(chalk.gray(' 未发现项目,跳过健康检查'));
493
- return;
411
+ const { getPlatformCatalog } = require('./services/platform-catalog');
412
+ const catalog = getPlatformCatalog();
413
+ const platforms = catalog.list({ capability: 'health' });
414
+ const summary = { total: 0, created: 0, errors: 0, healthy: 0 };
415
+ if (platforms.length === 0) {
416
+ console.log(chalk.gray(' 未配置健康检查能力,跳过健康检查'));
417
+ return { success: true, summary };
494
418
  }
495
419
 
496
- // 检查并创建缺失的目录
497
- const healthResult = healthCheckAllProjects(projects);
498
-
499
- if (healthResult.summary.created > 0) {
500
- console.log(chalk.green(` [v] 已为 ${healthResult.summary.created} 个项目创建 .claude/sessions 目录`));
420
+ for (const platform of platforms) {
421
+ try {
422
+ const driver = catalog.driver(platform.key, 'health');
423
+ if (!driver || typeof driver.healthCheck !== 'function') continue;
424
+ const result = await driver.healthCheck({ config, detail: false });
425
+ if (result?.success === false) {
426
+ summary.errors += 1;
427
+ console.error(chalk.yellow(` [!] ${platform.label || platform.key} 健康检查失败: ${result.error || 'unknown error'}`));
428
+ continue;
429
+ }
430
+ const platformSummary = result?.summary || {};
431
+ summary.total += Number(platformSummary.total || 0);
432
+ summary.created += Number(platformSummary.created || 0);
433
+ summary.errors += Number(platformSummary.errors || 0);
434
+ summary.healthy += Number(platformSummary.healthy || 0);
435
+ } catch (error) {
436
+ summary.errors += 1;
437
+ console.error(chalk.yellow(` [!] ${platform.label || platform.key} 健康检查失败: ${error.message}`));
438
+ }
501
439
  }
502
440
 
503
- if (healthResult.summary.errors > 0) {
504
- console.log(chalk.yellow(` [!] ${healthResult.summary.errors} 个项目检查失败`));
441
+ if (summary.total === 0 && summary.errors === 0) {
442
+ console.log(chalk.gray(' 没有可执行的健康检查'));
443
+ return { success: true, summary };
505
444
  }
506
445
 
507
- if (healthResult.summary.created === 0 && healthResult.summary.errors === 0) {
508
- console.log(chalk.green(` [v] 所有 ${healthResult.summary.healthy} 个项目状态正常`));
509
- }
446
+ const { created, errors, healthy } = summary;
510
447
 
448
+ if (created > 0) {
449
+ console.log(chalk.green(` [v] 已为 ${created} 个项目创建缺失的配置目录`));
450
+ }
451
+ if (errors > 0) {
452
+ console.log(chalk.yellow(` [!] ${errors} 个项目检查失败`));
453
+ }
454
+ if (created === 0 && errors === 0) {
455
+ console.log(chalk.green(` [v] 所有 ${healthy} 个项目状态正常`));
456
+ }
511
457
  console.log('');
458
+ return { success: true, summary };
512
459
  } catch (err) {
513
460
  console.error(chalk.red(' [x] 健康检查失败:'), err.message);
461
+ return { success: false, error: err.message, summary: { total: 0, created: 0, errors: 1, healthy: 0 } };
514
462
  }
515
463
  }
516
464
 
517
- module.exports = { startServer };
465
+ module.exports = {
466
+ startServer,
467
+ _test: { autoRestoreProxies, performStartupHealthCheck, traceStartupMemory }
468
+ };