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
@@ -0,0 +1,215 @@
1
+ 'use strict';
2
+
3
+ const { applyRequestCodec, applyResponseCodec } = require('./platform-api-config');
4
+
5
+ const STATUS_CODES = Object.freeze({ ok: 200, invalid: 400, conflict: 409, unprocessable: 422, unsupported: 404, unavailable: 503, failed: 500 });
6
+
7
+ const DEFAULT_ROUTES = Object.freeze([
8
+ { method: 'GET', path: '/projects', capability: 'projects', operation: 'listProjects', request: 'projects-list', response: 'projects-list' },
9
+ { method: 'GET', path: '/sessions/:projectName', capability: 'sessions', operation: 'listSessions', request: 'sessions-list', response: 'sessions-list' },
10
+ { method: 'GET', path: '/channels', capability: 'channels', operation: 'list', request: 'default', response: 'default' },
11
+ { method: 'POST', path: '/channels', capability: 'channels', operation: 'create', request: 'default', response: 'default' },
12
+ { method: 'PUT', path: '/channels/:channelId', capability: 'channels', operation: 'update', request: 'default', response: 'default' },
13
+ { method: 'DELETE', path: '/channels/:channelId', capability: 'channels', operation: 'remove', request: 'default', response: 'default' },
14
+ { method: 'GET', path: '/proxy/status', capability: 'proxy', operation: 'status', request: 'default', response: 'default' }
15
+ ]);
16
+
17
+ function isObject(value) {
18
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
19
+ }
20
+
21
+ function safeText(value, fallback) {
22
+ if (value == null || value === '') return fallback;
23
+ return String(value).slice(0, 4096);
24
+ }
25
+
26
+ function addContext(value, { platform, capability, operation }) {
27
+ const result = isObject(value) ? { ...value } : {};
28
+ if (result.platform == null) result.platform = platform;
29
+ if (result.capability == null) result.capability = capability;
30
+ if (result.operation == null) result.operation = operation;
31
+ if (result.code == null && result.status && result.status !== 'ok') result.code = result.status;
32
+ return result;
33
+ }
34
+
35
+ function makeFailure(context, error) {
36
+ const cause = error instanceof Error ? error : new Error(safeText(error, 'Platform route failed'));
37
+ const status = cause.statusCode === 409 ? 'conflict'
38
+ : cause.statusCode === 422 ? 'unprocessable'
39
+ : cause.statusCode === 400 ? 'invalid'
40
+ : cause.statusCode === 503 ? 'unavailable' : 'failed';
41
+ const result = {
42
+ status,
43
+ code: cause.code || status,
44
+ platform: context.platform,
45
+ capability: context.capability,
46
+ operation: context.operation,
47
+ error: safeText(cause.message, 'Platform route failed')
48
+ };
49
+ if (cause.conflictingChannelId) result.conflictingChannelId = cause.conflictingChannelId;
50
+ if (cause.conflictingChannelName) result.conflictingChannelName = cause.conflictingChannelName;
51
+ Object.defineProperty(result, 'cause', { value: cause, enumerable: false });
52
+ return result;
53
+ }
54
+
55
+ function makeUnsupported(context) {
56
+ return {
57
+ status: 'unsupported',
58
+
59
+ code: 'unsupported',
60
+ platform: context.platform,
61
+ capability: context.capability,
62
+ operation: context.operation
63
+ };
64
+ }
65
+ function legacyArguments(route, request) {
66
+ if (route.capability === 'projects' && route.operation === 'listProjects') return [{ force: request.query?.fresh === '1' }];
67
+ if (route.capability === 'sessions' && route.operation === 'listSessions') {
68
+ return [request.params.projectName, { force: request.query?.fresh === '1' }];
69
+ }
70
+ if (route.capability === 'channels') {
71
+ if (route.method === 'POST') return [request.body || {}];
72
+ if (route.method === 'PUT') return [request.params.channelId, request.body || {}];
73
+ if (route.method === 'DELETE') return [request.params.channelId];
74
+ return [{ force: request.query?.fresh === '1' }];
75
+ }
76
+ if (route.capability === 'proxy') return [{ force: request.query?.fresh === '1' }];
77
+ return [];
78
+ }
79
+
80
+ function makeSuccess(context, data) {
81
+ return { status: 'ok', platform: context.platform, capability: context.capability, operation: context.operation, data };
82
+ }
83
+
84
+ function resolvePlatform(registry, key) {
85
+ return registry && typeof registry.resolve === 'function' ? registry.resolve(key) : null;
86
+ }
87
+
88
+ function getDeclaredCapability(registry, platform, definition, capability) {
89
+ if (registry && typeof registry.getCapability === 'function') return registry.getCapability(platform, capability);
90
+ return definition?.capabilities?.[capability];
91
+ }
92
+
93
+ function normalizeResult(value, context) {
94
+ if (isObject(value) && typeof value.status === 'string') return addContext(value, context);
95
+ return makeSuccess(context, value);
96
+ }
97
+
98
+ function sendDriverResult(response, value, route, context) {
99
+ const result = normalizeResult(value, context);
100
+ const statusCode = STATUS_CODES[result.status];
101
+ if (!statusCode) return response.status(500).json({ error: makeFailure(context, new Error('Invalid Driver result status')) });
102
+ if (result.status === 'ok') {
103
+ const payload = applyResponseCodec(Object.prototype.hasOwnProperty.call(result, 'data') ? result.data : result, context.manifest, route, context);
104
+ return response.status(200).json(payload);
105
+ }
106
+ return response.status(statusCode).json({ error: result });
107
+ }
108
+
109
+ function routePlatform(request, manifest) {
110
+ return String(request.params.platform || manifest.key || '').trim().toLowerCase();
111
+ }
112
+
113
+ function sendLegacyResult(response, value, route, context) {
114
+ if (isObject(value) && typeof value.status === 'string') {
115
+ return sendDriverResult(response, value, route, context);
116
+ }
117
+ return response.status(200).json(value);
118
+ }
119
+
120
+ async function invokeRoute({ registry, runtime, config, request, response, manifest: suppliedManifest, route }) {
121
+ const platform = routePlatform(request, suppliedManifest || {});
122
+ const manifest = suppliedManifest?.key === platform
123
+ ? suppliedManifest
124
+ : resolvePlatform(registry, platform);
125
+ const operation = route.operation;
126
+ const context = { platform, manifest, config, capability: route.capability, operation };
127
+
128
+ if (!manifest) {
129
+ return response.status(404).json({ error: { status: 'invalid', code: 'not_found', platform, capability: route.capability, operation, error: `Unknown platform: ${platform}` } });
130
+ }
131
+
132
+ const legacyRoute = !manifest.api?.routes;
133
+ const declaredCapability = getDeclaredCapability(registry, platform, manifest, route.capability);
134
+ const absentCapability = declaredCapability === null || declaredCapability === undefined || declaredCapability === 'unsupported';
135
+ if (absentCapability) {
136
+ return sendDriverResult(response, makeUnsupported(context), route, context);
137
+ }
138
+
139
+ let driver;
140
+ const isDescriptorRoute = Boolean(manifest.api?.routes);
141
+ try {
142
+ driver = runtime?.getDriver?.(
143
+ platform,
144
+ route.capability,
145
+ { config, manifest, route, apiRoute: isDescriptorRoute }
146
+ );
147
+ } catch (error) {
148
+ return sendDriverResult(response, makeFailure(context, error), route, context);
149
+ }
150
+ if (!driver || typeof driver[operation] !== 'function') {
151
+ return sendDriverResult(response, makeUnsupported(context), route, context);
152
+ }
153
+
154
+ try {
155
+ const invocation = isDescriptorRoute
156
+ ? applyRequestCodec(request, manifest, route, config)
157
+ : legacyArguments(route, request);
158
+ const value = isDescriptorRoute
159
+ ? await driver[operation](invocation)
160
+ : await driver[operation](...invocation);
161
+ return isDescriptorRoute
162
+ ? sendDriverResult(response, value, route, { ...context, ...invocation })
163
+ : sendLegacyResult(response, value, route, context);
164
+ } catch (error) {
165
+ return sendDriverResult(response, makeFailure(context, error), route, context);
166
+ }
167
+ }
168
+
169
+ function normalizeRoute(route) {
170
+ return { method: 'GET', ...route, method: String(route.method || 'GET').toUpperCase() };
171
+ }
172
+
173
+ function joinPath(prefix, routePath) {
174
+ const left = String(prefix || '').replace(/\/$/, '');
175
+ const right = String(routePath || '/');
176
+ return `${left}${right.startsWith('/') ? right : `/${right}`}` || '/';
177
+ }
178
+
179
+ function createPlatformRouteFactory({ registry, runtime, config } = {}) {
180
+ function routesFor(manifest) {
181
+ return (manifest?.api?.routes || DEFAULT_ROUTES).map(normalizeRoute);
182
+ }
183
+
184
+ function createHandler(manifest, route) {
185
+ return (request, response) => invokeRoute({ registry, runtime, config, request, response, manifest, route });
186
+ }
187
+
188
+ return {
189
+ routesFor,
190
+ createHandler,
191
+ mount(router, { manifest, basePath = '/:platform', aliases = false } = {}) {
192
+ const definitions = manifest
193
+ ? [manifest]
194
+ : (registry?.list?.({ enabledOnly: true }) || []);
195
+ const manifests = definitions.length > 0 || !basePath.includes(':platform')
196
+ ? definitions
197
+ : [null];
198
+ for (const definition of manifests) {
199
+ for (const route of routesFor(definition)) {
200
+ if (aliases && !definition?.api?.rootAlias) continue;
201
+ const firstSegment = route.path.split('/').filter(Boolean)[0];
202
+ if (aliases && !definition.api.rootAliasPaths?.includes(firstSegment)) continue;
203
+ const path = joinPath(basePath, route.path);
204
+ router[route.method.toLowerCase()](path, createHandler(definition, route));
205
+ }
206
+ }
207
+ return router;
208
+ },
209
+ invokeRoute
210
+ };
211
+ }
212
+
213
+ module.exports = createPlatformRouteFactory;
214
+ module.exports.createPlatformRouteFactory = createPlatformRouteFactory;
215
+ module.exports._test = { addContext, makeFailure, makeUnsupported, sendDriverResult, invokeRoute, normalizeResult };
@@ -0,0 +1,26 @@
1
+ 'use strict';
2
+
3
+ const express = require('express');
4
+ const createPlatformRouteFactory = require('./platform-route-factory');
5
+
6
+
7
+ function mountPlatformApi(router, manifest, factory) {
8
+ if (!manifest?.api?.prefix) return;
9
+ factory.mount(router, { manifest, basePath: `/${manifest.api.prefix}` });
10
+ if (manifest.api.rootAlias) factory.mount(router, { manifest, basePath: '/', aliases: true });
11
+ }
12
+
13
+ function createPlatformApiRouter({ registry, runtime, config } = {}) {
14
+ const router = express.Router();
15
+ const factory = createPlatformRouteFactory({ registry, runtime, config });
16
+ const manifests = registry?.list?.({ enabledOnly: true }) || [];
17
+
18
+ for (const manifest of manifests) {
19
+ mountPlatformApi(router, manifest, factory);
20
+ }
21
+
22
+ return router;
23
+ }
24
+
25
+ module.exports = createPlatformApiRouter;
26
+ module.exports.mountPlatformApi = mountPlatformApi;
@@ -0,0 +1,110 @@
1
+ 'use strict';
2
+
3
+ const express = require('express');
4
+ const createPlatformRouteFactory = require('./platform-route-factory');
5
+
6
+ const PUBLIC_FIELDS = Object.freeze([
7
+ 'key',
8
+ 'label',
9
+ 'title',
10
+ 'command',
11
+ 'iconToken',
12
+ 'color',
13
+ 'defaultVisible',
14
+ 'defaultEnabled',
15
+ 'sortOrder',
16
+ 'portKey',
17
+ 'defaultPort',
18
+ 'portLabel',
19
+ 'apiPrefix',
20
+ 'modelCatalogKey',
21
+ 'promptLabel',
22
+ 'resourceTypes'
23
+ ]);
24
+
25
+ function publicCapabilities(capabilities) {
26
+ if (!capabilities || typeof capabilities !== 'object' || Array.isArray(capabilities)) {
27
+ return {};
28
+ }
29
+ return Object.fromEntries(Object.entries(capabilities).map(([key, value]) => [
30
+ key,
31
+ typeof value === 'boolean' ? value : value !== 'unsupported' && value != null
32
+ ]));
33
+ }
34
+
35
+ function publicResourceTypes(resourceTypes) {
36
+ if (!resourceTypes || typeof resourceTypes !== 'object' || Array.isArray(resourceTypes)) {
37
+ return null;
38
+ }
39
+ return Object.fromEntries(
40
+ Object.entries(resourceTypes).filter(([, value]) => typeof value === 'boolean')
41
+ );
42
+ }
43
+
44
+ function toPublicDefinition(definition) {
45
+ if (!definition || typeof definition !== 'object') return null;
46
+ const result = {};
47
+ for (const field of PUBLIC_FIELDS) {
48
+ if (field === 'resourceTypes') {
49
+ const resourceTypes = publicResourceTypes(definition.resourceTypes);
50
+ if (resourceTypes) result.resourceTypes = resourceTypes;
51
+ continue;
52
+ }
53
+ if (field === 'apiPrefix') {
54
+ const apiPrefix = definition.apiPrefix ?? definition.api?.prefix;
55
+ if (apiPrefix !== undefined) result.apiPrefix = apiPrefix || null;
56
+ continue;
57
+ }
58
+ if (field === 'modelCatalogKey') {
59
+ result.modelCatalogKey = definition.modelCatalogKey
60
+ || definition.modelConfig?.catalogKey
61
+ || definition.key;
62
+ continue;
63
+ }
64
+ if (definition[field] !== undefined) result[field] = definition[field];
65
+ }
66
+ result.capabilities = publicCapabilities(definition.capabilities);
67
+ return result.key ? result : null;
68
+ }
69
+
70
+ function defaultDependencies() {
71
+ return require('../platform-context').getPlatformContext();
72
+ }
73
+
74
+ function createPlatformRouter(options = {}) {
75
+ let registry = options.registry;
76
+ let runtime = options.runtime;
77
+ if (!registry || !runtime) {
78
+ const defaults = defaultDependencies();
79
+ registry = registry || defaults.registry;
80
+ runtime = runtime || defaults.runtime;
81
+ }
82
+ const router = express.Router();
83
+
84
+ router.get('/', (request, response) => {
85
+ try {
86
+ const definitions = typeof registry.list === 'function' ? registry.list() : [];
87
+ const platforms = definitions.map(toPublicDefinition).filter(Boolean);
88
+ return response.json({ platforms });
89
+ } catch (error) {
90
+ return response.status(500).json({
91
+ error: {
92
+ status: 'failed',
93
+ code: 'failed',
94
+ operation: 'list-platforms',
95
+ error: error && error.message ? error.message : String(error)
96
+ }
97
+ });
98
+ }
99
+ });
100
+
101
+ createPlatformRouteFactory({ registry, runtime }).mount(router);
102
+ return router;
103
+ }
104
+
105
+ module.exports = createPlatformRouter;
106
+ module.exports.createPlatformRouter = createPlatformRouter;
107
+ module.exports._test = {
108
+ publicCapabilities,
109
+ toPublicDefinition
110
+ };
@@ -35,7 +35,7 @@ function getPluginsService(req) {
35
35
  };
