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
@@ -6,18 +6,19 @@
6
6
  const fs = require('fs');
7
7
  const path = require('path');
8
8
  const https = require('https');
9
+ const { getPlatformCatalog } = require('./platform-catalog');
9
10
  const http = require('http');
10
11
  const { URL } = require('url');
11
12
  const crypto = require('crypto');
12
13
  const zlib = require('zlib');
13
14
  const { loadConfig } = require('../../config/loader');
14
- const { createCodexRequest, buildCodexTargetUrl } = require('./codex-wire');
15
- const { createClaudeRequest, buildClaudeTargetUrl } = require('./claude-wire');
15
+ const { createCodexRequest, buildCodexTargetUrl } = require('../../platforms/drivers/codex/wire');
16
+ const { createClaudeRequest, buildClaudeTargetUrl } = require('../../platforms/drivers/claude/wire');
16
17
  const {
17
18
  createGeminiRequest,
18
19
  buildGeminiTargetUrl,
19
20
  shouldUseGeminiCliFormat
20
- } = require('./gemini-wire');
21
+ } = require('../../platforms/drivers/gemini/wire');
21
22
  const { PATHS } = require('../../config/paths');
22
23
 
23
24
  // 内置模型优先级(当配置缺失时兜底)
@@ -60,11 +61,14 @@ MODEL_PRIORITY.openai_compatible = MODEL_PRIORITY.codex;
60
61
 
61
62
  function normalizeModelToolType(type) {
62
63
  const value = String(type || '').trim().toLowerCase();
64
+ if (!value) return '';
63
65
  if (value === 'openai_compatible') return 'codex';
64
- if (value === 'claude' || value === 'codex' || value === 'gemini' || value === 'opencode') {
65
- return value;
66
- }
67
- return '';
66
+ return value;
67
+ }
68
+
69
+ function getModelCatalogKey(toolType) {
70
+ const manifest = getPlatformCatalog().get(toolType);
71
+ return manifest?.modelConfig?.catalogKey || toolType;
68
72
  }
69
73
 
