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,136 +1,132 @@
1
1
  // 恢复会话命令
2
- const fs = require('fs');
3
- const path = require('path');
4
- const { execSync } = require('child_process');
5
2
  const chalk = require('chalk');
6
3
  const ora = require('ora');
7
- const { getSessionsDir } = require('../utils/session');
4
+ const { resolveOperation } = require('../platforms/access');
8
5
 
6
+ function isDriverResult(value) {
7
+ return value && typeof value === 'object' && typeof value.status === 'string';
8
+ }
9
+
10
+ function unwrapDriverResult(value) {
11
+ if (!isDriverResult(value) || value.status !== 'ok') return value;
12
+ return value.data;
13
+ }
14
+
15
+ function createFailureResult(error, platform) {
16
+ const result = {
17
+ status: error?.code || 'failed',
18
+ platform: error?.platform || platform || 'claude',
19
+ capability: 'sessions',
20
+ operation: 'launch',
21
+ error: error?.message || String(error)
22
+ };
23
+ if (error?.cause) {
24
+ Object.defineProperty(result, 'cause', { value: error.cause, enumerable: false });
25
+ }
26
+ return result;
27
+ }
28
+
29
+ function reportFailure(result) {
30
+ const message = result.error || `会话启动 ${result.operation || 'launch'} 不可用`;
31
+ const output = result.status === 'unsupported' || result.status === 'not_found'
32
+ ? chalk.yellow
33
+ : chalk.red;
34
+ console.log(output(`\n[${result.status === 'unsupported' ? 'WARN' : 'ERROR'}] ${message}\n`));
35
+ }
9
36
  /**
10
37
  * 恢复会话
11
38
  */
