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
@@ -1,28 +1,112 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
3
  const crypto = require('crypto');
4
- const { PATHS, NATIVE_PATHS } = require('../../config/paths');
5
- const { resolveChannelWebsiteUrl } = require('../../config/channel-preset-websites');
6
- const { clearNativeOAuth } = require('./native-oauth-adapters');
7
- const { normalizeGatewaySourceType } = require('./base/proxy-utils');
4
+ const { PATHS, NATIVE_PATHS } = require('../../../config/paths');
5
+ const { resolveChannelWebsiteUrl } = require('../../../config/channel-preset-websites');
6
+ const { clearNativeOAuth, readNativeOAuth, clearGeminiChannelConfig } = require('../../native-oauth-adapters');
7
+ const { normalizeGatewaySourceType } = require('../../../shared/proxy-utils');
8
8
  const {
9
9
  createSkippedResult,
10
10
  isLocalProxyBaseUrl,
11
11
  resolveApiKeyValue,
12
12
  resolveExistingActiveChannel,
13
13
  upsertSyncedChannels
14
- } = require('./channel-sync-utils');
14
+ } = require('../../../server/services/channel-sync-utils');
15
+
16
+ let configuredNative = NATIVE_PATHS.gemini;
17
+ let configuredState = { channels: PATHS.channels?.gemini };
18
+
19
+ function configure({ pathContext } = {}) {
20
+ const custom = pathContext?.customized === true;
21
+ configuredNative = custom && pathContext.native?.env
22
+ ? { ...NATIVE_PATHS.gemini, ...pathContext.native }
23
+ : NATIVE_PATHS.gemini;
24
+ configuredState = {
25
+ channels: custom ? (pathContext.state?.channels || PATHS.channels?.gemini) : PATHS.channels?.gemini
26
+ };
27
+ require('../../native-oauth-adapters').configure?.({ pathContext });
28
+ }
15
29
 
16
30
  const GEMINI_API_FORMATS = new Set(['gemini_api', 'vertex_ai_v1']);
17
31
 
18
32
  function clearChannelBalanceCache(channel) {
19
33
  try {
20
- require('./channel-balance').clearChannelBalanceCache('gemini', channel);
34
+ require('../../../server/services/channel-balance').clearChannelBalanceCache('gemini', channel);
21
35
  } catch (_) {
22
36
  // Balance cache invalidation is best-effort and should not block channel updates.
23
37
  }
24
38
  }
25
39
 