70
74
  /**
@@ -88,12 +92,12 @@ function getModelPriority(channelType, options = {}) {
88
92
  if (String(channelType || '').trim().toLowerCase() === 'openai_compatible' && !candidateTypes.includes('openai_compatible')) {
89
93
  candidateTypes.push('openai_compatible');
90
94
  }
91
-
92
95
  try {
93
96
  const config = loadConfig();
94
97
  const defaultModels = config?.defaultModels || {};
95
98
  for (const toolType of candidateTypes) {
96
- const models = defaultModels[toolType];
99
+ const catalogKey = getModelCatalogKey(toolType);
100
+ const models = defaultModels[catalogKey] || defaultModels[toolType];
97
101
  if (Array.isArray(models) && models.length > 0) {
98
102
  return normalizeModelCandidates(models);
99
103
  }
@@ -103,7 +107,8 @@ function getModelPriority(channelType, options = {}) {
103
107
  }
104
108
 
105
109
  for (const toolType of candidateTypes) {
106
- const models = MODEL_PRIORITY[toolType];
110
+ const catalogKey = getModelCatalogKey(toolType);
111
+ const models = MODEL_PRIORITY[catalogKey] || MODEL_PRIORITY[toolType];
107
112
  if (Array.isArray(models) && models.length > 0) {
108
113
  return normalizeModelCandidates(models);
109
114
  }
@@ -0,0 +1,214 @@
1
+ 'use strict';
2
+
3
+ const CHANNEL_PLACEHOLDERS = new Set(['', 'unknown', 'unkonwn', 'n/a', 'na', 'none', '-']);
4
+
5
+ function text(value) {
6
+ return typeof value === 'string' ? value.trim() : String(value || '').trim();
7
+ }
8
+
9
+ function normalizeIdentity(value) {
10
+ return text(value).toLowerCase();
11
+ }
12
+
13
+ function isChannelPlaceholder(value) {
14
+ return CHANNEL_PLACEHOLDERS.has(normalizeIdentity(value));
15
+ }
16
+
17
+ function meaningfulText(value) {
18
+ const result = text(value);
19
+ return isChannelPlaceholder(result) ? '' : result;
20
+ }
21
+
22
+ function identityAliases(value) {
23
+ const normalized = normalizeIdentity(value);
24
+ if (!normalized) return [];
25
+ const aliases = new Set([normalized]);
26
+ if (normalized.startsWith('ctx-')) aliases.add(normalized.slice(4));
27
+ if (normalized.startsWith('provider:')) aliases.add(normalized.slice('provider:'.length));
28
+ return [...aliases].filter(Boolean);
29
+ }
30
+
31
+ function normalizeUrl(value) {
32
+ const raw = text(value);
33
+ if (!raw) return '';
34
+ try {
35
+ const url = new URL(raw);
36
+ url.hash = '';
37
+ url.search = '';
38
+ url.pathname = url.pathname.replace(/\/+$/, '');
39
+ return url.toString().toLowerCase().replace(/\/$/, '');
40
+ } catch (_) {
41
+ return raw.toLowerCase().replace(/\/+$/, '');
42
+ }
43
+ }
44
+
45
+ function unwrapChannels(value) {
46
+ if (value && typeof value === 'object' && typeof value.status === 'string' && value.status !== 'ok') {
47
+ return [];
48
+ }
49
+ const data = value && typeof value === 'object' && Object.prototype.hasOwnProperty.call(value, 'data')
50
+ ? value.data
51
+ : value;
52
+ const channels = Array.isArray(data) ? data : data?.channels;
53
+ return Array.isArray(channels) ? channels.filter(channel => channel && typeof channel === 'object') : [];
54
+ }
55
+
56
+ function nestedValues(source, keys) {
57
+ if (!source || typeof source !== 'object') return [];
58
+ const values = [];
59
+ for (const key of keys) {
60
+ if (source[key] != null) values.push(source[key]);
61
+ }
62
+ return values;
63
+ }
64
+
65
+ function channelSources(channel) {
66
+ return [channel, channel?.extra, channel?.providerConfig]
67
+ .filter(source => source && typeof source === 'object');
68
+ }
69
+
70
+ function channelIdentityValues(channel) {
71
+ const values = [];
72
+ for (const source of channelSources(channel)) {
73
+ values.push(...nestedValues(source, [
74
+ 'id', 'name', 'providerKey', 'provider', 'providerId', 'providerID',
75
+ 'managedProviderId', 'oauthProviderId', 'providerName'
76
+ ]));
77
+ if (source.authRef && typeof source.authRef === 'object') {
78
+ values.push(...nestedValues(source.authRef, ['providerId', 'providerID', 'accountId', 'identityKey']));
79
+ }
80
+ }
81
+ return values.flatMap(identityAliases);
82
+ }
83
+
84
+ function eventIdentityValues(event) {
85
+ return nestedValues(event, [
86
+ 'provider', 'providerKey', 'providerId', 'providerID', 'managedProviderId', 'oauthProviderId'
87
+ ]).flatMap(identityAliases);
88
+ }
89
+
90
+ function channelUrlValues(channel) {
91
+ const values = [];
92
+ for (const source of channelSources(channel)) {
93
+ values.push(...nestedValues(source, ['baseUrl', 'baseURL', 'apiBaseUrl', 'apiBaseURL', 'endpoint', 'url']));
94
+ }
95
+ return values.map(normalizeUrl).filter(Boolean);
96
+ }
97
+
98
+ function eventUrlValues(event) {
99
+ return nestedValues(event, [
100
+ 'baseUrl', 'baseURL', 'apiBaseUrl', 'apiBaseURL', 'endpoint', 'url', 'providerBaseUrl'
101
+ ])
102
+ .concat(event?.providerConfig && typeof event.providerConfig === 'object'
103
+ ? nestedValues(event.providerConfig, ['baseUrl', 'baseURL', 'apiBaseUrl', 'apiBaseURL', 'endpoint', 'url'])
104
+ : [])
105
+ .map(normalizeUrl)
106
+ .filter(Boolean);
107
+ }
108
+
109
+ function modelAliases(value) {
110
+ const normalized = normalizeIdentity(value);
111
+ if (!normalized) return [];
112
+ const aliases = new Set([normalized]);
113
+ const slashIndex = normalized.lastIndexOf('/');
114
+ if (slashIndex >= 0) aliases.add(normalized.slice(slashIndex + 1));
115
+ return [...aliases].filter(Boolean);
116
+ }
117
+
118
+ function modelValues(value) {
119
+ if (Array.isArray(value)) return value.flatMap(modelValues);
120
+ if (value && typeof value === 'object') {
121
+ return nestedValues(value, ['id', 'name', 'model', 'modelId', 'modelID']).flatMap(modelValues);
122
+ }
123
+ return modelAliases(value);
124
+ }
125
+
126
+ function channelModelValues(channel) {
127
+ const values = [];
128
+ for (const source of channelSources(channel)) {
129
+ values.push(...nestedValues(source, [
130
+ 'model', 'models', 'allowedModels', 'modelConfig', 'modelRedirects', 'modelBindings'
131
+ ]).flatMap(modelValues));
132
+ }
133
+ return values;
134
+ }
135
+
136
+ function eventModelValues(event) {
137
+ return modelAliases(event?.model || event?.modelId || event?.modelID);
138
+ }
139
+
140
+ function matchesBy(channels, values, getValues) {
141
+ const wanted = new Set(values);
142
+ if (!wanted.size) return [];
143
+ return channels.filter(channel => getValues(channel).some(value => wanted.has(value)));
144
+ }
145
+
146
+ function matchedChannel(channel) {
147
+ const channelId = text(channel?.id);
148
+ const channelName = text(channel?.name) || text(channel?.providerKey) || channelId;
149
+ return {
150
+ ...(channelId ? { channelId } : {}),
151
+ ...(channelName ? { channel: channelName } : {})
152
+ };
153
+ }
154
+
155
+ function fallbackChannel(event) {
156
+ const channel = meaningfulText(event?.channel) || meaningfulText(event?.provider)
157
+ || meaningfulText(event?.providerKey) || meaningfulText(event?.providerId)
158
+ || meaningfulText(event?.channelId);
159
+ return channel ? { channel } : {};
160
+ }
161
+
162
+ /**
163
+ * Resolve the configured channel responsible for a native CLI usage event.
164
+ * Native log formats do not agree on a channel field, so provider/model metadata
165
+ * is matched against the channel registry at the observation boundary.
166
+ */
167
+ function resolveNativeLogChannel(event = {}, channels = []) {
168
+ const allChannels = unwrapChannels(channels);
169
+ const enabledChannels = allChannels.filter(channel => channel.enabled !== false);
170
+
171
+ const explicitId = normalizeIdentity(event.channelId);
172
+ if (explicitId) {
173
+ const match = allChannels.find(channel => normalizeIdentity(channel.id) === explicitId);
174
+ if (match) return matchedChannel(match);
175
+ return { channelId: text(event.channelId), channel: text(event.channel) || text(event.channelId) };
176
+ }
177
+
178
+ const explicitChannel = isChannelPlaceholder(event.channel) ? '' : normalizeIdentity(event.channel);
179
+ if (explicitChannel) {
180
+ const matches = matchesBy(allChannels, identityAliases(event.channel), channelIdentityValues);
181
+ if (matches.length === 1) return matchedChannel(matches[0]);
182
+ return { channel: meaningfulText(event.channel) };
183
+ }
184
+
185
+ const providerMatches = matchesBy(allChannels, eventIdentityValues(event), channelIdentityValues);
186
+ if (providerMatches.length === 1) return matchedChannel(providerMatches[0]);
187
+
188
+ const eventUrls = eventUrlValues(event);
189
+ const eventModels = eventModelValues(event);
190
+ if (providerMatches.length > 1 && eventModels.length) {
191
+ const providerModelMatches = matchesBy(providerMatches, eventModels, channelModelValues);
192
+ if (providerModelMatches.length === 1) return matchedChannel(providerModelMatches[0]);
193
+ }
194
+ if (providerMatches.length > 1 && eventUrls.length) {
195
+ const providerUrlMatches = matchesBy(providerMatches, eventUrls, channelUrlValues);
196
+ if (providerUrlMatches.length === 1) return matchedChannel(providerUrlMatches[0]);
197
+ }
198
+
199
+ if (eventUrls.length) {
200
+ const urlMatches = matchesBy(allChannels, eventUrls, channelUrlValues);
201
+ if (urlMatches.length === 1) return matchedChannel(urlMatches[0]);
202
+ }
203
+ if (eventModels.length) {
204
+ const modelMatches = matchesBy(allChannels, eventModels, channelModelValues);
205
+ if (modelMatches.length === 1) return matchedChannel(modelMatches[0]);
206
+ }
207
+
208
+ // A provider-less native format is still resolvable when only one channel is
209
+ // currently enabled. Do not guess when several channels could have produced it.
210
+ if (enabledChannels.length === 1) return matchedChannel(enabledChannels[0]);
211
+ return fallbackChannel(event);
212
+ }
213
+
214
+ module.exports = { resolveNativeLogChannel, unwrapChannels, isChannelPlaceholder };
@@ -0,0 +1,291 @@
1
+ 'use strict';
2
+
3
+ const eventBus = require('../../plugins/event-bus');
4
+ const { getPlatformRegistry, getPlatformRuntime } = require('../../platforms/runtime');
5
+ const { buildSuccessLogPayload, hasMeaningfulUsage, normalizeUsageTokens } = require('./usage-log-utils');
6
+ const { resolveNativeLogChannel, unwrapChannels, isChannelPlaceholder } = require('./native-log-channel-resolver');
7
+ const { broadcastLog } = require('../websocket-server');
8
+
9
+ const DEFAULT_INTERVAL_MS = 5000;
10
+ let pollTimer = null;
11
+ let enabled = false;
12
+ let intervalMs = DEFAULT_INTERVAL_MS;
13
+ let cursors = new Map();
14
+ let cursorDiagnostics = new Map();
15
+ let firstPollObserved = false;
16
+ let configSavedListener = null;
17
+ let lifecycleState = 'stopped';
18
+
19
+ function memoryTraceEnabled() {
20
+ return process.env.CC_TOOL_MEMORY_TRACE === '1';
21
+ }
22
+
23
+ function traceMemory(platform, stage, details = {}, durationMs = null) {
24
+ if (!memoryTraceEnabled()) return;
25
+ const usage = process.memoryUsage();
26
+ const payload = {
27
+ pid: process.pid,
28
+ platform,
29
+ stage,
30
+ ...(durationMs === null ? {} : { durationMs: Number(durationMs.toFixed(3)) }),
31
+ files: Number(details.files) || 0,
32
+ bytesRead: Number(details.bytesRead) || 0,
33
+ parsedRecords: Number(details.parsedRecords) || 0,
34
+ maxLineLength: Number(details.maxLineLength) || 0,
35
+ rss: usage.rss,
36
+ heapTotal: usage.heapTotal,
37
+ heapUsed: usage.heapUsed,
38
+ external: usage.external,
39
+ arrayBuffers: usage.arrayBuffers
40
+ };
41
+ console.log(`[MEM] native-log ${JSON.stringify(payload)}`);
42
+ }
43
+
44
+ function traceClock() {
45
+ return Number(process.hrtime.bigint()) / 1e6;
46
+ }
47
+
48
+ function normalizeInterval(value) {
49
+ const seconds = Number(value);
50
+ return Number.isFinite(seconds) && seconds >= 1 && seconds <= 60
51
+ ? seconds * 1000
52
+ : DEFAULT_INTERVAL_MS;
53
+ }
54
+
55
+ function getStatus() {
56
+ return {
57
+ running: Boolean(pollTimer),
58
+ state: lifecycleState,
59
+ enabled,
60
+ intervalMs,
61
+ platforms: [...cursors.keys()]
62
+ };
63
+ }
64
+
65
+ function closeCursor(cursor) {
66
+ try { cursor?.close?.(); } catch (_) {}
67
+ }
68
+
69
+ function resolveCursors(runtime = getPlatformRuntime(), registry = getPlatformRegistry()) {
70
+ const next = new Map();
71
+ const traceEnabled = memoryTraceEnabled();
72
+ const platforms = registry.list({ enabledOnly: true });
73
+ for (const platform of platforms) {
74
+ const key = platform.key;
75
+ let driver;
76
+ try { driver = runtime.getDriver(key, 'nativeLogs'); } catch (_) { driver = null; }
77
+ if (!driver || typeof driver.createNativeLogCursor !== 'function') continue;
78
+
79
+ const existing = cursors.get(key);
80
+ let cursor = existing;
81
+ if (!cursor) {
82
+ const options = { skipInitialParse: true };
83
+ if (traceEnabled) {
84
+ options.onDiagnostic = details => cursorDiagnostics.set(key, details || {});
85
+ }
86
+ cursor = driver.createNativeLogCursor(options);
87
+ const startedAt = traceClock();
88
+ traceMemory(key, 'cursor-initialize-before', {}, 0);
89
+ try { cursor.initialize?.(); } catch (error) {
90
+ console.warn(`[Native Logs] Failed to initialize ${key}:`, error.message);
91
+ }
92
+ traceMemory(key, 'cursor-initialize-after', cursorDiagnostics.get(key), traceClock() - startedAt);
93
+ }
94
+ next.set(key, cursor);
95
+ }
96
+ for (const [key, cursor] of cursors) {
97
+ if (!next.has(key)) {
98
+ closeCursor(cursor);
99
+ cursorDiagnostics.delete(key);
100
+ }
101
+ }
102
+ cursors = next;
103
+ }
104
+
105
+ function readConfiguredChannels(platform, runtime) {
106
+ let driver;
107
+ try { driver = runtime.getDriver(platform, 'channels'); } catch (_) { driver = null; }
108
+ if (!driver) return [];
109
+
110
+ // Read the full list first: a historical event may belong to a channel that
111
+ // was disabled after the CLI emitted the record.
112
+ for (const method of ['list', 'getChannels', 'getEnabled']) {
113
+ if (typeof driver[method] !== 'function') continue;
114
+ try {
115
+ const result = driver[method]();
116
+ if (result && typeof result.then === 'function') continue;
117
+ const channels = unwrapChannels(result);
118
+ if (channels.length) return channels;
119
+ } catch (_) {}
120
+ }
121
+ return [];
122
+ }
123
+
124
+ function recordEvent(platform, event, runtime = getPlatformRuntime(), channelCache = new Map()) {
125
+ const timestampValue = event.timestamp ? new Date(event.timestamp).getTime() : Date.now();
126
+ const timestamp = Number.isFinite(timestampValue) ? timestampValue : Date.now();
127
+ const tokens = normalizeUsageTokens(platform, event.tokens || event.usage || {});
128
+ if (!hasMeaningfulUsage(platform, tokens)) return false;
129
+ if (!channelCache.has(platform)) channelCache.set(platform, readConfiguredChannels(platform, runtime));
130
+ const resolvedChannel = resolveNativeLogChannel(event, channelCache.get(platform));
131
+ const channel = resolvedChannel.channel
132
+ || (isChannelPlaceholder(event.channel) ? '' : event.channel)
133
+ || (isChannelPlaceholder(event.provider) ? '' : event.provider)
134
+ || 'Unknown';
135
+ const channelId = resolvedChannel.channelId || event.channelId;
136
+
137
+ broadcastLog(buildSuccessLogPayload({
138
+ source: platform,
139
+ requestId: event.id,
140
+ channel,
141
+ model: event.model || '',
142
+ tokens,
143
+ cost: Number(event.cost) || 0,
144
+ timestamp,
145
+ usageMissing: false
146
+ }));
147
+
148
+ let statistics;
149
+ try { statistics = runtime.getDriver(platform, 'statistics'); } catch (_) { statistics = null; }
150
+ if (statistics && typeof statistics.recordRequest === 'function') {
151
+ statistics.recordRequest({
152
+ id: event.id,
153
+ timestamp: new Date(timestamp).toISOString(),
154
+ session: event.sessionId || null,
155
+ model: event.model || '',
156
+ channel,
157
+ channelId,
158
+ tokens,
159
+ cost: Number(event.cost) || 0,
160
+ duration: 0,
161
+ success: true
162
+ });
163
+ }
164
+ return true;
165
+ }
166
+
167
+ function pollNativeCliLogs({ runtime = getPlatformRuntime(), registry = getPlatformRegistry() } = {}) {
168
+ if (!enabled) return getStatus();
169
+ resolveCursors(runtime, registry);
170
+ const channelCache = new Map();
171
+ const traceFirstPoll = memoryTraceEnabled() && !firstPollObserved;
172
+ for (const [platform, cursor] of cursors) {
173
+ const startedAt = traceFirstPoll ? traceClock() : 0;
174
+ if (traceFirstPoll) traceMemory(platform, 'first-poll-before', cursorDiagnostics.get(platform), 0);
175
+ try {
176
+ const events = cursor.readNewEvents?.() || cursor.read?.() || [];
177
+ events.forEach(event => recordEvent(platform, event, runtime, channelCache));
178
+ } catch (error) {
179
+ console.warn(`[Native Logs] Failed to read ${platform}:`, error.message);
180
+ } finally {
181
+ if (traceFirstPoll) traceMemory(platform, 'first-poll-after', cursorDiagnostics.get(platform), traceClock() - startedAt);
182
+ }
183
+ }
184
+ if (traceFirstPoll) firstPollObserved = true;
185
+ return getStatus();
186
+ }
187
+
188
+ function clearTimer() {
189
+ if (pollTimer) clearInterval(pollTimer);
190
+ pollTimer = null;
191
+ }
192
+
193
+ function startTimer(options) {
194
+ clearTimer();
195
+ pollTimer = setInterval(() => {
196
+ try { pollNativeCliLogs(options); } catch (error) {
197
+ console.warn('[Native Logs] Poll failed:', error.message);
198
+ }
199
+ }, intervalMs);
200
+ if (typeof pollTimer.unref === 'function') pollTimer.unref();
201
+ }
202
+
203
+ function ensureConfigListener(options) {
204
+ if (configSavedListener) return;
205
+ configSavedListener = ({ config } = {}) => {
206
+ const next = config?.nativeCliLogs || {};
207
+ const nextOptions = { ...options, enabled: next.enabled, intervalSeconds: next.intervalSeconds };
208
+ if (lifecycleState === 'running') {
209
+ configureNativeCliLogObserver(nextOptions);
210
+ } else {
211
+ prepareNativeCliLogObserver(nextOptions);
212
+ }
213
+ };
214
+ eventBus.on('config:saved', configSavedListener);
215
+ }
216
+
217
+ function prepareNativeCliLogObserver({
218
+ enabled: nextEnabled = true,
219
+ intervalSeconds = 5,
220
+ runtime = getPlatformRuntime(),
221
+ registry = getPlatformRegistry()
222
+ } = {}) {
223
+ ensureConfigListener({ runtime, registry });
224
+ enabled = nextEnabled !== false;
225
+ intervalMs = normalizeInterval(intervalSeconds);
226
+ if (!enabled) {
227
+ clearTimer();
228
+ for (const cursor of cursors.values()) closeCursor(cursor);
229
+ cursors = new Map();
230
+ cursorDiagnostics = new Map();
231
+ firstPollObserved = false;
232
+ lifecycleState = 'stopped';
233
+ return getStatus();
234
+ }
235
+ resolveCursors(runtime, registry);
236
+ if (pollTimer) clearTimer();
237
+ lifecycleState = 'prepared';
238
+ return getStatus();
239
+ }
240
+
241
+ function startNativeCliLogObserver({
242
+ runtime = getPlatformRuntime(),
243
+ registry = getPlatformRegistry(),
244
+ pollImmediately = true
245
+ } = {}) {
246
+ if (!enabled) return getStatus();
247
+ resolveCursors(runtime, registry);
248
+ if (pollImmediately) {
249
+ try { pollNativeCliLogs({ runtime, registry }); } catch (error) {
250
+ console.warn('[Native Logs] Initial poll failed:', error.message);
251
+ }
252
+ }
253
+ startTimer({ runtime, registry });
254
+ lifecycleState = 'running';
255
+ return getStatus();
256
+ }
257
+
258
+ function configureNativeCliLogObserver(options = {}) {
259
+ prepareNativeCliLogObserver(options);
260
+ return startNativeCliLogObserver({
261
+ ...options,
262
+ // Preserve the legacy configure() behavior: it starts the interval but
263
+ // does not synchronously consume a new batch of native logs.
264
+ pollImmediately: options.pollImmediately === true
265
+ });
266
+ }
267
+
268
+ function shutdownNativeCliLogObserver() {
269
+ clearTimer();
270
+ for (const cursor of cursors.values()) closeCursor(cursor);
271
+ cursors = new Map();
272
+ cursorDiagnostics = new Map();
273
+ firstPollObserved = false;
274
+ enabled = false;
275
+ intervalMs = DEFAULT_INTERVAL_MS;
276
+ lifecycleState = 'stopped';
277
+ if (configSavedListener) {
278
+ eventBus.off('config:saved', configSavedListener);
279
+ configSavedListener = null;
280
+ }
281
+ return getStatus();
282
+ }
283
+
284
+ module.exports = {
285
+ prepareNativeCliLogObserver,
286
+ startNativeCliLogObserver,
287
+ configureNativeCliLogObserver,
288
+ shutdownNativeCliLogObserver,
289
+ pollNativeCliLogs,
290
+ _test: { getStatus, recordEvent, resolveCursors, readConfiguredChannels }
291
+ };
@@ -73,7 +73,7 @@ function createRemoteMutationGuard(options = {}) {
73
73
  }
