coding-tool-x 3.8.4 → 3.8.5

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-BG6P3DX-.js +1 -0
  4. package/dist/web/assets/AgentsDrawer-Pw2k-ZAa.css +1 -0
  5. package/dist/web/assets/Analytics--z_FmL5P.js +1 -0
  6. package/dist/web/assets/Analytics-CyHhR126.css +1 -0
  7. package/dist/web/assets/AssetPathField-CrJtRKHy.js +1 -0
  8. package/dist/web/assets/ChatHistoryDrawer-B2qBQs9L.css +1 -0
  9. package/dist/web/assets/ChatHistoryDrawer-BiuEryJj.js +1 -0
  10. package/dist/web/assets/CollapseItem-BAYEsL_Q.js +1 -0
  11. package/dist/web/assets/CommandsDrawer-BZJxlPvV.css +1 -0
  12. package/dist/web/assets/CommandsDrawer-YJ-U7BXN.js +1 -0
  13. package/dist/web/assets/ConfigExportDrawer-Ce_hAknW.js +1 -0
  14. package/dist/web/assets/ConfigExportDrawer-dwruRKWr.css +1 -0
  15. package/dist/web/assets/ConfigTemplates-Bvr1NN6K.js +1 -0
  16. package/dist/web/assets/ConfigTemplatesDrawer-BfDi8yO3.css +1 -0
  17. package/dist/web/assets/ConfigTemplatesDrawer-BjIeHfF-.js +1 -0
  18. package/dist/web/assets/ConfigTemplatesPanel-AV00tm_D.js +1 -0
  19. package/dist/web/assets/ConfigTemplatesPanel-CWyPtk67.css +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-Cz95o4CD.js +1 -0
  24. package/dist/web/assets/GatewayConvertDrawer-CuNlf7AC.css +1 -0
  25. package/dist/web/assets/GatewayConvertDrawer-DLlbMz27.js +1 -0
  26. package/dist/web/assets/Home-CSuFuJ9Y.css +1 -0
  27. package/dist/web/assets/Home-D7CCOFQC.js +1 -0
  28. package/dist/web/assets/MarkdownEditor-qfVgSre_.css +1 -0
  29. package/dist/web/assets/MarkdownEditor-rEZ-c72L.js +1 -0
  30. package/dist/web/assets/McpDrawer-C0dnsZhQ.css +1 -0
  31. package/dist/web/assets/McpDrawer-DfoCyxU2.js +1 -0
  32. package/dist/web/assets/McpFormDrawer-BRbDcawn.css +1 -0
  33. package/dist/web/assets/McpFormDrawer-SKu8HNUR.js +1 -0
  34. package/dist/web/assets/PluginManager-BbeK-3rV.js +1 -0
  35. package/dist/web/assets/PluginsDrawer-CWVJ7yjS.js +1 -0
  36. package/dist/web/assets/PluginsDrawer-DM3QjprE.css +1 -0
  37. package/dist/web/assets/PluginsPanel-Cm2N3StG.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-CsjnsNPv.js +1 -0
  41. package/dist/web/assets/PromptsDrawer-BIgiYCEQ.css +1 -0
  42. package/dist/web/assets/PromptsDrawer-EGd3djFa.js +1 -0
  43. package/dist/web/assets/RecentSessionsDrawer-CKFsOYDM.css +1 -0
  44. package/dist/web/assets/RecentSessionsDrawer-D1vtoLAs.js +1 -0
  45. package/dist/web/assets/SessionCard-BgLg49U3.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-TRiTklWk.js +1 -0
  49. package/dist/web/assets/SettingsDrawer-d5liXjHq.css +1 -0
  50. package/dist/web/assets/SettingsDrawer-rz2WejGY.js +1 -0
  51. package/dist/web/assets/SkillManager-BTjTUbHq.js +1 -0
  52. package/dist/web/assets/SkillManager-C0GCT2Kb.css +1 -0
  53. package/dist/web/assets/SkillsDrawer-BCNme-aJ.css +1 -0
  54. package/dist/web/assets/SkillsDrawer-pN0hbkK3.js +1 -0
  55. package/dist/web/assets/SkillsPanel-B3Nm5j32.css +1 -0
  56. package/dist/web/assets/SkillsPanel-gmBWEoiF.js +1 -0
  57. package/dist/web/assets/SpeedTestDrawer-Dc0WToJ_.css +1 -0
  58. package/dist/web/assets/SpeedTestDrawer-DcNzuVJk.js +1 -0
  59. package/dist/web/assets/Upload-v5rV0qTT.js +1 -0
  60. package/dist/web/assets/WorkspaceDrawer-CGnMu9p1.css +1 -0
  61. package/dist/web/assets/WorkspaceDrawer-DxmicM4U.js +1 -0
  62. package/dist/web/assets/WorkspaceManager-CJHCjARN.js +1 -0
  63. package/dist/web/assets/WorkspacePanel-D0-zHTHP.css +1 -0
  64. package/dist/web/assets/WorkspacePanel-D8_Zq-du.js +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-C0EHRym0.css +1 -0
  71. package/dist/web/assets/index-DnEocV0M.js +2 -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-B3uojyvV.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 +94 -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 +162 -0
  140. package/src/platforms/channel-auth-service.js +82 -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 +87 -417
  269. package/src/{server/services → platforms}/notification-hooks.js +140 -1023
  270. package/src/platforms/oauth-credentials-service.js +506 -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 +11 -20
  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 +1 -1
  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 +780 -213
  333. package/src/server/services/session-history-worker.js +228 -33
  334. package/src/server/services/session-snapshot-worker.js +124 -34
  335. package/src/server/services/session-snapshots.js +4 -2
  336. package/src/server/services/skill-artifact-store.js +355 -0
  337. package/src/server/services/skill-format-adapters.js +65 -0
  338. package/src/server/services/skill-projection-service.js +323 -0
  339. package/src/server/services/skill-refresh-task-service.js +275 -0
  340. package/src/server/services/skill-service.js +2020 -677
  341. package/src/server/services/snapshot-cache.js +30 -8
  342. package/src/server/services/speed-test.js +8 -8
  343. package/src/server/services/sqlite-connection.js +19 -3
  344. package/src/server/services/statistics-service.js +240 -57
  345. package/src/server/services/ui-config.js +107 -76
  346. package/src/server/services/usage-log-utils.js +98 -0
  347. package/src/server/services/web-build.js +1 -1
  348. package/src/server/services/workspace-service.js +59 -29
  349. package/src/server/utils/pricing.js +10 -19
  350. package/src/server/websocket-server.js +153 -28
  351. package/src/{server/services/base → shared}/base-channel-service.js +156 -28
  352. package/src/shared/capability-driver.js +104 -0
  353. package/src/shared/channel-auth-policy.js +27 -0
  354. package/src/shared/channel-driver.js +161 -0
  355. package/src/shared/driver-factories/api.js +180 -0
  356. package/src/shared/driver-factories/mcp.js +244 -0
  357. package/src/shared/driver-factories/projects.js +121 -0
  358. package/src/shared/driver-factories/prompts.js +58 -0
  359. package/src/shared/driver-factories/resource-sync.js +375 -0
  360. package/src/shared/driver-result.js +78 -0
  361. package/src/shared/mcp-format.js +189 -0
  362. package/src/shared/platforms.js +104 -167
  363. package/src/shared/project-config.js +512 -0
  364. package/src/shared/proxy-driver.js +57 -0
  365. package/src/{server/services/base → shared}/response-usage-parser.js +8 -4
  366. package/src/ui/menu.js +91 -67
  367. package/src/utils/port-helper.js +35 -19
  368. package/src/utils/session.js +1 -1
  369. package/dist/web/assets/Analytics-COEUXmd4.js +0 -25
  370. package/dist/web/assets/Analytics-RNn1BUbG.css +0 -1
  371. package/dist/web/assets/ConfigTemplates-DLLkS53H.js +0 -1
  372. package/dist/web/assets/Home-DCEyLzwX.js +0 -1
  373. package/dist/web/assets/Home-DYVy4cGZ.css +0 -1
  374. package/dist/web/assets/PluginManager-Oe1ZHoMO.js +0 -1
  375. package/dist/web/assets/ProjectList-CWjuAFof.js +0 -1
  376. package/dist/web/assets/ProjectList-VeMNXfgO.css +0 -1
  377. package/dist/web/assets/SessionList-C4chCPxK.css +0 -1
  378. package/dist/web/assets/SessionList-v3ehGg9U.js +0 -1
  379. package/dist/web/assets/SkillManager-BsX8Qpj0.css +0 -1
  380. package/dist/web/assets/SkillManager-DUF_qP5S.js +0 -1
  381. package/dist/web/assets/WorkspaceManager-CjAWBosO.js +0 -1
  382. package/dist/web/assets/icons-nGdSKQn2.js +0 -1
  383. package/dist/web/assets/index-D5hyHPj7.js +0 -2
  384. package/dist/web/assets/index-bJRG3RXH.css +0 -1
  385. package/dist/web/assets/markdown-BNJfEgCA.js +0 -1
  386. package/dist/web/assets/markdown-BfC0goYb.css +0 -10
  387. package/dist/web/assets/naive-ui-BALVpMgd.js +0 -1
  388. package/dist/web/assets/vendors-DQA4Hw9v.js +0 -7
  389. package/dist/web/assets/vue-vendor-B5RqjEjQ.js +0 -45
  390. package/src/config/model-pricing.js +0 -35
  391. package/src/server/api/channels.js +0 -439
  392. package/src/server/api/claude-hooks.js +0 -56
  393. package/src/server/api/claude-statistics.js +0 -57
  394. package/src/server/api/codex-channels.js +0 -491
  395. package/src/server/api/codex-projects.js +0 -117
  396. package/src/server/api/codex-proxy.js +0 -293
  397. package/src/server/api/codex-sessions.js +0 -652
  398. package/src/server/api/codex-statistics.js +0 -57
  399. package/src/server/api/gemini-channels.js +0 -392
  400. package/src/server/api/gemini-projects.js +0 -103
  401. package/src/server/api/gemini-proxy.js +0 -231
  402. package/src/server/api/gemini-sessions.js +0 -550
  403. package/src/server/api/gemini-statistics.js +0 -57
  404. package/src/server/api/health-check.js +0 -31
  405. package/src/server/api/oauth-credentials.js +0 -198
  406. package/src/server/api/omp-channels.js +0 -575
  407. package/src/server/api/omp-projects.js +0 -88
  408. package/src/server/api/omp-proxy.js +0 -125
  409. package/src/server/api/omp-sessions.js +0 -386
  410. package/src/server/api/omp-statistics.js +0 -40
  411. package/src/server/api/opencode-channels.js +0 -471
  412. package/src/server/api/opencode-projects.js +0 -110
  413. package/src/server/api/opencode-proxy.js +0 -354
  414. package/src/server/api/opencode-sessions.js +0 -466
  415. package/src/server/api/opencode-statistics.js +0 -57
  416. package/src/server/api/projects.js +0 -120
  417. package/src/server/api/proxy.js +0 -330
  418. package/src/server/api/sessions.js +0 -810
  419. package/src/server/services/oauth-credentials-service.js +0 -945
  420. package/src/server/services/omp-session-log-observer.js +0 -138
  421. package/src/server/services/opencode-gateway-adapters.js +0 -43
  422. package/src/server/services/project-snapshots.js +0 -59
  423. package/src/server/services/proxy-log-helper.js +0 -298
  424. package/src/server/services/session-cache.js +0 -127
  425. package/src/server/services/session-converter.js +0 -582
  426. package/src/server/services/session-history-adapters/claude.js +0 -235
  427. /package/src/{server/services/claude-token-recovery.js → platforms/drivers/claude/token-recovery.js} +0 -0
  428. /package/src/{server/services/codex-parser.js → platforms/drivers/codex/parser.js} +0 -0
  429. /package/src/{server/services/codex-speed-test-template.json → platforms/drivers/codex/speed-test-template.json} +0 -0
  430. /package/src/{server/services/codex-wire.js → platforms/drivers/codex/wire.js} +0 -0
  431. /package/src/{server/services/omp-auth-gateway-client.js → platforms/drivers/omp/auth-gateway-client.js} +0 -0
  432. /package/src/{server/services/opencode-normalization.js → platforms/drivers/opencode/normalization.js} +0 -0
  433. /package/src/{server/services → shared}/config-artifact-paths.js +0 -0
  434. /package/src/{server/services/base → shared}/proxy-utils.js +0 -0
