coding-tool-x 3.8.3 → 3.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (434) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +86 -15
  3. package/dist/web/assets/AgentsDrawer-BG6P3DX-.js +1 -0
  4. package/dist/web/assets/AgentsDrawer-Pw2k-ZAa.css +1 -0
  5. package/dist/web/assets/Analytics--z_FmL5P.js +1 -0
  6. package/dist/web/assets/Analytics-CyHhR126.css +1 -0
  7. package/dist/web/assets/AssetPathField-CrJtRKHy.js +1 -0
  8. package/dist/web/assets/ChatHistoryDrawer-B2qBQs9L.css +1 -0
  9. package/dist/web/assets/ChatHistoryDrawer-BiuEryJj.js +1 -0
  10. package/dist/web/assets/CollapseItem-BAYEsL_Q.js +1 -0
  11. package/dist/web/assets/CommandsDrawer-BZJxlPvV.css +1 -0
  12. package/dist/web/assets/CommandsDrawer-YJ-U7BXN.js +1 -0
  13. package/dist/web/assets/ConfigExportDrawer-Ce_hAknW.js +1 -0
  14. package/dist/web/assets/ConfigExportDrawer-dwruRKWr.css +1 -0
  15. package/dist/web/assets/ConfigTemplates-Bvr1NN6K.js +1 -0
  16. package/dist/web/assets/ConfigTemplatesDrawer-BfDi8yO3.css +1 -0
  17. package/dist/web/assets/ConfigTemplatesDrawer-BjIeHfF-.js +1 -0
  18. package/dist/web/assets/ConfigTemplatesPanel-AV00tm_D.js +1 -0
  19. package/dist/web/assets/ConfigTemplatesPanel-CWyPtk67.css +1 -0
  20. package/dist/web/assets/Divider-D28g1gwj.js +1 -0
  21. package/dist/web/assets/DrawerContent-ChXgZ1Xm.js +1 -0
  22. package/dist/web/assets/FavoritesDrawer-CG9eI9Ay.css +1 -0
  23. package/dist/web/assets/FavoritesDrawer-Cz95o4CD.js +1 -0
  24. package/dist/web/assets/GatewayConvertDrawer-CuNlf7AC.css +1 -0
  25. package/dist/web/assets/GatewayConvertDrawer-DLlbMz27.js +1 -0
  26. package/dist/web/assets/Home-CSuFuJ9Y.css +1 -0
  27. package/dist/web/assets/Home-D7CCOFQC.js +1 -0
  28. package/dist/web/assets/MarkdownEditor-qfVgSre_.css +1 -0
  29. package/dist/web/assets/MarkdownEditor-rEZ-c72L.js +1 -0
  30. package/dist/web/assets/McpDrawer-C0dnsZhQ.css +1 -0
  31. package/dist/web/assets/McpDrawer-DfoCyxU2.js +1 -0
  32. package/dist/web/assets/McpFormDrawer-BRbDcawn.css +1 -0
  33. package/dist/web/assets/McpFormDrawer-SKu8HNUR.js +1 -0
  34. package/dist/web/assets/PluginManager-BbeK-3rV.js +1 -0
  35. package/dist/web/assets/PluginsDrawer-CWVJ7yjS.js +1 -0
  36. package/dist/web/assets/PluginsDrawer-DM3QjprE.css +1 -0
  37. package/dist/web/assets/PluginsPanel-Cm2N3StG.js +1 -0
  38. package/dist/web/assets/PluginsPanel-D6ZqITcR.css +1 -0
  39. package/dist/web/assets/ProjectList-677RLZ8O.css +1 -0
  40. package/dist/web/assets/ProjectList-CsjnsNPv.js +1 -0
  41. package/dist/web/assets/PromptsDrawer-BIgiYCEQ.css +1 -0
  42. package/dist/web/assets/PromptsDrawer-EGd3djFa.js +1 -0
  43. package/dist/web/assets/RecentSessionsDrawer-CKFsOYDM.css +1 -0
  44. package/dist/web/assets/RecentSessionsDrawer-D1vtoLAs.js +1 -0
  45. package/dist/web/assets/SessionCard-BgLg49U3.js +1 -0
  46. package/dist/web/assets/SessionCard-aM2NHu6i.css +1 -0
  47. package/dist/web/assets/SessionList-BpYRz1cX.css +1 -0
  48. package/dist/web/assets/SessionList-TRiTklWk.js +1 -0
  49. package/dist/web/assets/SettingsDrawer-d5liXjHq.css +1 -0
  50. package/dist/web/assets/SettingsDrawer-rz2WejGY.js +1 -0
  51. package/dist/web/assets/SkillManager-BTjTUbHq.js +1 -0
  52. package/dist/web/assets/SkillManager-C0GCT2Kb.css +1 -0
  53. package/dist/web/assets/SkillsDrawer-BCNme-aJ.css +1 -0
  54. package/dist/web/assets/SkillsDrawer-pN0hbkK3.js +1 -0
  55. package/dist/web/assets/SkillsPanel-B3Nm5j32.css +1 -0
  56. package/dist/web/assets/SkillsPanel-gmBWEoiF.js +1 -0
  57. package/dist/web/assets/SpeedTestDrawer-Dc0WToJ_.css +1 -0
  58. package/dist/web/assets/SpeedTestDrawer-DcNzuVJk.js +1 -0
  59. package/dist/web/assets/Upload-v5rV0qTT.js +1 -0
  60. package/dist/web/assets/WorkspaceDrawer-CGnMu9p1.css +1 -0
  61. package/dist/web/assets/WorkspaceDrawer-DxmicM4U.js +1 -0
  62. package/dist/web/assets/WorkspaceManager-CJHCjARN.js +1 -0
  63. package/dist/web/assets/WorkspacePanel-D0-zHTHP.css +1 -0
  64. package/dist/web/assets/WorkspacePanel-D8_Zq-du.js +1 -0
  65. package/dist/web/assets/_plugin-vue_export-helper-BeLZJZij.js +1 -0
  66. package/dist/web/assets/client-UDwXBP9i.js +1 -0
  67. package/dist/web/assets/clipboard-B46yyoz_.js +1 -0
  68. package/dist/web/assets/headers-cf-QapUq.js +1 -0
  69. package/dist/web/assets/icons-B2Vg-IOC.js +1 -0
  70. package/dist/web/assets/index-C0EHRym0.css +1 -0
  71. package/dist/web/assets/index-DnEocV0M.js +2 -0
  72. package/dist/web/assets/light-Cp0t7v32.js +1 -0
  73. package/dist/web/assets/markdown-u082bbtj.js +1 -0
  74. package/dist/web/assets/platforms-CBkB45JP.js +1 -0
  75. package/dist/web/assets/platforms-FTpjxAT6.js +1 -0
  76. package/dist/web/assets/project-config-B2baPzYL.js +1 -0
  77. package/dist/web/assets/sessions-Cmy31slD.js +1 -0
  78. package/dist/web/assets/sessions-yC_oVVh8.js +1 -0
  79. package/dist/web/assets/skill-repo-input-B_fkGlUK.js +1 -0
  80. package/dist/web/assets/use-lock-html-scroll-DXIqmUW4.js +1 -0
  81. package/dist/web/assets/use-message-DoLM8C1C.js +1 -0
  82. package/dist/web/assets/useResponsiveDrawer-B3uojyvV.js +1 -0
  83. package/dist/web/assets/vendors-D0c_0KLP.js +1 -0
  84. package/dist/web/assets/vue-vendor-BqZbbXHA.js +1 -0
  85. package/dist/web/assets/workspaces-mjyPVGS8.js +1 -0
  86. package/dist/web/assets/xml-CgY3RMlX.js +1 -0
  87. package/dist/web/index.html +12 -8
  88. package/docs/native-log-memory-2026-09-16.md +43 -0
  89. package/docs/superpowers/plans/2026-08-26-configuration-driven-cli-platform.md +1578 -0
  90. package/docs/superpowers/plans/2026-08-30-project-scoped-config-control.md +1260 -0
  91. package/docs/superpowers/plans/2026-09-01-omp-skill-refresh-boundary.md +213 -0
  92. package/docs/superpowers/plans/2026-09-01-realtime-logs-panel.md +125 -0
  93. package/docs/superpowers/plans/2026-09-02-config-driven-platform-boundary.md +86 -0
  94. package/docs/superpowers/plans/2026-09-02-platform-implementation-drivers.md +508 -0
  95. package/docs/superpowers/plans/2026-09-03-cli-platform-correctness-boundary.md +760 -0
  96. package/docs/superpowers/plans/2026-09-03-dependency-security-remediation.md +133 -0
  97. package/docs/superpowers/plans/2026-09-04-model-catalog-pricing-sync.md +1078 -0
  98. package/docs/superpowers/plans/2026-09-05-codex-project-scan-500.md +128 -0
  99. package/docs/superpowers/plans/2026-09-06-project-skill-path-fix.md +97 -0
  100. package/docs/superpowers/specs/2026-08-26-configuration-driven-cli-platform-design.md +408 -0
  101. package/docs/superpowers/specs/2026-08-30-project-scoped-config-control-design.md +467 -0
  102. package/docs/superpowers/specs/2026-09-01-claude-session-content-repair-design.md +111 -0
  103. package/docs/superpowers/specs/2026-09-01-omp-skill-refresh-boundary-design.md +83 -0
  104. package/docs/superpowers/specs/2026-09-01-realtime-logs-panel-design.md +27 -0
  105. package/docs/superpowers/specs/2026-09-02-config-driven-platform-boundary-design.md +64 -0
  106. package/docs/superpowers/specs/2026-09-02-platform-implementation-driver-boundary-design.md +123 -0
  107. package/docs/superpowers/specs/2026-09-03-cli-platform-correctness-boundary-design.md +249 -0
  108. package/docs/superpowers/specs/2026-09-03-dependency-security-remediation-design.md +87 -0
  109. package/docs/superpowers/specs/2026-09-04-model-catalog-pricing-sync-design.md +301 -0
  110. package/docs/superpowers/specs/2026-09-05-codex-project-scan-500-design.md +24 -0
  111. package/package.json +13 -8
  112. package/src/commands/channels.js +88 -191
  113. package/src/commands/cli-type.js +19 -11
  114. package/src/commands/daemon.js +94 -32
  115. package/src/commands/doctor.js +16 -9
  116. package/src/commands/export-config.js +23 -5
  117. package/src/commands/list.js +130 -52
  118. package/src/commands/logs.js +44 -27
  119. package/src/commands/platform-command-registry.js +78 -0
  120. package/src/commands/plugin.js +11 -5
  121. package/src/commands/port-config.js +34 -52
  122. package/src/commands/proxy-control.js +207 -145
  123. package/src/commands/proxy.js +2 -2
  124. package/src/commands/resume.js +87 -91
  125. package/src/commands/search.js +137 -67
  126. package/src/commands/stats.js +30 -22
  127. package/src/commands/switch.js +1 -1
  128. package/src/commands/toggle-proxy.js +94 -111
  129. package/src/commands/ui.js +67 -6
  130. package/src/commands/workspace.js +90 -8
  131. package/src/config/default.js +6 -53
  132. package/src/config/loader.js +61 -24
  133. package/src/config/model-metadata.js +49 -59
  134. package/src/config/model-metadata.json +4095 -99
  135. package/src/config/paths.js +181 -8
  136. package/src/index.js +134 -71
  137. package/src/platforms/access.js +196 -0
  138. package/src/{server/services → platforms}/agents-service.js +354 -197
  139. package/src/platforms/channel-auth-registry.js +162 -0
  140. package/src/platforms/channel-auth-service.js +82 -0
  141. package/src/{server/services → platforms}/commands-service.js +195 -115
  142. package/src/platforms/driver-registry.js +59 -0
  143. package/src/platforms/drivers/claude/api-operations.js +62 -0
  144. package/src/{server/services/channels.js → platforms/drivers/claude/channels-implementation.js} +131 -33
  145. package/src/platforms/drivers/claude/channels.js +39 -0
  146. package/src/{server/services/config-sync-service.js → platforms/drivers/claude/config-sync.js} +29 -5
  147. package/src/{server/services → platforms/drivers/claude}/health-check.js +4 -4
  148. package/src/platforms/drivers/claude/health.js +22 -0
  149. package/src/platforms/drivers/claude/mcp.js +9 -0
  150. package/src/{server/services/settings-manager.js → platforms/drivers/claude/native-config-implementation.js} +13 -4
  151. package/src/platforms/drivers/claude/native-config.js +46 -0
  152. package/src/platforms/drivers/claude/native-logs.js +72 -0
  153. package/src/{server/services/claude-openai-gateway.js → platforms/drivers/claude/openai-gateway.js} +8 -65
  154. package/src/platforms/drivers/claude/project-config.js +21 -0
  155. package/src/platforms/drivers/claude/projects.js +62 -0
  156. package/src/platforms/drivers/claude/prompts.js +9 -0
  157. package/src/{server/proxy-server.js → platforms/drivers/claude/proxy-implementation.js} +41 -278
  158. package/src/platforms/drivers/claude/proxy.js +16 -0
  159. package/src/platforms/drivers/claude/resource-sync.js +9 -0
  160. package/src/platforms/drivers/claude/session-history-adapter.js +313 -0
  161. package/src/{server/services/sessions.js → platforms/drivers/claude/sessions-implementation.js} +145 -35
  162. package/src/platforms/drivers/claude/sessions.js +76 -0
  163. package/src/{server/services/claude-statistics-service.js → platforms/drivers/claude/statistics-implementation.js} +2 -2
  164. package/src/platforms/drivers/claude/statistics.js +16 -0
  165. package/src/{server/services/claude-wire.js → platforms/drivers/claude/wire.js} +1 -1
  166. package/src/platforms/drivers/codex/api-operations.js +9 -0
  167. package/src/{server/services/codex-channels.js → platforms/drivers/codex/channels-implementation.js} +149 -14
  168. package/src/platforms/drivers/codex/channels.js +45 -0
  169. package/src/{server/services/codex-config.js → platforms/drivers/codex/config.js} +12 -2
  170. package/src/{server/services/codex-env-manager.js → platforms/drivers/codex/env-manager.js} +1 -1
  171. package/src/platforms/drivers/codex/mcp.js +9 -0
  172. package/src/{server/services/codex-settings-manager.js → platforms/drivers/codex/native-config-implementation.js} +15 -6
  173. package/src/platforms/drivers/codex/native-config.js +47 -0
  174. package/src/platforms/drivers/codex/native-logs.js +166 -0
  175. package/src/platforms/drivers/codex/project-config.js +21 -0
  176. package/src/platforms/drivers/codex/projects.js +26 -0
  177. package/src/platforms/drivers/codex/prompts.js +9 -0
  178. package/src/{server/codex-proxy-server.js → platforms/drivers/codex/proxy-implementation.js} +30 -233
  179. package/src/platforms/drivers/codex/proxy.js +16 -0
  180. package/src/platforms/drivers/codex/resource-sync.js +9 -0
  181. package/src/{server/services/session-history-adapters/codex.js → platforms/drivers/codex/session-history-adapter.js} +37 -23
  182. package/src/{server/services/codex-sessions.js → platforms/drivers/codex/sessions-implementation.js} +81 -75
  183. package/src/platforms/drivers/codex/sessions.js +36 -0
  184. package/src/{server/services/codex-statistics-service.js → platforms/drivers/codex/statistics-implementation.js} +2 -2
  185. package/src/platforms/drivers/codex/statistics.js +16 -0
  186. package/src/platforms/drivers/gemini/api-operations.js +9 -0
  187. package/src/{server/services/gemini-channels.js → platforms/drivers/gemini/channels-implementation.js} +202 -37
  188. package/src/platforms/drivers/gemini/channels.js +39 -0
  189. package/src/{server/services/gemini-config.js → platforms/drivers/gemini/config.js} +12 -2
  190. package/src/platforms/drivers/gemini/mcp.js +9 -0
  191. package/src/{server/services/gemini-settings-manager.js → platforms/drivers/gemini/native-config-implementation.js} +14 -5
  192. package/src/platforms/drivers/gemini/native-config.js +39 -0
  193. package/src/platforms/drivers/gemini/native-logs.js +180 -0
  194. package/src/platforms/drivers/gemini/project-config.js +21 -0
  195. package/src/platforms/drivers/gemini/projects.js +26 -0
  196. package/src/platforms/drivers/gemini/prompts.js +9 -0
  197. package/src/{server/gemini-proxy-server.js → platforms/drivers/gemini/proxy-implementation.js} +36 -267
  198. package/src/platforms/drivers/gemini/proxy.js +16 -0
  199. package/src/platforms/drivers/gemini/resource-sync.js +9 -0
  200. package/src/{server/services/session-history-adapters/gemini.js → platforms/drivers/gemini/session-history-adapter.js} +70 -18
  201. package/src/{server/services/gemini-sessions.js → platforms/drivers/gemini/sessions-implementation.js} +53 -37
  202. package/src/platforms/drivers/gemini/sessions.js +47 -0
  203. package/src/{server/services/gemini-statistics-service.js → platforms/drivers/gemini/statistics-implementation.js} +2 -2
  204. package/src/platforms/drivers/gemini/statistics.js +16 -0
  205. package/src/{server/services/gemini-wire.js → platforms/drivers/gemini/wire.js} +1 -1
  206. package/src/platforms/drivers/generic-filesystem.js +166 -0
  207. package/src/platforms/drivers/generic-jsonl.js +141 -0
  208. package/src/platforms/drivers/generic-mcp.js +44 -0
  209. package/src/platforms/drivers/generic-openai-compatible.js +45 -0
  210. package/src/platforms/drivers/generic-prompt.js +41 -0
  211. package/src/platforms/drivers/legacy.js +677 -0
  212. package/src/platforms/drivers/native-config-snapshot.js +125 -0
  213. package/src/platforms/drivers/native-log-utils.js +1292 -0
  214. package/src/platforms/drivers/notification-hooks.js +578 -0
  215. package/src/{server/api/omp-config.js → platforms/drivers/omp/api-operations.js} +52 -64
  216. package/src/{server/services/omp-auth-provider-worker.js → platforms/drivers/omp/auth-provider-worker.js} +1 -1
  217. package/src/{server/services/omp-auth-providers.js → platforms/drivers/omp/auth-providers.js} +1 -1
  218. package/src/{server/services/omp-channels.js → platforms/drivers/omp/channels-implementation.js} +166 -39
  219. package/src/platforms/drivers/omp/channels.js +58 -0
  220. package/src/{server/services/omp-config.js → platforms/drivers/omp/config.js} +53 -13
  221. package/src/{server/services/omp-gateway-routing.js → platforms/drivers/omp/gateway-routing.js} +14 -7
  222. package/src/{server/services/omp-gateway.js → platforms/drivers/omp/gateway.js} +4 -262
  223. package/src/platforms/drivers/omp/mcp.js +9 -0
  224. package/src/{server/services/omp-settings-manager.js → platforms/drivers/omp/native-config-implementation.js} +75 -10
  225. package/src/platforms/drivers/omp/native-config.js +46 -0
  226. package/src/platforms/drivers/omp/native-logs.js +52 -0
  227. package/src/{server/services/omp-native-plugin-adapter.js → platforms/drivers/omp/native-plugin-adapter.js} +91 -4
  228. package/src/platforms/drivers/omp/project-config.js +29 -0
  229. package/src/platforms/drivers/omp/projects.js +20 -0
  230. package/src/platforms/drivers/omp/prompts.js +9 -0
  231. package/src/{server/omp-proxy-server.js → platforms/drivers/omp/proxy-implementation.js} +6 -15
  232. package/src/platforms/drivers/omp/proxy.js +16 -0
  233. package/src/platforms/drivers/omp/resource-sync.js +9 -0
  234. package/src/{server/services/session-history-adapters/omp.js → platforms/drivers/omp/session-history-adapter.js} +42 -18
  235. package/src/{server/services/omp-sessions.js → platforms/drivers/omp/sessions-implementation.js} +130 -94
  236. package/src/platforms/drivers/omp/sessions.js +36 -0
  237. package/src/{server/services/omp-skill-discovery.js → platforms/drivers/omp/skill-discovery.js} +159 -32
  238. package/src/{server/services/omp-skill-settings-service.js → platforms/drivers/omp/skill-settings.js} +1 -1
  239. package/src/{server/services/omp-statistics-service.js → platforms/drivers/omp/statistics-implementation.js} +3 -3
  240. package/src/platforms/drivers/omp/statistics.js +16 -0
  241. package/src/platforms/drivers/opencode/api-operations.js +9 -0
  242. package/src/{server/services/opencode-channels.js → platforms/drivers/opencode/channels-implementation.js} +28 -9
  243. package/src/platforms/drivers/opencode/channels.js +40 -0
  244. package/src/platforms/drivers/opencode/conversion.js +41 -0
  245. package/src/{server/services/opencode-gateway-converter.js → platforms/drivers/opencode/gateway-converter.js} +1 -1
  246. package/src/platforms/drivers/opencode/mcp.js +9 -0
  247. package/src/{server/services/opencode-settings-manager.js → platforms/drivers/opencode/native-config-implementation.js} +17 -6
  248. package/src/platforms/drivers/opencode/native-config.js +26 -0
  249. package/src/platforms/drivers/opencode/native-logs.js +133 -0
  250. package/src/platforms/drivers/opencode/project-config.js +98 -0
  251. package/src/platforms/drivers/opencode/projects.js +20 -0
  252. package/src/platforms/drivers/opencode/prompts.js +9 -0
  253. package/src/{server/opencode-proxy-server.js → platforms/drivers/opencode/proxy-implementation.js} +27 -362
  254. package/src/platforms/drivers/opencode/proxy.js +16 -0
  255. package/src/platforms/drivers/opencode/resource-sync.js +9 -0
  256. package/src/{server/services/opencode-sessions.js → platforms/drivers/opencode/sessions-implementation.js} +211 -64
  257. package/src/platforms/drivers/opencode/sessions.js +48 -0
  258. package/src/{server/services/opencode-statistics-service.js → platforms/drivers/opencode/statistics-implementation.js} +2 -2
  259. package/src/platforms/drivers/opencode/statistics.js +16 -0
  260. package/src/platforms/drivers/secure-file-driver.js +272 -0
  261. package/src/platforms/drivers/unsupported.js +7 -0
  262. package/src/platforms/manifest-schema.js +398 -0
  263. package/src/platforms/manifests/claude.json +489 -0
  264. package/src/platforms/manifests/codex.json +448 -0
  265. package/src/platforms/manifests/gemini.json +447 -0
  266. package/src/platforms/manifests/omp.json +496 -0
  267. package/src/platforms/manifests/opencode.json +422 -0
  268. package/src/{server/services → platforms}/native-oauth-adapters.js +87 -417
  269. package/src/{server/services → platforms}/notification-hooks.js +140 -1023
  270. package/src/platforms/oauth-credentials-service.js +506 -0
  271. package/src/platforms/path-resolver.js +179 -0
  272. package/src/platforms/platform-path-context.js +305 -0
  273. package/src/platforms/registry.js +248 -0
  274. package/src/platforms/remote-notification-providers.js +678 -0
  275. package/src/platforms/runtime.js +259 -0
  276. package/src/{server/services/session-history-adapters/index.js → platforms/session-history-adapters.js} +8 -1
  277. package/src/plugins/plugin-loader.js +2 -2
  278. package/src/plugins/plugin-manager.js +2 -2
  279. package/src/plugins/registry.js +27 -10
  280. package/src/reset-config.js +21 -5
  281. package/src/server/api/agents.js +46 -72
  282. package/src/server/api/commands.js +16 -6
  283. package/src/server/api/config-registry.js +206 -59
  284. package/src/server/api/config-sync.js +3 -2
  285. package/src/server/api/config-templates.js +20 -4
  286. package/src/server/api/config.js +111 -102
  287. package/src/server/api/convert.js +54 -178
  288. package/src/server/api/dashboard.js +168 -102
  289. package/src/server/api/hooks.js +1 -1
  290. package/src/server/api/mcp.js +224 -84
  291. package/src/server/api/platform-api-config.js +102 -0
  292. package/src/server/api/platform-route-factory.js +215 -0
  293. package/src/server/api/platform-routes.js +26 -0
  294. package/src/server/api/platforms.js +110 -0
  295. package/src/server/api/plugins.js +57 -57
  296. package/src/server/api/project-config.js +168 -0
  297. package/src/server/api/prompts.js +58 -45
  298. package/src/server/api/settings.js +55 -8
  299. package/src/server/api/skills.js +262 -133
  300. package/src/server/api/ui-config.js +2 -2
  301. package/src/server/api/validation-errors.js +18 -4
  302. package/src/server/api/workspaces.js +47 -12
  303. package/src/server/index.js +164 -213
  304. package/src/server/platform-context.js +81 -0
  305. package/src/server/services/channel-balance.js +11 -20
  306. package/src/server/services/channel-health.js +21 -6
  307. package/src/server/services/channel-scheduler.js +29 -39
  308. package/src/server/services/channel-sync-utils.js +23 -13
  309. package/src/server/services/config-export-service.js +350 -319
  310. package/src/server/services/config-registry-service.js +280 -54
  311. package/src/server/services/config-sync-manager.js +172 -944
  312. package/src/server/services/config-templates-service.js +419 -353
  313. package/src/server/services/control-manifest-store.js +206 -0
  314. package/src/server/services/dashboard-snapshot-worker.js +656 -100
  315. package/src/server/services/effective-control-service.js +557 -0
  316. package/src/server/services/local-resource-index.js +314 -0
  317. package/src/server/services/mcp-service.js +607 -977
  318. package/src/server/services/model-detector.js +15 -10
  319. package/src/server/services/native-log-channel-resolver.js +214 -0
  320. package/src/server/services/native-log-observer.js +291 -0
  321. package/src/server/services/network-access.js +1 -1
  322. package/src/server/services/platform-catalog.js +82 -0
  323. package/src/server/services/platform-resolution.js +32 -29
  324. package/src/server/services/plugins-service.js +376 -122
  325. package/src/server/services/project-config-service.js +550 -0
  326. package/src/server/services/project-path-validation.js +31 -0
  327. package/src/server/services/prompts-service.js +267 -233
  328. package/src/server/services/proxy-runtime.js +7 -4
  329. package/src/server/services/remote-credential-cache.js +43 -0
  330. package/src/server/services/repo-scanner-base.js +182 -75
  331. package/src/server/services/request-logger.js +7 -4
  332. package/src/server/services/session-history-index.js +780 -213
  333. package/src/server/services/session-history-worker.js +228 -33
  334. package/src/server/services/session-snapshot-worker.js +124 -34
  335. package/src/server/services/session-snapshots.js +4 -2
  336. package/src/server/services/skill-artifact-store.js +355 -0
  337. package/src/server/services/skill-format-adapters.js +65 -0
  338. package/src/server/services/skill-projection-service.js +323 -0
  339. package/src/server/services/skill-refresh-task-service.js +275 -0
  340. package/src/server/services/skill-service.js +2020 -677
  341. package/src/server/services/snapshot-cache.js +30 -8
  342. package/src/server/services/speed-test.js +8 -8
  343. package/src/server/services/sqlite-connection.js +19 -3
  344. package/src/server/services/statistics-service.js +240 -57
  345. package/src/server/services/ui-config.js +107 -76
  346. package/src/server/services/usage-log-utils.js +98 -0
  347. package/src/server/services/web-build.js +1 -1
  348. package/src/server/services/workspace-service.js +59 -29
  349. package/src/server/utils/pricing.js +10 -19
  350. package/src/server/websocket-server.js +153 -28
  351. package/src/{server/services/base → shared}/base-channel-service.js +156 -28
  352. package/src/shared/capability-driver.js +104 -0
  353. package/src/shared/channel-auth-policy.js +27 -0
  354. package/src/shared/channel-driver.js +161 -0
  355. package/src/shared/driver-factories/api.js +180 -0
  356. package/src/shared/driver-factories/mcp.js +244 -0
  357. package/src/shared/driver-factories/projects.js +121 -0
  358. package/src/shared/driver-factories/prompts.js +58 -0
  359. package/src/shared/driver-factories/resource-sync.js +375 -0
  360. package/src/shared/driver-result.js +78 -0
  361. package/src/shared/mcp-format.js +189 -0
  362. package/src/shared/platforms.js +104 -167
  363. package/src/shared/project-config.js +512 -0
  364. package/src/shared/proxy-driver.js +57 -0
  365. package/src/{server/services/base → shared}/response-usage-parser.js +8 -4
  366. package/src/ui/menu.js +91 -67
  367. package/src/utils/port-helper.js +35 -19
  368. package/src/utils/session.js +1 -1
  369. package/dist/web/assets/Analytics-COEUXmd4.js +0 -25
  370. package/dist/web/assets/Analytics-RNn1BUbG.css +0 -1
  371. package/dist/web/assets/ConfigTemplates-DLLkS53H.js +0 -1
  372. package/dist/web/assets/Home-DCEyLzwX.js +0 -1
  373. package/dist/web/assets/Home-DYVy4cGZ.css +0 -1
  374. package/dist/web/assets/PluginManager-Oe1ZHoMO.js +0 -1
  375. package/dist/web/assets/ProjectList-CWjuAFof.js +0 -1
  376. package/dist/web/assets/ProjectList-VeMNXfgO.css +0 -1
  377. package/dist/web/assets/SessionList-C4chCPxK.css +0 -1
  378. package/dist/web/assets/SessionList-v3ehGg9U.js +0 -1
  379. package/dist/web/assets/SkillManager-BsX8Qpj0.css +0 -1
  380. package/dist/web/assets/SkillManager-DUF_qP5S.js +0 -1
  381. package/dist/web/assets/WorkspaceManager-CjAWBosO.js +0 -1
  382. package/dist/web/assets/icons-nGdSKQn2.js +0 -1
  383. package/dist/web/assets/index-D5hyHPj7.js +0 -2
  384. package/dist/web/assets/index-bJRG3RXH.css +0 -1
  385. package/dist/web/assets/markdown-BNJfEgCA.js +0 -1
  386. package/dist/web/assets/markdown-BfC0goYb.css +0 -10
  387. package/dist/web/assets/naive-ui-BALVpMgd.js +0 -1
  388. package/dist/web/assets/vendors-DQA4Hw9v.js +0 -7
  389. package/dist/web/assets/vue-vendor-B5RqjEjQ.js +0 -45
  390. package/src/config/model-pricing.js +0 -35
  391. package/src/server/api/channels.js +0 -439
  392. package/src/server/api/claude-hooks.js +0 -56
  393. package/src/server/api/claude-statistics.js +0 -57
  394. package/src/server/api/codex-channels.js +0 -491
  395. package/src/server/api/codex-projects.js +0 -117
  396. package/src/server/api/codex-proxy.js +0 -293
  397. package/src/server/api/codex-sessions.js +0 -652
  398. package/src/server/api/codex-statistics.js +0 -57
  399. package/src/server/api/gemini-channels.js +0 -392
  400. package/src/server/api/gemini-projects.js +0 -103
  401. package/src/server/api/gemini-proxy.js +0 -231
  402. package/src/server/api/gemini-sessions.js +0 -550
  403. package/src/server/api/gemini-statistics.js +0 -57
  404. package/src/server/api/health-check.js +0 -31
  405. package/src/server/api/oauth-credentials.js +0 -198
  406. package/src/server/api/omp-channels.js +0 -575
  407. package/src/server/api/omp-projects.js +0 -88
  408. package/src/server/api/omp-proxy.js +0 -125
  409. package/src/server/api/omp-sessions.js +0 -386
  410. package/src/server/api/omp-statistics.js +0 -40
  411. package/src/server/api/opencode-channels.js +0 -471
  412. package/src/server/api/opencode-projects.js +0 -110
  413. package/src/server/api/opencode-proxy.js +0 -354
  414. package/src/server/api/opencode-sessions.js +0 -466
  415. package/src/server/api/opencode-statistics.js +0 -57
  416. package/src/server/api/projects.js +0 -120
  417. package/src/server/api/proxy.js +0 -330
  418. package/src/server/api/sessions.js +0 -810
  419. package/src/server/services/oauth-credentials-service.js +0 -945
  420. package/src/server/services/omp-session-log-observer.js +0 -138
  421. package/src/server/services/opencode-gateway-adapters.js +0 -43
  422. package/src/server/services/project-snapshots.js +0 -59
  423. package/src/server/services/proxy-log-helper.js +0 -298
  424. package/src/server/services/session-cache.js +0 -127
  425. package/src/server/services/session-converter.js +0 -582
  426. package/src/server/services/session-history-adapters/claude.js +0 -235
  427. /package/src/{server/services/claude-token-recovery.js → platforms/drivers/claude/token-recovery.js} +0 -0
  428. /package/src/{server/services/codex-parser.js → platforms/drivers/codex/parser.js} +0 -0
  429. /package/src/{server/services/codex-speed-test-template.json → platforms/drivers/codex/speed-test-template.json} +0 -0
  430. /package/src/{server/services/codex-wire.js → platforms/drivers/codex/wire.js} +0 -0
  431. /package/src/{server/services/omp-auth-gateway-client.js → platforms/drivers/omp/auth-gateway-client.js} +0 -0
  432. /package/src/{server/services/opencode-normalization.js → platforms/drivers/opencode/normalization.js} +0 -0
  433. /package/src/{server/services → shared}/config-artifact-paths.js +0 -0
  434. /package/src/{server/services/base → shared}/proxy-utils.js +0 -0