12
- async function resumeSession(config, sessionId, fork = false) {
13
- // 构建命令参数
14
- const args = ['-r', sessionId];
15
- if (fork) {
16
- args.push('--fork-session');
17
- }
18
-
19
- // 从会话文件中提取原始工作目录
20
- const sessionFile = path.join(getSessionsDir(config), `${sessionId}.jsonl`);
21
- let cwd = process.cwd();
22
-
39
+ async function resumeSession(config = {}, sessionId, fork = false, options = {}) {
40
+ const platform = options.platform || config.currentCliType || '';
41
+ let resolved;
23
42
  try {
24
- const fd = fs.openSync(sessionFile, 'r');
25
- const buffer = Buffer.alloc(2048);
26
- fs.readSync(fd, buffer, 0, 2048, 0);
27
- fs.closeSync(fd);
28
-
29
- const content = buffer.toString('utf8');
30
- const lines = content.split('\n').slice(0, 5);
31
-
32
- for (const line of lines) {
33
- try {
34
- const json = JSON.parse(line);
35
- if (json.cwd) {
36
- cwd = json.cwd;
37
- break;
38
- }
39
- } catch (e) {
40
- // 继续
41
- }
42
- }
43
- } catch (e) {
44
- // 使用当前目录
43
+ resolved = resolveOperation(platform, 'sessions', 'launch', {
44
+ ...options,
45
+ fallback: 'claude'
46
+ });
47
+ } catch (error) {
48
+ const result = createFailureResult(error, platform);
49
+ reportFailure(result);
50
+ return result;
45
51
  }
46
52
 
47
- // 显示加载动画
53
+ const manifest = resolved.manifest || {};
54
+ const displayName = manifest.label || manifest.title || resolved.key;
55
+ let cwd = options.cwd || process.cwd();
48
56
  const spinner = ora({
49
- text: chalk.cyan('正在准备启动 Claude...'),
50
- spinner: 'dots',
57
+ text: chalk.cyan(`正在准备启动 ${displayName}...`),
58
+ spinner: 'dots'
51
59
  }).start();
52
60
 
53
- // 等待 500ms,让用户看到加载状态
54
61
  await new Promise(resolve => setTimeout(resolve, 500));
55
62
 
56
63
  spinner.succeed(chalk.green('准备完成!\n'));
57
64
 
58
65
  console.log(chalk.gray('━'.repeat(50)));
59
- console.log(chalk.green.bold(`[NEW] 会话: ${sessionId.substring(0, 8)}`));
66
+ console.log(chalk.green.bold(`[NEW] 会话: ${String(sessionId).substring(0, 8)}`));
60
67
  console.log(chalk.gray(`[DIR] 目录: ${cwd}`));
61
68
  console.log(chalk.gray('━'.repeat(50)) + '\n');
62
69
 
63
- // 完全清理终端状态,避免输入冲突
64
70
  if (process.stdin.isTTY && process.stdin.setRawMode) {
65
71
  try {
66
72
  process.stdin.setRawMode(false);
67
- } catch (e) {
68
- // 忽略
73
+ } catch (error) {
74
+ // 忽略终端状态错误
69
75
  }
70
76
  }
71
77
 
72
- // 移除所有 stdin 监听器,防止残留事件
73
78
  process.stdin.removeAllListeners();
74
-
75
- // 添加错误处理器,防止 EIO 错误导致崩溃
76
- process.stdin.on('error', (err) => {
77
- // 忽略 EIO 错误(通常发生在 Ctrl+C 时)
78
- if (err.code === 'EIO' || err.code === 'ENOTTY') {
79
- // 安静退出
79
+ process.stdin.on('error', error => {
80
+ if (error.code === 'EIO' || error.code === 'ENOTTY') {
80
81
  process.exit(0);
81
82
  }
82
83
  });
83
-
84
- // 暂停输入流
85
84
  process.stdin.pause();
86
85
 
87
- // 再等待 100ms 确保终端状态稳定
88
86
  await new Promise(resolve => setTimeout(resolve, 100));
89
87
 
90
- // 使用 execSync 完全替代当前进程
91
- // ct 会阻塞等待 claude 完成,然后一起退出
92
- // Claude 独占终端,不会有输入冲突
93
- try {
94
- const command = `claude ${args.join(' ')}`;
95
-
96
- // 切换到目标目录
97
- const originalCwd = process.cwd();
98
- try {
99
- process.chdir(cwd);
100
- } catch (e) {
101
- console.log(chalk.yellow(`\n[WARN] 无法切换到目录: ${cwd}`));
102
- console.log(chalk.gray(`将在当前目录启动\n`));
103
- }
88
+ const launchOptions = {
89
+ fork,
90
+ config
91
+ };
92
+ if (options.cwd !== undefined) launchOptions.cwd = options.cwd;
93
+ if (options.processRunner) launchOptions.processRunner = options.processRunner;
104
94
 
105
- // execSync 会阻塞并完全接管终端
106
- // 此时 ct 进程只是等待,不处理任何输入
107
- execSync(command, {
108
- stdio: 'inherit', // 完全继承 stdio,让 claude 控制终端
109
- windowsHide: true,
110
- });
95
+ let launchResult;
96
+ try {
97
+ launchResult = unwrapDriverResult(await resolved.operation(sessionId, launchOptions));
98
+ } catch (error) {
99
+ launchResult = createFailureResult(error, resolved.key);
100
+ }
111
101
 
112
- // 恢复目录
113
- try {
114
- process.chdir(originalCwd);
115
- } catch (e) {
116
- // 忽略
102
+ if (isDriverResult(launchResult) && launchResult.status !== 'ok') {
103
+ reportFailure(launchResult);
104
+ if (launchResult.status === 'unsupported' || launchResult.status === 'not_found') {
105
+ return launchResult;
117
106
  }
107
+ process.exit(1);
108
+ return launchResult;
109
+ }
118
110
 
119
- // Claude 正常退出 - 立即退出避免清理错误
111
+ cwd = launchResult?.cwd || cwd;
112
+ if (launchResult?.signal === 'SIGINT') {
120
113
  process.exit(0);
121
- } catch (error) {
122
- // Claude 执行出错或被用户中断(Ctrl+C)
123
- // 立即退出,不等待清理
124
- if (error.status !== undefined) {
125
- process.exit(error.status);
126
- } else if (error.signal === 'SIGINT') {
127
- // 用户按了 Ctrl+C,正常退出
128
- process.exit(0);
129
- } else {
130
- console.log(chalk.red(`\n[ERROR] 启动失败: ${error.message}`));
131
- process.exit(1);
114
+ return launchResult;
115
+ }
116
+ if (launchResult?.error) {
117
+ console.log(chalk.red(`\n[ERROR] 启动失败: ${launchResult.error.message || launchResult.error}`));
118
+ process.exit(1);
119
+ return launchResult;
120
+ }
121
+ if (launchResult?.status !== undefined && launchResult.status !== null) {
122
+ const exitStatus = Number(launchResult.status);
123
+ if (Number.isFinite(exitStatus) && exitStatus !== 0) {
124
+ process.exit(exitStatus);
125
+ return launchResult;
132
126
  }
133
127
  }
128
+ process.exit(0);
129
+ return launchResult;
134
130
  }
135
131
 
136
132
  module.exports = {
@@ -4,40 +4,120 @@ const ora = require('ora');
4
4
  const inquirer = require('inquirer');
5
5
  const { promptSelectSession, promptSearchKeyword, promptForkConfirm } = require('../ui/prompts');
6
6
  const { resumeSession } = require('./resume');
7
- const { getProjects, searchSessions: searchSessionsInProject, parseRealProjectPath } = require('../server/services/sessions');
8
7
  const { loadAliases } = require('../server/services/alias');
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: 'sessions',
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 invokeSessionOperation(config, operation, args, options = {}) {
34
+ const platform = options.platform || config.currentCliType || '';
35
+ let resolved;
36
+ try {
37
+ resolved = resolveOperation(platform, 'sessions', operation, {
38
+ ...options,
39
+ fallback: 'claude'
40
+ });
41
+ } catch (error) {
42
+ return createFailureResult(error, platform, operation);
43
+ }
44
+
45
+ try {
46
+ return unwrapDriverResult(await resolved.operation(...args));
47
+ } catch (error) {
48
+ return createFailureResult(error, resolved.key, operation);
49
+ }
50
+ }
51
+
52
+ function normalizeSearchResults(value) {
53
+ const unwrapped = unwrapDriverResult(value);
54
+ if (isDriverResult(unwrapped)) return unwrapped;
55
+ if (Array.isArray(unwrapped)) return unwrapped;
56
+ if (Array.isArray(unwrapped?.sessions)) return unwrapped.sessions;
57
+ if (Array.isArray(unwrapped?.results)) return unwrapped.results;
58
+ return [];
59
+ }
60
+
61
+ function normalizeSearchResult(result = {}) {
62
+ const matches = Array.isArray(result.matches) ? result.matches : [];
63
+ const projectName = String(result.projectName || result.project || result.projectId || '');
64
+ return {
65
+ ...result,
66
+ sessionId: String(result.sessionId || result.id || ''),
67
+ projectName,
68
+ projectDisplayName: result.projectDisplayName || result.projectLabel || projectName || '未知项目',
69
+ matches,
70
+ matchCount: Number(result.matchCount ?? matches.length) || 0
71
+ };
72
+ }
73
+
74
+ function resolveLimitAndOptions(limitOrOptions, maybeOptions) {
75
+ if (limitOrOptions && typeof limitOrOptions === 'object' && !Array.isArray(limitOrOptions)) {
76
+ return {
77
+ limit: Number(limitOrOptions.limit) || 15,
78
+ options: limitOrOptions
79
+ };
80
+ }
81
+ return {
82
+ limit: Number(limitOrOptions) || 15,
83
+ options: maybeOptions || {}
84
+ };
85
+ }
86
+
87
+ function reportDriverFailure(result) {
88
+ if (!isDriverResult(result)) return;
89
+ const message = result.error || `会话操作 ${result.operation || 'unknown'} 不可用`;
90
+ console.log(chalk.yellow(`\n[WARN] ${message}\n`));
91
+ }
9
92
 
10
93
  /**
11
94
  * 跨所有项目搜索会话内容
12
95
  */
13
- async function searchSessionsAcrossProjects(config, keyword) {
96
+ async function searchSessionsAcrossProjects(config = {}, keyword, limitOrOptions = 15, maybeOptions = {}) {
97
+ const { limit, options } = resolveLimitAndOptions(limitOrOptions, maybeOptions);
14
98
  const spinner = ora(`[SEARCH] 正在搜索 "${keyword}"...`).start();
99
+ const result = await invokeSessionOperation(
100
+ config,
101
+ 'searchAcrossProjects',
102
+ [keyword, limit, { config }],
103
+ options
104
+ );
105
+ const searchResults = normalizeSearchResults(result);
106
+
107
+ if (isDriverResult(searchResults)) {
108
+ spinner.stop();
109
+ spinner.clear();
110
+ return searchResults;
111
+ }
15
112
 
16
- const projects = await getProjects(config);
17
113
  const aliases = loadAliases();
18
- const allResults = [];
19
-
20
- // 跨所有项目搜索
21
- for (const projectName of projects) {
22
- try {
23
- const { projectName: displayName } = parseRealProjectPath(projectName);
24
- spinner.text = `[SEARCH] 正在搜索项目: ${displayName}...`;
25
- const results = searchSessionsInProject(config, projectName, keyword, 15);
26
-
27
- if (results.length > 0) {
28
- results.forEach(result => {
29
- allResults.push({
30
- ...result,
31
- projectName: projectName,
32
- projectDisplayName: displayName,
33
- alias: aliases[result.sessionId] || null
34
- });
35
- });
36
- }
37
- } catch (e) {
38
- // 忽略单个项目的错误
39
- }
40
- }
114
+ const allResults = searchResults.map(item => {
115
+ const normalized = normalizeSearchResult(item);
116
+ return {
117
+ ...normalized,
118
+ alias: normalized.alias || aliases[normalized.sessionId] || null
119
+ };
120
+ });
41
121
 
42
122
  spinner.stop();
43
123
  spinner.clear();
@@ -48,66 +128,53 @@ async function searchSessionsAcrossProjects(config, keyword) {
48
128
  return [];
49
129
  }
50
130
 
51
- // 按匹配数量排序
52
131
  allResults.sort((a, b) => b.matchCount - a.matchCount);
53
-
54
- // 统计总匹配数
55
- const totalMatches = allResults.reduce((sum, r) => sum + r.matchCount, 0);
132
+ const totalMatches = allResults.reduce((sum, resultItem) => sum + resultItem.matchCount, 0);
56
133
 
57
134
  console.clear();
58
135
  console.log(chalk.green(`\n[NEW] 找到 ${allResults.length} 个对话,共 ${totalMatches} 处匹配\n`));
59
136
 
60
137
  const choices = [];
61
138
 
62
- allResults.forEach((result, index) => {
63
- // 构建显示名称
139
+ allResults.forEach((resultItem, index) => {
64
140
  let displayName = '';
65
-
66
- // 序号
67
141
  displayName += chalk.bold.white(`${index + 1}. `);
142
+ displayName += chalk.magenta.bold(`[${resultItem.projectDisplayName}] `);
68
143
 
69
- // 项目名(洋红色高亮)
70
- displayName += chalk.magenta.bold(`[${result.projectDisplayName}] `);
71
-
72
- // 会话别名或 ID
73
- if (result.alias) {
74
- displayName += chalk.yellow.bold(`[${result.alias}] `);
144
+ if (resultItem.alias) {
145
+ displayName += chalk.yellow.bold(`[${resultItem.alias}] `);
75
146
  } else {
76
- displayName += chalk.gray(`[${result.sessionId.substring(0, 8)}] `);
147
+ displayName += chalk.gray(`[${resultItem.sessionId.substring(0, 8)}] `);
77
148
  }
78
149
 
79
- // 匹配数量
80
- displayName += chalk.cyan(`(${result.matchCount} 处匹配)`);
150
+ displayName += chalk.cyan(`(${resultItem.matchCount} 处匹配)`);
81
151
 
82
152
  choices.push({
83
153
  name: displayName,
84
- value: { sessionId: result.sessionId, projectName: result.projectName },
85
- short: result.alias || result.sessionId.substring(0, 8)
154
+ value: { sessionId: resultItem.sessionId, projectName: resultItem.projectName },
155
+ short: resultItem.alias || resultItem.sessionId.substring(0, 8)
86
156
  });
87
157
 
88
- // 显示前 3 个匹配的上下文
89
- const matchesToShow = result.matches.slice(0, 3);
90
- matchesToShow.forEach((match, idx) => {
158
+ const matchesToShow = resultItem.matches.slice(0, 3);
159
+ matchesToShow.forEach(match => {
91
160
  const roleColor = match.role === 'user' ? chalk.blue : chalk.green;
92
161
  const roleLabel = match.role === 'user' ? '用户' : '助手';
93
162
 
94
163
  choices.push({
95
- name: ` ${roleColor(`[${roleLabel}]`)} ${chalk.gray(match.context)}`,
164
+ name: ` ${roleColor(`[${roleLabel}]`)} ${chalk.gray(match.context || match.text || '')}`,
96
165
  value: null,
97
166
  disabled: true
98
167
  });
99
168
  });
100
169
 
101
- // 如果还有更多匹配,显示提示
102
- if (result.matches.length > 3) {
170
+ if (resultItem.matches.length > 3) {
103
171
  choices.push({
104
- name: chalk.gray(` ... 还有 ${result.matches.length - 3} 处匹配`),
172
+ name: chalk.gray(` ... 还有 ${resultItem.matches.length - 3} 处匹配`),
105
173
  value: null,
106
174
  disabled: true
107
175
  });
108
176
  }
109
177
 
110
- // 添加分隔线(不是最后一个)
111
178
  if (index < allResults.length - 1) {
112
179
  choices.push(new inquirer.Separator(chalk.gray('─'.repeat(10))));
113
180
  }
@@ -119,11 +186,17 @@ async function searchSessionsAcrossProjects(config, keyword) {
119
186
  /**
120
187
  * 处理搜索会话
121
188
  */
122
- async function handleSearch(config, switchProjectCallback) {
189
+ async function handleSearch(config = {}, switchProjectCallback, options = {}) {
123
190
  while (true) {
124
191
  const keyword = await promptSearchKeyword();
125
- const choices = await searchSessionsAcrossProjects(config, keyword);
192
+ const result = await searchSessionsAcrossProjects(config, keyword, options);
126
193
 
194
+ if (isDriverResult(result)) {
195
+ reportDriverFailure(result);
196
+ return;
197
+ }
198
+
199
+ const choices = Array.isArray(result) ? result : [];
127
200
  if (choices.length === 0) {
128
201
  const { action } = await inquirer.prompt([
129
202
  {
@@ -132,29 +205,27 @@ async function handleSearch(config, switchProjectCallback) {
132
205
  message: '未找到匹配的对话',
133
206
  choices: [
134
207
  { name: chalk.blue('[<-] 返回主菜单'), value: 'back' },
135
- { name: chalk.cyan('[SEARCH] 重新搜索'), value: 'retry' },
136
- ],
137
- },
208
+ { name: chalk.cyan('[SEARCH] 重新搜索'), value: 'retry' }
209
+ ]
210
+ }
138
211
  ]);
139
212
 
140
213
  if (action === 'back') return;
141
214
  if (action === 'retry') continue;
142
215
  }
143
216
 
144
- // 添加操作选项
145
217
  choices.push(new inquirer.Separator(chalk.gray('='.repeat(80))));
146
218
  choices.push({ name: chalk.blue('[<-] 返回主菜单'), value: 'back' });
147
219
  choices.push({ name: chalk.cyan('[SEARCH] 重新搜索'), value: 'retry' });
148
220
 
149
- // 使用自定义 pageSize 以便显示更多结果
150
221
  const { selected } = await inquirer.prompt([
151
222
  {
152
223
  type: 'list',
153
224
  name: 'selected',
154
225
  message: '选择对话:',
155
226
  pageSize: 20,
156
- choices: choices,
157
- },
227
+ choices
228
+ }
158
229
  ]);
159
230
 
160
231
  if (selected === 'back') {
@@ -165,14 +236,10 @@ async function handleSearch(config, switchProjectCallback) {
165
236
  continue;
166
237
  }
167
238
 
168
- // selected 是 { sessionId, projectName }
169
239
  const sessionId = selected.sessionId;
170
240
  const projectName = selected.projectName;
171
-
172
- // 切换到该项目
173
241
  config.currentProject = projectName;
174
242
 
175
- // 询问是否 fork
176
243
  const action = await promptForkConfirm();
177
244
 
178
245
  if (action === 'back') {
@@ -180,7 +247,10 @@ async function handleSearch(config, switchProjectCallback) {
180
247
  }
181
248
 
182
249
  const fork = action === 'fork';
183
- await resumeSession(config, sessionId, fork);
250
+ await resumeSession(config, sessionId, fork, {
251
+ ...options,
252
+ platform: options.platform || config.currentCliType || ''
253
+ });
184
254
  }
185
255
  }
186
256
 
@@ -3,14 +3,22 @@ const http = require('http');
3
3
  const { loadConfig } = require('../config/loader');
4
4
  const { normalizePlatformKey } = require('../shared/platforms');
5
5
 
6
- const TOOL_TYPES = ['claude', 'codex', 'gemini', 'opencode', 'omp'];
7
- const TOOL_ENDPOINTS = {
8
- claude: '/api/claude/statistics',
9
- codex: '/api/codex/statistics',
10
- gemini: '/api/gemini/statistics',
11
- opencode: '/api/opencode/statistics',
12
- omp: '/api/omp/statistics'
13
- };
6
+ function getPlatformCommands() {
7
+ return require('./platform-command-registry').createPlatformCommandRegistry();
8
+ }
9
+
10
+ function getToolTypes() {
11
+ return getPlatformCommands().statsTypes();
12
+ }
13
+
14
+ function getToolDefinition(type) {
15
+ return getPlatformCommands().resolve(type);
16
+ }
17
+ function getToolEndpoint(type) {
18
+ const platform = getToolDefinition(type);
19
+ if (!platform || platform.capabilities?.statistics === 'unsupported') return null;
20
+ return platform.statisticsPath || `/api/${normalizePlatformKey(type)}/statistics`;
21
+ }
14
22
 
15
23
  /**
16
24
  * HTTP 请求辅助函数
@@ -81,7 +89,7 @@ async function checkUIService() {
81
89
 
82
90
  function validateToolType(type) {
83
91
  if (!type) return true;
84
- return TOOL_TYPES.includes(normalizePlatformKey(type));
92
+ return getToolTypes().includes(normalizePlatformKey(type));
85
93
  }
86
94
 
87
95
  function getDateString(offsetDays = 0) {
@@ -177,7 +185,7 @@ function getRangeDays(timeRange) {
177
185
 
178
186
  async function fetchToolStats(toolType, timeRange) {
179
187
  const normalizedToolType = normalizePlatformKey(toolType);
180
- const endpointBase = TOOL_ENDPOINTS[normalizedToolType];
188
+ const endpointBase = getToolEndpoint(normalizedToolType);
181
189
  if (!endpointBase) {
182
190
  throw new Error(`不支持的渠道类型: ${toolType}`);
183
191
  }
@@ -207,7 +215,7 @@ async function fetchOverallStats(timeRange) {
207
215
  const byToolType = {};
208
216
  const summary = emptySummary();
209
217
 
210
- for (const toolType of TOOL_TYPES) {
218
+ for (const toolType of getToolTypes()) {
211
219
  const toolSummary = await fetchToolStats(toolType, timeRange);
212
220
  byToolType[toolType] = toolSummary;
213
221
  mergeSummaries(summary, toolSummary);
@@ -252,7 +260,7 @@ async function handleStats(type = null, options = {}) {
252
260
  try {
253
261
  if (!validateToolType(normalizedType)) {
254
262
  console.error(chalk.red(`\n[ERROR] 无效的渠道类型: ${type}\n`));
255
- console.log(chalk.gray('支持的类型: claude, codex, gemini, opencode, omp\n'));
263
+ console.log(chalk.gray(`支持的类型: ${getToolTypes().join(', ')}\n`));
256
264
  process.exit(1);
257
265
  }
258
266
 
@@ -320,11 +328,11 @@ function displayStats(stats) {
320
328
  }
321
329
 
322
330
  if (!type && stats.byToolType) {
323
- const iconMap = { claude: '[*]', codex: '[*]', gemini: '[*]', opencode: '[*]', omp: '[*]' };
324
331
  console.log(chalk.bold('\n[CH] 分渠道汇总:'));
325
- TOOL_TYPES.forEach((toolType) => {
332
+ getToolTypes().forEach((toolType) => {
326
333
  const item = stats.byToolType[toolType] || emptySummary();
327
- console.log(chalk.gray(` ${iconMap[toolType]} ${getToolDisplayName(toolType)}:`));
334
+ const iconToken = getToolDefinition(toolType)?.iconToken || '*';
335
+ console.log(chalk.gray(` [${iconToken}] ${getToolDisplayName(toolType)}:`));
328
336
  console.log(
329
337
  chalk.gray(
330
338
  ` 请求: ${formatNumber(item.requests)} | Tokens: ${formatNumber(item.tokens)} | 成本: $${normalizeNumber(item.cost).toFixed(4)}`
@@ -333,17 +341,15 @@ function displayStats(stats) {
333
341
  });
334
342
  }
335
343
 
336
- console.log(chalk.gray('\n[TIP] 提示:'));
337
- console.log(chalk.gray(' • 使用 ') + chalk.cyan('ctx stats --today') + chalk.gray(' 查看今日统计'));
338
- console.log(chalk.gray(' • 使用 ') + chalk.cyan('ctx stats claude') + chalk.gray(' 查看特定渠道'));
339
- console.log(chalk.gray(' • 使用 ') + chalk.cyan('ctx stats omp') + chalk.gray(' 查看 OMP 统计'));
340
- console.log(chalk.gray(' • 使用 ') + chalk.cyan('ctx stats opencode') + chalk.gray(' 查看 OpenCode 统计'));
341
344
  console.log(chalk.gray(' • 使用 ') + chalk.cyan('ctx stats export') + chalk.gray(' 导出统计数据\n'));
342
345
  }
343
346
 
344
347
  function getToolDisplayName(type) {
345
348
  const normalizedType = normalizePlatformKey(type);
346
- if (normalizedType === 'omp') return 'OMP';
349
+ const platform = getToolDefinition(normalizedType);
350
+ if (platform?.proxyMode === 'managed') {
351
+ return platform.helpLabel || platform.label || normalizedType.toUpperCase();
352
+ }
347
353
  return String(normalizedType || '').toUpperCase();
348
354
  }
349
355
 
@@ -371,7 +377,7 @@ async function handleStatsExport(type = null, format = 'json') {
371
377
  try {
372
378
  if (!validateToolType(normalizedType)) {
373
379
  console.error(chalk.red(`\n[ERROR] 无效的渠道类型: ${type}\n`));
374
- console.log(chalk.gray('支持的类型: claude, codex, gemini, opencode, omp\n'));
380
+ console.log(chalk.gray(`支持的类型: ${getToolTypes().join(', ')}\n`));
375
381
  process.exit(1);
376
382
  }
377
383
 
@@ -411,6 +417,8 @@ module.exports = {
411
417
  buildDisplayPayload,
412
418
  fetchToolStats,
413
419
  getToolDisplayName,
420
+ getToolEndpoint,
421
+ getToolTypes,
414
422
  validateToolType
415
423
  }
416
424
  };
@@ -31,7 +31,7 @@ async function switchProject(config) {
31
31
  saveConfig({ ...config });
32
32
 
33
33
  // 使用解析后的名称显示
34
- const { parseRealProjectPath } = require('../server/services/sessions');
34
+ const { parseRealProjectPath } = require('../platforms/drivers/claude/sessions-implementation');
35
35
  const { displayName, fullPath } = parseRealProjectPath(selectedProject);
36
36
 
37
37
  console.log(chalk.green(`\n[OK] 已切换到: ${displayName}\n`));