@cloudcli-ai/cloudcli 1.29.5 → 1.31.0

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 (354) hide show
  1. package/README.de.md +1 -1
  2. package/README.ja.md +1 -1
  3. package/README.ko.md +1 -1
  4. package/README.md +1 -1
  5. package/README.ru.md +1 -1
  6. package/README.tr.md +252 -0
  7. package/README.zh-CN.md +1 -1
  8. package/dist/assets/index-DF8TTVHl.css +32 -0
  9. package/dist/assets/index-DbZAcc9z.js +1262 -0
  10. package/dist/index.html +2 -2
  11. package/dist-server/server/claude-sdk.js +13 -8
  12. package/dist-server/server/claude-sdk.js.map +1 -1
  13. package/dist-server/server/cursor-cli.js +7 -7
  14. package/dist-server/server/cursor-cli.js.map +1 -1
  15. package/dist-server/server/gemini-cli.js +5 -5
  16. package/dist-server/server/gemini-cli.js.map +1 -1
  17. package/dist-server/server/gemini-response-handler.js +2 -2
  18. package/dist-server/server/gemini-response-handler.js.map +1 -1
  19. package/dist-server/server/index.js +139 -977
  20. package/dist-server/server/index.js.map +1 -1
  21. package/dist-server/server/middleware/auth.js +1 -1
  22. package/dist-server/server/middleware/auth.js.map +1 -1
  23. package/dist-server/server/modules/database/connection.js +125 -0
  24. package/dist-server/server/modules/database/connection.js.map +1 -0
  25. package/dist-server/server/modules/database/index.js +13 -0
  26. package/dist-server/server/modules/database/index.js.map +1 -0
  27. package/dist-server/server/modules/database/init-db.js +18 -0
  28. package/dist-server/server/modules/database/init-db.js.map +1 -0
  29. package/dist-server/server/modules/database/migrations.js +370 -0
  30. package/dist-server/server/modules/database/migrations.js.map +1 -0
  31. package/dist-server/server/modules/database/repositories/api-keys.js +72 -0
  32. package/dist-server/server/modules/database/repositories/api-keys.js.map +1 -0
  33. package/dist-server/server/modules/database/repositories/app-config.js +47 -0
  34. package/dist-server/server/modules/database/repositories/app-config.js.map +1 -0
  35. package/dist-server/server/modules/database/repositories/credentials.js +68 -0
  36. package/dist-server/server/modules/database/repositories/credentials.js.map +1 -0
  37. package/dist-server/server/modules/database/repositories/github-tokens.js +54 -0
  38. package/dist-server/server/modules/database/repositories/github-tokens.js.map +1 -0
  39. package/dist-server/server/modules/database/repositories/notification-preferences.js +72 -0
  40. package/dist-server/server/modules/database/repositories/notification-preferences.js.map +1 -0
  41. package/dist-server/server/modules/database/repositories/projects.db.integration.test.js +64 -0
  42. package/dist-server/server/modules/database/repositories/projects.db.integration.test.js.map +1 -0
  43. package/dist-server/server/modules/database/repositories/projects.db.js +160 -0
  44. package/dist-server/server/modules/database/repositories/projects.db.js.map +1 -0
  45. package/dist-server/server/modules/database/repositories/push-subscriptions.js +49 -0
  46. package/dist-server/server/modules/database/repositories/push-subscriptions.js.map +1 -0
  47. package/dist-server/server/modules/database/repositories/scan-state.db.js +31 -0
  48. package/dist-server/server/modules/database/repositories/scan-state.db.js.map +1 -0
  49. package/dist-server/server/modules/database/repositories/sessions.db.js +109 -0
  50. package/dist-server/server/modules/database/repositories/sessions.db.js.map +1 -0
  51. package/dist-server/server/modules/database/repositories/users.js +88 -0
  52. package/dist-server/server/modules/database/repositories/users.js.map +1 -0
  53. package/dist-server/server/modules/database/repositories/vapid-keys.js +38 -0
  54. package/dist-server/server/modules/database/repositories/vapid-keys.js.map +1 -0
  55. package/dist-server/server/modules/database/schema.js +143 -0
  56. package/dist-server/server/modules/database/schema.js.map +1 -0
  57. package/dist-server/server/modules/projects/index.js +4 -0
  58. package/dist-server/server/modules/projects/index.js.map +1 -0
  59. package/dist-server/server/modules/projects/projects.routes.js +188 -0
  60. package/dist-server/server/modules/projects/projects.routes.js.map +1 -0
  61. package/dist-server/server/modules/projects/services/project-clone.service.js +219 -0
  62. package/dist-server/server/modules/projects/services/project-clone.service.js.map +1 -0
  63. package/dist-server/server/modules/projects/services/project-delete.service.js +65 -0
  64. package/dist-server/server/modules/projects/services/project-delete.service.js.map +1 -0
  65. package/dist-server/server/modules/projects/services/project-management.service.js +93 -0
  66. package/dist-server/server/modules/projects/services/project-management.service.js.map +1 -0
  67. package/dist-server/server/modules/projects/services/project-star.service.js +60 -0
  68. package/dist-server/server/modules/projects/services/project-star.service.js.map +1 -0
  69. package/dist-server/server/modules/projects/services/projects-has-taskmaster.service.js +167 -0
  70. package/dist-server/server/modules/projects/services/projects-has-taskmaster.service.js.map +1 -0
  71. package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js +169 -0
  72. package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js.map +1 -0
  73. package/dist-server/server/modules/projects/tests/project-clone.service.test.js +126 -0
  74. package/dist-server/server/modules/projects/tests/project-clone.service.test.js.map +1 -0
  75. package/dist-server/server/modules/projects/tests/project-management.service.test.js +85 -0
  76. package/dist-server/server/modules/projects/tests/project-management.service.test.js.map +1 -0
  77. package/dist-server/server/modules/projects/tests/project-star.service.test.js +95 -0
  78. package/dist-server/server/modules/projects/tests/project-star.service.test.js.map +1 -0
  79. package/dist-server/server/modules/projects/tests/projects-has-taskmaster.service.test.js +87 -0
  80. package/dist-server/server/modules/projects/tests/projects-has-taskmaster.service.test.js.map +1 -0
  81. package/dist-server/server/modules/providers/index.js +4 -0
  82. package/dist-server/server/modules/providers/index.js.map +1 -0
  83. package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js +103 -0
  84. package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js.map +1 -0
  85. package/dist-server/server/modules/providers/list/claude/claude-mcp.provider.js +103 -0
  86. package/dist-server/server/modules/providers/list/claude/claude-mcp.provider.js.map +1 -0
  87. package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js +63 -0
  88. package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js.map +1 -0
  89. package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js +403 -0
  90. package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js.map +1 -0
  91. package/dist-server/server/modules/providers/list/claude/claude.provider.js +15 -0
  92. package/dist-server/server/modules/providers/list/claude/claude.provider.js.map +1 -0
  93. package/dist-server/server/modules/providers/list/codex/codex-auth.provider.js +84 -0
  94. package/dist-server/server/modules/providers/list/codex/codex-auth.provider.js.map +1 -0
  95. package/dist-server/server/modules/providers/list/codex/codex-mcp.provider.js +107 -0
  96. package/dist-server/server/modules/providers/list/codex/codex-mcp.provider.js.map +1 -0
  97. package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +71 -0
  98. package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js.map +1 -0
  99. package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js +494 -0
  100. package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -0
  101. package/dist-server/server/modules/providers/list/codex/codex.provider.js +15 -0
  102. package/dist-server/server/modules/providers/list/codex/codex.provider.js.map +1 -0
  103. package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js +118 -0
  104. package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js.map +1 -0
  105. package/dist-server/server/modules/providers/list/cursor/cursor-mcp.provider.js +80 -0
  106. package/dist-server/server/modules/providers/list/cursor/cursor-mcp.provider.js.map +1 -0
  107. package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js +127 -0
  108. package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js.map +1 -0
  109. package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js +369 -0
  110. package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js.map +1 -0
  111. package/dist-server/server/modules/providers/list/cursor/cursor.provider.js +15 -0
  112. package/dist-server/server/modules/providers/list/cursor/cursor.provider.js.map +1 -0
  113. package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js +131 -0
  114. package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js.map +1 -0
  115. package/dist-server/server/modules/providers/list/gemini/gemini-mcp.provider.js +82 -0
  116. package/dist-server/server/modules/providers/list/gemini/gemini-mcp.provider.js.map +1 -0
  117. package/dist-server/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.js +296 -0
  118. package/dist-server/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.js.map +1 -0
  119. package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js +474 -0
  120. package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js.map +1 -0
  121. package/dist-server/server/modules/providers/list/gemini/gemini.provider.js +15 -0
  122. package/dist-server/server/modules/providers/list/gemini/gemini.provider.js.map +1 -0
  123. package/dist-server/server/modules/providers/provider.registry.js +31 -0
  124. package/dist-server/server/modules/providers/provider.registry.js.map +1 -0
  125. package/dist-server/server/modules/providers/provider.routes.js +331 -0
  126. package/dist-server/server/modules/providers/provider.routes.js.map +1 -0
  127. package/dist-server/server/modules/providers/services/mcp.service.js +69 -0
  128. package/dist-server/server/modules/providers/services/mcp.service.js.map +1 -0
  129. package/dist-server/server/modules/providers/services/provider-auth.service.js +25 -0
  130. package/dist-server/server/modules/providers/services/provider-auth.service.js.map +1 -0
  131. package/dist-server/server/modules/providers/services/session-conversations-search.service.js +868 -0
  132. package/dist-server/server/modules/providers/services/session-conversations-search.service.js.map +1 -0
  133. package/dist-server/server/modules/providers/services/session-synchronizer.service.js +56 -0
  134. package/dist-server/server/modules/providers/services/session-synchronizer.service.js.map +1 -0
  135. package/dist-server/server/modules/providers/services/sessions-watcher.service.js +234 -0
  136. package/dist-server/server/modules/providers/services/sessions-watcher.service.js.map +1 -0
  137. package/dist-server/server/modules/providers/services/sessions.service.js +104 -0
  138. package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -0
  139. package/dist-server/server/modules/providers/shared/base/abstract.provider.js +14 -0
  140. package/dist-server/server/modules/providers/shared/base/abstract.provider.js.map +1 -0
  141. package/dist-server/server/modules/providers/shared/mcp/mcp.provider.js +102 -0
  142. package/dist-server/server/modules/providers/shared/mcp/mcp.provider.js.map +1 -0
  143. package/dist-server/server/modules/providers/tests/mcp.test.js +250 -0
  144. package/dist-server/server/modules/providers/tests/mcp.test.js.map +1 -0
  145. package/dist-server/server/modules/websocket/index.js +3 -0
  146. package/dist-server/server/modules/websocket/index.js.map +1 -0
  147. package/dist-server/server/modules/websocket/services/chat-websocket.service.js +188 -0
  148. package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -0
  149. package/dist-server/server/modules/websocket/services/plugin-websocket-proxy.service.js +52 -0
  150. package/dist-server/server/modules/websocket/services/plugin-websocket-proxy.service.js.map +1 -0
  151. package/dist-server/server/modules/websocket/services/shell-websocket.service.js +334 -0
  152. package/dist-server/server/modules/websocket/services/shell-websocket.service.js.map +1 -0
  153. package/dist-server/server/modules/websocket/services/websocket-auth.service.js +32 -0
  154. package/dist-server/server/modules/websocket/services/websocket-auth.service.js.map +1 -0
  155. package/dist-server/server/modules/websocket/services/websocket-server.service.js +36 -0
  156. package/dist-server/server/modules/websocket/services/websocket-server.service.js.map +1 -0
  157. package/dist-server/server/modules/websocket/services/websocket-state.service.js +14 -0
  158. package/dist-server/server/modules/websocket/services/websocket-state.service.js.map +1 -0
  159. package/dist-server/server/modules/websocket/services/websocket-writer.service.js +32 -0
  160. package/dist-server/server/modules/websocket/services/websocket-writer.service.js.map +1 -0
  161. package/dist-server/server/openai-codex.js +5 -5
  162. package/dist-server/server/openai-codex.js.map +1 -1
  163. package/dist-server/server/routes/agent.js +10 -17
  164. package/dist-server/server/routes/agent.js.map +1 -1
  165. package/dist-server/server/routes/auth.js +3 -1
  166. package/dist-server/server/routes/auth.js.map +1 -1
  167. package/dist-server/server/routes/commands.js +1 -45
  168. package/dist-server/server/routes/commands.js.map +1 -1
  169. package/dist-server/server/routes/cursor.js +9 -489
  170. package/dist-server/server/routes/cursor.js.map +1 -1
  171. package/dist-server/server/routes/gemini.js +2 -2
  172. package/dist-server/server/routes/gemini.js.map +1 -1
  173. package/dist-server/server/routes/git.js +33 -29
  174. package/dist-server/server/routes/git.js.map +1 -1
  175. package/dist-server/server/routes/mcp-utils.js +1 -18
  176. package/dist-server/server/routes/mcp-utils.js.map +1 -1
  177. package/dist-server/server/routes/settings.js +11 -1
  178. package/dist-server/server/routes/settings.js.map +1 -1
  179. package/dist-server/server/routes/taskmaster.js +97 -569
  180. package/dist-server/server/routes/taskmaster.js.map +1 -1
  181. package/dist-server/server/routes/user.js +1 -1
  182. package/dist-server/server/routes/user.js.map +1 -1
  183. package/dist-server/server/services/notification-orchestrator.js +2 -2
  184. package/dist-server/server/services/notification-orchestrator.js.map +1 -1
  185. package/dist-server/server/services/vapid-keys.js +2 -1
  186. package/dist-server/server/services/vapid-keys.js.map +1 -1
  187. package/dist-server/server/shared/interfaces.js +2 -0
  188. package/dist-server/server/shared/interfaces.js.map +1 -0
  189. package/dist-server/server/shared/types.js +2 -0
  190. package/dist-server/server/shared/types.js.map +1 -0
  191. package/dist-server/server/shared/utils.js +551 -0
  192. package/dist-server/server/shared/utils.js.map +1 -0
  193. package/dist-server/server/utils/mcp-detector.js +0 -49
  194. package/dist-server/server/utils/mcp-detector.js.map +1 -1
  195. package/dist-server/server/utils/taskmaster-websocket.js +18 -12
  196. package/dist-server/server/utils/taskmaster-websocket.js.map +1 -1
  197. package/dist-server/shared/modelConstants.js +3 -1
  198. package/dist-server/shared/modelConstants.js.map +1 -1
  199. package/package.json +7 -2
  200. package/server/claude-sdk.js +14 -8
  201. package/server/cursor-cli.js +7 -7
  202. package/server/gemini-cli.js +5 -5
  203. package/server/gemini-response-handler.js +2 -2
  204. package/server/index.js +173 -1035
  205. package/server/middleware/auth.js +1 -1
  206. package/server/modules/database/connection.ts +143 -0
  207. package/server/modules/database/index.ts +12 -0
  208. package/server/modules/database/init-db.ts +17 -0
  209. package/server/modules/database/migrations.ts +442 -0
  210. package/server/modules/database/repositories/api-keys.ts +119 -0
  211. package/server/modules/database/repositories/app-config.ts +53 -0
  212. package/server/modules/database/repositories/credentials.ts +106 -0
  213. package/server/modules/database/repositories/github-tokens.ts +100 -0
  214. package/server/modules/database/repositories/notification-preferences.ts +103 -0
  215. package/server/modules/database/repositories/projects.db.integration.test.ts +72 -0
  216. package/server/modules/database/repositories/projects.db.ts +183 -0
  217. package/server/modules/database/repositories/push-subscriptions.ts +80 -0
  218. package/server/modules/database/repositories/scan-state.db.ts +42 -0
  219. package/server/modules/database/repositories/sessions.db.ts +174 -0
  220. package/server/modules/database/repositories/users.ts +140 -0
  221. package/server/modules/database/repositories/vapid-keys.ts +57 -0
  222. package/server/modules/database/schema.ts +152 -0
  223. package/server/modules/projects/index.ts +6 -0
  224. package/server/modules/projects/projects.routes.ts +247 -0
  225. package/server/modules/projects/services/project-clone.service.ts +321 -0
  226. package/server/modules/projects/services/project-delete.service.ts +75 -0
  227. package/server/modules/projects/services/project-management.service.ts +150 -0
  228. package/server/modules/projects/services/project-star.service.ts +78 -0
  229. package/server/modules/projects/services/projects-has-taskmaster.service.ts +248 -0
  230. package/server/modules/projects/services/projects-with-sessions-fetch.service.ts +285 -0
  231. package/server/modules/projects/tests/project-clone.service.test.ts +183 -0
  232. package/server/modules/projects/tests/project-management.service.test.ts +117 -0
  233. package/server/modules/projects/tests/project-star.service.test.ts +123 -0
  234. package/server/modules/projects/tests/projects-has-taskmaster.service.test.ts +105 -0
  235. package/server/modules/providers/index.ts +4 -0
  236. package/server/modules/providers/list/claude/claude-auth.provider.ts +123 -0
  237. package/server/modules/providers/list/claude/claude-mcp.provider.ts +135 -0
  238. package/server/modules/providers/list/claude/claude-session-synchronizer.provider.ts +110 -0
  239. package/server/modules/providers/list/claude/claude-sessions.provider.ts +475 -0
  240. package/server/modules/providers/list/claude/claude.provider.ts +17 -0
  241. package/server/modules/providers/list/codex/codex-auth.provider.ts +100 -0
  242. package/server/modules/providers/list/codex/codex-mcp.provider.ts +135 -0
  243. package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +119 -0
  244. package/server/modules/providers/list/codex/codex-sessions.provider.ts +564 -0
  245. package/server/modules/providers/list/codex/codex.provider.ts +17 -0
  246. package/server/modules/providers/list/cursor/cursor-auth.provider.ts +143 -0
  247. package/server/modules/providers/list/cursor/cursor-mcp.provider.ts +108 -0
  248. package/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.ts +176 -0
  249. package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +421 -0
  250. package/server/modules/providers/list/cursor/cursor.provider.ts +17 -0
  251. package/server/modules/providers/list/gemini/gemini-auth.provider.ts +151 -0
  252. package/server/modules/providers/list/gemini/gemini-mcp.provider.ts +110 -0
  253. package/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.ts +401 -0
  254. package/server/modules/providers/list/gemini/gemini-sessions.provider.ts +541 -0
  255. package/server/modules/providers/list/gemini/gemini.provider.ts +17 -0
  256. package/server/modules/providers/provider.registry.ts +36 -0
  257. package/server/modules/providers/provider.routes.ts +425 -0
  258. package/server/modules/providers/services/mcp.service.ts +94 -0
  259. package/server/modules/providers/services/provider-auth.service.ts +26 -0
  260. package/server/modules/providers/services/session-conversations-search.service.ts +1167 -0
  261. package/server/modules/providers/services/session-synchronizer.service.ts +74 -0
  262. package/server/modules/providers/services/sessions-watcher.service.ts +283 -0
  263. package/server/modules/providers/services/sessions.service.ts +130 -0
  264. package/server/modules/providers/shared/base/abstract.provider.ts +27 -0
  265. package/server/modules/providers/shared/mcp/mcp.provider.ts +151 -0
  266. package/server/modules/providers/tests/mcp.test.ts +293 -0
  267. package/server/modules/websocket/README.md +267 -0
  268. package/server/modules/websocket/index.ts +2 -0
  269. package/server/modules/websocket/services/chat-websocket.service.ts +271 -0
  270. package/server/modules/websocket/services/plugin-websocket-proxy.service.ts +65 -0
  271. package/server/modules/websocket/services/shell-websocket.service.ts +453 -0
  272. package/server/modules/websocket/services/websocket-auth.service.ts +54 -0
  273. package/server/modules/websocket/services/websocket-server.service.ts +58 -0
  274. package/server/modules/websocket/services/websocket-state.service.ts +16 -0
  275. package/server/modules/websocket/services/websocket-writer.service.ts +38 -0
  276. package/server/openai-codex.js +5 -5
  277. package/server/routes/agent.js +11 -16
  278. package/server/routes/auth.js +4 -2
  279. package/server/routes/commands.js +1 -50
  280. package/server/routes/cursor.js +15 -527
  281. package/server/routes/gemini.js +3 -2
  282. package/server/routes/git.js +33 -28
  283. package/server/routes/mcp-utils.js +2 -19
  284. package/server/routes/settings.js +11 -1
  285. package/server/routes/taskmaster.js +107 -602
  286. package/server/routes/user.js +1 -1
  287. package/server/services/notification-orchestrator.js +3 -2
  288. package/server/services/vapid-keys.js +2 -1
  289. package/server/shared/interfaces.ts +92 -0
  290. package/server/shared/types.ts +327 -0
  291. package/server/shared/utils.ts +669 -0
  292. package/server/tsconfig.json +6 -3
  293. package/server/utils/mcp-detector.js +0 -51
  294. package/server/utils/taskmaster-websocket.js +19 -13
  295. package/shared/modelConstants.js +3 -1
  296. package/dist/assets/index-BBAE1OJ_.css +0 -32
  297. package/dist/assets/index-Ci1GOyV7.js +0 -1262
  298. package/dist-server/server/database/db.js +0 -535
  299. package/dist-server/server/database/db.js.map +0 -1
  300. package/dist-server/server/database/schema.js +0 -97
  301. package/dist-server/server/database/schema.js.map +0 -1
  302. package/dist-server/server/projects.js +0 -2325
  303. package/dist-server/server/projects.js.map +0 -1
  304. package/dist-server/server/providers/claude/adapter.js +0 -266
  305. package/dist-server/server/providers/claude/adapter.js.map +0 -1
  306. package/dist-server/server/providers/claude/status.js +0 -121
  307. package/dist-server/server/providers/claude/status.js.map +0 -1
  308. package/dist-server/server/providers/codex/adapter.js +0 -232
  309. package/dist-server/server/providers/codex/adapter.js.map +0 -1
  310. package/dist-server/server/providers/codex/status.js +0 -70
  311. package/dist-server/server/providers/codex/status.js.map +0 -1
  312. package/dist-server/server/providers/cursor/adapter.js +0 -342
  313. package/dist-server/server/providers/cursor/adapter.js.map +0 -1
  314. package/dist-server/server/providers/cursor/status.js +0 -118
  315. package/dist-server/server/providers/cursor/status.js.map +0 -1
  316. package/dist-server/server/providers/gemini/adapter.js +0 -174
  317. package/dist-server/server/providers/gemini/adapter.js.map +0 -1
  318. package/dist-server/server/providers/gemini/status.js +0 -104
  319. package/dist-server/server/providers/gemini/status.js.map +0 -1
  320. package/dist-server/server/providers/registry.js +0 -58
  321. package/dist-server/server/providers/registry.js.map +0 -1
  322. package/dist-server/server/providers/types.js +0 -117
  323. package/dist-server/server/providers/types.js.map +0 -1
  324. package/dist-server/server/providers/utils.js +0 -28
  325. package/dist-server/server/providers/utils.js.map +0 -1
  326. package/dist-server/server/routes/cli-auth.js +0 -25
  327. package/dist-server/server/routes/cli-auth.js.map +0 -1
  328. package/dist-server/server/routes/codex.js +0 -294
  329. package/dist-server/server/routes/codex.js.map +0 -1
  330. package/dist-server/server/routes/mcp.js +0 -480
  331. package/dist-server/server/routes/mcp.js.map +0 -1
  332. package/dist-server/server/routes/messages.js +0 -56
  333. package/dist-server/server/routes/messages.js.map +0 -1
  334. package/dist-server/server/routes/projects.js +0 -505
  335. package/dist-server/server/routes/projects.js.map +0 -1
  336. package/server/database/db.js +0 -593
  337. package/server/database/schema.js +0 -102
  338. package/server/projects.js +0 -2555
  339. package/server/providers/claude/adapter.js +0 -278
  340. package/server/providers/claude/status.js +0 -136
  341. package/server/providers/codex/adapter.js +0 -248
  342. package/server/providers/codex/status.js +0 -78
  343. package/server/providers/cursor/adapter.js +0 -348
  344. package/server/providers/cursor/status.js +0 -128
  345. package/server/providers/gemini/adapter.js +0 -186
  346. package/server/providers/gemini/status.js +0 -111
  347. package/server/providers/registry.js +0 -67
  348. package/server/providers/types.js +0 -132
  349. package/server/providers/utils.js +0 -29
  350. package/server/routes/cli-auth.js +0 -27
  351. package/server/routes/codex.js +0 -329
  352. package/server/routes/mcp.js +0 -552
  353. package/server/routes/messages.js +0 -61
  354. package/server/routes/projects.js +0 -548
