coding-tool-x 3.8.4 → 3.8.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (434) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +86 -15
  3. package/dist/web/assets/AgentsDrawer-Db9wAyaZ.js +1 -0
  4. package/dist/web/assets/AgentsDrawer-Pw2k-ZAa.css +1 -0
  5. package/dist/web/assets/Analytics-C4gMzv7g.js +1 -0
  6. package/dist/web/assets/Analytics-CyHhR126.css +1 -0
  7. package/dist/web/assets/AssetPathField-Bd_nMFu3.js +1 -0
  8. package/dist/web/assets/ChatHistoryDrawer-B2qBQs9L.css +1 -0
  9. package/dist/web/assets/ChatHistoryDrawer-CwzJHyNP.js +1 -0
  10. package/dist/web/assets/CollapseItem-CTCfSiue.js +1 -0
  11. package/dist/web/assets/CommandsDrawer-BZJxlPvV.css +1 -0
  12. package/dist/web/assets/CommandsDrawer-vDghB1U9.js +1 -0
  13. package/dist/web/assets/ConfigExportDrawer-C-XDvlHc.js +1 -0
  14. package/dist/web/assets/ConfigExportDrawer-dwruRKWr.css +1 -0
  15. package/dist/web/assets/ConfigTemplates-CYY8XRED.js +1 -0
  16. package/dist/web/assets/ConfigTemplatesDrawer-BfDi8yO3.css +1 -0
  17. package/dist/web/assets/ConfigTemplatesDrawer-CbsJp76X.js +1 -0
  18. package/dist/web/assets/ConfigTemplatesPanel-CWyPtk67.css +1 -0
  19. package/dist/web/assets/ConfigTemplatesPanel-liMSTh7s.js +1 -0
  20. package/dist/web/assets/Divider-D28g1gwj.js +1 -0
  21. package/dist/web/assets/DrawerContent-ChXgZ1Xm.js +1 -0
  22. package/dist/web/assets/FavoritesDrawer-CG9eI9Ay.css +1 -0
  23. package/dist/web/assets/FavoritesDrawer-D6QSiYaL.js +1 -0
  24. package/dist/web/assets/GatewayConvertDrawer-CuNlf7AC.css +1 -0
  25. package/dist/web/assets/GatewayConvertDrawer-RvET6iNG.js +1 -0
  26. package/dist/web/assets/Home-CSuFuJ9Y.css +1 -0
  27. package/dist/web/assets/Home-P8eseZcf.js +1 -0
  28. package/dist/web/assets/MarkdownEditor-CYmLGYju.js +1 -0
  29. package/dist/web/assets/MarkdownEditor-qfVgSre_.css +1 -0
  30. package/dist/web/assets/McpDrawer-C0dnsZhQ.css +1 -0
  31. package/dist/web/assets/McpDrawer-Ch3bkKIq.js +1 -0
  32. package/dist/web/assets/McpFormDrawer-BRbDcawn.css +1 -0
  33. package/dist/web/assets/McpFormDrawer-BcTlgSpN.js +1 -0
  34. package/dist/web/assets/PluginManager-Dh2o0xkZ.js +1 -0
  35. package/dist/web/assets/PluginsDrawer-DM3QjprE.css +1 -0
  36. package/dist/web/assets/PluginsDrawer-deKQ5u3I.js +1 -0
  37. package/dist/web/assets/PluginsPanel-BofsgQgj.js +1 -0
  38. package/dist/web/assets/PluginsPanel-D6ZqITcR.css +1 -0
  39. package/dist/web/assets/ProjectList-677RLZ8O.css +1 -0
  40. package/dist/web/assets/ProjectList-DhSWJHTA.js +1 -0
  41. package/dist/web/assets/PromptsDrawer-BIgiYCEQ.css +1 -0
  42. package/dist/web/assets/PromptsDrawer-Cfb2FeeU.js +1 -0
  43. package/dist/web/assets/RecentSessionsDrawer-3eXamJhr.js +1 -0
  44. package/dist/web/assets/RecentSessionsDrawer-CKFsOYDM.css +1 -0
  45. package/dist/web/assets/SessionCard-CW0eHURI.js +1 -0
  46. package/dist/web/assets/SessionCard-aM2NHu6i.css +1 -0
  47. package/dist/web/assets/SessionList-BpYRz1cX.css +1 -0
  48. package/dist/web/assets/SessionList-DQfyDGWU.js +1 -0
  49. package/dist/web/assets/SettingsDrawer-Dw5NUgHC.css +1 -0
  50. package/dist/web/assets/SettingsDrawer-u_vwUuPS.js +1 -0
  51. package/dist/web/assets/SkillManager-C0GCT2Kb.css +1 -0
  52. package/dist/web/assets/SkillManager-DHMprJLA.js +1 -0
  53. package/dist/web/assets/SkillsDrawer-BCNme-aJ.css +1 -0
  54. package/dist/web/assets/SkillsDrawer-CkF3BpyC.js +1 -0
  55. package/dist/web/assets/SkillsPanel-B3Nm5j32.css +1 -0
  56. package/dist/web/assets/SkillsPanel-DWE4pFGx.js +1 -0
  57. package/dist/web/assets/SpeedTestDrawer-BgfG1Ked.js +1 -0
  58. package/dist/web/assets/SpeedTestDrawer-Dc0WToJ_.css +1 -0
  59. package/dist/web/assets/Upload-CNwaGOws.js +1 -0
  60. package/dist/web/assets/WorkspaceDrawer-Br5CeCzL.js +1 -0
  61. package/dist/web/assets/WorkspaceDrawer-CGnMu9p1.css +1 -0
  62. package/dist/web/assets/WorkspaceManager-B2O8-qIw.js +1 -0
  63. package/dist/web/assets/WorkspacePanel-5oDKit3c.js +1 -0
  64. package/dist/web/assets/WorkspacePanel-D0-zHTHP.css +1 -0
  65. package/dist/web/assets/_plugin-vue_export-helper-BeLZJZij.js +1 -0
  66. package/dist/web/assets/client-UDwXBP9i.js +1 -0
  67. package/dist/web/assets/clipboard-B46yyoz_.js +1 -0
  68. package/dist/web/assets/headers-cf-QapUq.js +1 -0
  69. package/dist/web/assets/icons-B2Vg-IOC.js +1 -0
  70. package/dist/web/assets/index-BMAUPq5K.js +2 -0
  71. package/dist/web/assets/index-Bi1MQHYX.css +1 -0
  72. package/dist/web/assets/light-Cp0t7v32.js +1 -0
  73. package/dist/web/assets/markdown-u082bbtj.js +1 -0
  74. package/dist/web/assets/platforms-CBkB45JP.js +1 -0
  75. package/dist/web/assets/platforms-FTpjxAT6.js +1 -0
  76. package/dist/web/assets/project-config-B2baPzYL.js +1 -0
  77. package/dist/web/assets/sessions-Cmy31slD.js +1 -0
  78. package/dist/web/assets/sessions-yC_oVVh8.js +1 -0
  79. package/dist/web/assets/skill-repo-input-B_fkGlUK.js +1 -0
  80. package/dist/web/assets/use-lock-html-scroll-DXIqmUW4.js +1 -0
  81. package/dist/web/assets/use-message-DoLM8C1C.js +1 -0
  82. package/dist/web/assets/useResponsiveDrawer-jbmqZuJs.js +1 -0
  83. package/dist/web/assets/vendors-D0c_0KLP.js +1 -0
  84. package/dist/web/assets/vue-vendor-BqZbbXHA.js +1 -0
  85. package/dist/web/assets/workspaces-mjyPVGS8.js +1 -0
  86. package/dist/web/assets/xml-CgY3RMlX.js +1 -0
  87. package/dist/web/index.html +12 -8
  88. package/docs/native-log-memory-2026-09-16.md +43 -0
  89. package/docs/superpowers/plans/2026-08-26-configuration-driven-cli-platform.md +1578 -0
  90. package/docs/superpowers/plans/2026-08-30-project-scoped-config-control.md +1260 -0
  91. package/docs/superpowers/plans/2026-09-01-omp-skill-refresh-boundary.md +213 -0
  92. package/docs/superpowers/plans/2026-09-01-realtime-logs-panel.md +125 -0
  93. package/docs/superpowers/plans/2026-09-02-config-driven-platform-boundary.md +86 -0
  94. package/docs/superpowers/plans/2026-09-02-platform-implementation-drivers.md +508 -0
  95. package/docs/superpowers/plans/2026-09-03-cli-platform-correctness-boundary.md +760 -0
  96. package/docs/superpowers/plans/2026-09-03-dependency-security-remediation.md +133 -0
  97. package/docs/superpowers/plans/2026-09-04-model-catalog-pricing-sync.md +1078 -0
  98. package/docs/superpowers/plans/2026-09-05-codex-project-scan-500.md +128 -0
  99. package/docs/superpowers/plans/2026-09-06-project-skill-path-fix.md +97 -0
  100. package/docs/superpowers/specs/2026-08-26-configuration-driven-cli-platform-design.md +408 -0
  101. package/docs/superpowers/specs/2026-08-30-project-scoped-config-control-design.md +467 -0
  102. package/docs/superpowers/specs/2026-09-01-claude-session-content-repair-design.md +111 -0
  103. package/docs/superpowers/specs/2026-09-01-omp-skill-refresh-boundary-design.md +83 -0
  104. package/docs/superpowers/specs/2026-09-01-realtime-logs-panel-design.md +27 -0
  105. package/docs/superpowers/specs/2026-09-02-config-driven-platform-boundary-design.md +64 -0
  106. package/docs/superpowers/specs/2026-09-02-platform-implementation-driver-boundary-design.md +123 -0
  107. package/docs/superpowers/specs/2026-09-03-cli-platform-correctness-boundary-design.md +249 -0
  108. package/docs/superpowers/specs/2026-09-03-dependency-security-remediation-design.md +87 -0
  109. package/docs/superpowers/specs/2026-09-04-model-catalog-pricing-sync-design.md +301 -0
  110. package/docs/superpowers/specs/2026-09-05-codex-project-scan-500-design.md +24 -0
  111. package/package.json +8 -4
  112. package/src/commands/channels.js +88 -191
  113. package/src/commands/cli-type.js +19 -11
  114. package/src/commands/daemon.js +104 -32
  115. package/src/commands/doctor.js +16 -9
  116. package/src/commands/export-config.js +23 -5
  117. package/src/commands/list.js +130 -52
  118. package/src/commands/logs.js +44 -27
  119. package/src/commands/platform-command-registry.js +78 -0
  120. package/src/commands/plugin.js +11 -5
  121. package/src/commands/port-config.js +34 -52
  122. package/src/commands/proxy-control.js +207 -145
  123. package/src/commands/proxy.js +2 -2
  124. package/src/commands/resume.js +87 -91
  125. package/src/commands/search.js +137 -67
  126. package/src/commands/stats.js +30 -22
  127. package/src/commands/switch.js +1 -1
  128. package/src/commands/toggle-proxy.js +94 -111
  129. package/src/commands/ui.js +67 -6
  130. package/src/commands/workspace.js +90 -8
  131. package/src/config/default.js +6 -53
  132. package/src/config/loader.js +61 -24
  133. package/src/config/model-metadata.js +49 -59
  134. package/src/config/model-metadata.json +4095 -99
  135. package/src/config/paths.js +181 -8
  136. package/src/index.js +134 -71
  137. package/src/platforms/access.js +196 -0
  138. package/src/{server/services → platforms}/agents-service.js +354 -197
  139. package/src/platforms/channel-auth-registry.js +165 -0
  140. package/src/platforms/channel-auth-service.js +107 -0
  141. package/src/{server/services → platforms}/commands-service.js +195 -115
  142. package/src/platforms/driver-registry.js +59 -0
  143. package/src/platforms/drivers/claude/api-operations.js +62 -0
  144. package/src/{server/services/channels.js → platforms/drivers/claude/channels-implementation.js} +131 -33
  145. package/src/platforms/drivers/claude/channels.js +39 -0
  146. package/src/{server/services/config-sync-service.js → platforms/drivers/claude/config-sync.js} +29 -5
  147. package/src/{server/services → platforms/drivers/claude}/health-check.js +4 -4
  148. package/src/platforms/drivers/claude/health.js +22 -0
  149. package/src/platforms/drivers/claude/mcp.js +9 -0
  150. package/src/{server/services/settings-manager.js → platforms/drivers/claude/native-config-implementation.js} +13 -4
  151. package/src/platforms/drivers/claude/native-config.js +46 -0
  152. package/src/platforms/drivers/claude/native-logs.js +72 -0
  153. package/src/{server/services/claude-openai-gateway.js → platforms/drivers/claude/openai-gateway.js} +8 -65
  154. package/src/platforms/drivers/claude/project-config.js +21 -0
  155. package/src/platforms/drivers/claude/projects.js +62 -0
  156. package/src/platforms/drivers/claude/prompts.js +9 -0
  157. package/src/{server/proxy-server.js → platforms/drivers/claude/proxy-implementation.js} +41 -278
  158. package/src/platforms/drivers/claude/proxy.js +16 -0
  159. package/src/platforms/drivers/claude/resource-sync.js +9 -0
  160. package/src/platforms/drivers/claude/session-history-adapter.js +313 -0
  161. package/src/{server/services/sessions.js → platforms/drivers/claude/sessions-implementation.js} +145 -35
  162. package/src/platforms/drivers/claude/sessions.js +76 -0
  163. package/src/{server/services/claude-statistics-service.js → platforms/drivers/claude/statistics-implementation.js} +2 -2
  164. package/src/platforms/drivers/claude/statistics.js +16 -0
  165. package/src/{server/services/claude-wire.js → platforms/drivers/claude/wire.js} +1 -1
  166. package/src/platforms/drivers/codex/api-operations.js +9 -0
  167. package/src/{server/services/codex-channels.js → platforms/drivers/codex/channels-implementation.js} +149 -14
  168. package/src/platforms/drivers/codex/channels.js +45 -0
  169. package/src/{server/services/codex-config.js → platforms/drivers/codex/config.js} +12 -2
  170. package/src/{server/services/codex-env-manager.js → platforms/drivers/codex/env-manager.js} +1 -1
  171. package/src/platforms/drivers/codex/mcp.js +9 -0
  172. package/src/{server/services/codex-settings-manager.js → platforms/drivers/codex/native-config-implementation.js} +15 -6
  173. package/src/platforms/drivers/codex/native-config.js +47 -0
  174. package/src/platforms/drivers/codex/native-logs.js +166 -0
  175. package/src/platforms/drivers/codex/project-config.js +21 -0
  176. package/src/platforms/drivers/codex/projects.js +26 -0
  177. package/src/platforms/drivers/codex/prompts.js +9 -0
  178. package/src/{server/codex-proxy-server.js → platforms/drivers/codex/proxy-implementation.js} +30 -233
  179. package/src/platforms/drivers/codex/proxy.js +16 -0
  180. package/src/platforms/drivers/codex/resource-sync.js +9 -0
  181. package/src/{server/services/session-history-adapters/codex.js → platforms/drivers/codex/session-history-adapter.js} +37 -23
  182. package/src/{server/services/codex-sessions.js → platforms/drivers/codex/sessions-implementation.js} +81 -75
  183. package/src/platforms/drivers/codex/sessions.js +36 -0
  184. package/src/{server/services/codex-statistics-service.js → platforms/drivers/codex/statistics-implementation.js} +2 -2
  185. package/src/platforms/drivers/codex/statistics.js +16 -0
  186. package/src/platforms/drivers/gemini/api-operations.js +9 -0
  187. package/src/{server/services/gemini-channels.js → platforms/drivers/gemini/channels-implementation.js} +202 -37
  188. package/src/platforms/drivers/gemini/channels.js +39 -0
  189. package/src/{server/services/gemini-config.js → platforms/drivers/gemini/config.js} +12 -2
  190. package/src/platforms/drivers/gemini/mcp.js +9 -0
  191. package/src/{server/services/gemini-settings-manager.js → platforms/drivers/gemini/native-config-implementation.js} +14 -5
  192. package/src/platforms/drivers/gemini/native-config.js +39 -0
  193. package/src/platforms/drivers/gemini/native-logs.js +180 -0
  194. package/src/platforms/drivers/gemini/project-config.js +21 -0
  195. package/src/platforms/drivers/gemini/projects.js +26 -0
  196. package/src/platforms/drivers/gemini/prompts.js +9 -0
  197. package/src/{server/gemini-proxy-server.js → platforms/drivers/gemini/proxy-implementation.js} +36 -267
  198. package/src/platforms/drivers/gemini/proxy.js +16 -0
  199. package/src/platforms/drivers/gemini/resource-sync.js +9 -0
  200. package/src/{server/services/session-history-adapters/gemini.js → platforms/drivers/gemini/session-history-adapter.js} +70 -18
  201. package/src/{server/services/gemini-sessions.js → platforms/drivers/gemini/sessions-implementation.js} +53 -37
  202. package/src/platforms/drivers/gemini/sessions.js +47 -0
  203. package/src/{server/services/gemini-statistics-service.js → platforms/drivers/gemini/statistics-implementation.js} +2 -2
  204. package/src/platforms/drivers/gemini/statistics.js +16 -0
  205. package/src/{server/services/gemini-wire.js → platforms/drivers/gemini/wire.js} +1 -1
  206. package/src/platforms/drivers/generic-filesystem.js +166 -0
  207. package/src/platforms/drivers/generic-jsonl.js +141 -0
  208. package/src/platforms/drivers/generic-mcp.js +44 -0
  209. package/src/platforms/drivers/generic-openai-compatible.js +45 -0
  210. package/src/platforms/drivers/generic-prompt.js +41 -0
  211. package/src/platforms/drivers/legacy.js +677 -0
  212. package/src/platforms/drivers/native-config-snapshot.js +125 -0
  213. package/src/platforms/drivers/native-log-utils.js +1292 -0
  214. package/src/platforms/drivers/notification-hooks.js +578 -0
  215. package/src/{server/api/omp-config.js → platforms/drivers/omp/api-operations.js} +52 -64
  216. package/src/{server/services/omp-auth-provider-worker.js → platforms/drivers/omp/auth-provider-worker.js} +1 -1
  217. package/src/{server/services/omp-auth-providers.js → platforms/drivers/omp/auth-providers.js} +1 -1
  218. package/src/{server/services/omp-channels.js → platforms/drivers/omp/channels-implementation.js} +166 -39
  219. package/src/platforms/drivers/omp/channels.js +58 -0
  220. package/src/{server/services/omp-config.js → platforms/drivers/omp/config.js} +53 -13
  221. package/src/{server/services/omp-gateway-routing.js → platforms/drivers/omp/gateway-routing.js} +14 -7
  222. package/src/{server/services/omp-gateway.js → platforms/drivers/omp/gateway.js} +4 -262
  223. package/src/platforms/drivers/omp/mcp.js +9 -0
  224. package/src/{server/services/omp-settings-manager.js → platforms/drivers/omp/native-config-implementation.js} +75 -10
  225. package/src/platforms/drivers/omp/native-config.js +46 -0
  226. package/src/platforms/drivers/omp/native-logs.js +52 -0
  227. package/src/{server/services/omp-native-plugin-adapter.js → platforms/drivers/omp/native-plugin-adapter.js} +91 -4
  228. package/src/platforms/drivers/omp/project-config.js +29 -0
  229. package/src/platforms/drivers/omp/projects.js +20 -0
  230. package/src/platforms/drivers/omp/prompts.js +9 -0
  231. package/src/{server/omp-proxy-server.js → platforms/drivers/omp/proxy-implementation.js} +6 -15
  232. package/src/platforms/drivers/omp/proxy.js +16 -0
  233. package/src/platforms/drivers/omp/resource-sync.js +9 -0
  234. package/src/{server/services/session-history-adapters/omp.js → platforms/drivers/omp/session-history-adapter.js} +42 -18
  235. package/src/{server/services/omp-sessions.js → platforms/drivers/omp/sessions-implementation.js} +130 -94
  236. package/src/platforms/drivers/omp/sessions.js +36 -0
  237. package/src/{server/services/omp-skill-discovery.js → platforms/drivers/omp/skill-discovery.js} +159 -32
  238. package/src/{server/services/omp-skill-settings-service.js → platforms/drivers/omp/skill-settings.js} +1 -1
  239. package/src/{server/services/omp-statistics-service.js → platforms/drivers/omp/statistics-implementation.js} +3 -3
  240. package/src/platforms/drivers/omp/statistics.js +16 -0
  241. package/src/platforms/drivers/opencode/api-operations.js +9 -0
  242. package/src/{server/services/opencode-channels.js → platforms/drivers/opencode/channels-implementation.js} +28 -9
  243. package/src/platforms/drivers/opencode/channels.js +40 -0
  244. package/src/platforms/drivers/opencode/conversion.js +41 -0
  245. package/src/{server/services/opencode-gateway-converter.js → platforms/drivers/opencode/gateway-converter.js} +1 -1
  246. package/src/platforms/drivers/opencode/mcp.js +9 -0
  247. package/src/{server/services/opencode-settings-manager.js → platforms/drivers/opencode/native-config-implementation.js} +17 -6
  248. package/src/platforms/drivers/opencode/native-config.js +26 -0
  249. package/src/platforms/drivers/opencode/native-logs.js +133 -0
  250. package/src/platforms/drivers/opencode/project-config.js +98 -0
  251. package/src/platforms/drivers/opencode/projects.js +20 -0
  252. package/src/platforms/drivers/opencode/prompts.js +9 -0
  253. package/src/{server/opencode-proxy-server.js → platforms/drivers/opencode/proxy-implementation.js} +27 -362
  254. package/src/platforms/drivers/opencode/proxy.js +16 -0
  255. package/src/platforms/drivers/opencode/resource-sync.js +9 -0
  256. package/src/{server/services/opencode-sessions.js → platforms/drivers/opencode/sessions-implementation.js} +211 -64
  257. package/src/platforms/drivers/opencode/sessions.js +48 -0
  258. package/src/{server/services/opencode-statistics-service.js → platforms/drivers/opencode/statistics-implementation.js} +2 -2
  259. package/src/platforms/drivers/opencode/statistics.js +16 -0
  260. package/src/platforms/drivers/secure-file-driver.js +272 -0
  261. package/src/platforms/drivers/unsupported.js +7 -0
  262. package/src/platforms/manifest-schema.js +398 -0
  263. package/src/platforms/manifests/claude.json +489 -0
  264. package/src/platforms/manifests/codex.json +448 -0
  265. package/src/platforms/manifests/gemini.json +447 -0
  266. package/src/platforms/manifests/omp.json +496 -0
  267. package/src/platforms/manifests/opencode.json +422 -0
  268. package/src/{server/services → platforms}/native-oauth-adapters.js +98 -418
  269. package/src/{server/services → platforms}/notification-hooks.js +190 -1023
  270. package/src/platforms/oauth-credentials-service.js +635 -0
  271. package/src/platforms/path-resolver.js +179 -0
  272. package/src/platforms/platform-path-context.js +305 -0
  273. package/src/platforms/registry.js +248 -0
  274. package/src/platforms/remote-notification-providers.js +678 -0
  275. package/src/platforms/runtime.js +259 -0
  276. package/src/{server/services/session-history-adapters/index.js → platforms/session-history-adapters.js} +8 -1
  277. package/src/plugins/plugin-loader.js +2 -2
  278. package/src/plugins/plugin-manager.js +2 -2
  279. package/src/plugins/registry.js +27 -10
  280. package/src/reset-config.js +21 -5
  281. package/src/server/api/agents.js +46 -72
  282. package/src/server/api/commands.js +16 -6
  283. package/src/server/api/config-registry.js +206 -59
  284. package/src/server/api/config-sync.js +3 -2
  285. package/src/server/api/config-templates.js +20 -4
  286. package/src/server/api/config.js +111 -102
  287. package/src/server/api/convert.js +54 -178
  288. package/src/server/api/dashboard.js +168 -102
  289. package/src/server/api/hooks.js +1 -1
  290. package/src/server/api/mcp.js +224 -84
  291. package/src/server/api/platform-api-config.js +102 -0
  292. package/src/server/api/platform-route-factory.js +215 -0
  293. package/src/server/api/platform-routes.js +26 -0
  294. package/src/server/api/platforms.js +110 -0
  295. package/src/server/api/plugins.js +57 -57
  296. package/src/server/api/project-config.js +168 -0
  297. package/src/server/api/prompts.js +58 -45
  298. package/src/server/api/settings.js +55 -8
  299. package/src/server/api/skills.js +262 -133
  300. package/src/server/api/ui-config.js +2 -2
  301. package/src/server/api/validation-errors.js +18 -4
  302. package/src/server/api/workspaces.js +47 -12
  303. package/src/server/index.js +164 -213
  304. package/src/server/platform-context.js +81 -0
  305. package/src/server/services/channel-balance.js +133 -24
  306. package/src/server/services/channel-health.js +21 -6
  307. package/src/server/services/channel-scheduler.js +29 -39
  308. package/src/server/services/channel-sync-utils.js +23 -13
  309. package/src/server/services/config-export-service.js +350 -319
  310. package/src/server/services/config-registry-service.js +280 -54
  311. package/src/server/services/config-sync-manager.js +172 -944
  312. package/src/server/services/config-templates-service.js +419 -353
  313. package/src/server/services/control-manifest-store.js +206 -0
  314. package/src/server/services/dashboard-snapshot-worker.js +656 -100
  315. package/src/server/services/effective-control-service.js +557 -0
  316. package/src/server/services/local-resource-index.js +314 -0
  317. package/src/server/services/mcp-service.js +607 -977
  318. package/src/server/services/model-detector.js +15 -10
  319. package/src/server/services/native-log-channel-resolver.js +214 -0
  320. package/src/server/services/native-log-observer.js +291 -0
  321. package/src/server/services/network-access.js +12 -2
  322. package/src/server/services/platform-catalog.js +82 -0
  323. package/src/server/services/platform-resolution.js +32 -29
  324. package/src/server/services/plugins-service.js +376 -122
  325. package/src/server/services/project-config-service.js +550 -0
  326. package/src/server/services/project-path-validation.js +31 -0
  327. package/src/server/services/prompts-service.js +267 -233
  328. package/src/server/services/proxy-runtime.js +7 -4
  329. package/src/server/services/remote-credential-cache.js +43 -0
  330. package/src/server/services/repo-scanner-base.js +182 -75
  331. package/src/server/services/request-logger.js +7 -4
  332. package/src/server/services/session-history-index.js +876 -214
  333. package/src/server/services/session-history-worker.js +228 -33
  334. package/src/server/services/session-snapshot-worker.js +124 -34
  335. package/src/server/services/session-snapshots.js +4 -2
  336. package/src/server/services/skill-artifact-store.js +355 -0
  337. package/src/server/services/skill-format-adapters.js +65 -0
  338. package/src/server/services/skill-projection-service.js +323 -0
  339. package/src/server/services/skill-refresh-task-service.js +275 -0
  340. package/src/server/services/skill-service.js +2020 -677
  341. package/src/server/services/snapshot-cache.js +30 -8
  342. package/src/server/services/speed-test.js +8 -8
  343. package/src/server/services/sqlite-connection.js +19 -3
  344. package/src/server/services/statistics-service.js +240 -57
  345. package/src/server/services/ui-config.js +107 -76
  346. package/src/server/services/usage-log-utils.js +98 -0
  347. package/src/server/services/web-build.js +1 -1
  348. package/src/server/services/workspace-service.js +59 -29
  349. package/src/server/utils/pricing.js +10 -19
  350. package/src/server/websocket-server.js +153 -28
  351. package/src/{server/services/base → shared}/base-channel-service.js +156 -28
  352. package/src/shared/capability-driver.js +104 -0
  353. package/src/shared/channel-auth-policy.js +27 -0
  354. package/src/shared/channel-driver.js +161 -0
  355. package/src/shared/driver-factories/api.js +180 -0
  356. package/src/shared/driver-factories/mcp.js +244 -0
  357. package/src/shared/driver-factories/projects.js +121 -0
  358. package/src/shared/driver-factories/prompts.js +58 -0
  359. package/src/shared/driver-factories/resource-sync.js +375 -0
  360. package/src/shared/driver-result.js +78 -0
  361. package/src/shared/mcp-format.js +189 -0
  362. package/src/shared/platforms.js +104 -167
  363. package/src/shared/project-config.js +512 -0
  364. package/src/shared/proxy-driver.js +57 -0
  365. package/src/{server/services/base → shared}/response-usage-parser.js +8 -4
  366. package/src/ui/menu.js +91 -67
  367. package/src/utils/port-helper.js +35 -19
  368. package/src/utils/session.js +1 -1
  369. package/dist/web/assets/Analytics-COEUXmd4.js +0 -25
  370. package/dist/web/assets/Analytics-RNn1BUbG.css +0 -1
  371. package/dist/web/assets/ConfigTemplates-DLLkS53H.js +0 -1
  372. package/dist/web/assets/Home-DCEyLzwX.js +0 -1
  373. package/dist/web/assets/Home-DYVy4cGZ.css +0 -1
  374. package/dist/web/assets/PluginManager-Oe1ZHoMO.js +0 -1
  375. package/dist/web/assets/ProjectList-CWjuAFof.js +0 -1
  376. package/dist/web/assets/ProjectList-VeMNXfgO.css +0 -1
  377. package/dist/web/assets/SessionList-C4chCPxK.css +0 -1
  378. package/dist/web/assets/SessionList-v3ehGg9U.js +0 -1
  379. package/dist/web/assets/SkillManager-BsX8Qpj0.css +0 -1
  380. package/dist/web/assets/SkillManager-DUF_qP5S.js +0 -1
  381. package/dist/web/assets/WorkspaceManager-CjAWBosO.js +0 -1
  382. package/dist/web/assets/icons-nGdSKQn2.js +0 -1
  383. package/dist/web/assets/index-D5hyHPj7.js +0 -2
  384. package/dist/web/assets/index-bJRG3RXH.css +0 -1
  385. package/dist/web/assets/markdown-BNJfEgCA.js +0 -1
  386. package/dist/web/assets/markdown-BfC0goYb.css +0 -10
  387. package/dist/web/assets/naive-ui-BALVpMgd.js +0 -1
  388. package/dist/web/assets/vendors-DQA4Hw9v.js +0 -7
  389. package/dist/web/assets/vue-vendor-B5RqjEjQ.js +0 -45
  390. package/src/config/model-pricing.js +0 -35
  391. package/src/server/api/channels.js +0 -439
  392. package/src/server/api/claude-hooks.js +0 -56
  393. package/src/server/api/claude-statistics.js +0 -57
  394. package/src/server/api/codex-channels.js +0 -491
  395. package/src/server/api/codex-projects.js +0 -117
  396. package/src/server/api/codex-proxy.js +0 -293
  397. package/src/server/api/codex-sessions.js +0 -652
  398. package/src/server/api/codex-statistics.js +0 -57
  399. package/src/server/api/gemini-channels.js +0 -392
  400. package/src/server/api/gemini-projects.js +0 -103
  401. package/src/server/api/gemini-proxy.js +0 -231
  402. package/src/server/api/gemini-sessions.js +0 -550
  403. package/src/server/api/gemini-statistics.js +0 -57
  404. package/src/server/api/health-check.js +0 -31
  405. package/src/server/api/oauth-credentials.js +0 -198
  406. package/src/server/api/omp-channels.js +0 -575
  407. package/src/server/api/omp-projects.js +0 -88
  408. package/src/server/api/omp-proxy.js +0 -125
  409. package/src/server/api/omp-sessions.js +0 -386
  410. package/src/server/api/omp-statistics.js +0 -40
  411. package/src/server/api/opencode-channels.js +0 -471
  412. package/src/server/api/opencode-projects.js +0 -110
  413. package/src/server/api/opencode-proxy.js +0 -354
  414. package/src/server/api/opencode-sessions.js +0 -466
  415. package/src/server/api/opencode-statistics.js +0 -57
  416. package/src/server/api/projects.js +0 -120
  417. package/src/server/api/proxy.js +0 -330
  418. package/src/server/api/sessions.js +0 -810
  419. package/src/server/services/oauth-credentials-service.js +0 -945
  420. package/src/server/services/omp-session-log-observer.js +0 -138
  421. package/src/server/services/opencode-gateway-adapters.js +0 -43
  422. package/src/server/services/project-snapshots.js +0 -59
  423. package/src/server/services/proxy-log-helper.js +0 -298
  424. package/src/server/services/session-cache.js +0 -127
  425. package/src/server/services/session-converter.js +0 -582
  426. package/src/server/services/session-history-adapters/claude.js +0 -235
  427. /package/src/{server/services/claude-token-recovery.js → platforms/drivers/claude/token-recovery.js} +0 -0
  428. /package/src/{server/services/codex-parser.js → platforms/drivers/codex/parser.js} +0 -0
  429. /package/src/{server/services/codex-speed-test-template.json → platforms/drivers/codex/speed-test-template.json} +0 -0
  430. /package/src/{server/services/codex-wire.js → platforms/drivers/codex/wire.js} +0 -0
  431. /package/src/{server/services/omp-auth-gateway-client.js → platforms/drivers/omp/auth-gateway-client.js} +0 -0
  432. /package/src/{server/services/opencode-normalization.js → platforms/drivers/opencode/normalization.js} +0 -0
  433. /package/src/{server/services → shared}/config-artifact-paths.js +0 -0
  434. /package/src/{server/services/base → shared}/proxy-utils.js +0 -0
