coding-tool-x 3.8.3 → 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 +18 -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 +13 -8
  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
@@ -4,10 +4,48 @@ const fs = require('fs');
4
4
  const path = require('path');
5
5
  const crypto = require('crypto');
6
6
  const { HOME_DIR } = require('../../../config/paths');
7
- const { getGeminiDir } = require('../gemini-config');
7
+ const { getGeminiDir } = require('./config');
8
8
 
9
9
  const HASH_RE = /^[a-f0-9]{64}$/;
10
10
  const SESSION_FILE_RE = /^session-(.*)-([a-f0-9]+)\.(json|jsonl)$/;
11
+ const DESCRIPTOR_HEADER_BYTES = 64 * 1024;
12
+
13
+ function configure({ pathContext } = {}) {
14
+ require('./config').configure?.({ pathContext });
15
+ }
16
+
17
+ function readDescriptorMetadata(filePath, size) {
18
+ let fd;
19
+ try {
20
+ fd = fs.openSync(filePath, 'r');
21
+ const buffer = Buffer.alloc(Math.min(Math.max(Number(size) || 0, 1), DESCRIPTOR_HEADER_BYTES));
22
+ const bytesRead = fs.readSync(fd, buffer, 0, buffer.length, 0);
23
+ const chunk = buffer.toString('utf8', 0, bytesRead);
24
+ let state = null;
25
+ try {
26
+ const parsed = JSON.parse(chunk);
27
+ state = parsed?.$set || parsed;
28
+ } catch (_) {
29
+ const firstLine = chunk.split(/\r?\n/, 1)[0];
30
+ try {
31
+ const parsed = JSON.parse(firstLine);
32
+ state = parsed?.$set || parsed;
33
+ } catch (_) {}
34
+ }
35
+ if (!state || typeof state !== 'object') return {};
36
+ return {
37
+ sessionId: state.sessionId || state.id || null,
38
+ projectHash: state.projectHash || state.project_hash || null,
39
+ projectPath: state.projectPath || state.project_path || state.cwd || null
40
+ };
41
+ } catch (_) {
42
+ return {};
43
+ } finally {
44
+ if (fd !== undefined) {
45
+ try { fs.closeSync(fd); } catch (_) {}
46
+ }
47
+ }
48
+ }
11
49
 
12
50
  /**
13
51
  * Extract text content from Gemini message parts.
@@ -30,10 +68,10 @@ function extractContentText(value) {
30
68
  /**
31
69
  * Load storage-name → project root mapping from projects.json.
32
70
  */