@@ -4,6 +4,11 @@
4
4
 
5
5
  const express = require('express');
6
6
  const { SkillService } = require('../services/skill-service');
7
+ const { ControlManifestStore } = require('../services/control-manifest-store');
8
+ const { EffectiveControlService } = require('../services/effective-control-service');
9
+ const { SkillProjectionService } = require('../services/skill-projection-service');
10
+ const { SkillRefreshTaskService } = require('../services/skill-refresh-task-service');
11
+ const { PATHS } = require('../../config/paths');
7
12
  const { maskToken } = require('../services/oauth-utils');
8
13
  const { sendApiError } = require('./validation-errors');
9
14
  const { resolveManagedPlatform } = require('../services/platform-resolution');
@@ -11,10 +16,14 @@ const { validateKnownProjectCwd } = require('../services/project-path-validation
11
16
  const {
12
17
  readOmpSkillSettings,
13
18
  updateOmpSkillSettings
14
- } = require('../services/omp-skill-settings-service');
19
+ } = require('../../platforms/drivers/omp/skill-settings');
20
+ const { getPlatformContext } = require('../platform-context');
15
21
 
16
22
  const router = express.Router();
17
23
  const skillServices = new Map();
24
+ let routerOptions = {};
25
+ let defaultControlService;
26
+ let defaultRefreshTasks;
18
27
 