@@ -3,78 +3,85 @@ const fs = require('fs');
3
3
  const chalk = require('chalk');
4
4
  const inquirer = require('inquirer');
5
5
  const { loadConfig } = require('../config/loader');
6
- const { PATHS } = require('../config/paths');
7
- const { clearNativeOAuth } = require('../server/services/native-oauth-adapters');
6
+ const { getPlatformStatePath } = require('../config/paths');
8
7
  const { normalizePlatformKey } = require('../shared/platforms');
9
- const SETTINGS_MANAGERS = {
10
- claude: () => require('../server/services/settings-manager'),
11
- codex: () => require('../server/services/codex-settings-manager'),
12
- gemini: () => require('../server/services/gemini-settings-manager'),
13
- opencode: () => require('../server/services/opencode-settings-manager')
14
- };
8
+ const { getPlatformRuntime, getPlatformRegistry } = require('../platforms/runtime');
15
9
 
16
10
  /**
17
11
  * 获取当前类型的代理服务
18
12
  */
19
- function getProxyServices(cliType) {
20
- const normalizedCliType = normalizePlatformKey(cliType || 'claude');
21
- if (normalizedCliType === 'claude') {
22
- const { getProxyStatus, startProxyServer, stopProxyServer } = require('../server/proxy-server');
23
- return { getProxyStatus, startProxyServer, stopProxyServer, defaultPort: 20088 };
24
- } else if (normalizedCliType === 'codex') {
25
- const { getCodexProxyStatus, startCodexProxyServer, stopCodexProxyServer } = require('../server/codex-proxy-server');
26
- return {
27
- getProxyStatus: getCodexProxyStatus,
28
- startProxyServer: startCodexProxyServer,
29
- stopProxyServer: stopCodexProxyServer,
30
- defaultPort: 20089
31
- };
32
- } else if (normalizedCliType === 'gemini') {
33
- const { getGeminiProxyStatus, startGeminiProxyServer, stopGeminiProxyServer } = require('../server/gemini-proxy-server');
34
- return {
35
- getProxyStatus: getGeminiProxyStatus,
36
- startProxyServer: startGeminiProxyServer,
37
- stopProxyServer: stopGeminiProxyServer,
38
- defaultPort: 20090
39
- };
40
- } else if (normalizedCliType === 'opencode') {
41
- const { getOpenCodeProxyStatus, startOpenCodeProxyServer, stopOpenCodeProxyServer } = require('../server/opencode-proxy-server');
42
- return {
43
- getProxyStatus: getOpenCodeProxyStatus,
44
- startProxyServer: startOpenCodeProxyServer,
45
- stopProxyServer: stopOpenCodeProxyServer,
46
- defaultPort: 20091
47
- };
48
- } else if (normalizedCliType === 'omp') {
49
- const { getOmpProxyStatus, startOmpProxyServer, stopOmpProxyServer } = require('../server/omp-proxy-server');
50
- const config = loadConfig();
51
- return {
52
- getProxyStatus: getOmpProxyStatus,
53
- startProxyServer: startOmpProxyServer,
54
- stopProxyServer: stopOmpProxyServer,
55
- defaultPort: config.ports?.ompProxy || 20092,
56
- managedProviderConfig: true
57
- };
58
- }
13
+ function unwrapDriverResult(result) {
14
+ if (!result || typeof result !== 'object' || !result.status) return result;
15
+ if (result.status === 'ok') return result.data;
16
+ throw result.cause instanceof Error
17
+ ? result.cause
18
+ : new Error(result.error || `Driver operation ${result.operation || 'unknown'} failed`);
19
+ }
59
20
 
60
- return null;
21
+ function getProxyServices(cliType, runtime = getPlatformRuntime()) {
22
+ const platform = normalizePlatformKey(cliType || 'claude');
23
+ const proxyDriver = runtime?.getDriver?.(platform, 'proxy');
24
+ const channelsDriver = runtime?.getDriver?.(platform, 'channels');
25
+ if (!proxyDriver) return null;
26
+
27
+ const metadata = {
28
+ ...(channelsDriver?.getCliMetadata?.() || {}),
29
+ ...(proxyDriver.getCliMetadata?.() || {})
30
+ };
31
+ return {
32
+ ...metadata,
33
+ getProxyStatus: () => unwrapDriverResult(proxyDriver.status()),
34
+ startProxyServer: options => unwrapDriverResult(proxyDriver.start(options)),
35
+ stopProxyServer: options => unwrapDriverResult(proxyDriver.stop(options)),
36
+ getAllChannels: () => {
37
+ if (typeof channelsDriver?.list !== 'function') return [];
38
+ const result = unwrapDriverResult(channelsDriver.list());
39
+ return Array.isArray(result) ? result : (result?.channels || []);
40
+ },
41
+ applyChannelToSettings: id => channelsDriver?.applyNativeConfig
42
+ ? unwrapDriverResult(channelsDriver.applyNativeConfig(id))
43
+ : null,
44
+ channelsDriver,
45
+ proxyDriver
46
+ };
47
+ }
48
+ function getPlatformLabel(platform) {
49
+ return getPlatformRegistry().resolve(platform)?.label || platform;
61
50
  }
62
51
 
63
- function getSettingsManager(cliType) {
64
- const normalizedCliType = normalizePlatformKey(cliType || 'claude');
65
- const loader = SETTINGS_MANAGERS[normalizedCliType] || SETTINGS_MANAGERS.claude;
66
- const manager = loader();
52
+
53
+ function getSettingsManager(cliType, runtime = getPlatformRuntime()) {
54
+ const platform = normalizePlatformKey(cliType || 'claude');
55
+ const driver = runtime?.getDriver?.(platform, 'nativeConfig');
56
+ if (!driver || typeof driver.setProxyConfig !== 'function') {
57
+ return null;
58
+ }
59
+
67
60
  return {
68
- setProxyConfig: manager.setProxyConfig,
69
- restoreSettings: manager.restoreSettings,
70
- hasBackup: manager.hasBackup,
71
- deleteBackup: manager.deleteBackup
61
+ setProxyConfig: (...args) => unwrapDriverResult(driver.setProxyConfig(...args)),
62
+ clearNativeOAuth: typeof driver.clearNativeOAuth === 'function'
63
+ ? (...args) => unwrapDriverResult(driver.clearNativeOAuth(...args))
64
+ : undefined,
65
+ preserveNativeOAuthOnProxyStart: driver.preserveNativeOAuthOnProxyStart === true,
66
+ restoreNativeSettingsOnProxyStop: driver.restoreNativeSettingsOnProxyStop === true,
67
+ restoreSettings: typeof driver.restoreSettings === 'function'
68
+ ? (...args) => unwrapDriverResult(driver.restoreSettings(...args))
69
+ : undefined,
70
+ isProxyConfig: typeof driver.isProxyConfig === 'function'
71
+ ? (...args) => unwrapDriverResult(driver.isProxyConfig(...args))
72
+ : undefined,
73
+ hasBackup: typeof driver.hasBackup === 'function'
74
+ ? (...args) => unwrapDriverResult(driver.hasBackup(...args))
75
+ : () => false,
76
+ deleteBackup: typeof driver.deleteBackup === 'function'
77
+ ? (...args) => unwrapDriverResult(driver.deleteBackup(...args))
78
+ : undefined,
72
79
  };
73
80
  }
74
81
 
75
82
  function removeActiveChannelMarker(cliType) {
76
83
  const normalizedCliType = normalizePlatformKey(cliType || 'claude');
77
- const markerPath = PATHS.activeChannel?.[normalizedCliType];
84
+ const markerPath = getPlatformStatePath('activeChannel', normalizedCliType);
78
85
  if (!markerPath) {
79
86
  return;
80
87
  }
@@ -90,7 +97,7 @@ function removeActiveChannelMarker(cliType) {
90
97
 
91
98
  function loadActiveChannelId(cliType) {
92
99
  const normalizedCliType = normalizePlatformKey(cliType || 'claude');
93
- const markerPath = PATHS.activeChannel?.[normalizedCliType];
100
+ const markerPath = getPlatformStatePath('activeChannel', normalizedCliType);
94
101
  if (!markerPath) {
95
102
  return null;
96
103
  }
@@ -132,33 +139,22 @@ function pickRestoredChannel(cliType, channels = []) {
132
139
  return pickLatestEnabledChannel(channels);
133
140
  }
134
141
 
135
- function restoreSingleChannelMode(cliType) {
136
- const normalizedCliType = normalizePlatformKey(cliType || 'claude');
137
- if (normalizedCliType === 'claude') {
138
- const { getAllChannels, applyChannelToSettings } = require('../server/services/channels');
139
- const target = pickRestoredChannel(normalizedCliType, getAllChannels());
140
- return target ? applyChannelToSettings(target.id) : null;
141
- }
142
-
143
- if (normalizedCliType === 'codex') {
144
- const { getChannels, applyChannelToSettings } = require('../server/services/codex-channels');
145
- const target = pickRestoredChannel(normalizedCliType, getChannels().channels || []);
146
- return target ? applyChannelToSettings(target.id) : null;
147
- }
148
-
149
- if (normalizedCliType === 'gemini') {
150
- const { getChannels, applyChannelToSettings } = require('../server/services/gemini-channels');
151
- const target = pickRestoredChannel(normalizedCliType, getChannels().channels || []);
152
- return target ? applyChannelToSettings(target.id) : null;
142
+ function restoreSingleChannelMode(cliType, runtime = getPlatformRuntime()) {
143
+ const platform = normalizePlatformKey(cliType || 'claude');
144
+ const channelsDriver = runtime?.getDriver?.(platform, 'channels');
145
+ if (!channelsDriver || typeof channelsDriver.list !== 'function') {
146
+ return null;
153
147
  }
154
148
 
155
- if (normalizedCliType === 'opencode') {
156
- const { getChannels, applyChannelToSettings } = require('../server/services/opencode-channels');
157
- const target = pickRestoredChannel(normalizedCliType, getChannels().channels || []);
158
- return target ? applyChannelToSettings(target.id) : null;
159
- }
149
+ const listed = unwrapDriverResult(channelsDriver.list());
150
+ const channels = Array.isArray(listed) ? listed : (listed?.channels || []);
151
+ const target = pickRestoredChannel(platform, channels);
152
+ if (!target) return null;
160
153
 
161
- return null;
154
+ const result = typeof channelsDriver.applyNativeConfig === 'function'
155
+ ? channelsDriver.applyNativeConfig(target.id)
156
+ : channelsDriver.applyChannelToSettings?.(target.id);
157
+ return unwrapDriverResult(result);
162
158
  }
163
159
 
164
160
  /**
@@ -190,17 +186,7 @@ async function handleToggleProxy() {
190
186
  async function handleStartProxy(cliType, services) {
191
187
  console.clear();
192
188
  console.log(chalk.bold.cyan('\n╔=======================================╗'));
193
- console.log(chalk.bold.cyan('║ 开启动态切换 ║'));
194
- console.log(chalk.bold.cyan('╚=======================================╝\n'));
195
-
196
- const toolNameMap = {
197
- claude: 'Claude Code',
198
- codex: 'Codex',
199
- gemini: 'Gemini',
200
- opencode: 'OpenCode',
201
- omp: 'OMP'
202
- };
203
- const toolName = toolNameMap[cliType] || 'Claude Code';
189
+ const toolName = getPlatformLabel(cliType);
204
190
  const defaultPort = services.defaultPort;
205
191
 
206
192
  console.log(chalk.cyan('动态切换功能说明:'));
@@ -254,18 +240,17 @@ async function handleStartProxy(cliType, services) {
254
240
 
255
241
  if (services.managedProviderConfig) {
256
242
  console.log(chalk.green('[OK] OMP models.yml 受管 provider 已启用'));
257
- console.log(chalk.gray('OMP 新增会话用量日志观察已启用'));
258
243
  (proxyResult.warnings || []).forEach((warning) => {
259
244
  console.log(chalk.yellow(`[WARN] ${warning}`));
260
245
  });
261
246
  } else {
262
- console.log(chalk.green(`[OK] 代理服务已启动: http://127.0.0.1:${proxyResult.port}`));
263
- }
264
-
265
- // 修改配置文件
266
- if (!services.managedProviderConfig) {
267
247
  const settingsManager = getSettingsManager(cliType);
268
- clearNativeOAuth(cliType);
248
+ if (!settingsManager) {
249
+ throw new Error(`平台 ${cliType} 未提供 nativeConfig 能力`);
250
+ }
251
+ if (!settingsManager.preserveNativeOAuthOnProxyStart) {
252
+ settingsManager.clearNativeOAuth?.(cliType);
253
+ }
269
254
  settingsManager.setProxyConfig(proxyResult.port);
270
255
  console.log(chalk.green('[OK] 配置文件已更新'));
271
256
 
@@ -310,21 +295,13 @@ async function handleStopProxy(cliType, services) {
310
295
  console.log(chalk.bold.cyan('║ 关闭动态切换 ║'));
311
296
  console.log(chalk.bold.cyan('╚=======================================╝\n'));
312
297
 
313
- const toolNameMap = {
314
- claude: 'Claude Code',
315
- codex: 'Codex',
316
- gemini: 'Gemini',
317
- opencode: 'OpenCode',
318
- omp: 'OMP'
319
- };
320
- const toolName = toolNameMap[cliType] || 'Claude Code';
298
+ const toolName = getPlatformLabel(cliType);
321
299
  const proxyStatus = services.getProxyStatus();
322
300
 
323
301
  console.log(chalk.cyan('当前状态:'));
324
302
  if (services.managedProviderConfig) {
325
303
  console.log(chalk.gray(`• 动态切换: ${chalk.green('已启用')}`));
326
304
  console.log(chalk.gray(`• 配置模式: ${proxyStatus.mode || 'models-yml-provider-config'}`));
327
- console.log(chalk.gray(`• 会话日志观察: ${proxyStatus.sessionLogObserver?.running ? chalk.green('运行中') : chalk.yellow('未运行')}\n`));
328
305
  } else {
329
306
  console.log(chalk.gray(`• 代理服务: ${chalk.green('运行中')}`));
330
307
  console.log(chalk.gray(`• 代理端口: ${proxyStatus.port}`));
@@ -369,7 +346,12 @@ async function handleStopProxy(cliType, services) {
369
346
  // 恢复配置文件
370
347
  if (!services.managedProviderConfig) {
371
348
  const settingsManager = getSettingsManager(cliType);
372
- settingsManager.deleteBackup?.();
349
+ const hasBackup = settingsManager?.hasBackup?.() === true;
350
+ if (hasBackup && settingsManager.restoreNativeSettingsOnProxyStop && settingsManager.restoreSettings) {
351
+ settingsManager.restoreSettings();
352
+ } else if (hasBackup) {
353
+ settingsManager.deleteBackup?.();
354
+ }
373
355
  const restoredChannel = restoreSingleChannelMode(cliType);
374
356
  removeActiveChannelMarker(cliType);
375
357
  if (restoredChannel?.name) {
@@ -413,6 +395,7 @@ module.exports = {
413
395
  handleToggleProxy,
414
396
  _test: {
415
397
  getProxyServices,
398
+ getSettingsManager,
416
399
  pickRestoredChannel,
417
400
  restoreSingleChannelMode
418
401
  }
@@ -1,10 +1,57 @@
1
1
  const chalk = require('chalk');
2
+ const path = require('path');
3
+ const { spawn } = require('child_process');
2
4
  const { startServer } = require('../server');
3
5
  const open = require('open');
4
- const { getProxyStatus } = require('../server/proxy-server');
6
+ const { getProxyStatus } = require('../platforms/drivers/claude/proxy-implementation');
5
7
  const { loadConfig } = require('../config/loader');
8
+ const { shutdownNativeCliLogObserver } = require('../server/services/native-log-observer');
6
9
  const { hasHostFlag } = require('../utils/cli-flags');
7
10
 
11
+ function getWindowsPowerShellPath(env = process.env) {
12
+ const systemRoot = env.SystemRoot || env.SYSTEMROOT;
13
+ return systemRoot
14
+ ? path.win32.join(systemRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe')
15
+ : 'powershell.exe';
16
+ }
17
+
18
+ function quotePowerShellLiteral(value) {
19
+ return `'${String(value).replace(/'/g, "''")}'`;
20
+ }
21
+
22
+ function buildWindowsOpenSpec(url, env = process.env) {
23
+ return {
24
+ command: getWindowsPowerShellPath(env),
25
+ args: [
26
+ '-NoProfile',
27
+ '-NonInteractive',
28
+ '-ExecutionPolicy',
29
+ 'Bypass',
30
+ '-Command',
31
+ `Start-Process -FilePath ${quotePowerShellLiteral(url)}`
32
+ ],
33
+ options: {
34
+ detached: true,
35
+ stdio: 'ignore',
36
+ windowsHide: true
37
+ }
38
+ };
39
+ }
40
+
41
+ function openUrl(url, options = {}) {
42
+ const platform = options.platform || process.platform;
43
+ if (platform !== 'win32') {
44
+ return (options.openImpl || open)(url);
45
+ }
46
+
47
+ const spec = buildWindowsOpenSpec(url, options.env || process.env);
48
+ const child = (options.spawnImpl || spawn)(spec.command, spec.args, spec.options);
49
+ if (child && typeof child.unref === 'function') {
50
+ child.unref();
51
+ }
52
+ return Promise.resolve(child);
53
+ }
54
+
8
55
  async function handleUI() {
9
56
  // 检查是否为 daemon 模式(PM2 启动)
10
57
  const isDaemon = process.argv.includes('--daemon');
@@ -20,7 +67,7 @@ async function handleUI() {
20
67
  console.log(chalk.cyan.bold('\n[NET] 启动 Coding-Tool Web UI...\n'));
21
68
  if (enableHost) {
22
69
  console.log(chalk.yellow('[WARN] LAN 访问已启用 (--host/--hosts)\n'));
23
- console.log(chalk.yellow('[WARN] LAN 模式默认允许远程写操作,可设置 CC_TOOL_ALLOW_REMOTE_WRITE=false 禁止\n'));
70
+ console.log(chalk.yellow('[WARN] LAN 模式默认禁止远程写操作,仅显式设置 CC_TOOL_ALLOW_REMOTE_WRITE=true 才允许\n'));
24
71
  }
25
72
  if (enableHttps) {
26
73
  console.log(chalk.yellow('[LOCK] 已启用本地 HTTPS 模式 (--https)\n'));
@@ -39,7 +86,7 @@ async function handleUI() {
39
86
  if (!isDaemon) {
40
87
  setTimeout(async () => {
41
88
  try {
42
- await open(url);
89
+ await openUrl(url);
43
90
  console.log(chalk.green(`[OK] 已在浏览器中打开: ${url}\n`));
44
91
  } catch (err) {
45
92
  console.log(chalk.yellow(`[TIP] 请手动打开: ${url}\n`));
@@ -75,7 +122,7 @@ async function handleUI() {
75
122
  process.off('SIGINT', secondSigint);
76
123
 
77
124
  if (stopProxy) {
78
- const { stopProxyServer } = require('../server/proxy-server');
125
+ const { stopProxyServer } = require('../platforms/drivers/claude/proxy-implementation');
79
126
  await stopProxyServer();
80
127
  console.log(chalk.green('[OK] 代理服务已停止\n'));
81
128
  } else {
@@ -89,7 +136,7 @@ async function handleUI() {
89
136
 
90
137
  // OMP 动态网关持有 models.yml 事务,退出前必须先恢复原生配置。
91
138
  try {
92
- const { getOmpProxyStatus, stopOmpProxyServer } = require('../server/omp-proxy-server');
139
+ const { getOmpProxyStatus, stopOmpProxyServer } = require('../platforms/drivers/omp/proxy-implementation');
93
140
  if (getOmpProxyStatus().running) {
94
141
  await stopOmpProxyServer({ forceAfterMs: 1500 });
95
142
  }
@@ -97,6 +144,12 @@ async function handleUI() {
97
144
  console.error(chalk.yellow(`[WARN] OMP 动态网关退出清理失败: ${err.message}`));
98
145
  }
99
146
 
147
+ shutdownNativeCliLogObserver();
148
+ try {
149
+ await require('../server/websocket-server').flushPendingLogs();
150
+ } catch (flushError) {
151
+ console.error(chalk.yellow(`[WARN] 日志持久化退出清理失败: ${flushError.message}`));
152
+ }
100
153
  console.log(chalk.green('[OK] Web UI 已停止\n'));
101
154
  process.exit(0);
102
155
  });
@@ -113,4 +166,12 @@ async function handleUI() {
113
166
  }
114
167
  }
115
168
 
116
- module.exports = { handleUI };
169
+ module.exports = {
170
+ handleUI,
171
+ _test: {
172
+ buildWindowsOpenSpec,
173
+ getWindowsPowerShellPath,
174
+ openUrl,
175
+ quotePowerShellLiteral
176
+ }
177
+ };
@@ -4,8 +4,71 @@ const inquirer = require('inquirer');
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
6
  const workspaceService = require('../server/services/workspace-service');
7
- const { getProjectsWithStats } = require('../server/services/sessions');
8
7
  const { loadConfig } = require('../config/loader');
8
+ const { resolveOperation } = require('../platforms/access');
9
+
10
+ function isDriverResult(value) {
11
+ return value && typeof value === 'object' && typeof value.status === 'string';
12
+ }
13
+
14
+ function unwrapDriverResult(value) {
15
+ if (!isDriverResult(value) || value.status !== 'ok') return value;
16
+ return value.data;
17
+ }
18
+
19
+ function createFailureResult(error, platform, operation) {
20
+ const result = {
21
+ status: error?.code || 'failed',
22
+ platform: error?.platform || platform || 'claude',
23
+ capability: 'projects',
24
+ operation,
25
+ error: error?.message || String(error)
26
+ };
27
+ if (error?.cause) {
28
+ Object.defineProperty(result, 'cause', { value: error.cause, enumerable: false });
29
+ }
30
+ return result;
31
+ }
32
+
33
+ async function listProjectsForWorkspace(config, options = {}) {
34
+ const platform = options.platform || config.currentCliType || '';
35
+ try {
36
+ const resolved = resolveOperation(platform, 'projects', 'listProjects', {
37
+ ...options,
38
+ fallback: 'claude'
39
+ });
40
+ const result = unwrapDriverResult(await resolved.operation({ config }));
41
+ return { result, platform: resolved };
42
+ } catch (error) {
43
+ return {
44
+ result: createFailureResult(error, platform, 'listProjects'),
45
+ platform: null
46
+ };
47
+ }
48
+ }
49
+
50
+ function normalizeProjects(value) {
51
+ const unwrapped = unwrapDriverResult(value);
52
+ if (isDriverResult(unwrapped)) return unwrapped;
53
+ const projects = Array.isArray(unwrapped)
54
+ ? unwrapped
55
+ : Array.isArray(unwrapped?.projects) ? unwrapped.projects : [];
56
+ return projects
57
+ .map(project => {
58
+ const fullPath = project.fullPath || project.path || project.sourcePath || project.worktree || '';
59
+ const name = project.name || project.projectName || project.id || path.basename(fullPath);
60
+ return {
61
+ ...project,
62
+ name,
63
+ displayName: project.displayName || project.label || project.title || name,
64
+ fullPath,
65
+ sessionCount: Number(
66
+ project.sessionCount ?? project.sessions ?? project.stats?.sessionCount ?? 0
67
+ ) || 0
68
+ };
69
+ })
70
+ .filter(project => project.fullPath);
71
+ }
9
72
 
10
73
  /**
11
74
  * 列出所有工作区
@@ -42,9 +105,13 @@ async function listWorkspaces() {
42
105
  /**
43
106
  * 创建工作区
44
107
  */
45
- async function createWorkspace() {
108
+ async function createWorkspace(options = {}) {
46
109
  try {
47
- const config = loadConfig();
110
+ const config = options.config || loadConfig();
111
+ const accessOptions = {
112
+ ...options,
113
+ platform: options.platform || config.currentCliType || ''
114
+ };
48
115
 
49
116
  // 1. 输入工作区名称
50
117
  const { name } = await inquirer.prompt([
@@ -116,9 +183,21 @@ async function createWorkspace() {
116
183
  // 4. 选择项目
117
184
  const projects = [];
118
185
  let continueAdding = true;
186
+ let selectedPlatform = null;
119
187
 
120
188
  while (continueAdding) {
121
- const availableProjects = await getProjectsWithStats(config);
189
+ const projectResult = await listProjectsForWorkspace(config, accessOptions);
190
+ const availableProjects = normalizeProjects(projectResult.result);
191
+ selectedPlatform = projectResult.platform || selectedPlatform;
192
+
193
+ if (isDriverResult(availableProjects)) {
194
+ const platformLabel = selectedPlatform?.manifest?.label
195
+ || selectedPlatform?.manifest?.title
196
+ || accessOptions.platform
197
+ || '当前平台';
198
+ console.log(chalk.yellow(`\n${platformLabel} 不支持项目发现,无法创建工作区\n`));
199
+ return;
200
+ }
122
201
 
123
202
  if (availableProjects.length === 0) {
124
203
  console.log(chalk.yellow('\n没有可用的项目\n'));
@@ -299,7 +378,7 @@ async function createWorkspace() {
299
378
  // 创建工作区
300
379
  console.log(chalk.cyan('\n正在创建工作区...\n'));
301
380
 
302
- const workspace = workspaceService.createWorkspace({
381
+ const workspace = await workspaceService.createWorkspace({
303
382
  name,
304
383
  description,
305
384
  baseDir,
@@ -308,7 +387,10 @@ async function createWorkspace() {
308
387
 
309
388
  console.log(chalk.green(`\n[OK] 工作区创建成功!\n`));
310
389
  console.log(chalk.gray(`工作区路径: ${workspace.path}\n`));
311
- console.log(chalk.gray(`提示: 可以在此路径下启动 Claude Code 以访问所有项目\n`));
390
+ const platformManifest = selectedPlatform?.manifest || {};
391
+ const platformLabel = platformManifest.label || platformManifest.title || accessOptions.platform || '当前 CLI';
392
+ const platformCommand = platformManifest.command || platformLabel;
393
+ console.log(chalk.gray(`提示: 可以在此路径下启动 ${platformCommand}(${platformLabel})以访问所有项目\n`));
312
394
 
313
395
  } catch (error) {
314
396
  console.error(chalk.red(`\n[ERROR] ${error.message}\n`));
@@ -436,7 +518,7 @@ async function deleteWorkspace() {
436
518
  /**
437
519
  * 工作区管理主菜单
438
520
  */
439
- async function workspaceMenu() {
521
+ async function workspaceMenu(options = {}) {
440
522
  let continueMenu = true;
441
523
 
442
524
  while (continueMenu) {
@@ -459,7 +541,7 @@ async function workspaceMenu() {
459
541
 
460
542
  switch (action) {
461
543
  case 'create':
462
- await createWorkspace();
544
+ await createWorkspace(options);
463
545
  break;
464
546
  case 'view':
465
547
  await viewWorkspace();
@@ -25,62 +25,15 @@ const DEFAULT_CONFIG = {
25
25
  },
26
26
  maxLogs: 100,
27
27
  statsInterval: 30,
28
+ nativeCliLogs: {
29
+ enabled: true,
30
+ intervalSeconds: 5
31
+ },
28
32
  modelDiscovery: {
29
33
  useV1ModelsEndpoint: false
30
34
  },
31
- defaultModels: modelMetadataConfig.defaultModels || { claude: [], codex: [], gemini: [] },
32
- defaultSpeedTestModels: modelMetadataConfig.defaultSpeedTestModels || {
33
- claude: 'claude-haiku-4-5',
34
- codex: 'gpt-5.4',
35
- gemini: 'gemini-2.5-pro'
36
- },
37
- pricing: {
38
- claude: {
39
- mode: 'auto',
40
- input: 3,
41
- output: 15,
42
- cacheCreation: 3.75,
43
- cacheRead: 0.30,
44
- models: {
45
- // All models use centralized pricing from src/config/model-pricing.js
46
- // Add custom entries here only if you need to override official pricing
47
- }
48
- },
49
- codex: {
50
- mode: 'auto',
51
- input: 2.5,
52
- output: 15,
53
- cacheRead: 0.25,
54
- models: {
55
- 'gpt-5-codex': { mode: 'auto' },
56
- 'gpt-4o-mini': { mode: 'auto' }
57
- }
58
- },
59
- gemini: {
60
- mode: 'auto',
61
- input: 1.25,
62
- output: 10,
63
- cacheRead: 0.125,
64
- models: {
65
- 'gemini-2.5-pro': { mode: 'auto' },
66
- 'gemini-2.5-flash': { mode: 'auto' }
67
- }
68
- },
69
- opencode: {
70
- mode: 'auto',
71
- input: 2.5,
72
- output: 15,
73
- cacheRead: 0.25,
74
- models: {}
75
- },
76
- omp: {
77
- mode: 'auto',
78
- input: 2.5,
79
- output: 15,
80
- cacheRead: 0.25,
81
- models: {}
82
- }
83
- }
35
+ defaultModels: modelMetadataConfig.defaultModels || {},
36
+ defaultSpeedTestModels: modelMetadataConfig.defaultSpeedTestModels || {}
84
37
  };
85
38
 
86
39
  module.exports = DEFAULT_CONFIG;