@@ -0,0 +1,81 @@
1
+ 'use strict';
2
+
3
+ const { createPlatformRegistry } = require('../platforms/registry');
4
+ const { getDriverRegistry } = require('../platforms/driver-registry');
5
+ const { createSessionHistoryIndex } = require('./services/session-history-index');
6
+
7
+ let defaultContext;
8
+
9
+ function getPlatformRuntimeModule() {
10
+ return require('../platforms/runtime');
11
+ }
12
+
13
+ function createLazyDependency(getValue) {
14
+ return new Proxy({}, {
15
+ get(_target, property) {
16
+ const value = getValue();
17
+ return typeof value?.[property] === 'function' ? value[property].bind(value) : value?.[property];
18
+ }
19
+ });
20
+ }
21
+
22
+ function getDefaultPlatformBindings() {
23
+ const runtimeModule = getPlatformRuntimeModule();
24
+ const registry = typeof runtimeModule.getPlatformRegistry === 'function'
25
+ ? runtimeModule.getPlatformRegistry()
26
+ : createPlatformRegistry();
27
+ const runtime = typeof runtimeModule.getPlatformRuntime === 'function'
28
+ ? runtimeModule.getPlatformRuntime()
29
+ : null;
30
+ return { registry, runtime };
31
+ }
32
+
33
+ function createPlatformContext({ registry, runtime, dependencies = {}, driverRegistry } = {}) {
34
+ const runtimeModule = getPlatformRuntimeModule();
35
+ const resolvedRegistry = registry || createPlatformRegistry();
36
+ let sessionHistoryIndex = dependencies.sessionHistoryIndex || null;
37
+ const indexDependency = sessionHistoryIndex || createLazyDependency(() => sessionHistoryIndex);
38
+ const resolvedDependencies = {
39
+ ...(typeof runtimeModule.getDefaultDependencies === 'function'
40
+ ? runtimeModule.getDefaultDependencies()
41
+ : {}),
42
+ ...dependencies,
43
+ sessionHistoryIndex: indexDependency
44
+ };
45
+ if (typeof runtimeModule.configureDefaultDependencies === 'function') {
46
+ runtimeModule.configureDefaultDependencies(resolvedDependencies);
47
+ }
48
+ const resolvedRuntime = runtime || (typeof runtimeModule.createPlatformRuntime === 'function'
49
+ ? runtimeModule.createPlatformRuntime({
50
+ registry: resolvedRegistry,
51
+ driverRegistry: driverRegistry || getDriverRegistry(),
52
+ dependencies: resolvedDependencies
53
+ })
54
+ : { getDriver: () => null });
55
+ if (!sessionHistoryIndex) {
56
+ sessionHistoryIndex = createSessionHistoryIndex({ runtime: resolvedRuntime });
57
+ }
58
+ return Object.freeze({
59
+ registry: resolvedRegistry,
60
+ runtime: resolvedRuntime,
61
+ dependencies: Object.freeze({ ...resolvedDependencies, sessionHistoryIndex })
62
+ });
63
+ }
64
+
65
+ function getPlatformContext(options = {}) {
66
+ const hasOptions = Object.keys(options).length > 0;
67
+ const bindings = hasOptions ? null : getDefaultPlatformBindings();
68
+ if (!defaultContext
69
+ || hasOptions
70
+ || (bindings.runtime && defaultContext.runtime !== bindings.runtime)
71
+ || (bindings.registry && defaultContext.registry !== bindings.registry)) {
72
+ defaultContext = createPlatformContext(hasOptions ? options : bindings);
73
+ }
74
+ return defaultContext;
75
+ }
76
+
77
+ function resetPlatformContext() {
78
+ defaultContext = undefined;
79
+ }
80
+
81
+ module.exports = { createPlatformContext, getPlatformContext, resetPlatformContext };
@@ -6,6 +6,7 @@ const path = require('path');
6
6
  const { URL } = require('url');