74
74
 
75
75
  function isRemoteMutationAllowedByEnv(env = process.env) {
76
- return String(env.CC_TOOL_ALLOW_REMOTE_WRITE || '').toLowerCase() !== 'false';
76
+ return String(env.CC_TOOL_ALLOW_REMOTE_WRITE || '').toLowerCase() === 'true';
77
77
  }
78
78
 
79
79
  function createRemoteRouteGuard(options = {}) {
@@ -0,0 +1,82 @@
1
+ 'use strict';
2
+
3
+ const { normalizePlatformKey } = require('../../shared/platforms');
4
+ const { getPlatformContext } = require('../platform-context');
5
+
6
+ const WEB_UI_PORT = 19999;
7
+
8
+ function isSupportedCapability(manifest, capability) {
9
+ if (!capability) return true;
10
+ const driver = manifest?.capabilities?.[capability];
11
+ return driver !== undefined && driver !== null && driver !== 'unsupported';
12
+ }
13
+
14
+ function supportsResourceType(manifest, resourceType) {
15
+ if (!resourceType) return true;
16
+ if (manifest?.resourceTypes?.[resourceType] === false) return false;
17
+ return isSupportedCapability(manifest, 'resourceSync');
18
+ }
19
+
20
+ function createPlatformCatalog({ registry, runtime } = {}) {
21
+ if (!registry || typeof registry.list !== 'function') {
22
+ throw new TypeError('Platform catalog requires a registry');
23
+ }
24
+ if (!runtime || typeof runtime.getDriver !== 'function') {
25
+ throw new TypeError('Platform catalog requires a runtime');
26
+ }
27
+
28
+ function list({ capability, resourceType, enabledOnly = true } = {}) {
29
+ return registry.list({ enabledOnly })
30
+ .filter(manifest => manifest && manifest.key)
31
+ .filter(manifest => isSupportedCapability(manifest, capability))
32
+ .filter(manifest => supportsResourceType(manifest, resourceType));
33
+ }
34
+
35
+ function get(key) {
36
+ const normalizedKey = normalizePlatformKey(key);
37
+ return normalizedKey ? list({ enabledOnly: false }).find(manifest => (
38
+ normalizePlatformKey(manifest.key) === normalizedKey
39
+ )) || null : null;
40
+ }
41
+
42
+ function driver(key, capability) {
43
+ const manifest = get(key);
44
+ if (!manifest || !isSupportedCapability(manifest, capability)) return null;
45
+ const driverId = manifest.capabilities?.[capability];
46
+ if (!driverId || driverId === 'unsupported') return null;
47
+ const driver = runtime.getDriver(manifest.key, capability);
48
+ return driver && driver.status === 'unsupported' ? null : driver;
49
+ }
50
+
51
+ function ports() {
52
+ const entries = [{ key: 'webUI', defaultPort: WEB_UI_PORT, label: 'Web UI', platform: null }];
53
+ for (const manifest of list({ enabledOnly: true })) {
54
+ if (!manifest.portKey || !Number.isFinite(manifest.defaultPort)) continue;
55
+ entries.push({
56
+ key: manifest.portKey,
57
+ defaultPort: manifest.defaultPort,
58
+ label: manifest.portLabel || manifest.label || manifest.key,
59
+ platform: manifest.key
60
+ });
61
+ }
62
+ return entries;
63
+ }
64
+
65
+ return Object.freeze({
66
+ list,
67
+ keys(options = {}) {
68
+ return list(options).map(manifest => normalizePlatformKey(manifest.key));
69
+ },
70
+ get,
71
+ driver,
72
+ ports
73
+ });
74
+ }
75
+
76
+
77
+ function getPlatformCatalog() {
78
+ const context = getPlatformContext();
79
+ return createPlatformCatalog(context);
80
+ }
81
+
82
+ module.exports = { createPlatformCatalog, getPlatformCatalog, WEB_UI_PORT };