36
36
  }
37
37
 
38
- async function getRequestOptions(req) {
38
+ async function getRequestOptions(req, service) {
39
39
  const scope = String(req.query?.scope || req.body?.scope || '').trim();
40
40
  if (scope && scope !== 'user' && scope !== 'project') {
41
41
  throw new Error('Invalid scope: expected "user" or "project"');
@@ -44,10 +44,13 @@ async function getRequestOptions(req) {
44
44
  if (scope === 'project' && !cwd) {
45
45
  throw new Error('Project scope requires a valid cwd');
46
46
  }
47
- return {
47
+ const requestData = {
48
48
  ...(cwd ? { cwd } : {}),
49
49
  ...(scope ? { scope } : {})
50
50
  };
51
+ return typeof service?.getRequestOptions === 'function'
52
+ ? service.getRequestOptions(requestData)
53
+ : requestData;
51
54
  }
52
55
 
53
56
  function extractRepoPayload(source = {}) {
@@ -58,7 +61,7 @@ function extractRepoPayload(source = {}) {
58
61
  host: repo.host || source.host || '',
59
62
  owner: repo.owner || source.owner || '',
60
63
  name: repo.name || source.name || '',
61
- branch: repo.branch || source.branch || 'main',
64
+ branch: repo.branch || source.branch || '',
62
65
  directory: repo.directory || source.directory || '',
63
66
  projectPath: repo.projectPath || source.projectPath || '',
64
67
  localPath: repo.localPath || source.localPath || '',
@@ -86,11 +89,33 @@ function sanitizeRepos(service, repos = []) {
86
89
  }
87
90
  return (Array.isArray(repos) ? repos : []).map(sanitizeRepo);
88
91
  }
92
+ function normalizeInstallMetadata(service, requestBody, repoPayload) {
93
+ if (typeof service?.normalizeInstallMetadata === 'function') {
94
+ return service.normalizeInstallMetadata(requestBody, repoPayload);
95
+ }
96
+ if (repoPayload) return repoPayload;
97
+ const metadata = requestBody.metadata && typeof requestBody.metadata === 'object'
98
+ ? requestBody.metadata
99
+ : {};
100
+ if (Object.keys(metadata).length === 0 && !requestBody.pluginId && !requestBody.name) return null;
101
+ return {
102
+ ...metadata,
103
+ name: requestBody.name || metadata.name,
104
+ pluginId: requestBody.pluginId || metadata.pluginId,
105
+ pluginKind: requestBody.pluginKind || metadata.pluginKind,
106
+ marketplace: requestBody.marketplace || metadata.marketplace,
107
+ installSource: requestBody.installSource || metadata.installSource,
108
+ version: requestBody.version || metadata.version,
109
+ description: requestBody.description || metadata.description,
110
+ resourceTypes: requestBody.resourceTypes || metadata.resourceTypes
111
+ };
112
+ }
89
113
 
90
- function hasRequestOptions(options = {}) {
91
- return Object.keys(options).length > 0;
114
+ function invokeWithOptions(method, args, options) {
115
+ return Object.keys(options || {}).length > 0 ? method(...args, options) : method(...args);
92
116
  }
93
117
 
118
+
94
119
  /**
95
120
  * 获取平台插件能力
96
121
  * GET /api/plugins/capabilities
@@ -120,10 +145,8 @@ router.get('/capabilities', (req, res) => {
120
145
  router.get('/', async (req, res) => {
121
146
  try {
122
147
  const { platform, service, warning } = getPluginsService(req);
123
- const options = await getRequestOptions(req);
124
- const result = platform === 'omp' || hasRequestOptions(options)
125
- ? service.listPlugins(options)
126
- : service.listPlugins();
148
+ const options = await getRequestOptions(req, service);
149
+ const result = invokeWithOptions(service.listPlugins.bind(service), [], options);
127
150
 
128
151
  res.json({
129
152
  success: true,
@@ -144,14 +167,12 @@ router.get('/', async (req, res) => {
144
167
  router.get('/market', async (req, res) => {
145
168
  try {
146
169
  const { platform, service, warning } = getPluginsService(req);
147
- const options = await getRequestOptions(req);
170
+ const options = await getRequestOptions(req, service);
148
171
  const forceRefresh = req.query.refresh === '1';
149
172
  if (forceRefresh) {
150
173
  console.log(`[Plugins API] Refreshing market plugins for ${platform}...`);
151
174
  }
152
- const plugins = platform === 'omp' || hasRequestOptions(options)
153
- ? await service.getMarketPlugins(forceRefresh, options)
154
- : await service.getMarketPlugins(forceRefresh);
175
+ const plugins = await invokeWithOptions(service.getMarketPlugins.bind(service), [forceRefresh], options);
155
176
  console.log(`[Plugins API] ${platform}: ${plugins.length} market plugins loaded (refresh=${forceRefresh})`);
156
177
 
157
178
  res.json({
@@ -174,7 +195,7 @@ router.get('/market', async (req, res) => {
174
195
  router.post('/install', async (req, res) => {
175
196
  try {
176
197
  const { platform, service, warning } = getPluginsService(req);
177
- const options = await getRequestOptions(req);
198
+ const options = await getRequestOptions(req, service);
178
199
  const { directory, repo, gitUrl, source, pluginId } = req.body;
179
200
  const hasDirectoryField = Object.prototype.hasOwnProperty.call(req.body, 'directory');
180
201
 
@@ -193,25 +214,14 @@ router.post('/install', async (req, res) => {
193
214
  });
194
215
  }
195
216
 
196
- const repoMetadata = repo && hasDirectoryField
197
- ? {
198
- ...extractRepoPayload({ repo }),
199
- directory: directory || ''
200
- }
201
- : platform === 'omp' ? {
202
- ...(req.body.metadata && typeof req.body.metadata === 'object' ? req.body.metadata : {}),
203
- name: req.body.name || req.body.metadata?.name,
204
- pluginId: pluginId || req.body.metadata?.pluginId,
205
- pluginKind: req.body.pluginKind || req.body.metadata?.pluginKind,
206
- marketplace: req.body.marketplace || req.body.metadata?.marketplace,
207
- installSource: req.body.installSource || req.body.metadata?.installSource,
208
- version: req.body.version || req.body.metadata?.version,
209
- description: req.body.description || req.body.metadata?.description,
210
- resourceTypes: req.body.resourceTypes || req.body.metadata?.resourceTypes
211
- } : null;
212
- const result = platform === 'omp' || hasRequestOptions(options)
213
- ? await service.installPlugin(installUrl, repoMetadata, options)
214
- : await service.installPlugin(installUrl, repoMetadata);
217
+ const repoMetadata = normalizeInstallMetadata(
218
+ service,
219
+ req.body,
220
+ repo && hasDirectoryField
221
+ ? { ...extractRepoPayload({ repo }), directory: directory || '' }
222
+ : null
223
+ );
224
+ const result = await invokeWithOptions(service.installPlugin.bind(service), [installUrl, repoMetadata], options);
215
225
 
216
226
  if (!result.success) {
217
227
  return res.status(400).json({
@@ -242,10 +252,8 @@ router.post('/install', async (req, res) => {
242
252
  router.get('/repos', async (req, res) => {
243
253
  try {
244
254
  const { platform, service, warning } = getPluginsService(req);
245
- const options = await getRequestOptions(req);
246
- const repos = platform === 'omp' || hasRequestOptions(options)
247
- ? service.getRepos(options)
248
- : service.getRepos();
255
+ const options = await getRequestOptions(req, service);
256
+ const repos = invokeWithOptions(service.getRepos.bind(service), [], options);
249
257
  res.json({
250
258
  success: true,
251
259
  platform,
@@ -271,7 +279,7 @@ router.get('/repos', async (req, res) => {
271
279
  router.post('/repos', async (req, res) => {
272
280
  try {
273
281
  const { platform, service } = getPluginsService(req);
274
- const options = await getRequestOptions(req);
282
+ const options = await getRequestOptions(req, service);
275
283
  const repo = extractRepoPayload(req.body);
276
284
  repo.enabled = req.body.enabled !== false;
277
285
 
@@ -282,9 +290,7 @@ router.post('/repos', async (req, res) => {
282
290
  });
283
291
  }
284
292
 
285
- const repos = platform === 'omp' || hasRequestOptions(options)
286
- ? service.addRepo(repo, options)
287
- : service.addRepo(repo);
293
+ const repos = invokeWithOptions(service.addRepo.bind(service), [repo], options);
288
294
 
289
295
  res.json({
290
296
  success: true,
@@ -301,11 +307,9 @@ router.post('/repos', async (req, res) => {
301
307
  router.delete('/repos', async (req, res) => {
302
308
  try {
303
309
  const { platform, service } = getPluginsService(req);
304
- const options = await getRequestOptions(req);
310
+ const options = await getRequestOptions(req, service);
305
311
  const { id = '', owner = '', name = '' } = req.query;
306
- const repos = platform === 'omp' || hasRequestOptions(options)
307
- ? service.removeRepo(owner, name, id, options)
308
- : service.removeRepo(owner, name, id);
312
+ const repos = invokeWithOptions(service.removeRepo.bind(service), [owner, name, id], options);
309
313
 
310
314
  res.json({
311
315
  success: true,
@@ -437,7 +441,7 @@ router.put('/repos/auth', (req, res) => {
437
441
  router.post('/repos/sync', async (req, res) => {
438
442
  try {
439
443
  const { platform, service } = getPluginsService(req);
440
- const options = await getRequestOptions(req);
444
+ const options = await getRequestOptions(req, service);
441
445
  const result = await service.syncRepos(options);
442
446
 
443
447
  res.json({
@@ -459,7 +463,7 @@ router.post('/repos/sync', async (req, res) => {
459
463
  router.post('/sync', async (req, res) => {
460
464
  try {
461
465
  const { platform, service } = getPluginsService(req);
462
- const options = await getRequestOptions(req);
466
+ const options = await getRequestOptions(req, service);
463
467
  const result = await service.syncPlugins(options);
464
468
 
465
469
  res.json({
@@ -539,7 +543,7 @@ router.get('/:name', async (req, res) => {
539
543
  try {
540
544
  const { platform, service } = getPluginsService(req);
541
545
  const { name } = req.params;
542
- const options = await getRequestOptions(req);
546
+ const options = await getRequestOptions(req, service);
543
547
 
544
548
  const plugin = service.getPlugin(req.query.pluginId || name, options);
545
549
 
@@ -569,7 +573,7 @@ router.delete('/:name', async (req, res) => {
569
573
  try {
570
574
  const { platform, service } = getPluginsService(req);
571
575
  const { name } = req.params;
572
- const options = await getRequestOptions(req);
576
+ const options = await getRequestOptions(req, service);
573
577
  const pluginId = req.query.pluginId || name;
574
578
 
575
579
  const result = service.uninstallPlugin(pluginId, options);
@@ -602,7 +606,7 @@ router.put('/:name/toggle', async (req, res) => {
602
606
  const { platform, service } = getPluginsService(req);
603
607
  const { name } = req.params;
604
608
  const { enabled, pluginId = name } = req.body;
605
- const options = await getRequestOptions(req);
609
+ const options = await getRequestOptions(req, service);
606
610
 
607
611
  if (typeof enabled !== 'boolean') {
608
612
  return res.status(400).json({
@@ -611,9 +615,7 @@ router.put('/:name/toggle', async (req, res) => {
611
615
  });
612
616
  }
613
617
 
614
- const plugin = platform === 'omp' || hasRequestOptions(options)
615
- ? service.togglePlugin(pluginId, enabled, options)
616
- : service.togglePlugin(pluginId, enabled);
618
+ const plugin = invokeWithOptions(service.togglePlugin.bind(service), [pluginId, enabled], options);
617
619
 
618
620
  res.json({
619
621
  success: true,
@@ -637,7 +639,7 @@ router.put('/:name/config', async (req, res) => {
637
639
  const { platform, service } = getPluginsService(req);
638
640
  const { name } = req.params;
639
641
  const { config, pluginId = name } = req.body;
640
- const options = await getRequestOptions(req);
642
+ const options = await getRequestOptions(req, service);
641
643
 
642
644
  if (!config || typeof config !== 'object') {
643
645
  return res.status(400).json({
@@ -646,9 +648,7 @@ router.put('/:name/config', async (req, res) => {
646
648
  });
647
649
  }
648
650
 
649
- const result = platform === 'omp' || hasRequestOptions(options)
650
- ? service.updatePluginConfig(pluginId, config, options)
651
- : service.updatePluginConfig(pluginId, config);
651
+ const result = invokeWithOptions(service.updatePluginConfig.bind(service), [pluginId, config], options);
652
652
 
653
653
  res.json({
654
654
  success: true,