7
7
  const { PATHS } = require('../../config/paths');
8
8
  const { loadUIConfig } = require('./ui-config');
9
+ const { getPlatformContext } = require('../platform-context');
9
10
  const {
10
11
  getSnapshot,
11
12
  refreshSnapshot,
@@ -18,7 +19,6 @@ const MAX_RESPONSE_BYTES = 1024 * 1024;
18
19
  const QUOTA_UNIT = 500000;
19
20
  const VELOERA_QUOTA_UNIT = 1000000;
20
21
  const NEWCLI_QUOTA_UNIT = 1000000;
21
- const VALID_SOURCES = new Set(['claude', 'codex', 'gemini', 'opencode', 'omp']);
22
22
  const GATEWAY_PATH_SEGMENTS = new Set(['openai', 'anthropic', 'claude', 'gemini', 'codex']);
23
23
  const HUB_PLATFORMS = new Set(['new-api', 'one-api', 'one-hub', 'done-hub', 'veloera', 'anyrouter', 'aihubmix']);
24
24
  const UNSUPPORTED_BALANCE_PLATFORMS = new Set(['dashscope', 'modelscope']);
@@ -120,6 +120,22 @@ function resolveBalanceToken(channel = {}) {
120
120
  );
121
121
  }
122
122
 
123
+ function isOAuthChannel(channel = {}) {
124
+ return channel?.authMode === 'oauth';
125
+ }
126
+
127
+ function resolveOAuthCacheIdentity(channel = {}) {
128
+ const ref = channel.authRef && typeof channel.authRef === 'object' ? channel.authRef : {};
129
+ return [
130
+ channel.oauthProviderId,
131
+ ref.credentialId,
132
+ ref.providerId,
133
+ ref.accountId,
134
+ ref.identityKey,
135
+ ref.accountEmail
136
+ ].map(value => String(value || '').trim()).filter(Boolean).join('|');
137
+ }
138
+
123
139
  function resolveBalanceUserId(channel = {}) {
124
140
  const raw = channel.balanceUserId
125
141
  ?? channel.platformUserId
@@ -250,6 +266,64 @@ function makeVisibleSnapshot(platform, input, { stale = false, updatedAt = nowIs
250
266
  return snapshot;
251
267
  }
252
268
 
269
+ function formatPercent(value) {
270
+ const number = parseFiniteNumber(value);
271
+ if (number == null) return '';
272
+ return Number.isInteger(number) ? String(number) : number.toFixed(1).replace(/\.0$/, '');
273
+ }
274
+
275
+ function normalizeOAuthWindows(quota = {}) {
276
+ const source = quota && typeof quota === 'object' ? quota : {};
277
+ const candidates = [];
278
+ if (source.primary) candidates.push({ id: 'primary', ...source.primary });
279
+ if (source.secondary) candidates.push({ id: 'secondary', ...source.secondary });
280
+ if (Array.isArray(source.windows)) candidates.push(...source.windows);
281
+ if (Array.isArray(source.additional)) candidates.push(...source.additional);
282
+
283
+ const seen = new Set();
284
+ return candidates.map((window, index) => {
285
+ const remainingPercent = parseFiniteNumber(window?.remainingPercent);
286
+ const usedPercent = parseFiniteNumber(window?.usedPercent);
287
+ if (remainingPercent == null && usedPercent == null) return null;
288
+ const id = String(window?.id || `window-${index}`);
289
+ if (seen.has(id)) return null;
290
+ seen.add(id);
291
+ return {
292
+ id,
293
+ label: String(window?.label || id),
294
+ remainingPercent: remainingPercent != null
295
+ ? Math.max(0, Math.min(100, remainingPercent))
296
+ : Math.max(0, Math.min(100, 100 - usedPercent)),
297
+ usedPercent: usedPercent != null
298
+ ? Math.max(0, Math.min(100, usedPercent))
299
+ : Math.max(0, Math.min(100, 100 - remainingPercent)),
300
+ resetsAt: window?.resetsAt || null,
301
+ scope: window?.scope || null
302
+ };
303
+ }).filter(Boolean);
304
+ }
305
+
306
+ function buildOAuthBalanceSnapshot(platform, quota, { stale = false, updatedAt = nowIso() } = {}) {
307
+ const windows = normalizeOAuthWindows(quota);
308
+ if (!windows.length) return makeHiddenSnapshot(platform, { stale, updatedAt });
309
+
310
+ const primaryWindows = windows.filter(window => window.id === 'primary' || window.id === 'secondary');
311
+ const displayWindows = primaryWindows.length ? primaryWindows : windows;
312
+ const label = displayWindows
313
+ .map(window => `${window.label} 剩余 ${formatPercent(window.remainingPercent)}%`)
314
+ .join(' · ');
315
+
316
+ return {
317
+ visible: true,
318
+ kind: 'oauth-quota',
319
+ platform,
320
+ label,
321
+ windows,
322
+ updatedAt,
323
+ stale
324
+ };
325
+ }
326
+
253
327
  function normalizeBaseUrl(value) {
254
328
  const raw = String(value || '').trim();
255
329
  if (!raw) return '';
@@ -1483,14 +1557,16 @@ async function probeBalanceFromBase(baseUrl, channel, token) {
1483
1557
 
1484
1558
  function buildCacheKey(source, channel) {
1485
1559
  const token = resolveBalanceToken(channel);
1486
- const tokenHash = token
1487
- ? crypto.createHash('sha256').update(token).digest('hex').slice(0, 16)
1488
- : 'no-token';
1560
+ const identity = isOAuthChannel(channel)
1561
+ ? `oauth:${resolveOAuthCacheIdentity(channel) || channel.id || channel.name || 'unknown'}`
1562
+ : token
1563
+ ? crypto.createHash('sha256').update(token).digest('hex').slice(0, 16)
1564
+ : 'no-token';
1489
1565
  return [
1490
1566
  source,
1491
1567
  channel.id || channel.name || 'unknown',
1492
1568
  normalizeBaseUrl(channel.baseUrl || ''),
1493
- tokenHash
1569
+ identity
1494
1570
  ].join(':');
1495
1571
  }
1496
1572
 
@@ -1512,6 +1588,44 @@ async function refreshChannelBalanceSnapshot(source, channel, options = {}) {
1512
1588
  return cached.snapshot;
1513
1589
  }
1514
1590
 
1591
+ if (isOAuthChannel(channel)) {
1592
+ try {
1593
+ const { fetchChannelAuthQuota } = require('../../platforms/channel-auth-service');
1594
+ const result = await fetchChannelAuthQuota(source, channel.id, { refresh: force });
1595
+ const snapshot = buildOAuthBalanceSnapshot(source, result?.quota, {
1596
+ updatedAt: result?.checkedAt || nowIso(currentTime)
1597
+ });
1598
+ if (snapshot.visible) {
1599
+ balanceCache.set(key, {
1600
+ snapshot,
1601
+ expiresAt: currentTime + CACHE_TTL_MS
1602
+ });
1603
+ return snapshot;
1604
+ }
1605
+ } catch {
1606
+ // OAuth providers are best-effort; preserve a previous successful quota below.
1607
+ }
1608
+
1609
+ if (cached?.snapshot?.visible) {
1610
+ const staleSnapshot = {
1611
+ ...cached.snapshot,
1612
+ stale: true
1613
+ };
1614
+ balanceCache.set(key, {
1615
+ snapshot: staleSnapshot,
1616
+ expiresAt: currentTime + CACHE_TTL_MS
1617
+ });
1618
+ return staleSnapshot;
1619
+ }
1620
+
1621
+ const hiddenSnapshot = makeHiddenSnapshot(source, { updatedAt: nowIso(currentTime) });
1622
+ balanceCache.set(key, {
1623
+ snapshot: hiddenSnapshot,
1624
+ expiresAt: currentTime + CACHE_TTL_MS
1625
+ });
1626
+ return hiddenSnapshot;
1627
+ }
1628
+
1515
1629
  const bases = buildBaseCandidates(channel.baseUrl);
1516
1630
  if (!bases.length || !resolveBalanceToken(channel)) {
1517
1631
  return makeHiddenSnapshot(null, { updatedAt: nowIso(currentTime) });
@@ -1622,24 +1736,16 @@ function clearChannelBalanceCache(source, channel) {
1622
1736
  writePersistedStrategyCache();
1623
1737
  }
1624
1738
  }
1625
-
1626
1739
  function getChannelsForSource(source) {
1627
- if (source === 'claude') {
1628
- return require('./channels').getAllChannels() || [];
1629
- }
1630
- if (source === 'codex') {
1631
- return require('./codex-channels').getChannels().channels || [];
1632
- }
1633
- if (source === 'gemini') {
1634
- return require('./gemini-channels').getChannels().channels || [];
1635
- }
1636
- if (source === 'opencode') {
1637
- return require('./opencode-channels').getChannels().channels || [];
1638
- }
1639
- if (source === 'omp') {
1640
- return require('./omp-channels').getChannels().channels || [];
1740
+ try {
1741
+ const driver = getPlatformContext().runtime.getDriver(source, 'channels');
1742
+ if (!driver) return [];
1743
+ const value = typeof driver.list === 'function' ? driver.list() : driver.getChannels?.();
1744
+ if (value?.status === 'ok') return value.data?.channels || value.data || [];
1745
+ return Array.isArray(value) ? value : value?.channels || [];
1746
+ } catch (_) {
1747
+ return [];
1641
1748
  }
1642
- return [];
1643
1749
  }
1644
1750
 
1645
1751
  function getEnabledBalanceChannels(source) {
@@ -1656,7 +1762,7 @@ function isBalanceDisplayEnabled() {
1656
1762
 
1657
1763
  function validateSource(source) {
1658
1764
  const normalized = String(source || '').trim().toLowerCase();
1659
- if (!VALID_SOURCES.has(normalized)) {
1765
+ if (!getPlatformContext().registry.resolve(normalized)) {
1660
1766
  throw new Error('Invalid channel balance source');
1661
1767
  }
1662
1768
  return normalized;
@@ -1737,8 +1843,7 @@ async function refreshChannelBalance(source, channelId) {
1737
1843
 
1738
1844
  const entry = await refreshSnapshot(
1739
1845
  buildBalanceSnapshotKey(normalizedSource, channel),
1740
- () => refreshChannelBalanceSnapshot(normalizedSource, channel, { force: true }),
1741
- { bypassInflight: true }
1846
+ () => refreshChannelBalanceSnapshot(normalizedSource, channel, { force: true })
1742
1847
  );
1743
1848
  const balance = entry.value;
1744
1849
  return {
@@ -1775,6 +1880,10 @@ module.exports = {
1775
1880
  buildSub2ApiUsageSnapshot,
1776
1881
  resolveStatusPlatform,
1777
1882
  resolveBalanceToken,
1883
+ isOAuthChannel,
1884
+ resolveOAuthCacheIdentity,
1885
+ normalizeOAuthWindows,
1886
+ buildOAuthBalanceSnapshot,
1778
1887
  resolveBalanceUserId,
1779
1888
  buildCookieCandidates,
1780
1889
  build88CodeApiBaseCandidates,
@@ -16,9 +16,9 @@ const healthConfig = {
16
16
  // 渠道健康状态
17
17
  const channelHealth = new Map(); // `${source}:${channelId}` → health info
18
18
 
19
- // 冻结回调(用于通知调度器解绑会话)
20
19
  let onChannelFrozenCallback = null;
21
20
  let channelListProvider = null;
21
+ let channelHealthPolicyProvider = null;
22
22
 
23
23
  /**
24
24
  * 设置渠道冻结时的回调
@@ -31,6 +31,22 @@ function setChannelListProvider(provider) {
31
31
  channelListProvider = typeof provider === 'function' ? provider : null;
32
32
  }
33
33
 
34
+
35
+ function setChannelHealthPolicyProvider(provider) {
36
+ channelHealthPolicyProvider = typeof provider === 'function' ? provider : null;
37
+ }
38
+
39
+ function getHealthPolicy(source = 'claude') {
40
+ try {
41
+ const policy = channelHealthPolicyProvider?.(source);
42
+ if (policy && typeof policy === 'object') {
43
+ return { freezeOnFailure: policy.freezeOnFailure !== false };
44
+ }
45
+ } catch (_error) {
46
+ // Fall through to the safe default freeze policy.
47
+ }
48
+ return { freezeOnFailure: true };
49
+ }
34
50
  /**
35
51
  * 初始化渠道健康信息
36
52
  */
@@ -128,9 +144,8 @@ function recordSuccess(channelId, source = 'claude') {
128
144
  health.nextFreezeTime = healthConfig.initialFreezeTime; // 重置冻结时间
129
145
  console.log(`[ChannelHealth] Channel ${channelId} recovered and marked as healthy`);
130
146
  }
131
- }
132
147
  }
133
-
148
+ }
134
149
  /**
135
150
  * 记录失败请求
136
151
  */
@@ -142,12 +157,11 @@ function recordFailure(channelId, source = 'claude', error) {
142
157
  health.consecutiveFailures++;
143
158
  health.consecutiveSuccesses = 0;
144
159
  health.lastCheckTime = now;
145
-
146
- // omp 动态切换按请求即时回退其他渠道,失败渠道无需冻结(冻结会将其排除在后续分配之外)
147
- if (source === 'omp') {
160
+ if (!getHealthPolicy(source).freezeOnFailure) {
148
161
  return;
149
162
  }
150
163
 
164
+
151
165
  // 如果当前是健康状态或检测中状态,检查是否需要冻结
152
166
  if (health.status === 'healthy' || health.status === 'checking') {
153
167
  if (health.consecutiveFailures >= healthConfig.failureThreshold) {
@@ -291,5 +305,6 @@ module.exports = {
291
305
  resetChannelHealth,
292
306
  setOnChannelFrozen,
293
307
  setChannelListProvider,
308
+ setChannelHealthPolicyProvider,
294
309
  healthConfig,
295
310
  };
@@ -1,29 +1,25 @@
1
- const { getAllChannels } = require('./channels');
2
- const { getChannels: getCodexChannels } = require('./codex-channels');
3
- const { getChannels: getGeminiChannels } = require('./gemini-channels');
4
- const { getChannels: getOpenCodeChannels } = require('./opencode-channels');
5
- const { getChannels: getOmpChannels } = require('./omp-channels');
6
- const { isChannelAvailable, getChannelHealthStatus, setOnChannelFrozen, setChannelListProvider } = require('./channel-health');
1
+ const { getPlatformContext } = require('../platform-context');
2
+ const { isChannelAvailable, getChannelHealthStatus, setOnChannelFrozen, setChannelListProvider, setChannelHealthPolicyProvider } = require('./channel-health');
7
3
 
8
- const channelProviders = {
9
- claude: () => getAllChannels(),
10
- codex: () => {
11
- const data = getCodexChannels();
12
- return Array.isArray(data?.channels) ? data.channels : [];
13
- },
14
- gemini: () => {
15
- const data = getGeminiChannels();
16
- return Array.isArray(data?.channels) ? data.channels : [];
17
- },
18
- opencode: () => {
19
- const data = getOpenCodeChannels();
20
- return Array.isArray(data?.channels) ? data.channels : [];
21
- },
22
- omp: () => {
23
- const data = getOmpChannels();
24
- return Array.isArray(data?.channels) ? data.channels : [];
4
+ function readChannels(source = 'claude') {
5
+ try {
6
+ const driver = getPlatformContext().runtime.getDriver(source, 'channels');
7
+ if (!driver || (typeof driver.status === 'string' && driver.status !== 'ok')) {
8
+ return [];
9
+ }
10
+ const list = typeof driver.list === 'function'
11
+ ? driver.list.bind(driver)
12
+ : typeof driver.getChannels === 'function'
13
+ ? driver.getChannels.bind(driver)
14
+ : null;
15
+ if (!list) return [];
16
+ const result = list();
17
+ const payload = result?.status === 'ok' ? result.data : result;
18
+ return Array.isArray(payload) ? payload : (Array.isArray(payload?.channels) ? payload.channels : []);
19
+ } catch (_) {
20
+ return [];
25
21
  }
26
- };
22
+ }
27
23
 
28
24
  function createState() {
29
25
  return {
@@ -33,14 +29,7 @@ function createState() {
33
29
  queue: []
34
30
  };
35
31
  }
36
-
37
- const schedulerStates = {
38
- claude: createState(),
39
- codex: createState(),
40
- gemini: createState(),
41
- opencode: createState(),
42
- omp: createState()
43
- };
32
+ const schedulerStates = Object.create(null);
44
33
 
45
34
  function getState(source = 'claude') {
46
35
  if (!schedulerStates[source]) {
@@ -70,19 +59,20 @@ function unbindChannelSessions(source, channelId) {
70
59
 
71
60
  // 注册冻结回调,当渠道被冻结时解绑其会话
72
61
  setOnChannelFrozen(unbindChannelSessions);
73
- setChannelListProvider((source = 'claude') => {
74
- const provider = channelProviders[source || 'claude'];
75
- if (!provider) return [];
76
- return provider();
62
+ setChannelListProvider((source = 'claude') => readChannels(source));
63
+ setChannelHealthPolicyProvider((source = 'claude') => {
64
+ try {
65
+ return getPlatformContext().runtime.getDriver(source, 'channels')?.getHealthPolicy?.() || { freezeOnFailure: true };
66
+ } catch (_error) {
67
+ return { freezeOnFailure: true };
68
+ }
77
69
  });
78
70
 
79
71
  function refreshChannels(source = 'claude') {
80
72
  const state = getState(source);
81
- const provider = channelProviders[source];
82
- if (!provider) return;
73
+ const raw = readChannels(source);
83
74
 
84
75
  // 每次直接读取最新配置,不做缓存
85
- const raw = provider();
86
76
  state.channels = raw
87
77
  .filter(ch => ch.enabled !== false)
88
78
  .map(ch => ({
@@ -1,7 +1,7 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
3
  const crypto = require('crypto');
4
- const { PATHS, ensureStorageDirMigrated } = require('../../config/paths');
4
+ const { PATHS, ensureStorageDirMigrated, getPlatformStatePath } = require('../../config/paths');
5
5
 
6
6
  function normalizeString(value) {
7
7
  return String(value || '').trim();
@@ -74,7 +74,7 @@ function resolveApiKeyValue(value, env = process.env) {
74
74
  function readActiveChannelId(platform) {
75
75
  try {
76
76
  ensureStorageDirMigrated?.();
77
- const filePath = PATHS.activeChannel?.[platform];
77
+ const filePath = getPlatformStatePath('activeChannel', platform);
78
78
  if (!filePath || !fs.existsSync(filePath)) return null;
79
79
  const data = JSON.parse(fs.readFileSync(filePath, 'utf8'));
80
80
  return data?.activeChannelId || null;
@@ -231,12 +231,25 @@ function sanitizeChannel(channel) {
231
231
  const sanitized = {
232
232
  id: channel.id,
233
233
  name: channel.name,
234
- baseUrl: channel.baseUrl,
235
- enabled: channel.enabled !== false
234
+ baseUrl: channel.authMode === 'oauth' ? '' : channel.baseUrl,
235
+ apiKey: channel.apiKey || '',
236
+ enabled: channel.enabled !== false,
237
+ authMode: channel.authMode || 'api_key'
236
238
  };
237
239
  if (channel.providerKey) sanitized.providerKey = channel.providerKey;
238
240
  if (channel.model) sanitized.model = channel.model;
239
241
  if (channel.credentialSource) sanitized.credentialSource = channel.credentialSource;
242
+ if (channel.authMode === 'oauth') {
243
+ sanitized.authRef = {
244
+ credentialId: String(channel.authRef?.credentialId || ''),
245
+ providerId: String(channel.authRef?.providerId || ''),
246
+ accountId: String(channel.authRef?.accountId || ''),
247
+ identityKey: String(channel.authRef?.identityKey || ''),
248
+ accountEmail: String(channel.authRef?.accountEmail || '')
249
+ };
250
+ sanitized.authSource = channel.authSource || 'synced-local';
251
+ sanitized.authStatus = channel.authStatus || 'available';
252
+ }
240
253
  return sanitized;
241
254
  }
242
255
 
@@ -272,26 +285,23 @@ function upsertSyncedChannels({
272
285
  channels: [],
273
286
  warnings: []
274
287
  };
275
-
276
288
  let changed = false;
277
289
  for (const rawCandidate of candidates || []) {
278
290
  const candidate = rawCandidate || {};
279
- if (candidate.warning) {
280
- result.warnings.push(candidate.warning);
281
- }
291
+ if (candidate.warning) result.warnings.push(candidate.warning);
282
292
  if (candidate.skip) {
283
293
  result.skipped += 1;
284
294
  if (candidate.channel) result.channels.push(sanitizeChannel(candidate.channel));
285
295
  continue;
286
296
  }
287
- if (!normalizeString(candidate.baseUrl)) {
297
+ if (candidate.authMode !== 'oauth' && !normalizeString(candidate.baseUrl)) {
288
298
  result.skipped += 1;
289
299
  result.warnings.push(`${toolType}: missing baseUrl, skipped current channel sync`);
290
300
  continue;
291
301
  }
292
- if (!normalizeString(candidate.apiKey) && candidate.authMode !== 'none') {
302
+ if (!normalizeString(candidate.apiKey) && candidate.authMode !== 'oauth' && candidate.authMode !== 'none') {
293
303
  result.skipped += 1;
294
- result.warnings.push(`${toolType}: missing API key or OAuth-only config, skipped current channel sync`);
304
+ result.warnings.push(`${toolType}: missing API key, skipped current channel sync`);
295
305
  continue;
296
306
  }
297
307
 
@@ -317,7 +327,7 @@ function upsertSyncedChannels({
317
327
  const now = Date.now();
318
328
  const created = applyDefaults ? applyDefaults({
319
329
  id: candidate.id || crypto.randomUUID(),
320
- enabled: candidate.enabled !== false,
330
+ enabled: candidate.authMode === 'oauth' ? false : candidate.enabled !== false,
321
331
  weight: candidate.weight || 1,
322
332
  maxConcurrency: candidate.maxConcurrency || null,
323
333
  ...normalizedCandidate,
@@ -325,7 +335,7 @@ function upsertSyncedChannels({
325
335
  updatedAt: now
326
336
  }) : {
327
337
  id: candidate.id || crypto.randomUUID(),
328
- enabled: candidate.enabled !== false,
338
+ enabled: candidate.authMode === 'oauth' ? false : candidate.enabled !== false,
329
339
  weight: candidate.weight || 1,
330
340
  maxConcurrency: candidate.maxConcurrency || null,
331
341
  ...normalizedCandidate,