19
28
  function resolvePlatform(rawPlatform) {
20
29
  return resolveManagedPlatform(rawPlatform);
@@ -24,19 +33,81 @@ function getPlatform(req) {
24
33
  return resolvePlatform(req.query?.platform || req.body?.platform);
25
34
  }
26
35
 
27
- function getSkillService(req) {
28
- const resolution = getPlatform(req);
29
- const platform = resolution.platform;
36
+ function getSkillServiceForPlatform(platform) {
37
+ if (typeof routerOptions.skillServiceFactory === 'function') {
38
+ return routerOptions.skillServiceFactory(platform);
39
+ }
30
40
  if (!skillServices.has(platform)) {
31
41
  skillServices.set(platform, new SkillService(platform));
32
42
  }
43
+ return skillServices.get(platform);
44
+ }
45
+
46
+ function getSkillService(req) {
47
+ const resolution = getPlatform(req);
33
48
  return {
34
- platform,
35
- service: skillServices.get(platform),
49
+ platform: resolution.platform,
50
+ service: getSkillServiceForPlatform(resolution.platform),
36
51
  warning: resolution.warning
37
52
  };
38
53
  }
39
54
 
55
+ function getControlService() {
56
+ if (routerOptions.controlService) return routerOptions.controlService;
57
+ if (!defaultControlService && PATHS.effectiveControlManifest) {
58
+ const registry = getPlatformContext().registry;
59
+ defaultControlService = new EffectiveControlService({
60
+ store: new ControlManifestStore({
61
+ userPath: PATHS.effectiveControlManifest,
62
+ projectPathResolver: ({ projectPath }) => require('path').join(projectPath, '.ctx-control.json')
63
+ }),
64
+ projection: new SkillProjectionService({ registry })
65
+ });
66
+ }
67
+ return defaultControlService;
68
+ }
69
+
70
+ function getRefreshTasks() {
71
+ if (routerOptions.refreshTasks) return routerOptions.refreshTasks;
72
+ if (!defaultRefreshTasks && PATHS.skillRefreshTasks) {
73
+ defaultRefreshTasks = new SkillRefreshTaskService({
74
+ persistencePath: PATHS.skillRefreshTasks,
75
+ worker: context => getSkillServiceForPlatform(context.platform).refreshRemoteSkills(context)
76
+ });
77
+ }
78
+ return defaultRefreshTasks;
79
+ }
80
+
81
+ function createRouter(options = {}) {
82
+ routerOptions = options;
83
+ return router;
84
+ }
85
+
86
+ async function getScopeOptions(source = {}) {
87
+ const scope = source.scope;
88
+ if (scope && scope !== 'user' && scope !== 'project') {
89
+ throw new Error('Invalid scope: expected "user" or "project"');
90
+ }
91
+
92
+ const cwd = await validateKnownProjectCwd(source.cwd);
93
+ if (scope === 'project' && !cwd) {
94
+ throw new Error('Project scope requires a valid cwd');
95
+ }
96
+
97
+ return {
98
+ ...(cwd ? { cwd } : {}),
99
+ ...(scope ? { scope } : {})
100
+ };
101
+ }
102
+
103
+ function toControlScopeOptions(options = {}) {
104
+ const scope = options.scope || 'user';
105
+ return {
106
+ scope,
107
+ projectPath: scope === 'project' ? options.cwd : null
108
+ };
109
+ }
110
+
40
111
  function extractRepoPayload(source = {}) {
41
112
  const repo = source.repo && typeof source.repo === 'object' ? source.repo : source;
42
113
  return {
@@ -45,7 +116,7 @@ function extractRepoPayload(source = {}) {
45
116
  host: repo.host || source.host || '',
46
117
  owner: repo.owner || source.owner || '',
47
118
  name: repo.name || source.name || '',
48
- branch: repo.branch || source.branch || 'main',
119
+ branch: repo.branch || source.branch || '',
49
120
  directory: repo.directory || source.directory || '',
50
121
  projectPath: repo.projectPath || source.projectPath || '',
51
122
  localPath: repo.localPath || source.localPath || '',
@@ -73,26 +144,44 @@ function sanitizeRepos(service, repos = []) {
73
144
  }
74
145
 
75
146
  /**
76
- * 获取技能列表
147
+ * 获取本地 Skill 列表
77
148
  * GET /api/skills
78
- * Query: refresh=1 强制刷新缓存
149
+ *
150
+ * `refresh=1` is intentionally ignored here. Remote work only starts through
151
+ * POST /refresh.
79
152
  */
80
153
  router.get('/', async (req, res) => {
81
154
  try {
82
155
  const { platform, service, warning } = getSkillService(req);
83
- const forceRefresh = req.query.refresh === '1';
84
- const cwd = await validateKnownProjectCwd(req.query.cwd);
85
- if (forceRefresh) {
86
- console.log(`[Skills API] Refreshing skills for ${platform}...`);
156
+ const options = { ...(await getScopeOptions(req.query)), scope: req.query.scope || 'user' };
157
+ const snapshot = typeof service.scanSkills === 'function'
158
+ ? await service.scanSkills(options)
159
+ : {
160
+ skills: await service.listSkills(false, options),
161
+ refresh: { state: 'never_fetched', taskId: null, fetchedAt: null, error: null }
162
+ };
163
+ const skills = Array.isArray(snapshot.skills) ? snapshot.skills : [];
164
+ const refreshService = getRefreshTasks();
165
+ const refreshOptions = {
166
+ platform,
167
+ scope: options.scope || 'user',
168
+ projectPath: options.scope === 'project' ? options.cwd : null
169
+ };
170
+ const activeTask = refreshService?.getActive?.(refreshOptions);
171
+ const latestTask = activeTask || refreshService?.listRecent?.(refreshOptions)?.[0];
172
+ if (latestTask) {
173
+ snapshot.refresh = {
174
+ ...(snapshot.refresh || {}),
175
+ state: latestTask.status,
176
+ taskId: latestTask.id,
177
+ error: latestTask.error || snapshot.refresh?.error || null
178
+ };
87
179
  }
88
- const skills = await service.listSkills(forceRefresh, { cwd });
89
- console.log(`[Skills API] ${platform}: ${skills.length} skills loaded (refresh=${forceRefresh})`);
90
180
  res.json({
91
181
  success: true,
92
182
  platform,
93
- skills,
183
+ ...snapshot,
94
184
  total: skills.length,
95
- installed: skills.filter(s => s.installed).length,
96
185
  ...(warning ? { warnings: [warning] } : {})
97
186
  });
98
187
  } catch (err) {
@@ -101,6 +190,93 @@ router.get('/', async (req, res) => {
101
190
  }
102
191
  });
103
192
 
193
+ router.post('/refresh', async (req, res) => {
194
+ try {
195
+ const { platform } = getSkillService(req);
196
+ const options = await getScopeOptions(req.body);
197
+ const refreshTasks = getRefreshTasks();
198
+ if (!refreshTasks) throw new Error('Skill refresh task service is unavailable');
199
+ const task = refreshTasks.enqueue({
200
+ platform,
201
+ scope: options.scope || 'user',
202
+ projectPath: options.scope === 'project' ? options.cwd : null,
203
+ reason: 'manual'
204
+ });
205
+ res.status(202).json({ success: true, platform, task });
206
+ } catch (err) {
207
+ console.error('[Skills API] Refresh skills error:', err);
208
+ sendApiError(res, err);
209
+ }
210
+ });
211
+
212
+ router.get('/refresh/:taskId', async (req, res) => {
213
+ try {
214
+ const { platform } = getSkillService(req);
215
+ const options = await getScopeOptions(req.query);
216
+ const refreshTasks = getRefreshTasks();
217
+ const task = refreshTasks?.get(req.params.taskId);
218
+ if (!task) return res.status(404).json({ success: false, message: 'Refresh task not found' });
219
+ const taskScope = task.scope || 'user';
220
+ const taskProjectPath = taskScope === 'project' ? (task.projectPath || null) : null;
221
+ const requestedProjectPath = options.scope === 'project' ? (options.cwd || null) : null;
222
+ if (
223
+ task.platform !== platform
224
+ || taskScope !== (options.scope || 'user')
225
+ || taskProjectPath !== requestedProjectPath
226
+ ) {
227
+ return res.status(404).json({ success: false, message: 'Refresh task not found' });
228
+ }
229
+ return res.json({ success: true, task });
230
+ } catch (err) {
231
+ console.error('[Skills API] Get refresh task error:', err);
232
+ return sendApiError(res, err);
233
+ }
234
+ });
235
+
236
+ router.put('/toggle', async (req, res) => {
237
+ try {
238
+ const { platform } = getSkillService(req);
239
+ if (!req.body?.controlKey) {
240
+ return res.status(400).json({ success: false, message: 'Missing controlKey' });
241
+ }
242
+ const options = await getScopeOptions(req.body);
243
+ const controlService = getControlService();
244
+ if (!controlService) throw new Error('Effective control service is unavailable');
245
+ const result = controlService.setSkillEnabled({
246
+ platform,
247
+ controlKey: req.body.controlKey,
248
+ enabled: req.body.enabled,
249
+ ...toControlScopeOptions(options)
250
+ });
251
+ return res.json({ success: true, platform, ...result });
252
+ } catch (err) {
253
+ console.error('[Skills API] Toggle skill error:', err);
254
+ return sendApiError(res, err);
255
+ }
256
+ });
257
+
258
+ router.put('/trust', async (req, res) => {
259
+ try {
260
+ const { platform } = getSkillService(req);
261
+ if (!req.body?.controlKey) {
262
+ return res.status(400).json({ success: false, message: 'Missing controlKey' });
263
+ }
264
+ const options = await getScopeOptions(req.body);
265
+ const controlService = getControlService();
266
+ if (!controlService) throw new Error('Effective control service is unavailable');
267
+ const result = controlService.setSkillTrust({
268
+ platform,
269
+ controlKey: req.body.controlKey,
270
+ trust: req.body.trust,
271
+ ...toControlScopeOptions(options)
272
+ });
273
+ return res.json({ success: true, platform, ...result });
274
+ } catch (err) {
275
+ console.error('[Skills API] Set Skill trust error:', err);
276
+ return sendApiError(res, err);
277
+ }
278
+ });
279
+
104
280
  /**
105
281
  * 获取 OMP 技能设置
106
282
  * GET /api/skills/omp-settings
@@ -144,9 +320,21 @@ router.get('/detail/*', async (req, res) => {
144
320
  });
145
321
  }
146
322
 
323
+ const options = await getScopeOptions(req.query);
147
324
  const repoHint = extractRepoPayload(req.query || {});
148
325
  const hasRepoHint = Object.values(repoHint).some(Boolean);
149
- const result = await service.getSkillDetail(directory, hasRepoHint ? repoHint : null, req.query.fullDirectory || '');
326
+ const result = Object.keys(options).length > 0
327
+ ? await service.getSkillDetail(
328
+ directory,
329
+ hasRepoHint ? repoHint : null,
330
+ req.query.fullDirectory || '',
331
+ options
332
+ )
333
+ : await service.getSkillDetail(
334
+ directory,
335
+ hasRepoHint ? repoHint : null,
336
+ req.query.fullDirectory || ''
337
+ );
150
338
  res.json({
151
339
  success: true,
152
340
  platform,
@@ -162,14 +350,16 @@ router.get('/detail/*', async (req, res) => {
162
350
  * 获取已安装的技能
163
351
  * GET /api/skills/installed
164
352
  */
165
- router.get('/installed', (req, res) => {
353
+ router.get('/installed', async (req, res) => {
166
354
  try {
167
355
  const { platform, service } = getSkillService(req);
168
- const skills = service.getInstalledSkills();
356
+ const options = await getScopeOptions(req.query);
357
+ const snapshot = await service.scanSkills(options);
169
358
  res.json({
170
359
  success: true,
171
360
  platform,
172
- skills
361
+ ...snapshot,
362
+ total: Array.isArray(snapshot.skills) ? snapshot.skills.length : 0
173
363
  });
174
364
  } catch (err) {
175
365
  console.error('[Skills API] Get installed skills error:', err);
@@ -178,68 +368,23 @@ router.get('/installed', (req, res) => {
178
368
  });
179
369
 
180
370
  /**
181
- * 安装技能
182
- * POST /api/skills/install
183
- * Body: { directory, fullDirectory, repo }
184
- * - directory: 本地安装目录(相对路径)
185
- * - fullDirectory: 仓库中的完整路径(当指定了仓库子目录时使用)
371
+ * 旧安装入口已被 Skill control plane 取代。
186
372
  */
187
- router.post('/install', async (req, res) => {
188
- try {
189
- const { platform, service } = getSkillService(req);
190
- const { directory, fullDirectory, repo } = req.body;
191
-
192
- if (!directory) {
193
- return res.status(400).json({
194
- success: false,
195
- message: 'Missing directory'
196
- });
197
- }
198
-
199
- if (!repo) {
200
- return res.status(400).json({
201
- success: false,
202
- message: 'Missing repo info'
203
- });
204
- }
205
-
206
- const result = await service.installSkill(
207
- directory,
208
- extractRepoPayload({ repo }),
209
- fullDirectory || null // 传递 fullDirectory 用于从仓库子目录下载
210
- );
211
-
212
- res.json({
213
- success: true,
214
- platform,
215
- ...result
216
- });
217
- } catch (err) {
218
- console.error('[Skills API] Install skill error:', err);
219
- sendApiError(res, err);
220
- }
373
+ router.post('/install', (req, res) => {
374
+ res.status(410).json({
375
+ success: false,
376
+ message: 'Skill installation is replaced by POST /api/skills/refresh and PUT /api/skills/toggle'
377
+ });
221
378
  });
222
379
 
223
380
  /**
224
- * 安装本地 cc-tool 托管的技能
225
- * POST /api/skills/install-local
226
- * Body: { directory }
381
+ * 旧本地安装入口已被 Skill control plane 取代。
227
382
  */
228
383
  router.post('/install-local', (req, res) => {
229
- try {
230
- const { platform, service } = getSkillService(req);
231
- const { directory } = req.body;
232
-
233
- if (!directory) {
234
- return res.status(400).json({ success: false, message: 'Missing directory' });
235
- }
236
-
237
- const result = service.installLocalSkill(directory);
238
- res.json({ success: true, platform, ...result });
239
- } catch (err) {
240
- console.error('[Skills API] Install local skill error:', err);
241
- sendApiError(res, err);
242
- }
384
+ res.status(410).json({
385
+ success: false,
386
+ message: 'Skill installation is replaced by PUT /api/skills/toggle'
387
+ });
243
388
  });
244
389
 
245
390
  /**
@@ -247,10 +392,11 @@ router.post('/install-local', (req, res) => {
247
392
  * POST /api/skills/create
248
393
  * Body: { name, directory, description, content }
249
394
  */
250
- router.post('/create', (req, res) => {
395
+ router.post('/create', async (req, res) => {
251
396
  try {
252
397
  const { platform, service } = getSkillService(req);
253
398
  const { name, directory, description, content } = req.body;
399
+ const options = await getScopeOptions(req.body);
254
400
 
255
401
  if (!directory) {
256
402
  return res.status(400).json({
@@ -273,12 +419,12 @@ router.post('/create', (req, res) => {
273
419
  message: '请输入技能内容'
274
420
  });
275
421
  }
276
-
277
- const result = service.createCustomSkill({
422
+ const result = await service.createCustomSkill({
278
423
  name: name || directory,
279
424
  directory,
280
425
  description: description || '',
281
- content
426
+ content,
427
+ ...options
282
428
  });
283
429
 
284
430
  res.json({
@@ -293,46 +439,13 @@ router.post('/create', (req, res) => {
293
439
  });
294
440
 
295
441
  /**
296
- * 卸载技能
297
- * POST /api/skills/uninstall
298
- * Body: { directory }
442
+ * 旧卸载入口已被 Skill toggle 取代。
299
443
  */
300
- router.post('/uninstall', async (req, res) => {
301
- try {
302
- const { platform, service } = getSkillService(req);
303
- const { directory, scope } = req.body;
304
-
305
- if (!directory) {
306
- return res.status(400).json({
307
- success: false,
308
- message: 'Missing directory'
309
- });
310
- }
311
-
312
- const cwd = await validateKnownProjectCwd(req.body.cwd);
313
- if (scope && scope !== 'user' && scope !== 'project') {
314
- throw new Error('Invalid scope: expected "user" or "project"');
315
- }
316
- if (scope === 'project' && !cwd) {
317
- throw new Error('Project scope requires a valid cwd');
318
- }
319
- const options = {
320
- ...(cwd ? { cwd } : {}),
321
- ...(scope ? { scope } : {})
322
- };
323
- const result = platform === 'omp' && Object.keys(options).length > 0
324
- ? service.uninstallSkill(directory, options)
325
- : service.uninstallSkill(directory);
326
-
327
- res.json({
328
- success: true,
329
- platform,
330
- ...result
331
- });
332
- } catch (err) {
333
- console.error('[Skills API] Uninstall skill error:', err);
334
- sendApiError(res, err);
335
- }
444
+ router.post('/uninstall', (req, res) => {
445
+ res.status(410).json({
446
+ success: false,
447
+ message: 'Skill uninstallation is replaced by PUT /api/skills/toggle'
448
+ });
336
449
  });
337
450
 
338
451
  /**
@@ -508,10 +621,11 @@ router.put('/repos/:owner/:name/toggle', (req, res) => {
508
621
  * POST /api/skills/create-with-files
509
622
  * Body: { directory, files: [{path, content, isBase64?}] }
510
623
  */
511
- router.post('/create-with-files', (req, res) => {
624
+ router.post('/create-with-files', async (req, res) => {
512
625
  try {
513
626
  const { platform, service } = getSkillService(req);
514
627
  const { directory, files } = req.body;
628
+ const options = await getScopeOptions(req.body);
515
629
 
516
630
  if (!directory) {
517
631
  return res.status(400).json({
@@ -535,7 +649,7 @@ router.post('/create-with-files', (req, res) => {
535
649
  });
536
650
  }
537
651
 
538
- const result = service.createSkillWithFiles({ directory, files });
652
+ const result = await service.createSkillWithFiles({ directory, files, ...options });
539
653
 
540
654
  res.json({
541
655
  success: true,
@@ -552,12 +666,14 @@ router.post('/create-with-files', (req, res) => {
552
666
  * 获取技能文件列表
553
667
  * GET /api/skills/:directory/files
554
668
  */
555
- router.get('/:directory/files', (req, res) => {
669
+ router.get('/:directory/files', async (req, res) => {
556
670
  try {
557
671
  const { platform, service } = getSkillService(req);
558
672
  const { directory } = req.params;
559
- const files = service.getSkillFiles(directory);
560
-
673
+ const options = await getScopeOptions(req.query);
674
+ const files = Object.keys(options).length > 0
675
+ ? service.getSkillFiles(directory, options)
676
+ : service.getSkillFiles(directory);
561
677
  res.json({
562
678
  success: true,
563
679
  platform,
@@ -575,10 +691,11 @@ router.get('/:directory/files', (req, res) => {
575
691
  * GET /api/skills/:directory/files/:filePath
576
692
  * 注意:filePath 可能包含子目录,使用通配符
577
693
  */
578
- router.get('/:directory/file/*', (req, res) => {
694
+ router.get('/:directory/file/*', async (req, res) => {
579
695
  try {
580
696
  const { platform, service } = getSkillService(req);
581
697
  const { directory } = req.params;
698
+ const options = await getScopeOptions(req.query);
582
699
  const filePath = req.params[0];
583
700
 
584
701
  if (!filePath) {
@@ -588,7 +705,9 @@ router.get('/:directory/file/*', (req, res) => {
588
705
  });
589
706
  }
590
707
 
591
- const result = service.getSkillFileContent(directory, filePath);
708
+ const result = Object.keys(options).length > 0
709
+ ? service.getSkillFileContent(directory, filePath, options)
710
+ : service.getSkillFileContent(directory, filePath);
592
711
 
593
712
  res.json({
594
713
  success: true,
@@ -606,11 +725,12 @@ router.get('/:directory/file/*', (req, res) => {
606
725
  * POST /api/skills/:directory/files
607
726
  * Body: { files: [{path, content, isBase64?}] }
608
727
  */
609
- router.post('/:directory/files', (req, res) => {
728
+ router.post('/:directory/files', async (req, res) => {
610
729
  try {
611
730
  const { platform, service } = getSkillService(req);
612
731
  const { directory } = req.params;
613
732
  const { files } = req.body;
733
+ const options = await getScopeOptions(req.body);
614
734
 
615
735
  if (!files || !Array.isArray(files) || files.length === 0) {
616
736
  return res.status(400).json({
@@ -619,7 +739,9 @@ router.post('/:directory/files', (req, res) => {
619
739
  });
620
740
  }
621
741
 
622
- const result = service.addSkillFiles(directory, files);
742
+ const result = Object.keys(options).length > 0
743
+ ? service.addSkillFiles(directory, files, options)
744
+ : service.addSkillFiles(directory, files);
623
745
 
624
746
  res.json({
625
747
  success: true,
@@ -636,10 +758,11 @@ router.post('/:directory/files', (req, res) => {
636
758
  * 删除技能中的文件
637
759
  * DELETE /api/skills/:directory/file/*
638
760
  */
639
- router.delete('/:directory/file/*', (req, res) => {
761
+ router.delete('/:directory/file/*', async (req, res) => {
640
762
  try {
641
763
  const { platform, service } = getSkillService(req);
642
764
  const { directory } = req.params;
765
+ const options = await getScopeOptions(req.query);
643
766
  const filePath = req.params[0];
644
767
 
645
768
  if (!filePath) {
@@ -649,7 +772,9 @@ router.delete('/:directory/file/*', (req, res) => {
649
772
  });
650
773
  }
651
774
 
652
- const result = service.deleteSkillFile(directory, filePath);
775
+ const result = Object.keys(options).length > 0
776
+ ? service.deleteSkillFile(directory, filePath, options)
777
+ : service.deleteSkillFile(directory, filePath);
653
778
 
654
779
  res.json({
655
780
  success: true,
@@ -667,10 +792,11 @@ router.delete('/:directory/file/*', (req, res) => {
667
792
  * PUT /api/skills/:directory/file/*
668
793
  * Body: { content, isBase64? }
669
794
  */
670
- router.put('/:directory/file/*', (req, res) => {
795
+ router.put('/:directory/file/*', async (req, res) => {
671
796
  try {
672
797
  const { platform, service } = getSkillService(req);
673
798
  const { directory } = req.params;
799
+ const options = await getScopeOptions(req.body);
674
800
  const filePath = req.params[0];
675
801
  const { content, isBase64 = false } = req.body;
676
802
 
@@ -688,7 +814,9 @@ router.put('/:directory/file/*', (req, res) => {
688
814
  });
689
815
  }
690
816
 
691
- const result = service.updateSkillFile(directory, filePath, content, isBase64);
817
+ const result = Object.keys(options).length > 0
818
+ ? service.updateSkillFile(directory, filePath, content, isBase64, options)
819
+ : service.updateSkillFile(directory, filePath, content, isBase64);
692
820
 
693
821
  res.json({
694
822
  success: true,
@@ -701,4 +829,5 @@ router.put('/:directory/file/*', (req, res) => {
701
829
  }
702
830
  });
703
831
 
832
+ router.createRouter = createRouter;
704
833
  module.exports = router;
@@ -30,8 +30,8 @@ router.post('/', (req, res) => {
30
30
  if (!config) {
31
31
  return res.status(400).json({ error: 'Missing config' });
32
32
  }
33
- saveUIConfig(config);
34
- res.json({ success: true, config });
33
+ const normalizedConfig = saveUIConfig(config);
34
+ res.json({ success: true, config: normalizedConfig });
35
35
  } catch (error) {
36
36
  console.error('Error saving UI config:', error);
37
37
  res.status(500).json({ error: error.message });
@@ -11,7 +11,10 @@ const VALIDATION_ERROR_PATTERNS = [
11
11
  /required/i,
12
12
  /escapes/i,
13
13
  /not support/i,
14
- /不支持/
14
+ /不支持/,
15
+ /symlink/i,
16
+ /string/i,
17
+ /MCP server ID/i,
15
18
  ];
16
19
 
17
20
  function isValidationError(error) {
@@ -20,12 +23,23 @@ function isValidationError(error) {
20
23
  }
21
24
 
22
25
  function sendApiError(res, error, fallbackStatus = 500, extra = {}) {
23
- const status = isValidationError(error) ? 400 : fallbackStatus;
24
- return res.status(status).json({
26
+ const status = ['not_found', 'unsupported'].includes(error?.code)
27
+ ? 404
28
+ : error?.code === 'invalid'
29
+ ? 400
30
+ : error?.code === 'failed'
31
+ ? 500
32
+ : (isValidationError(error) ? 400 : fallbackStatus);
33
+ const payload = {
25
34
  success: false,
26
35
  message: error?.message || String(error),
27
36
  ...extra
28
- });
37
+ };
38
+ if (error?.code) payload.code = error.code;
39
+ if (error?.platform) payload.platform = error.platform;
40
+ if (error?.capability) payload.capability = error.capability;
41
+ if (error?.operation) payload.operation = error.operation;
42
+ return res.status(status).json(payload);
29
43
  }
30
44
 
31
45
  module.exports = {