40
+ function createGeminiOAuthError(message, code, statusCode) {
41
+ const error = new Error(message);
42
+ error.code = code;
43
+ error.statusCode = statusCode;
44
+ return error;
45
+ }
46
+
47
+ function getGeminiProxyState() {
48
+ return require('./proxy-implementation').getGeminiProxyStatus();
49
+ }
50
+
51
+ function assertGeminiOAuthIsNativeOnly() {
52
+ if (getGeminiProxyState().running) {
53
+ throw createGeminiOAuthError(
54
+ 'Gemini OAuth channels are native-only',
55
+ 'gemini_oauth_proxy_unsupported',
56
+ 409
57
+ );
58
+ }
59
+ }
60
+
61
+ function assertNativeGeminiOAuthAvailable() {
62
+ if (!readNativeOAuth('gemini')) {
63
+ throw createGeminiOAuthError(
64
+ 'Gemini native OAuth credential is unavailable',
65
+ 'gemini_oauth_credential_unavailable',
66
+ 422
67
+ );
68
+ }
69
+ }
70
+
71
+ function validateGeminiOAuthMutation(channel, operation) {
72
+ if (channel?.authMode !== 'oauth') {
73
+ return;
74
+ }
75
+ if (operation !== 'apply' && channel.enabled === false) {
76
+ return;
77
+ }
78
+ assertGeminiOAuthIsNativeOnly();
79
+ assertNativeGeminiOAuthAvailable();
80
+ }
81
+
82
+ function applyNativeGeminiOAuth(channel) {
83
+ validateGeminiOAuthMutation(channel, 'apply');
84
+ clearGeminiChannelConfig();
85
+
86
+ const settingsPath = configuredNative.settings;
87
+ const settings = readGeminiSettings();
88
+ settings.security = settings.security || {};
89
+ settings.security.auth = settings.security.auth || {};
90
+ settings.security.auth.selectedType = 'oauth-personal';
91
+ fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
92
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
93
+ console.log(`[Gemini Channels] Applied native OAuth channel ${channel.name}`);
94
+ return channel;
95
+ }
96
+
97
+ function assertGeminiEnabledChannelsAreValid(channels = []) {
98
+ const enabledChannels = channels.filter(channel => channel?.enabled !== false);
99
+ const oauthChannels = enabledChannels.filter(channel => channel?.authMode === 'oauth');
100
+ if (oauthChannels.length && enabledChannels.some(channel => channel?.authMode !== 'oauth')) {
101
+ throw createGeminiOAuthError(
102
+ 'Gemini OAuth channels cannot be mixed with API-key channels',
103
+ 'gemini_oauth_mixed_channels',
104
+ 409
105
+ );
106
+ }
107
+ }
108
+
109
+
26
110
  function normalizeGeminiApiFormat(value) {
27
111
  const normalized = String(value || '').trim().toLowerCase();
28
112
  return GEMINI_API_FORMATS.has(normalized) ? normalized : 'gemini_api';
@@ -45,16 +129,16 @@ function normalizeGeminiApiFormat(value) {
45
129
 
46
130
  // 获取 Gemini 配置目录
47
131
  function getGeminiDir() {
48
- return path.dirname(NATIVE_PATHS.gemini.env);
132
+ return configuredNative.dir || path.dirname(configuredNative.env);
49
133
  }
50
134
 
51
135
  // 获取渠道存储文件路径
52
136
  function getChannelsFilePath() {
53
- const channelsDir = path.dirname(PATHS.channels.gemini);
137
+ const channelsDir = path.dirname(configuredState.channels);
54
138
  if (!fs.existsSync(channelsDir)) {
55
139
  fs.mkdirSync(channelsDir, { recursive: true });
56
140
  }
57
- return PATHS.channels.gemini;
141
+ return configuredState.channels;
58
142
  }
59
143
 
60
144
  function readExistingGeminiEnv() {
@@ -216,10 +300,17 @@ function initializeFromEnv() {
216
300
  }
217
301
  }
218
302
 
303
+ function invalidateDashboardSource() {
304
+ try {
305
+ require('../../../server/services/snapshot-cache').invalidateDashboardSourceSnapshot('gemini');
306
+ } catch (_) {}
307
+ }
308
+
219
309
  // 保存渠道数据
220
310
  function saveChannels(data) {
221
311
  const filePath = getChannelsFilePath();
222
312
  fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf8');
313
+ invalidateDashboardSource();
223
314
  }
224
315
 
225
316
  // 获取所有渠道
@@ -230,24 +321,61 @@ function getChannels() {
230
321
  };
231
322
  }
232
323
 
324
+ function normalizeAuthConfig(extraConfig = {}, existing = null) {
325
+ const unsafeField = Object.keys(extraConfig).find(key => /token|secret|password|refresh|access/i.test(key));
326
+ if (unsafeField) {
327
+ const error = new Error('Invalid OAuth auth payload');
328
+ error.statusCode = 400;
329
+ error.code = 'invalid_auth_payload';
330
+ throw error;
331
+ }
332
+ const mode = extraConfig.authMode || existing?.authMode || 'api_key';
333
+ if (!['api_key', 'oauth', 'none'].includes(mode)) {
334
+ const error = new Error('Invalid auth mode');
335
+ error.statusCode = 400;
336
+ error.code = 'invalid_auth_payload';
337
+ throw error;
338
+ }
339
+ if (existing && extraConfig.authMode && extraConfig.authMode !== (existing.authMode || 'api_key')) {
340
+ const error = new Error('Channel authMode is immutable');
341
+ error.statusCode = 409;
342
+ error.code = 'auth_mode_immutable';
343
+ throw error;
344
+ }
345
+ if (mode === 'oauth') {
346
+ const allowed = ['credentialId', 'providerId', 'accountId', 'identityKey', 'accountEmail'];
347
+ const ref = extraConfig.authRef || existing?.authRef;
348
+ if (!ref || Object.keys(ref).some(key => !allowed.includes(key)) || !ref.credentialId) {
349
+ const error = new Error('OAuth reference unavailable');
350
+ error.statusCode = 422;
351
+ error.code = 'oauth_reference_unavailable';
352
+ throw error;
353
+ }
354
+ return { authMode: mode, authRef: Object.fromEntries(allowed.map(key => [key, String(ref[key] || '')])), authSource: 'synced-local', authStatus: existing?.authStatus || 'available' };
355
+ }
356
+ return { authMode: mode };
357
+ }
358
+
233
359
  // 添加渠道
234
360
  function createChannel(name, baseUrl, apiKey, model = 'gemini-2.5-pro', extraConfig = {}) {
235
361
  const data = loadChannels();
236
-
237
- // 检查名称是否已存在
362
+ const auth = normalizeAuthConfig(extraConfig);
238
363
  const existing = data.channels.find(c => c.name === name);
239
- if (existing) {
240
- throw new Error(`Channel name "${name}" already exists`);
364
+ if (existing) throw new Error(`Channel name "${name}" already exists`);
365
+ if (auth.authMode !== 'oauth' && auth.authMode !== 'none' && (!baseUrl || !apiKey)) {
366
+ const error = new Error('Base URL and API key are required');
367
+ error.statusCode = 400;
368
+ throw error;
241
369
  }
242
-
243
370
  const newChannel = {
244
371
  id: crypto.randomUUID(),
245
372
  name,
246
- baseUrl,
247
- apiKey,
373
+ baseUrl: auth.authMode === 'oauth' ? '' : baseUrl,
374
+ apiKey: auth.authMode === 'oauth' || auth.authMode === 'none' ? '' : apiKey,
248
375
  model,
376
+ ...auth,
249
377
  websiteUrl: extraConfig.websiteUrl || '',
250
- enabled: extraConfig.enabled !== false, // 默认启用
378
+ enabled: extraConfig.enabled !== false,
251
379
  weight: extraConfig.weight || 1,
252
380
  maxConcurrency: extraConfig.maxConcurrency || null,
253
381
  balanceToken: extraConfig.balanceToken || '',
@@ -260,17 +388,12 @@ function createChannel(name, baseUrl, apiKey, model = 'gemini-2.5-pro', extraCon
260
388
  updatedAt: Date.now()
261
389
  };
262
390
  newChannel.websiteUrl = resolveChannelWebsiteUrl('gemini', newChannel);
263
-
391
+ validateGeminiOAuthMutation(newChannel, 'create');
392
+ assertGeminiEnabledChannelsAreValid([...data.channels, newChannel]);
264
393
  data.channels.push(newChannel);
265
394
  saveChannels(data);
266
-
267
- // 仅在非动态切换模式下写入 Gemini 配置文件
268
- const { getGeminiProxyStatus } = require('../gemini-proxy-server');
269
- const proxyStatus = getGeminiProxyStatus();
270
- if (!proxyStatus.running) {
271
- writeGeminiConfigForMultiChannel(data.channels);
272
- }
273
-
395
+ const { getGeminiProxyStatus } = require('./proxy-implementation');
396
+ if (!getGeminiProxyStatus().running) writeGeminiConfigForMultiChannel(data.channels);
274
397
  return newChannel;
275
398
  }
276
399
 
@@ -284,6 +407,18 @@ function updateChannel(channelId, updates) {
284
407
  }
285
408
 
286
409
  const oldChannel = data.channels[index];
410
+ const auth = normalizeAuthConfig(updates, oldChannel);
411
+ if (auth.authMode === 'oauth' && updates.enabled !== false) {
412
+ const conflict = data.channels.find(channel => channel.id !== channelId && channel.authMode === 'oauth' && channel.enabled !== false);
413
+ if (conflict) {
414
+ const error = new Error(`OAuth channel conflicts with ${conflict.name || conflict.id}`);
415
+ error.statusCode = 409;
416
+ error.code = 'oauth_channel_conflict';
417
+ error.conflictingChannelId = conflict.id;
418
+ error.conflictingChannelName = conflict.name || conflict.id;
419
+ throw error;
420
+ }
421
+ }
287
422
 
288
423
  // 检查名称冲突
289
424
  if (updates.name && updates.name !== oldChannel.name) {
@@ -293,11 +428,13 @@ function updateChannel(channelId, updates) {
293
428
  }
294
429
  }
295
430
 
296
- const merged = { ...oldChannel, ...updates };
431
+ const merged = { ...oldChannel, ...updates, ...auth };
297
432
  const nextChannel = {
298
433
  ...merged,
299
- id: channelId, // 保持 ID 不变
300
- createdAt: oldChannel.createdAt, // 保持创建时间
434
+ id: channelId,
435
+ createdAt: oldChannel.createdAt,
436
+ baseUrl: auth.authMode === 'oauth' ? '' : (merged.baseUrl || ''),
437
+ apiKey: auth.authMode === 'oauth' || auth.authMode === 'none' ? '' : merged.apiKey,
301
438
  modelRedirects: updates.modelRedirects !== undefined ? updates.modelRedirects : (oldChannel.modelRedirects || []),
302
439
  speedTestModel: updates.speedTestModel !== undefined ? updates.speedTestModel : (oldChannel.speedTestModel || null),
303
440
  apiFormat: updates.apiFormat !== undefined ? normalizeGeminiApiFormat(updates.apiFormat) : normalizeGeminiApiFormat(oldChannel.apiFormat),
@@ -306,11 +443,13 @@ function updateChannel(channelId, updates) {
306
443
  gatewaySourceType: normalizeGatewaySourceType(merged.gatewaySourceType, 'gemini'),
307
444
  updatedAt: Date.now()
308
445
  };
309
- nextChannel.websiteUrl = resolveChannelWebsiteUrl('gemini', nextChannel);
446
+ validateGeminiOAuthMutation(nextChannel, 'update');
447
+ const nextChannels = data.channels.map((channel, currentIndex) => (
448
+ currentIndex === index ? nextChannel : channel
449
+ ));
450
+ assertGeminiEnabledChannelsAreValid(nextChannels);
310
451
  data.channels[index] = nextChannel;
311
-
312
- // Get proxy status
313
- const { getGeminiProxyStatus } = require('../gemini-proxy-server');
452
+ const { getGeminiProxyStatus } = require('./proxy-implementation');
314
453
  const proxyStatus = getGeminiProxyStatus();
315
454
  const isProxyRunning = proxyStatus.running;
316
455
 
@@ -357,6 +496,7 @@ function applyChannelToSettings(channelId, channels = null) {
357
496
  if (!channel) {
358
497
  throw new Error('Channel not found');
359
498
  }
499
+ validateGeminiOAuthMutation(channel, 'apply');
360
500
 
361
501
  const wasEnabled = channel.enabled !== false;
362
502
  // In single-channel mode, only this channel should be enabled
@@ -371,6 +511,10 @@ function applyChannelToSettings(channelId, channels = null) {
371
511
  clearChannelBalanceCache(channel);
372
512
  }
373
513
 
514
+ if (channel.authMode === 'oauth') {
515
+ return applyNativeGeminiOAuth(channel);
516
+ }
517
+
374
518
  clearNativeOAuth('gemini');
375
519
 
376
520
  const geminiDir = getGeminiDir();
@@ -422,7 +566,7 @@ async function deleteChannel(channelId) {
422
566
  saveChannels(data);
423
567
 
424
568
  // 仅在非动态切换模式下更新 Gemini 配置文件
425
- const { getGeminiProxyStatus } = require('../gemini-proxy-server');
569
+ const { getGeminiProxyStatus } = require('./proxy-implementation');
426
570
  const proxyStatus = getGeminiProxyStatus();
427
571
  if (!proxyStatus.running) {
428
572
  writeGeminiConfigForMultiChannel(data.channels);
@@ -440,8 +584,14 @@ function writeGeminiConfigForMultiChannel(allChannels) {
440
584
  }
441
585
 
442
586
  // 获取第一个启用的渠道作为默认配置
443
- const enabledChannels = allChannels.filter(c => c.enabled !== false);
444
- const defaultChannel = enabledChannels[0] || allChannels[0];
587
+ const channels = Array.isArray(allChannels) ? allChannels : [];
588
+ const enabledChannels = channels.filter(c => c.enabled !== false);
589
+ assertGeminiEnabledChannelsAreValid(channels);
590
+ const oauthChannels = enabledChannels.filter(channel => channel.authMode === 'oauth');
591
+ if (oauthChannels.length) {
592
+ return applyNativeGeminiOAuth(oauthChannels[0]);
593
+ }
594
+ const defaultChannel = enabledChannels[0];
445
595
 
446
596
  const env = readExistingGeminiEnv();
447
597
 
@@ -485,6 +635,16 @@ function getEnabledChannels() {
485
635
  return data.channels.filter(c => c.enabled !== false);
486
636
  }
487
637
 
638
+ function getGeminiProxyExcludedChannelIds(channels = null) {
639
+ const candidates = Array.isArray(channels) ? channels : getEnabledChannels();
640
+ return [...new Set(
641
+ candidates
642
+ .filter(channel => channel?.enabled !== false && channel?.authMode === 'oauth')
643
+ .map(channel => channel.id)
644
+ .filter(Boolean)
645
+ )];
646
+ }
647
+
488
648
  function markChannelAsRecentlyUsed(channelId) {
489
649
  return updateChannel(channelId, {});
490
650
  }
@@ -521,10 +681,13 @@ function disableAllChannels() {
521
681
  const data = loadChannels();
522
682
  data.channels.forEach(ch => { ch.enabled = false; });
523
683
  saveChannels(data);
684
+ if (!getGeminiProxyState().running) {
685
+ clearGeminiChannelConfig();
686
+ }
524
687
  }
525
688
 
526
689
  function readGeminiSettings() {
527
- const settingsPath = NATIVE_PATHS.gemini.settings;
690
+ const settingsPath = configuredNative.settings;
528
691
  if (!settingsPath || !fs.existsSync(settingsPath)) {
529
692
  return {};
530
693
  }
@@ -639,6 +802,7 @@ function syncCurrentGeminiChannel() {
639
802
  }
640
803
 
641
804
  module.exports = {
805
+ configure,
642
806
  getChannels,
643
807
  createChannel,
644
808
  updateChannel,
@@ -646,6 +810,7 @@ module.exports = {
646
810
  deleteChannel,
647
811
  getEnabledChannels,
648
812
  getEffectiveApiKey,
813
+ getGeminiProxyExcludedChannelIds,
649
814
  saveChannelOrder,
650
815
  isProxyConfig,
651
816
  getGeminiDir,
@@ -0,0 +1,39 @@
1
+ 'use strict';
2
+
3
+ const { createChannelDriver } = require('../../../shared/channel-driver');
4
+
5
+ function createDriver(context = {}) {
6
+ const driver = createChannelDriver({
7
+ ...context,
8
+ platform: 'gemini',
9
+ servicePath: './gemini/channels-implementation',
10
+ localServicePath: '../platforms/drivers/gemini/channels-implementation',
11
+ syncMethod: 'syncCurrentGeminiChannel',
12
+ createArgs: (input, rest) => typeof input === 'object'
13
+ ? (() => {
14
+ const { name, baseUrl, apiKey, model, extra, ...channelExtra } = input;
15
+ return [name, baseUrl, apiKey, model, { ...channelExtra, ...extra }];
16
+ })()
17
+ : [input, ...rest],
18
+ cliMetadata: {
19
+ supportsCliCreate: true,
20
+ supportsCliToggle: true,
21
+ defaultPort: 20090,
22
+ createDefaults: { model: 'gemini-2.5-pro' },
23
+ createQuestions: [{
24
+ type: 'input',
25
+ name: 'model',
26
+ message: '模型名称 (默认: gemini-2.5-pro):',
27
+ default: 'gemini-2.5-pro'
28
+ }]
29
+ },
30
+ formatCliChannelDetails: channel => channel.model ? [`model ${channel.model}`] : []
31
+ });
32
+ const auth = require('../../channel-auth-service');
33
+ driver.getAuth = context => auth.getChannelAuth('gemini', { channelId: context?.params?.channelId || context?.query?.channelId || '' });
34
+ driver.syncLocalAuth = context => auth.syncLocalChannelAuth('gemini', { channelId: context?.body?.channelId || context?.params?.channelId || context?.query?.channelId || '' });
35
+ driver.getAuthQuota = context => auth.fetchChannelAuthQuota('gemini', context?.params?.channelId, { refresh: context?.query?.refresh === 'true' });
36
+ return driver;
37
+ }
38
+
39
+ module.exports = { createDriver };
@@ -1,12 +1,21 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
- const { NATIVE_PATHS } = require('../../config/paths');
3
+ const { NATIVE_PATHS } = require('../../../config/paths');
4
+
5
+ const DEFAULT_GEMINI_DIR = path.dirname(NATIVE_PATHS.gemini.env);
6
+ let geminiDirOverride = null;
7
+
8
+ function configure({ pathContext } = {}) {
9
+ geminiDirOverride = pathContext?.customized
10
+ ? (pathContext.native?.dir || path.dirname(pathContext.native?.env || DEFAULT_GEMINI_DIR))
11
+ : null;
12
+ }
4
13
 
5
14
  /**
6
15
  * 获取 Gemini 配置目录
7
16
  */
8
17
  function getGeminiDir() {
9
- return path.dirname(NATIVE_PATHS.gemini.env);
18
+ return geminiDirOverride || DEFAULT_GEMINI_DIR;
10
19
  }
11
20
 
12
21
  /**
@@ -66,6 +75,7 @@ function isGeminiInstalled() {
66
75
  }
67
76
 
68
77
  module.exports = {
78
+ configure,
69
79
  getGeminiDir,
70
80
  loadEnv,
71
81
  loadSettings,
@@ -0,0 +1,9 @@
1
+ 'use strict';
2
+
3
+ const { createMcpDriver } = require('../../../shared/driver-factories/mcp');
4
+
5
+ function createDriver(context = {}) {
6
+ return createMcpDriver({ ...context, platform: 'gemini' });
7
+ }
8
+
9
+ module.exports = { createDriver };
@@ -1,23 +1,31 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
- const { NATIVE_PATHS } = require('../../config/paths');
3
+ const { NATIVE_PATHS } = require('../../../config/paths');
4
+ const DEFAULT_NATIVE_PATHS = NATIVE_PATHS.gemini;
5
+ let nativePaths = DEFAULT_NATIVE_PATHS;
6
+
7
+ function configure({ pathContext } = {}) {
8
+ nativePaths = pathContext?.customized && pathContext.native?.env
9
+ ? { ...DEFAULT_NATIVE_PATHS, ...pathContext.native }
10
+ : DEFAULT_NATIVE_PATHS;
11
+ }
4
12
 
5
13
  // Gemini 配置文件路径
6
14
  function getEnvPath() {
7
- return NATIVE_PATHS.gemini.env;
15
+ return nativePaths.env;
8
16
  }
9
17
 
10
18
  function getSettingsPath() {
11
- return path.join(path.dirname(NATIVE_PATHS.gemini.env), 'settings.json');
19
+ return nativePaths.settings || path.join(path.dirname(nativePaths.env), 'settings.json');
12
20
  }
13
21
 
14
22
  // 备份文件路径
15
23
  function getEnvBackupPath() {
16
- return NATIVE_PATHS.gemini.envBackup;
24
+ return nativePaths.envBackup;
17
25
  }
18
26
 
19
27
  function getSettingsBackupPath() {
20
- return path.join(path.dirname(NATIVE_PATHS.gemini.env), 'settings.json.cc-tool-backup');
28
+ return nativePaths.settingsBackup || path.join(path.dirname(nativePaths.env), 'settings.json.cc-tool-backup');
21
29
  }
22
30
 
23
31
  // 检查配置文件是否存在
@@ -257,6 +265,7 @@ function getCurrentProxyPort() {
257
265
  }
258
266
 
259
267
  module.exports = {
268
+ configure,
260
269
  getEnvPath,
261
270
  getSettingsPath,
262
271
  getEnvBackupPath,
@@ -0,0 +1,39 @@
1
+ 'use strict';
2
+
3
+ const implementation = require('./native-config-implementation');
4
+ const path = require('path');
5
+ const { createNativeSnapshotMethods } = require('../native-config-snapshot');
6
+
7
+ function createDriver({ requireImpl, ...context } = {}) {
8
+ const configuredPaths = context.pathContext?.native;
9
+ const currentNativePaths = context.pathContext?.customized && configuredPaths?.env
10
+ ? configuredPaths
11
+ : require('../../../config/paths').NATIVE_PATHS.gemini;
12
+ const settings = requireImpl
13
+ ? requireImpl('./gemini/native-config-implementation')
14
+ : implementation;
15
+ settings.configure?.({ pathContext: context.pathContext });
16
+ const snapshotMethods = createNativeSnapshotMethods({
17
+ env: { path: currentNativePaths.env, format: 'text', mode: 0o600 },
18
+ settings: {
19
+ path: currentNativePaths.settings || path.join(path.dirname(currentNativePaths.env), 'settings.json'),
20
+ format: 'json'
21
+ }
22
+ }, { platform: 'gemini', runtime: context.runtime });
23
+ return {
24
+ platform: 'gemini',
25
+ capability: 'nativeConfig',
26
+ ...context,
27
+ ...settings,
28
+ ...snapshotMethods,
29
+ clearNativeOAuth: () => {
30
+ const adapters = require('../../native-oauth-adapters');
31
+ adapters.configure?.(context);
32
+ return adapters.clearNativeOAuth('gemini');
33
+ },
34
+ preserveNativeOAuthOnProxyStart: true,
35
+ restoreNativeSettingsOnProxyStop: true
36
+ };
37
+ }
38
+
39
+ module.exports = { createDriver };