33
- function loadProjectRootsByStorageName() {
71
+ async function loadProjectRootsByStorageName() {
34
72
  const projectsPath = path.join(getGeminiDir(), 'projects.json');
35
73
  try {
36
- const content = fs.readFileSync(projectsPath, 'utf8');
74
+ const content = await fs.promises.readFile(projectsPath, 'utf8');
37
75
  const parsed = JSON.parse(content);
38
76
  const projects = parsed && typeof parsed.projects === 'object' ? parsed.projects : {};
39
77
  return new Map(
@@ -122,7 +160,7 @@ function parseSessionContentFromState(state, sessionId) {
122
160
  const meta = {
123
161
  sessionId: state.sessionId || state.id || sessionId,
124
162
  projectHash: state.projectHash || state.project_hash || null,
125
- projectPath: state.projectPath || state.project_path || null,
163
+ projectPath: state.projectPath || state.project_path || state.cwd || null,
126
164
  lastUpdated: state.lastUpdated || state.last_updated || null,
127
165
  messages: state.messages || []
128
166
  };
@@ -194,12 +232,16 @@ async function inventory() {
194
232
  const descriptors = [];
195
233
  const tmpDir = path.join(getGeminiDir(), 'tmp');
196
234
 
197
- if (!fs.existsSync(tmpDir)) return descriptors;
235
+ try {
236
+ await fs.promises.stat(tmpDir);
237
+ } catch (_) {
238
+ return descriptors;
239
+ }
198
240
 
199
- const projectRoots = loadProjectRootsByStorageName();
241
+ const projectRoots = await loadProjectRootsByStorageName();
200
242
  let entries;
201
243
  try {
202
- entries = fs.readdirSync(tmpDir, { withFileTypes: true });
244
+ entries = await fs.promises.readdir(tmpDir, { withFileTypes: true });
203
245
  } catch (_) {
204
246
  return descriptors;
205
247
  }
@@ -208,11 +250,15 @@ async function inventory() {
208
250
  if (!entry.isDirectory()) continue;
209
251
  const hashDir = path.join(tmpDir, entry.name);
210
252
  const chatsDir = path.join(hashDir, 'chats');
211
- if (!fs.existsSync(chatsDir)) continue;
253
+ try {
254
+ await fs.promises.stat(chatsDir);
255
+ } catch (_) {
256
+ continue;
257
+ }
212
258
 
213
259
  let chatFiles;
214
260
  try {
215
- chatFiles = fs.readdirSync(chatsDir);
261
+ chatFiles = await fs.promises.readdir(chatsDir);
216
262
  } catch (_) {
217
263
  continue;
218
264
  }
@@ -223,24 +269,27 @@ async function inventory() {
223
269
  const filePath = path.join(chatsDir, f);
224
270
  let stat;
225
271
  try {
226
- stat = fs.statSync(filePath);
272
+ stat = await fs.promises.stat(filePath);
227
273
  } catch (_) {
228
274
  continue;
229
275
  }
230
- const projectRoot = projectRoots.get(entry.name) || (() => {
276
+ const header = readDescriptorMetadata(filePath, stat.size);
277
+ let projectRoot = projectRoots.get(entry.name) || null;
278
+ if (!projectRoot) {
231
279
  try {
232
- return fs.readFileSync(path.join(hashDir, '.project_root'), 'utf8').trim() || null;
280
+ projectRoot = (await fs.promises.readFile(path.join(hashDir, '.project_root'), 'utf8')).trim() || null;
233
281
  } catch (_) {
234
- return null;
282
+ projectRoot = null;
235
283
  }
236
- })();
284
+ }
285
+ projectRoot = header.projectPath || projectRoot;
237
286
 
238
287
  descriptors.push({
239
288
  filePath,
240
289
  size: stat.size,
241
290
  mtimeMs: stat.mtimeMs,
242
- sessionId: match[1],
243
- projectName: projectRoot ? getFilePathHash(projectRoot) : entry.name,
291
+ sessionId: header.sessionId || match[2] || match[1],
292
+ projectName: header.projectHash || (projectRoot ? getFilePathHash(projectRoot) : entry.name),
244
293
  projectRoot
245
294
  });
246
295
  }
@@ -257,8 +306,11 @@ async function parse(descriptor) {
257
306
 
258
307
  const { messages, meta } = parseSessionContent(filePath, sessionId);
259
308
  const resolvedSessionId = meta.sessionId || sessionId;
260
- const resolvedProjectName = meta.projectHash || projectName || 'unknown';
261
309
  const resolvedProjectPath = meta.projectPath || projectRoot || null;
310
+ const resolvedProjectName = meta.projectHash
311
+ || (resolvedProjectPath ? getFilePathHash(resolvedProjectPath) : null)
312
+ || projectName
313
+ || 'unknown';
262
314
  const firstUserMsg = messages.find(m => m.role === 'user' && m.content);
263
315
  const usage = messages.reduce((total, message) => {
264
316
  const value = message.usage || {};
@@ -289,4 +341,4 @@ async function parse(descriptor) {
289
341
  return { session, messages };
290
342
  }
291
343
 
292
- module.exports = { inventory, parse, extractContentText, normalizeMessageRecord, parseSessionContent };
344
+ module.exports = { configure, inventory, parse, extractContentText, normalizeMessageRecord, parseSessionContent };
@@ -1,10 +1,31 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
3
  const crypto = require('crypto');
4
- const { HOME_DIR } = require('../../config/paths');
5
- const { getGeminiDir } = require('./gemini-config');
6
- const { resolveModelPricing } = require('../utils/pricing');
7
- const { listProjects: idxListProjects, listSessions: idxListSessions, getSessionStatus: idxGetSessionStatus, getRecentSessions: idxGetRecent, searchSessions: idxSearch } = require('./session-history-index');
4
+ const { HOME_DIR } = require('../../../config/paths');
5
+ const { getGeminiDir } = require('./config');
6
+ const { normalizeUsageTokens } = require('../../../server/services/usage-log-utils');
7
+ const { resolveModelPricing, calculateTokenCost } = require('../../../server/utils/pricing');
8
+ let sessionHistoryIndex = null;
9
+ let geminiDirOverride = null;
10
+
11
+ function configure({ sessionHistoryIndex: index, pathContext } = {}) {
12
+ sessionHistoryIndex = index || null;
13
+ geminiDirOverride = pathContext?.customized ? (pathContext.native?.dir || null) : null;
14
+ }
15
+
16
+ function getSessionHistoryIndex(options = {}) {
17
+ if (!sessionHistoryIndex) {
18
+ const { getDefaultDependencies } = require('../../../platforms/runtime');
19
+ sessionHistoryIndex = getDefaultDependencies().sessionHistoryIndex;
20
+ if (!sessionHistoryIndex) {
21
+ const indexModule = require('../../../server/services/session-history-index');
22
+ sessionHistoryIndex = Object.keys(options).length > 0
23
+ ? indexModule.createSessionHistoryIndex({ config: options })
24
+ : indexModule;
25
+ }
26
+ }
27
+ return sessionHistoryIndex;
28
+ }
8
29
 
9
30
  const HASH_RE = /^[a-f0-9]{64}$/;
10
31
  const SESSION_FILE_RE = /^session-(.*)-([a-f0-9]+)\.(json|jsonl)$/;
@@ -18,7 +39,7 @@ const PATH_MAPPING_CACHE_TTL = 60000; // 1分钟缓存
18
39
  * 获取 Gemini tmp 目录(包含所有项目)
19
40
  */
20
41
  function getTmpDir() {
21
- return path.join(getGeminiDir(), 'tmp');
42
+ return path.join(geminiDirOverride || getGeminiDir(), 'tmp');
22
43
  }
23
44
 
24
45
  /**
@@ -52,7 +73,7 @@ function readProjectRootFile(projectDir) {
52
73
  }
53
74
 
54
75
  function loadProjectRootsByStorageName() {
55
- const projectsPath = path.join(getGeminiDir(), 'projects.json');
76
+ const projectsPath = path.join(geminiDirOverride || getGeminiDir(), 'projects.json');
56
77
  const content = readTextFile(projectsPath);
57
78
  if (!content) {
58
79
  return new Map();
@@ -487,17 +508,15 @@ function readSessionMeta(filePath) {
487
508
 
488
509
  messages.forEach(msg => {
489
510
  if (msg.tokens) {
490
- totalTokens += msg.tokens.total || 0;
511
+ const tokens = normalizeUsageTokens('gemini', msg.tokens);
512
+ totalTokens += tokens.total || 0;
491
513
 
492
- // 计算成本(简化版本,使用 gemini-2.5-pro 的定价)
493
514
  if (msg.model) {
494
515
  model = msg.model;
495
- const inputTokens = msg.tokens.input || 0;
496
- const outputTokens = msg.tokens.output || 0;
497
516
  const pricing = resolveModelPricing('gemini', msg.model);
498
- const inputRate = typeof pricing.input === 'number' ? pricing.input : 1.25;
499
- const outputRate = typeof pricing.output === 'number' ? pricing.output : 10;
500
- totalCost += (inputTokens * inputRate / 1000000) + (outputTokens * outputRate / 1000000);
517
+ totalCost += calculateTokenCost(pricing, tokens, {
518
+ reasoningBilledAsOutput: true
519
+ });
501
520
  }
502
521
  }
503
522
  });
@@ -630,7 +649,7 @@ function getProjectPath(projectHash, options = {}) {
630
649
  * @returns {Array} 项目对象数组
631
650
  */
632
651
  async function getProjects(options = {}) {
633
- const projects = await idxListProjects('gemini', options);
652
+ const projects = await getSessionHistoryIndex().listProjects('gemini', options);
634
653
  const paths = buildPathMapping();
635
654
  return projects.map(project => {
636
655
  const fullPath = project.fullPath || paths.get(project.name) || null;
@@ -651,7 +670,7 @@ async function getProjects(options = {}) {
651
670
  * @returns {Array} 会话对象数组
652
671
  */
653
672
  async function getProjectSessions(projectHash, options = {}) {
654
- const indexed = await idxListSessions('gemini', projectHash, options);
673
+ const indexed = await getSessionHistoryIndex().listSessions('gemini', projectHash, options);
655
674
  return indexed.map(session => ({
656
675
  sessionId: session.sessionId,
657
676
  mtime: new Date(session.mtime).toISOString(),
@@ -677,7 +696,7 @@ async function getProjectSessions(projectHash, options = {}) {
677
696
  * @returns {Object|null} 完整会话数据
678
697
  */
679
698
  async function getSession(sessionId) {
680
- const status = await idxGetSessionStatus('gemini', sessionId);
699
+ const status = await getSessionHistoryIndex().getSessionStatus('gemini', sessionId);
681
700
  return status ? readSessionFull(status.filePath) : null;
682
701
  }
683
702
 
@@ -696,6 +715,7 @@ function deleteSession(sessionId) {
696
715
 
697
716
  try {
698
717
  fs.unlinkSync(session.filePath);
718
+ getSessionHistoryIndex().invalidateSource('gemini');
699
719
  return { success: true, sessionId };
700
720
  } catch (err) {
701
721
  throw new Error('Failed to delete session: ' + err.message);
@@ -718,6 +738,7 @@ function deleteProject(projectHash) {
718
738
 
719
739
  try {
720
740
  fs.rmSync(projectDir, { recursive: true, force: true });
741
+ getSessionHistoryIndex().invalidateSource('gemini', { projectName: projectHash });
721
742
  return { success: true, projectHash };
722
743
  } catch (err) {
723
744
  throw new Error('Failed to delete project: ' + err.message);
@@ -740,7 +761,7 @@ function saveProjectOrder(order) {
740
761
  * @returns {Array} 会话对象数组
741
762
  */
742
763
  async function getRecentSessions(limit = 5) {
743
- const sessions = await idxGetRecent('gemini', limit);
764
+ const sessions = await getSessionHistoryIndex().getRecentSessions('gemini', limit);
744
765
  return sessions.map(session => ({
745
766
  ...session,
746
767
  mtime: new Date(session.mtime).toISOString(),
@@ -754,7 +775,7 @@ async function getRecentSessions(limit = 5) {
754
775
  * @returns {Object|null} 完整会话数据
755
776
  */
756
777
  async function getSessionById(sessionId) {
757
- const status = await idxGetSessionStatus('gemini', sessionId);
778
+ const status = await getSessionHistoryIndex().getSessionStatus('gemini', sessionId);
758
779
  if (!status) return null;
759
780
  const fullSession = readSessionFull(status.filePath);
760
781
  if (!fullSession) return null;
@@ -773,8 +794,11 @@ async function getSessionById(sessionId) {
773
794
  * @param {number} contextLength - 上下文长度(可选)
774
795
  * @returns {Array} 搜索结果数组
775
796
  */
776
- async function searchSessions(keyword, contextLength = 35) {
777
- const results = await idxSearch('gemini', keyword, { contextLength });
797
+ async function searchSessions(keyword, contextLength = 35, options = {}) {
798
+ const results = await getSessionHistoryIndex().searchSessions('gemini', keyword, {
799
+ ...options,
800
+ contextLength
801
+ });
778
802
  return results.map(result => ({
779
803
  sessionId: result.sessionId,
780
804
  projectHash: result.projectName,
@@ -879,10 +903,11 @@ function forkSession(sessionId, options = {}) {
879
903
  fs.writeFileSync(newFilePath, JSON.stringify(newSession, null, 2), 'utf8');
880
904
  }
881
905
  if (options.alias) {
882
- const { setAlias } = require('./alias');
906
+ const { setAlias } = require('../../../server/services/alias');
883
907
  setAlias(newSessionId, options.alias);
884
908
  }
885
909
 
910
+ getSessionHistoryIndex(options).invalidateSource('gemini');
886
911
  return {
887
912
  success: true,
888
913
  sessionId: newSessionId,
@@ -899,25 +924,16 @@ function forkSession(sessionId, options = {}) {
899
924
  /**
900
925
  * 获取 Gemini 项目与会话数量(仪表盘轻量统计)
901
926
  */
902
- function getProjectAndSessionCounts() {
903
- try {
904
- const projectEntries = scanProjectEntries();
905
- let sessionCount = 0;
906
- const projectHashes = new Set();
907
- projectEntries.forEach((entry) => {
908
- projectHashes.add(resolveProjectHashForEntry(entry));
909
- sessionCount += scanEntrySessionFiles(entry).length;
910
- });
911
- return {
912
- projectCount: Array.from(projectHashes).filter(Boolean).length,
913
- sessionCount
914
- };
915
- } catch (err) {
916
- return { projectCount: 0, sessionCount: 0 };
917
- }
927
+ async function getProjectAndSessionCounts(options = {}) {
928
+ const projects = await getSessionHistoryIndex().listProjects('gemini', options);
929
+ return {
930
+ projectCount: projects.length,
931
+ sessionCount: projects.reduce((sum, project) => sum + (project.sessionCount || 0), 0)
932
+ };
918
933
  }
919
934
 
920
935
  module.exports = {
936
+ configure,
921
937
  getAllSessions,
922
938
  getProjects,
923
939
  getProjectSessions,
@@ -0,0 +1,47 @@
1
+ 'use strict';
2
+
3
+ const { createCapabilityDriver } = require('../../../shared/capability-driver');
4
+
5
+ function createDriver(context = {}) {
6
+ return createCapabilityDriver({
7
+ ...context,
8
+ platform: 'gemini',
9
+ capability: 'sessions',
10
+ parserVersion: 2,
11
+ servicePath: './gemini/sessions-implementation',
12
+ localServicePath: '../platforms/drivers/gemini/sessions-implementation',
13
+ adapterLocalPath: '../platforms/drivers/gemini/session-history-adapter',
14
+ adapterMethods: { inventory: 'inventory', parse: 'parse' },
15
+ methods: {
16
+ listSessions: 'getProjectSessions',
17
+ recent: 'getRecentSessions',
18
+ getSessionById: 'getSessionById',
19
+ getAllSessions: 'getAllSessions',
20
+ delete: 'deleteSession',
21
+ fork: 'forkSession',
22
+ saveSessionOrder: 'saveSessionOrder',
23
+ getProjectPath: 'getProjectPath',
24
+ status: 'getSessionStatus',
25
+ messages: 'getSessionMessages'
26
+ },
27
+ customMethods: {
28
+ search: (service, projectName, keyword, contextLength, options = {}) => service.searchSessions(
29
+ keyword,
30
+ contextLength,
31
+ { ...options, projectName }
32
+ ),
33
+ searchAcrossProjects: (service, keyword, contextLength, options = {}) => service.searchSessions(
34
+ keyword,
35
+ contextLength,
36
+ options
37
+ )
38
+ },
39
+ onSuccess: operation => {
40
+ if (['delete', 'fork', 'saveSessionOrder'].includes(operation)) {
41
+ context.sessionHistoryIndex?.invalidateSource('gemini');
42
+ }
43
+ }
44
+ });
45
+ }
46
+
47
+ module.exports = { createDriver };
@@ -3,8 +3,8 @@ const {
3
3
  getStatistics: getSharedStatistics,
4
4
  getDailyStatistics: getSharedDailyStatistics,
5
5
  getTodayStatistics: getSharedTodayStatistics
6
- } = require('./statistics-service');
7
- const { normalizeUsageTokens, toNumber } = require('./proxy-log-helper');
6
+ } = require('../../../server/services/statistics-service');
7
+ const { normalizeUsageTokens, toNumber } = require('../../../server/services/usage-log-utils');
8
8
 
9
9
  const TOOL_TYPE = 'gemini';
10
10
 
@@ -0,0 +1,16 @@
1
+ 'use strict';
2
+
3
+ const { createCapabilityDriver } = require('../../../shared/capability-driver');
4
+
5
+ function createDriver(context = {}) {
6
+ return createCapabilityDriver({
7
+ ...context,
8
+ platform: 'gemini',
9
+ capability: 'statistics',
10
+ servicePath: './gemini/statistics-implementation',
11
+ localServicePath: '../platforms/drivers/gemini/statistics-implementation',
12
+ methods: {"getStatistics":"getStatistics","getDailyStatistics":"getDailyStatistics","getTodayStatistics":"getTodayStatistics","recordRequest":"recordRequest","resetStatistics":"resetStatistics"}
13
+ });
14
+ }
15
+
16
+ module.exports = { createDriver };
@@ -6,7 +6,7 @@ const {
6
6
  normalizeToolResultContent,
7
7
  extractText,
8
8
  normalizeStopSequences
9
- } = require('./opencode-normalization');
9
+ } = require('../opencode/normalization');
10
10
 
11
11
  // ---------------------------------------------------------------------------
12
12
  // Gemini CLI constants
@@ -0,0 +1,166 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs/promises');
4
+ const path = require('path');
5
+
6
+ function failure({ platform, operation, type, name, error }) {
7
+ const result = {
8
+ status: 'failed',
9
+ platform,
10
+ capability: 'resourceSync',
11
+ operation,
12
+ error: error && error.message ? error.message : String(error)
13
+ };
14
+ if (type !== undefined) result.type = type;
15
+ if (name !== undefined) result.name = name;
16
+ if (error) Object.defineProperty(result, 'cause', { value: error, enumerable: false });
17
+ return result;
18
+ }
19
+
20
+ function mappedRoot(manifest, type) {
21
+ const mapping = manifest.resourceMappings || {};
22
+ return mapping[type] || (manifest.paths && manifest.paths[type]);
23
+ }
24
+
25
+ function normalizeName(name) {
26
+ const rawName = String(name || '');
27
+ if (path.isAbsolute(rawName)) throw new Error(`Resource path must be relative: ${name}`);
28
+ const normalizedName = path.normalize(rawName);
29
+ if (!normalizedName || normalizedName === '.' || normalizedName === '..' || normalizedName.startsWith(`..${path.sep}`)) {
30
+ throw new Error(`Resource path escapes target root: ${name}`);
31
+ }
32
+ return normalizedName;
33
+ }
34
+
35
+ function resolveTarget(root, name) {
36
+ const normalizedName = normalizeName(name);
37
+ const resolvedRoot = path.resolve(root);
38
+ const target = path.resolve(resolvedRoot, normalizedName);
39
+ const relative = path.relative(resolvedRoot, target);
40
+ if (relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
41
+ throw new Error(`Resource path escapes target root: ${name}`);
42
+ }
43
+ return target;
44
+ }
45
+ async function assertNoSymlinkComponents(fsImpl, target) {
46
+ if (typeof fsImpl.lstat !== 'function') return;
47
+ const resolved = path.resolve(target);
48
+ const root = path.parse(resolved).root;
49
+ const parts = path.relative(root, resolved).split(path.sep).filter(Boolean);
50
+ let current = root;
51
+ for (let index = 0; index < parts.length; index += 1) {
52
+ current = path.join(current, parts[index]);
53
+ try {
54
+ const stat = await fsImpl.lstat(current);
55
+ if (index > 0 && stat.isSymbolicLink()) throw new Error(`Resource path contains symlink: ${target}`);
56
+ } catch (error) {
57
+ if (error.code === 'ENOENT') break;
58
+ throw error;
59
+ }
60
+ }
61
+ const finalStat = await fsImpl.lstat(resolved).catch(error => {
62
+ if (error.code === 'ENOENT') return null;
63
+ throw error;
64
+ });
65
+ if (finalStat && finalStat.isSymbolicLink()) throw new Error(`Resource path contains symlink: ${target}`);
66
+ }
67
+
68
+ async function assertSafeRoot(fsImpl, root, { allowMissingRoot = false } = {}) {
69
+ if (typeof fsImpl.realpath !== 'function' || typeof fsImpl.lstat !== 'function') return path.resolve(root);
70
+ const resolvedRoot = path.resolve(root);
71
+ await assertNoSymlinkComponents(fsImpl, resolvedRoot);
72
+ try {
73
+ return await fsImpl.realpath(resolvedRoot);
74
+ } catch (error) {
75
+ if (error.code !== 'ENOENT' || !allowMissingRoot) throw error;
76
+ let current = path.dirname(resolvedRoot);
77
+ while (true) {
78
+ try {
79
+ await assertNoSymlinkComponents(fsImpl, current);
80
+ return await fsImpl.realpath(current);
81
+ } catch (parentError) {
82
+ if (parentError.code !== 'ENOENT') throw parentError;
83
+ const next = path.dirname(current);
84
+ if (next === current) return resolvedRoot;
85
+ current = next;
86
+ }
87
+ }
88
+ }
89
+ }
90
+
91
+ async function assertContainedTarget(fsImpl, root, target, { allowMissingTarget = false, allowMissingRoot = false } = {}) {
92
+ if (typeof fsImpl.realpath !== 'function' || typeof fsImpl.lstat !== 'function') return;
93
+ const rootReal = await assertSafeRoot(fsImpl, root, { allowMissingRoot });
94
+ await assertNoSymlinkComponents(fsImpl, target);
95
+ const targetReal = await fsImpl.realpath(target).catch(error => {
96
+ if (allowMissingTarget && error.code === 'ENOENT') return null;
97
+ throw error;
98
+ });
99
+ if (!targetReal) return;
100
+ const relative = path.relative(rootReal, targetReal);
101
+ if (relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
102
+ throw new Error(`Resource path escapes target root: ${target}`);
103
+ }
104
+ }
105
+ function createGenericFilesystemDriver({ platform, manifest = {}, fsImpl = fs } = {}) {
106
+ function getRoot(type) {
107
+ const root = mappedRoot(manifest, type);
108
+ if (!root) throw new Error(`Unknown resource mapping: ${type}`);
109
+ return root;
110
+ }
111
+
112
+ return {
113
+ async list(type) {
114
+ try {
115
+ const root = getRoot(type);
116
+ await assertSafeRoot(fsImpl, root);
117
+ const names = await fsImpl.readdir(root);
118
+ const resources = [];
119
+ for (const name of names) {
120
+ const target = resolveTarget(root, name);
121
+ await assertContainedTarget(fsImpl, root, target);
122
+ const stat = await fsImpl.stat(target);
123
+ resources.push({ name, target, type: stat.isDirectory() ? 'directory' : 'file', size: stat.size, mtimeMs: stat.mtimeMs });
124
+ }
125
+ return resources;
126
+ } catch (error) {
127
+ return failure({ platform, operation: 'list', type, error });
128
+ }
129
+ },
130
+ async sync(type, name, sourceRoot) {
131
+ try {
132
+ const root = getRoot(type);
133
+ const target = resolveTarget(root, name);
134
+ await assertSafeRoot(fsImpl, root, { allowMissingRoot: true });
135
+ await assertContainedTarget(fsImpl, root, path.dirname(target), { allowMissingTarget: true, allowMissingRoot: true });
136
+ const sourceStat = await fsImpl.stat(sourceRoot);
137
+ await fsImpl.mkdir(path.dirname(target), { recursive: true });
138
+ await assertSafeRoot(fsImpl, root);
139
+ await assertContainedTarget(fsImpl, root, path.dirname(target));
140
+ await assertContainedTarget(fsImpl, root, target, { allowMissingTarget: true });
141
+ if (sourceStat.isDirectory()) {
142
+ await fsImpl.cp(sourceRoot, target, { recursive: true });
143
+ } else {
144
+ await fsImpl.copyFile(sourceRoot, target);
145
+ }
146
+ return { status: 'ok', target };
147
+ } catch (error) {
148
+ return failure({ platform, operation: 'sync', type, name, error });
149
+ }
150
+ },
151
+ async remove(type, name) {
152
+ try {
153
+ const root = getRoot(type);
154
+ const target = resolveTarget(root, name);
155
+ await assertSafeRoot(fsImpl, root);
156
+ await assertContainedTarget(fsImpl, root, target, { allowMissingTarget: true });
157
+ await fsImpl.rm(target, { recursive: true, force: true });
158
+ return { status: 'ok', target };
159
+ } catch (error) {
160
+ return failure({ platform, operation: 'remove', type, name, error });
161
+ }
162
+ }
163
+ };
164
+ }
165
+
166
+ module.exports = { createGenericFilesystemDriver };