@@ -0,0 +1,74 @@
1
+ import { scanStateDb } from '@/modules/database/index.js';
2
+ import { providerRegistry } from '@/modules/providers/provider.registry.js';
3
+ import type { LLMProvider } from '@/shared/types.js';
4
+
5
+ type SessionSynchronizeResult = {
6
+ processedByProvider: Record<LLMProvider, number>;
7
+ failures: string[];
8
+ };
9
+
10
+ /**
11
+ * Orchestrates provider-specific session indexers and indexed-session lifecycle operations.
12
+ */
13
+ export const sessionSynchronizerService = {
14
+ /**
15
+ * Runs all provider synchronizers and updates scan_state.last_scanned_at.
16
+ */
17
+ async synchronizeSessions(): Promise<SessionSynchronizeResult> {
18
+ const lastScanAt = scanStateDb.getLastScannedAt();
19
+ const scanBoundary = new Date();
20
+ const processedByProvider: Record<LLMProvider, number> = {
21
+ claude: 0,
22
+ codex: 0,
23
+ cursor: 0,
24
+ gemini: 0,
25
+ };
26
+ const failures: string[] = [];
27
+
28
+ const results = await Promise.allSettled(
29
+ providerRegistry.listProviders().map(async (provider) => ({
30
+ provider: provider.id,
31
+ processed: await provider.sessionSynchronizer.synchronize(lastScanAt ?? undefined),
32
+ }))
33
+ );
34
+
35
+ for (const result of results) {
36
+ if (result.status === 'fulfilled') {
37
+ processedByProvider[result.value.provider] = result.value.processed;
38
+ continue;
39
+ }
40
+
41
+ const reason = result.reason instanceof Error ? result.reason.message : String(result.reason);
42
+ failures.push(reason);
43
+ }
44
+
45
+ if (failures.length === 0) {
46
+ scanStateDb.updateLastScannedAt(scanBoundary);
47
+ } else {
48
+ console.warn(
49
+ `[Sessions] Skipping scan_state cursor advance because ${failures.length} provider sync(s) failed.`,
50
+ );
51
+ }
52
+
53
+ return {
54
+ processedByProvider,
55
+ failures,
56
+ };
57
+ },
58
+
59
+ /**
60
+ * Indexes one provider artifact file without running a full provider rescan.
61
+ */
62
+ async synchronizeProviderFile(
63
+ provider: LLMProvider,
64
+ filePath: string
65
+ ): Promise<{ provider: LLMProvider; indexed: boolean; sessionId: string | null }> {
66
+ const resolvedProvider = providerRegistry.resolveProvider(provider);
67
+ const sessionId = await resolvedProvider.sessionSynchronizer.synchronizeFile(filePath);
68
+ return {
69
+ provider,
70
+ indexed: Boolean(sessionId),
71
+ sessionId,
72
+ };
73
+ },
74
+ };
@@ -0,0 +1,283 @@
1
+ import os from 'node:os';
2
+ import path from 'node:path';
3
+ import { promises as fsPromises } from 'node:fs';
4
+
5
+ import chokidar, { type FSWatcher } from 'chokidar';
6
+
7
+ import { sessionSynchronizerService } from '@/modules/providers/services/session-synchronizer.service.js';
8
+ import { WS_OPEN_STATE, connectedClients } from '@/modules/websocket/index.js';
9
+ import type { LLMProvider } from '@/shared/types.js';
10
+ import { getProjectsWithSessions } from '@/modules/projects/index.js';
11
+
12
+ type WatcherEventType = 'add' | 'change';
13
+
14
+ const PROVIDER_WATCH_PATHS: Array<{ provider: LLMProvider; rootPath: string }> = [
15
+ {
16
+ provider: 'claude',
17
+ rootPath: path.join(os.homedir(), '.claude', 'projects'),
18
+ },
19
+ {
20
+ provider: 'cursor',
21
+ rootPath: path.join(os.homedir(), '.cursor', 'chats'),
22
+ },
23
+ {
24
+ provider: 'codex',
25
+ rootPath: path.join(os.homedir(), '.codex', 'sessions'),
26
+ },
27
+ {
28
+ provider: 'gemini',
29
+ rootPath: path.join(os.homedir(), '.gemini', 'sessions'),
30
+ },
31
+ {
32
+ provider: 'gemini',
33
+ rootPath: path.join(os.homedir(), '.gemini', 'tmp'),
34
+ },
35
+ ];
36
+
37
+ const WATCHER_IGNORED_PATTERNS = [
38
+ '**/node_modules/**',
39
+ '**/.git/**',
40
+ '**/dist/**',
41
+ '**/build/**',
42
+ '**/*.tmp',
43
+ '**/*.swp',
44
+ '**/.DS_Store',
45
+ ];
46
+
47
+ const PROJECTS_UPDATE_DEBOUNCE_MS = 500;
48
+ const PROJECTS_UPDATE_MAX_WAIT_MS = 2_000;
49
+
50
+ const watchers: FSWatcher[] = [];
51
+
52
+ type PendingWatcherUpdate = {
53
+ providers: Set<LLMProvider>;
54
+ changeTypes: Set<WatcherEventType>;
55
+ updatedSessionIds: Set<string>;
56
+ };
57
+
58
+ let pendingWatcherUpdate: PendingWatcherUpdate | null = null;
59
+ let pendingWatcherUpdateStartedAt: number | null = null;
60
+ let pendingWatcherFlushTimer: ReturnType<typeof setTimeout> | null = null;
61
+ let watcherRefreshInFlight = false;
62
+ let watcherRescheduleAfterRefresh = false;
63
+
64
+ /**
65
+ * Filters watcher events to provider-specific session artifact file types.
66
+ */
67
+ function isWatcherTargetFile(provider: LLMProvider, filePath: string): boolean {
68
+ if (provider === 'gemini') {
69
+ return filePath.endsWith('.json') || filePath.endsWith('.jsonl');
70
+ }
71
+
72
+ return filePath.endsWith('.jsonl');
73
+ }
74
+
75
+ function clearPendingWatcherFlushTimer(): void {
76
+ if (pendingWatcherFlushTimer) {
77
+ clearTimeout(pendingWatcherFlushTimer);
78
+ pendingWatcherFlushTimer = null;
79
+ }
80
+ }
81
+
82
+ function schedulePendingWatcherFlush(): void {
83
+ if (!pendingWatcherUpdate) {
84
+ return;
85
+ }
86
+
87
+ const now = Date.now();
88
+ if (pendingWatcherUpdateStartedAt === null) {
89
+ pendingWatcherUpdateStartedAt = now;
90
+ }
91
+
92
+ const elapsed = now - pendingWatcherUpdateStartedAt;
93
+ const remainingMaxWait = Math.max(0, PROJECTS_UPDATE_MAX_WAIT_MS - elapsed);
94
+ const delay = Math.min(PROJECTS_UPDATE_DEBOUNCE_MS, remainingMaxWait);
95
+
96
+ clearPendingWatcherFlushTimer();
97
+ pendingWatcherFlushTimer = setTimeout(() => {
98
+ void flushPendingWatcherUpdate();
99
+ }, delay);
100
+ }
101
+
102
+ function queuePendingWatcherUpdate(
103
+ eventType: WatcherEventType,
104
+ provider: LLMProvider,
105
+ updatedSessionId: string | null
106
+ ): void {
107
+ if (!pendingWatcherUpdate) {
108
+ pendingWatcherUpdate = {
109
+ providers: new Set<LLMProvider>(),
110
+ changeTypes: new Set<WatcherEventType>(),
111
+ updatedSessionIds: new Set<string>(),
112
+ };
113
+ }
114
+
115
+ pendingWatcherUpdate.providers.add(provider);
116
+ pendingWatcherUpdate.changeTypes.add(eventType);
117
+ if (updatedSessionId) {
118
+ pendingWatcherUpdate.updatedSessionIds.add(updatedSessionId);
119
+ }
120
+
121
+ schedulePendingWatcherFlush();
122
+ }
123
+
124
+ async function flushPendingWatcherUpdate(): Promise<void> {
125
+ clearPendingWatcherFlushTimer();
126
+
127
+ if (!pendingWatcherUpdate) {
128
+ return;
129
+ }
130
+
131
+ if (watcherRefreshInFlight) {
132
+ watcherRescheduleAfterRefresh = true;
133
+ return;
134
+ }
135
+
136
+ const queuedUpdate = pendingWatcherUpdate;
137
+ pendingWatcherUpdate = null;
138
+ pendingWatcherUpdateStartedAt = null;
139
+ watcherRefreshInFlight = true;
140
+
141
+ try {
142
+ const updatedProjects = await getProjectsWithSessions({ skipSynchronization: true });
143
+ const changeTypes = Array.from(queuedUpdate.changeTypes);
144
+ const watchProviders = Array.from(queuedUpdate.providers);
145
+ const updatedSessionIds = Array.from(queuedUpdate.updatedSessionIds);
146
+
147
+ // Backward-compatible fields stay populated with the first queued values.
148
+ const updateMessage = JSON.stringify({
149
+ type: 'projects_updated',
150
+ projects: updatedProjects,
151
+ timestamp: new Date().toISOString(),
152
+ changeType: changeTypes[0] ?? 'change',
153
+ updatedSessionId: updatedSessionIds[0] ?? undefined,
154
+ watchProvider: watchProviders[0] ?? undefined,
155
+ changeTypes,
156
+ updatedSessionIds,
157
+ watchProviders,
158
+ batched: true,
159
+ });
160
+
161
+ connectedClients.forEach(client => {
162
+ if (client.readyState === WS_OPEN_STATE) {
163
+ client.send(updateMessage);
164
+ }
165
+ });
166
+ } catch (error) {
167
+ const message = error instanceof Error ? error.message : String(error);
168
+ console.error('Session watcher refresh failed while broadcasting projects_updated', { error: message });
169
+ } finally {
170
+ watcherRefreshInFlight = false;
171
+
172
+ if (pendingWatcherUpdate || watcherRescheduleAfterRefresh) {
173
+ watcherRescheduleAfterRefresh = false;
174
+ schedulePendingWatcherFlush();
175
+ }
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Handles file watcher updates and triggers provider file-level synchronization.
181
+ */
182
+ async function onUpdate(
183
+ eventType: WatcherEventType,
184
+ filePath: string,
185
+ provider: LLMProvider
186
+ ): Promise<void> {
187
+ if (!isWatcherTargetFile(provider, filePath)) {
188
+ return;
189
+ }
190
+
191
+ try {
192
+ const result = await sessionSynchronizerService.synchronizeProviderFile(provider, filePath);
193
+ if (!result.indexed) {
194
+ return;
195
+ }
196
+
197
+ console.log(`Session synchronization triggered by ${eventType} event for provider "${provider}"`, {
198
+ filePath,
199
+ sessionId: result.sessionId,
200
+ });
201
+ queuePendingWatcherUpdate(eventType, provider, result.sessionId);
202
+ } catch (error) {
203
+ const message = error instanceof Error ? error.message : String(error);
204
+ console.error(`Session watcher sync failed for provider "${provider}"`, {
205
+ eventType,
206
+ filePath,
207
+ error: message,
208
+ });
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Starts provider filesystem watchers and performs initial DB synchronization.
214
+ */
215
+ export async function initializeSessionsWatcher(): Promise<void> {
216
+ console.log('Setting up session watchers');
217
+
218
+ const initialSync = await sessionSynchronizerService.synchronizeSessions();
219
+ console.log('Initial session synchronization complete', {
220
+ processedByProvider: initialSync.processedByProvider,
221
+ failures: initialSync.failures,
222
+ });
223
+
224
+ for (const { provider, rootPath } of PROVIDER_WATCH_PATHS) {
225
+ try {
226
+ await fsPromises.mkdir(rootPath, { recursive: true });
227
+
228
+ const watcher = chokidar.watch(rootPath, {
229
+ ignored: WATCHER_IGNORED_PATTERNS,
230
+ persistent: true,
231
+ ignoreInitial: true,
232
+ followSymlinks: false,
233
+ depth: 6,
234
+ usePolling: true,
235
+ interval: 6_000,
236
+ binaryInterval: 6_000,
237
+ });
238
+
239
+ watcher
240
+ .on('add', (filePath: string) => {
241
+ void onUpdate('add', filePath, provider);
242
+ })
243
+ .on('change', (filePath: string) => {
244
+ void onUpdate('change', filePath, provider);
245
+ })
246
+ .on('error', (error: unknown) => {
247
+ const message = error instanceof Error ? error.message : String(error);
248
+ console.error(`Session watcher error for provider "${provider}"`, { error: message });
249
+ });
250
+
251
+ watchers.push(watcher);
252
+ } catch (error) {
253
+ const message = error instanceof Error ? error.message : String(error);
254
+ console.error(`Failed to initialize session watcher for provider "${provider}"`, {
255
+ rootPath,
256
+ error: message,
257
+ });
258
+ }
259
+ }
260
+ }
261
+
262
+ /**
263
+ * Stops all active provider session watchers.
264
+ */
265
+ export async function closeSessionsWatcher(): Promise<void> {
266
+ clearPendingWatcherFlushTimer();
267
+
268
+ await Promise.all(
269
+ watchers.map(async (watcher) => {
270
+ try {
271
+ await watcher.close();
272
+ } catch (error) {
273
+ const message = error instanceof Error ? error.message : String(error);
274
+ console.error('Failed to close session watcher', { error: message });
275
+ }
276
+ })
277
+ );
278
+ watchers.length = 0;
279
+ pendingWatcherUpdate = null;
280
+ pendingWatcherUpdateStartedAt = null;
281
+ watcherRefreshInFlight = false;
282
+ watcherRescheduleAfterRefresh = false;
283
+ }
@@ -0,0 +1,130 @@
1
+ import fsp from 'node:fs/promises';
2
+
3
+ import { sessionsDb } from '@/modules/database/index.js';
4
+ import { providerRegistry } from '@/modules/providers/provider.registry.js';
5
+ import type {
6
+ FetchHistoryOptions,
7
+ FetchHistoryResult,
8
+ LLMProvider,
9
+ NormalizedMessage,
10
+ } from '@/shared/types.js';
11
+ import { AppError } from '@/shared/utils.js';
12
+
13
+ /**
14
+ * Removes one file if it exists.
15
+ */
16
+ async function removeFileIfExists(filePath: string): Promise<boolean> {
17
+ try {
18
+ await fsp.unlink(filePath);
19
+ return true;
20
+ } catch (error) {
21
+ const code = (error as NodeJS.ErrnoException).code;
22
+ if (code === 'ENOENT') {
23
+ return false;
24
+ }
25
+ throw error;
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Application service for provider-backed session message operations.
31
+ *
32
+ * Callers pass a provider id and this service resolves the concrete provider
33
+ * class, keeping normalization/history call sites decoupled from implementation
34
+ * file layout.
35
+ */
36
+ export const sessionsService = {
37
+ /**
38
+ * Lists provider ids that can load session history and normalize live messages.
39
+ */
40
+ listProviderIds(): LLMProvider[] {
41
+ return providerRegistry.listProviders().map((provider) => provider.id);
42
+ },
43
+
44
+ /**
45
+ * Normalizes one provider-native event into frontend session message events.
46
+ */
47
+ normalizeMessage(
48
+ providerName: string,
49
+ raw: unknown,
50
+ sessionId: string | null,
51
+ ): NormalizedMessage[] {
52
+ return providerRegistry.resolveProvider(providerName).sessions.normalizeMessage(raw, sessionId);
53
+ },
54
+
55
+ /**
56
+ * Fetches persisted history by session id.
57
+ *
58
+ * Provider and provider-specific lookup hints are resolved from the indexed
59
+ * session metadata in the database.
60
+ */
61
+ fetchHistory(
62
+ sessionId: string,
63
+ options: Pick<FetchHistoryOptions, 'limit' | 'offset'> = {},
64
+ ): Promise<FetchHistoryResult> {
65
+ const session = sessionsDb.getSessionById(sessionId);
66
+ if (!session) {
67
+ throw new AppError(`Session "${sessionId}" was not found.`, {
68
+ code: 'SESSION_NOT_FOUND',
69
+ statusCode: 404,
70
+ });
71
+ }
72
+
73
+ const provider = session.provider as LLMProvider;
74
+ return providerRegistry.resolveProvider(provider).sessions.fetchHistory(sessionId, {
75
+ limit: options.limit ?? null,
76
+ offset: options.offset ?? 0,
77
+ projectPath: session.project_path ?? '',
78
+ });
79
+ },
80
+
81
+ /**
82
+ * Deletes one persisted session row by id.
83
+ *
84
+ * When `deletedFromDisk` is true and a session `jsonl_path` exists, the path
85
+ * is deleted from disk before the DB row is removed.
86
+ */
87
+ async deleteSessionById(
88
+ sessionId: string,
89
+ deletedFromDisk = false,
90
+ ): Promise<{ sessionId: string; deletedFromDisk: boolean }> {
91
+ const session = sessionsDb.getSessionById(sessionId);
92
+ if (!session) {
93
+ throw new AppError(`Session "${sessionId}" was not found.`, {
94
+ code: 'SESSION_NOT_FOUND',
95
+ statusCode: 404,
96
+ });
97
+ }
98
+
99
+ let removedFromDisk = false;
100
+ if (deletedFromDisk && session.jsonl_path) {
101
+ removedFromDisk = await removeFileIfExists(session.jsonl_path);
102
+ }
103
+
104
+ const deleted = sessionsDb.deleteSessionById(sessionId);
105
+ if (!deleted) {
106
+ throw new AppError(`Session "${sessionId}" was not found.`, {
107
+ code: 'SESSION_NOT_FOUND',
108
+ statusCode: 404,
109
+ });
110
+ }
111
+
112
+ return { sessionId, deletedFromDisk: removedFromDisk };
113
+ },
114
+
115
+ /**
116
+ * Renames one session by id without requiring the caller to pass provider.
117
+ */
118
+ renameSessionById(sessionId: string, summary: string): { sessionId: string; summary: string } {
119
+ const session = sessionsDb.getSessionById(sessionId);
120
+ if (!session) {
121
+ throw new AppError(`Session "${sessionId}" was not found.`, {
122
+ code: 'SESSION_NOT_FOUND',
123
+ statusCode: 404,
124
+ });
125
+ }
126
+
127
+ sessionsDb.updateSessionCustomName(sessionId, summary);
128
+ return { sessionId, summary };
129
+ },
130
+ };
@@ -0,0 +1,27 @@
1
+ import type {
2
+ IProvider,
3
+ IProviderAuth,
4
+ IProviderMcp,
5
+ IProviderSessionSynchronizer,
6
+ IProviderSessions,
7
+ } from '@/shared/interfaces.js';
8
+ import type { LLMProvider } from '@/shared/types.js';
9
+
10
+ /**
11
+ * Shared provider base.
12
+ *
13
+ * Concrete providers must expose auth/MCP handlers and implement message
14
+ * normalization/history loading because those behaviors depend on native
15
+ * SDK/CLI formats.
16
+ */
17
+ export abstract class AbstractProvider implements IProvider {
18
+ readonly id: LLMProvider;
19
+ abstract readonly mcp: IProviderMcp;
20
+ abstract readonly auth: IProviderAuth;
21
+ abstract readonly sessions: IProviderSessions;
22
+ abstract readonly sessionSynchronizer: IProviderSessionSynchronizer;
23
+
24
+ protected constructor(id: LLMProvider) {
25
+ this.id = id;
26
+ }
27
+ }
@@ -0,0 +1,151 @@
1
+ import path from 'node:path';
2
+
3
+ import type { IProviderMcp } from '@/shared/interfaces.js';
4
+ import type { LLMProvider, McpScope, McpTransport, ProviderMcpServer, UpsertProviderMcpServerInput } from '@/shared/types.js';
5
+ import { AppError } from '@/shared/utils.js';
6
+
7
+ const resolveWorkspacePath = (workspacePath?: string): string =>
8
+ path.resolve(workspacePath ?? process.cwd());
9
+
10
+ const normalizeServerName = (name: string): string => {
11
+ const normalized = name.trim();
12
+ if (!normalized) {
13
+ throw new AppError('MCP server name is required.', {
14
+ code: 'MCP_SERVER_NAME_REQUIRED',
15
+ statusCode: 400,
16
+ });
17
+ }
18
+
19
+ return normalized;
20
+ };
21
+
22
+ /**
23
+ * Shared MCP provider for provider-specific config readers/writers.
24
+ */
25
+ export abstract class McpProvider implements IProviderMcp {
26
+ protected readonly provider: LLMProvider;
27
+ protected readonly supportedScopes: McpScope[];
28
+ protected readonly supportedTransports: McpTransport[];
29
+
30
+ protected constructor(
31
+ provider: LLMProvider,
32
+ supportedScopes: McpScope[],
33
+ supportedTransports: McpTransport[],
34
+ ) {
35
+ this.provider = provider;
36
+ this.supportedScopes = supportedScopes;
37
+ this.supportedTransports = supportedTransports;
38
+ }
39
+
40
+ async listServers(options?: { workspacePath?: string }): Promise<Record<McpScope, ProviderMcpServer[]>> {
41
+ const grouped: Record<McpScope, ProviderMcpServer[]> = {
42
+ user: [],
43
+ local: [],
44
+ project: [],
45
+ };
46
+
47
+ for (const scope of this.supportedScopes) {
48
+ grouped[scope] = await this.listServersForScope(scope, options);
49
+ }
50
+
51
+ return grouped;
52
+ }
53
+
54
+ async listServersForScope(
55
+ scope: McpScope,
56
+ options?: { workspacePath?: string },
57
+ ): Promise<ProviderMcpServer[]> {
58
+ if (!this.supportedScopes.includes(scope)) {
59
+ return [];
60
+ }
61
+
62
+ const workspacePath = resolveWorkspacePath(options?.workspacePath);
63
+ const scopedServers = await this.readScopedServers(scope, workspacePath);
64
+ return Object.entries(scopedServers)
65
+ .map(([name, rawConfig]) => this.normalizeServerConfig(scope, name, rawConfig))
66
+ .filter((entry): entry is ProviderMcpServer => entry !== null);
67
+ }
68
+
69
+ async upsertServer(input: UpsertProviderMcpServerInput): Promise<ProviderMcpServer> {
70
+ const scope = input.scope ?? 'project';
71
+ this.assertScopeAndTransport(scope, input.transport);
72
+
73
+ const workspacePath = resolveWorkspacePath(input.workspacePath);
74
+ const normalizedName = normalizeServerName(input.name);
75
+ const scopedServers = await this.readScopedServers(scope, workspacePath);
76
+ scopedServers[normalizedName] = this.buildServerConfig(input);
77
+ await this.writeScopedServers(scope, workspacePath, scopedServers);
78
+
79
+ return {
80
+ provider: this.provider,
81
+ name: normalizedName,
82
+ scope,
83
+ transport: input.transport,
84
+ command: input.command,
85
+ args: input.args,
86
+ env: input.env,
87
+ cwd: input.cwd,
88
+ url: input.url,
89
+ headers: input.headers,
90
+ envVars: input.envVars,
91
+ bearerTokenEnvVar: input.bearerTokenEnvVar,
92
+ envHttpHeaders: input.envHttpHeaders,
93
+ };
94
+ }
95
+
96
+ async removeServer(
97
+ input: { name: string; scope?: McpScope; workspacePath?: string },
98
+ ): Promise<{ removed: boolean; provider: LLMProvider; name: string; scope: McpScope }> {
99
+ const scope = input.scope ?? 'project';
100
+ this.assertScope(scope);
101
+
102
+ const workspacePath = resolveWorkspacePath(input.workspacePath);
103
+ const normalizedName = normalizeServerName(input.name);
104
+ const scopedServers = await this.readScopedServers(scope, workspacePath);
105
+ const removed = Object.prototype.hasOwnProperty.call(scopedServers, normalizedName);
106
+ if (removed) {
107
+ delete scopedServers[normalizedName];
108
+ await this.writeScopedServers(scope, workspacePath, scopedServers);
109
+ }
110
+
111
+ return { removed, provider: this.provider, name: normalizedName, scope };
112
+ }
113
+
114
+ protected abstract readScopedServers(
115
+ scope: McpScope,
116
+ workspacePath: string,
117
+ ): Promise<Record<string, unknown>>;
118
+
119
+ protected abstract writeScopedServers(
120
+ scope: McpScope,
121
+ workspacePath: string,
122
+ servers: Record<string, unknown>,
123
+ ): Promise<void>;
124
+
125
+ protected abstract buildServerConfig(input: UpsertProviderMcpServerInput): Record<string, unknown>;
126
+
127
+ protected abstract normalizeServerConfig(
128
+ scope: McpScope,
129
+ name: string,
130
+ rawConfig: unknown,
131
+ ): ProviderMcpServer | null;
132
+
133
+ protected assertScope(scope: McpScope): void {
134
+ if (!this.supportedScopes.includes(scope)) {
135
+ throw new AppError(`Provider "${this.provider}" does not support "${scope}" MCP scope.`, {
136
+ code: 'MCP_SCOPE_NOT_SUPPORTED',
137
+ statusCode: 400,
138
+ });
139
+ }
140
+ }
141
+
142
+ protected assertScopeAndTransport(scope: McpScope, transport: McpTransport): void {
143
+ this.assertScope(scope);
144
+ if (!this.supportedTransports.includes(transport)) {
145
+ throw new AppError(`Provider "${this.provider}" does not support "${transport}" MCP transport.`, {
146
+ code: 'MCP_TRANSPORT_NOT_SUPPORTED',
147
+ statusCode: 400,
148
+ });
149
+ }
150
+ }
151
+ }