@glwhappen/web-code 1.32.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 (479) hide show
  1. package/LICENSE +718 -0
  2. package/README.de.md +250 -0
  3. package/README.ja.md +242 -0
  4. package/README.ko.md +242 -0
  5. package/README.md +252 -0
  6. package/README.ru.md +250 -0
  7. package/README.tr.md +252 -0
  8. package/README.zh-CN.md +242 -0
  9. package/dist/api-docs.html +879 -0
  10. package/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  11. package/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  12. package/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  13. package/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  14. package/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  15. package/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  16. package/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  17. package/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  18. package/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  19. package/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  20. package/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  21. package/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  22. package/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  23. package/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  24. package/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  25. package/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  26. package/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  27. package/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  28. package/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  29. package/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  30. package/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  31. package/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  32. package/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  33. package/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  34. package/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  35. package/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  36. package/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  37. package/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  38. package/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  39. package/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  40. package/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  41. package/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  42. package/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  43. package/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  44. package/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  45. package/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  46. package/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  47. package/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  48. package/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  49. package/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  50. package/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  51. package/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  52. package/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  53. package/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  54. package/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  55. package/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  56. package/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  57. package/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  58. package/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  59. package/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  60. package/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  61. package/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  62. package/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  63. package/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  64. package/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  65. package/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  66. package/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  67. package/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  68. package/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  69. package/dist/assets/index-Ct6oPUQk.css +32 -0
  70. package/dist/assets/index-u6XmIqLb.js +1346 -0
  71. package/dist/assets/vendor-codemirror-OwyKSvPE.js +41 -0
  72. package/dist/assets/vendor-react-BGZc9oRE.js +59 -0
  73. package/dist/assets/vendor-xterm-CJZjLICi.js +66 -0
  74. package/dist/clear-cache.html +85 -0
  75. package/dist/convert-icons.md +53 -0
  76. package/dist/favicon.png +0 -0
  77. package/dist/favicon.svg +9 -0
  78. package/dist/generate-icons.js +49 -0
  79. package/dist/icons/claude-ai-icon.svg +1 -0
  80. package/dist/icons/codex-white.svg +3 -0
  81. package/dist/icons/codex.svg +3 -0
  82. package/dist/icons/cursor-white.svg +12 -0
  83. package/dist/icons/cursor.svg +1 -0
  84. package/dist/icons/gemini-ai-icon.svg +1 -0
  85. package/dist/icons/icon-128x128.png +0 -0
  86. package/dist/icons/icon-128x128.svg +12 -0
  87. package/dist/icons/icon-144x144.png +0 -0
  88. package/dist/icons/icon-144x144.svg +12 -0
  89. package/dist/icons/icon-152x152.png +0 -0
  90. package/dist/icons/icon-152x152.svg +12 -0
  91. package/dist/icons/icon-192x192.png +0 -0
  92. package/dist/icons/icon-192x192.svg +12 -0
  93. package/dist/icons/icon-384x384.png +0 -0
  94. package/dist/icons/icon-384x384.svg +12 -0
  95. package/dist/icons/icon-512x512.png +0 -0
  96. package/dist/icons/icon-512x512.svg +12 -0
  97. package/dist/icons/icon-72x72.png +0 -0
  98. package/dist/icons/icon-72x72.svg +12 -0
  99. package/dist/icons/icon-96x96.png +0 -0
  100. package/dist/icons/icon-96x96.svg +12 -0
  101. package/dist/icons/icon-template.svg +12 -0
  102. package/dist/index.html +52 -0
  103. package/dist/logo-128.png +0 -0
  104. package/dist/logo-256.png +0 -0
  105. package/dist/logo-32.png +0 -0
  106. package/dist/logo-512.png +0 -0
  107. package/dist/logo-64.png +0 -0
  108. package/dist/logo.svg +17 -0
  109. package/dist/manifest.json +61 -0
  110. package/dist/screenshots/cli-selection.png +0 -0
  111. package/dist/screenshots/desktop-main.png +0 -0
  112. package/dist/screenshots/mobile-chat.png +0 -0
  113. package/dist/screenshots/tools-modal.png +0 -0
  114. package/dist/sw.js +124 -0
  115. package/dist-server/server/claude-sdk.js +738 -0
  116. package/dist-server/server/claude-sdk.js.map +1 -0
  117. package/dist-server/server/cli.js +641 -0
  118. package/dist-server/server/cli.js.map +1 -0
  119. package/dist-server/server/constants/config.js +6 -0
  120. package/dist-server/server/constants/config.js.map +1 -0
  121. package/dist-server/server/cursor-cli.js +271 -0
  122. package/dist-server/server/cursor-cli.js.map +1 -0
  123. package/dist-server/server/gemini-cli.js +539 -0
  124. package/dist-server/server/gemini-cli.js.map +1 -0
  125. package/dist-server/server/gemini-response-handler.js +72 -0
  126. package/dist-server/server/gemini-response-handler.js.map +1 -0
  127. package/dist-server/server/index.js +1340 -0
  128. package/dist-server/server/index.js.map +1 -0
  129. package/dist-server/server/load-env.js +32 -0
  130. package/dist-server/server/load-env.js.map +1 -0
  131. package/dist-server/server/middleware/auth.js +117 -0
  132. package/dist-server/server/middleware/auth.js.map +1 -0
  133. package/dist-server/server/modules/database/connection.js +125 -0
  134. package/dist-server/server/modules/database/connection.js.map +1 -0
  135. package/dist-server/server/modules/database/index.js +13 -0
  136. package/dist-server/server/modules/database/index.js.map +1 -0
  137. package/dist-server/server/modules/database/init-db.js +18 -0
  138. package/dist-server/server/modules/database/init-db.js.map +1 -0
  139. package/dist-server/server/modules/database/migrations.js +419 -0
  140. package/dist-server/server/modules/database/migrations.js.map +1 -0
  141. package/dist-server/server/modules/database/repositories/api-keys.js +72 -0
  142. package/dist-server/server/modules/database/repositories/api-keys.js.map +1 -0
  143. package/dist-server/server/modules/database/repositories/app-config.js +47 -0
  144. package/dist-server/server/modules/database/repositories/app-config.js.map +1 -0
  145. package/dist-server/server/modules/database/repositories/credentials.js +68 -0
  146. package/dist-server/server/modules/database/repositories/credentials.js.map +1 -0
  147. package/dist-server/server/modules/database/repositories/github-tokens.js +54 -0
  148. package/dist-server/server/modules/database/repositories/github-tokens.js.map +1 -0
  149. package/dist-server/server/modules/database/repositories/notification-preferences.js +72 -0
  150. package/dist-server/server/modules/database/repositories/notification-preferences.js.map +1 -0
  151. package/dist-server/server/modules/database/repositories/projects.db.integration.test.js +67 -0
  152. package/dist-server/server/modules/database/repositories/projects.db.integration.test.js.map +1 -0
  153. package/dist-server/server/modules/database/repositories/projects.db.js +185 -0
  154. package/dist-server/server/modules/database/repositories/projects.db.js.map +1 -0
  155. package/dist-server/server/modules/database/repositories/push-subscriptions.js +49 -0
  156. package/dist-server/server/modules/database/repositories/push-subscriptions.js.map +1 -0
  157. package/dist-server/server/modules/database/repositories/scan-state.db.js +31 -0
  158. package/dist-server/server/modules/database/repositories/scan-state.db.js.map +1 -0
  159. package/dist-server/server/modules/database/repositories/sessions.db.integration.test.js +64 -0
  160. package/dist-server/server/modules/database/repositories/sessions.db.integration.test.js.map +1 -0
  161. package/dist-server/server/modules/database/repositories/sessions.db.js +150 -0
  162. package/dist-server/server/modules/database/repositories/sessions.db.js.map +1 -0
  163. package/dist-server/server/modules/database/repositories/users.js +116 -0
  164. package/dist-server/server/modules/database/repositories/users.js.map +1 -0
  165. package/dist-server/server/modules/database/repositories/vapid-keys.js +38 -0
  166. package/dist-server/server/modules/database/repositories/vapid-keys.js.map +1 -0
  167. package/dist-server/server/modules/database/schema.js +150 -0
  168. package/dist-server/server/modules/database/schema.js.map +1 -0
  169. package/dist-server/server/modules/projects/index.js +4 -0
  170. package/dist-server/server/modules/projects/index.js.map +1 -0
  171. package/dist-server/server/modules/projects/projects.routes.js +225 -0
  172. package/dist-server/server/modules/projects/projects.routes.js.map +1 -0
  173. package/dist-server/server/modules/projects/services/project-clone.service.js +220 -0
  174. package/dist-server/server/modules/projects/services/project-clone.service.js.map +1 -0
  175. package/dist-server/server/modules/projects/services/project-delete.service.js +83 -0
  176. package/dist-server/server/modules/projects/services/project-delete.service.js.map +1 -0
  177. package/dist-server/server/modules/projects/services/project-management.service.js +99 -0
  178. package/dist-server/server/modules/projects/services/project-management.service.js.map +1 -0
  179. package/dist-server/server/modules/projects/services/project-star.service.js +60 -0
  180. package/dist-server/server/modules/projects/services/project-star.service.js.map +1 -0
  181. package/dist-server/server/modules/projects/services/projects-has-taskmaster.service.js +171 -0
  182. package/dist-server/server/modules/projects/services/projects-has-taskmaster.service.js.map +1 -0
  183. package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js +213 -0
  184. package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js.map +1 -0
  185. package/dist-server/server/modules/projects/tests/project-clone.service.test.js +129 -0
  186. package/dist-server/server/modules/projects/tests/project-clone.service.test.js.map +1 -0
  187. package/dist-server/server/modules/projects/tests/project-management.service.test.js +89 -0
  188. package/dist-server/server/modules/projects/tests/project-management.service.test.js.map +1 -0
  189. package/dist-server/server/modules/projects/tests/project-star.service.test.js +99 -0
  190. package/dist-server/server/modules/projects/tests/project-star.service.test.js.map +1 -0
  191. package/dist-server/server/modules/projects/tests/projects-has-taskmaster.service.test.js +88 -0
  192. package/dist-server/server/modules/projects/tests/projects-has-taskmaster.service.test.js.map +1 -0
  193. package/dist-server/server/modules/providers/index.js +5 -0
  194. package/dist-server/server/modules/providers/index.js.map +1 -0
  195. package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js +104 -0
  196. package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js.map +1 -0
  197. package/dist-server/server/modules/providers/list/claude/claude-mcp.provider.js +103 -0
  198. package/dist-server/server/modules/providers/list/claude/claude-mcp.provider.js.map +1 -0
  199. package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js +116 -0
  200. package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js.map +1 -0
  201. package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js +546 -0
  202. package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js.map +1 -0
  203. package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js +198 -0
  204. package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js.map +1 -0
  205. package/dist-server/server/modules/providers/list/claude/claude.provider.js +17 -0
  206. package/dist-server/server/modules/providers/list/claude/claude.provider.js.map +1 -0
  207. package/dist-server/server/modules/providers/list/codex/codex-auth.provider.js +84 -0
  208. package/dist-server/server/modules/providers/list/codex/codex-auth.provider.js.map +1 -0
  209. package/dist-server/server/modules/providers/list/codex/codex-mcp.provider.js +107 -0
  210. package/dist-server/server/modules/providers/list/codex/codex-mcp.provider.js.map +1 -0
  211. package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +123 -0
  212. package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js.map +1 -0
  213. package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js +513 -0
  214. package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -0
  215. package/dist-server/server/modules/providers/list/codex/codex-skills.provider.js +82 -0
  216. package/dist-server/server/modules/providers/list/codex/codex-skills.provider.js.map +1 -0
  217. package/dist-server/server/modules/providers/list/codex/codex.provider.js +17 -0
  218. package/dist-server/server/modules/providers/list/codex/codex.provider.js.map +1 -0
  219. package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js +118 -0
  220. package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js.map +1 -0
  221. package/dist-server/server/modules/providers/list/cursor/cursor-mcp.provider.js +80 -0
  222. package/dist-server/server/modules/providers/list/cursor/cursor-mcp.provider.js.map +1 -0
  223. package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js +105 -0
  224. package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js.map +1 -0
  225. package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js +545 -0
  226. package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js.map +1 -0
  227. package/dist-server/server/modules/providers/list/cursor/cursor-skills.provider.js +28 -0
  228. package/dist-server/server/modules/providers/list/cursor/cursor-skills.provider.js.map +1 -0
  229. package/dist-server/server/modules/providers/list/cursor/cursor.provider.js +17 -0
  230. package/dist-server/server/modules/providers/list/cursor/cursor.provider.js.map +1 -0
  231. package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js +254 -0
  232. package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js.map +1 -0
  233. package/dist-server/server/modules/providers/list/gemini/gemini-mcp.provider.js +82 -0
  234. package/dist-server/server/modules/providers/list/gemini/gemini-mcp.provider.js.map +1 -0
  235. package/dist-server/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.js +312 -0
  236. package/dist-server/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.js.map +1 -0
  237. package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js +484 -0
  238. package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js.map +1 -0
  239. package/dist-server/server/modules/providers/list/gemini/gemini-skills.provider.js +33 -0
  240. package/dist-server/server/modules/providers/list/gemini/gemini-skills.provider.js.map +1 -0
  241. package/dist-server/server/modules/providers/list/gemini/gemini.provider.js +17 -0
  242. package/dist-server/server/modules/providers/list/gemini/gemini.provider.js.map +1 -0
  243. package/dist-server/server/modules/providers/provider.registry.js +31 -0
  244. package/dist-server/server/modules/providers/provider.registry.js.map +1 -0
  245. package/dist-server/server/modules/providers/provider.routes.js +377 -0
  246. package/dist-server/server/modules/providers/provider.routes.js.map +1 -0
  247. package/dist-server/server/modules/providers/services/mcp.service.js +69 -0
  248. package/dist-server/server/modules/providers/services/mcp.service.js.map +1 -0
  249. package/dist-server/server/modules/providers/services/provider-auth.service.js +25 -0
  250. package/dist-server/server/modules/providers/services/provider-auth.service.js.map +1 -0
  251. package/dist-server/server/modules/providers/services/session-conversations-search.service.js +984 -0
  252. package/dist-server/server/modules/providers/services/session-conversations-search.service.js.map +1 -0
  253. package/dist-server/server/modules/providers/services/session-synchronizer.service.js +56 -0
  254. package/dist-server/server/modules/providers/services/session-synchronizer.service.js.map +1 -0
  255. package/dist-server/server/modules/providers/services/sessions-watcher.service.js +269 -0
  256. package/dist-server/server/modules/providers/services/sessions-watcher.service.js.map +1 -0
  257. package/dist-server/server/modules/providers/services/sessions.service.js +179 -0
  258. package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -0
  259. package/dist-server/server/modules/providers/services/skills.service.js +11 -0
  260. package/dist-server/server/modules/providers/services/skills.service.js.map +1 -0
  261. package/dist-server/server/modules/providers/shared/base/abstract.provider.js +14 -0
  262. package/dist-server/server/modules/providers/shared/base/abstract.provider.js.map +1 -0
  263. package/dist-server/server/modules/providers/shared/mcp/mcp.provider.js +102 -0
  264. package/dist-server/server/modules/providers/shared/mcp/mcp.provider.js.map +1 -0
  265. package/dist-server/server/modules/providers/shared/skills/skills.provider.js +45 -0
  266. package/dist-server/server/modules/providers/shared/skills/skills.provider.js.map +1 -0
  267. package/dist-server/server/modules/providers/tests/mcp.test.js +250 -0
  268. package/dist-server/server/modules/providers/tests/mcp.test.js.map +1 -0
  269. package/dist-server/server/modules/providers/tests/skills.test.js +226 -0
  270. package/dist-server/server/modules/providers/tests/skills.test.js.map +1 -0
  271. package/dist-server/server/modules/websocket/index.js +3 -0
  272. package/dist-server/server/modules/websocket/index.js.map +1 -0
  273. package/dist-server/server/modules/websocket/services/chat-websocket.service.js +192 -0
  274. package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -0
  275. package/dist-server/server/modules/websocket/services/plugin-websocket-proxy.service.js +52 -0
  276. package/dist-server/server/modules/websocket/services/plugin-websocket-proxy.service.js.map +1 -0
  277. package/dist-server/server/modules/websocket/services/shell-websocket.service.js +360 -0
  278. package/dist-server/server/modules/websocket/services/shell-websocket.service.js.map +1 -0
  279. package/dist-server/server/modules/websocket/services/websocket-auth.service.js +32 -0
  280. package/dist-server/server/modules/websocket/services/websocket-auth.service.js.map +1 -0
  281. package/dist-server/server/modules/websocket/services/websocket-server.service.js +36 -0
  282. package/dist-server/server/modules/websocket/services/websocket-server.service.js.map +1 -0
  283. package/dist-server/server/modules/websocket/services/websocket-state.service.js +14 -0
  284. package/dist-server/server/modules/websocket/services/websocket-state.service.js.map +1 -0
  285. package/dist-server/server/modules/websocket/services/websocket-writer.service.js +32 -0
  286. package/dist-server/server/modules/websocket/services/websocket-writer.service.js.map +1 -0
  287. package/dist-server/server/openai-codex.js +418 -0
  288. package/dist-server/server/openai-codex.js.map +1 -0
  289. package/dist-server/server/routes/admin.js +109 -0
  290. package/dist-server/server/routes/admin.js.map +1 -0
  291. package/dist-server/server/routes/agent.js +1145 -0
  292. package/dist-server/server/routes/agent.js.map +1 -0
  293. package/dist-server/server/routes/auth.js +123 -0
  294. package/dist-server/server/routes/auth.js.map +1 -0
  295. package/dist-server/server/routes/commands.js +487 -0
  296. package/dist-server/server/routes/commands.js.map +1 -0
  297. package/dist-server/server/routes/cursor.js +49 -0
  298. package/dist-server/server/routes/cursor.js.map +1 -0
  299. package/dist-server/server/routes/gemini.js +25 -0
  300. package/dist-server/server/routes/gemini.js.map +1 -0
  301. package/dist-server/server/routes/git.js +1263 -0
  302. package/dist-server/server/routes/git.js.map +1 -0
  303. package/dist-server/server/routes/mcp-utils.js +29 -0
  304. package/dist-server/server/routes/mcp-utils.js.map +1 -0
  305. package/dist-server/server/routes/plugins.js +266 -0
  306. package/dist-server/server/routes/plugins.js.map +1 -0
  307. package/dist-server/server/routes/settings.js +259 -0
  308. package/dist-server/server/routes/settings.js.map +1 -0
  309. package/dist-server/server/routes/taskmaster.js +1360 -0
  310. package/dist-server/server/routes/taskmaster.js.map +1 -0
  311. package/dist-server/server/routes/user.js +115 -0
  312. package/dist-server/server/routes/user.js.map +1 -0
  313. package/dist-server/server/services/notification-orchestrator.js +177 -0
  314. package/dist-server/server/services/notification-orchestrator.js.map +1 -0
  315. package/dist-server/server/services/vapid-keys.js +27 -0
  316. package/dist-server/server/services/vapid-keys.js.map +1 -0
  317. package/dist-server/server/sessionManager.js +215 -0
  318. package/dist-server/server/sessionManager.js.map +1 -0
  319. package/dist-server/server/shared/claude-cli-path.js +103 -0
  320. package/dist-server/server/shared/claude-cli-path.js.map +1 -0
  321. package/dist-server/server/shared/claude-cli-path.test.js +45 -0
  322. package/dist-server/server/shared/claude-cli-path.test.js.map +1 -0
  323. package/dist-server/server/shared/default-user.js +29 -0
  324. package/dist-server/server/shared/default-user.js.map +1 -0
  325. package/dist-server/server/shared/frontmatter.js +16 -0
  326. package/dist-server/server/shared/frontmatter.js.map +1 -0
  327. package/dist-server/server/shared/interfaces.js +2 -0
  328. package/dist-server/server/shared/interfaces.js.map +1 -0
  329. package/dist-server/server/shared/types.js +2 -0
  330. package/dist-server/server/shared/types.js.map +1 -0
  331. package/dist-server/server/shared/utils.js +633 -0
  332. package/dist-server/server/shared/utils.js.map +1 -0
  333. package/dist-server/server/utils/colors.js +20 -0
  334. package/dist-server/server/utils/colors.js.map +1 -0
  335. package/dist-server/server/utils/commandParser.js +255 -0
  336. package/dist-server/server/utils/commandParser.js.map +1 -0
  337. package/dist-server/server/utils/gitConfig.js +36 -0
  338. package/dist-server/server/utils/gitConfig.js.map +1 -0
  339. package/dist-server/server/utils/mcp-detector.js +134 -0
  340. package/dist-server/server/utils/mcp-detector.js.map +1 -0
  341. package/dist-server/server/utils/plugin-loader.js +413 -0
  342. package/dist-server/server/utils/plugin-loader.js.map +1 -0
  343. package/dist-server/server/utils/plugin-process-manager.js +163 -0
  344. package/dist-server/server/utils/plugin-process-manager.js.map +1 -0
  345. package/dist-server/server/utils/runtime-paths.js +30 -0
  346. package/dist-server/server/utils/runtime-paths.js.map +1 -0
  347. package/dist-server/server/utils/taskmaster-websocket.js +124 -0
  348. package/dist-server/server/utils/taskmaster-websocket.js.map +1 -0
  349. package/dist-server/server/utils/url-detection.js +58 -0
  350. package/dist-server/server/utils/url-detection.js.map +1 -0
  351. package/dist-server/shared/modelConstants.js +99 -0
  352. package/dist-server/shared/modelConstants.js.map +1 -0
  353. package/dist-server/shared/networkHosts.js +20 -0
  354. package/dist-server/shared/networkHosts.js.map +1 -0
  355. package/package.json +169 -0
  356. package/scripts/fix-node-pty.js +67 -0
  357. package/server/claude-sdk.js +864 -0
  358. package/server/cli.js +688 -0
  359. package/server/constants/config.js +5 -0
  360. package/server/cursor-cli.js +334 -0
  361. package/server/gemini-cli.js +622 -0
  362. package/server/gemini-response-handler.js +79 -0
  363. package/server/index.js +1505 -0
  364. package/server/load-env.js +34 -0
  365. package/server/middleware/auth.js +142 -0
  366. package/server/modules/database/connection.ts +143 -0
  367. package/server/modules/database/index.ts +12 -0
  368. package/server/modules/database/init-db.ts +17 -0
  369. package/server/modules/database/migrations.ts +496 -0
  370. package/server/modules/database/repositories/api-keys.ts +119 -0
  371. package/server/modules/database/repositories/app-config.ts +53 -0
  372. package/server/modules/database/repositories/credentials.ts +106 -0
  373. package/server/modules/database/repositories/github-tokens.ts +100 -0
  374. package/server/modules/database/repositories/notification-preferences.ts +103 -0
  375. package/server/modules/database/repositories/projects.db.integration.test.ts +78 -0
  376. package/server/modules/database/repositories/projects.db.ts +210 -0
  377. package/server/modules/database/repositories/push-subscriptions.ts +80 -0
  378. package/server/modules/database/repositories/scan-state.db.ts +42 -0
  379. package/server/modules/database/repositories/sessions.db.integration.test.ts +78 -0
  380. package/server/modules/database/repositories/sessions.db.ts +230 -0
  381. package/server/modules/database/repositories/users.ts +186 -0
  382. package/server/modules/database/repositories/vapid-keys.ts +57 -0
  383. package/server/modules/database/schema.ts +159 -0
  384. package/server/modules/projects/index.ts +6 -0
  385. package/server/modules/projects/projects.routes.ts +292 -0
  386. package/server/modules/projects/services/project-clone.service.ts +327 -0
  387. package/server/modules/projects/services/project-delete.service.ts +95 -0
  388. package/server/modules/projects/services/project-management.service.ts +158 -0
  389. package/server/modules/projects/services/project-star.service.ts +78 -0
  390. package/server/modules/projects/services/projects-has-taskmaster.service.ts +257 -0
  391. package/server/modules/projects/services/projects-with-sessions-fetch.service.ts +355 -0
  392. package/server/modules/projects/tests/project-clone.service.test.ts +186 -0
  393. package/server/modules/projects/tests/project-management.service.test.ts +122 -0
  394. package/server/modules/projects/tests/project-star.service.test.ts +128 -0
  395. package/server/modules/projects/tests/projects-has-taskmaster.service.test.ts +107 -0
  396. package/server/modules/providers/README.md +346 -0
  397. package/server/modules/providers/index.ts +5 -0
  398. package/server/modules/providers/list/claude/claude-auth.provider.ts +124 -0
  399. package/server/modules/providers/list/claude/claude-mcp.provider.ts +135 -0
  400. package/server/modules/providers/list/claude/claude-session-synchronizer.provider.ts +179 -0
  401. package/server/modules/providers/list/claude/claude-sessions.provider.ts +642 -0
  402. package/server/modules/providers/list/claude/claude-skills.provider.ts +257 -0
  403. package/server/modules/providers/list/claude/claude.provider.ts +24 -0
  404. package/server/modules/providers/list/codex/codex-auth.provider.ts +100 -0
  405. package/server/modules/providers/list/codex/codex-mcp.provider.ts +135 -0
  406. package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +182 -0
  407. package/server/modules/providers/list/codex/codex-sessions.provider.ts +589 -0
  408. package/server/modules/providers/list/codex/codex-skills.provider.ts +100 -0
  409. package/server/modules/providers/list/codex/codex.provider.ts +24 -0
  410. package/server/modules/providers/list/cursor/cursor-auth.provider.ts +143 -0
  411. package/server/modules/providers/list/cursor/cursor-mcp.provider.ts +108 -0
  412. package/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.ts +155 -0
  413. package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +624 -0
  414. package/server/modules/providers/list/cursor/cursor-skills.provider.ts +31 -0
  415. package/server/modules/providers/list/cursor/cursor.provider.ts +24 -0
  416. package/server/modules/providers/list/gemini/gemini-auth.provider.ts +307 -0
  417. package/server/modules/providers/list/gemini/gemini-mcp.provider.ts +110 -0
  418. package/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.ts +407 -0
  419. package/server/modules/providers/list/gemini/gemini-sessions.provider.ts +552 -0
  420. package/server/modules/providers/list/gemini/gemini-skills.provider.ts +36 -0
  421. package/server/modules/providers/list/gemini/gemini.provider.ts +24 -0
  422. package/server/modules/providers/provider.registry.ts +36 -0
  423. package/server/modules/providers/provider.routes.ts +488 -0
  424. package/server/modules/providers/services/mcp.service.ts +94 -0
  425. package/server/modules/providers/services/provider-auth.service.ts +26 -0
  426. package/server/modules/providers/services/session-conversations-search.service.ts +1319 -0
  427. package/server/modules/providers/services/session-synchronizer.service.ts +75 -0
  428. package/server/modules/providers/services/sessions-watcher.service.ts +318 -0
  429. package/server/modules/providers/services/sessions.service.ts +240 -0
  430. package/server/modules/providers/services/skills.service.ts +15 -0
  431. package/server/modules/providers/shared/base/abstract.provider.ts +29 -0
  432. package/server/modules/providers/shared/mcp/mcp.provider.ts +151 -0
  433. package/server/modules/providers/shared/skills/skills.provider.ts +64 -0
  434. package/server/modules/providers/tests/mcp.test.ts +293 -0
  435. package/server/modules/providers/tests/skills.test.ts +446 -0
  436. package/server/modules/websocket/README.md +267 -0
  437. package/server/modules/websocket/index.ts +2 -0
  438. package/server/modules/websocket/services/chat-websocket.service.ts +275 -0
  439. package/server/modules/websocket/services/plugin-websocket-proxy.service.ts +65 -0
  440. package/server/modules/websocket/services/shell-websocket.service.ts +489 -0
  441. package/server/modules/websocket/services/websocket-auth.service.ts +54 -0
  442. package/server/modules/websocket/services/websocket-server.service.ts +58 -0
  443. package/server/modules/websocket/services/websocket-state.service.ts +16 -0
  444. package/server/modules/websocket/services/websocket-writer.service.ts +38 -0
  445. package/server/openai-codex.js +474 -0
  446. package/server/routes/admin.js +128 -0
  447. package/server/routes/agent.js +1246 -0
  448. package/server/routes/auth.js +144 -0
  449. package/server/routes/commands.js +556 -0
  450. package/server/routes/cursor.js +52 -0
  451. package/server/routes/gemini.js +30 -0
  452. package/server/routes/git.js +1493 -0
  453. package/server/routes/mcp-utils.js +31 -0
  454. package/server/routes/plugins.js +307 -0
  455. package/server/routes/settings.js +286 -0
  456. package/server/routes/taskmaster.js +1468 -0
  457. package/server/routes/user.js +123 -0
  458. package/server/services/notification-orchestrator.js +228 -0
  459. package/server/services/vapid-keys.js +36 -0
  460. package/server/sessionManager.js +248 -0
  461. package/server/shared/claude-cli-path.test.ts +61 -0
  462. package/server/shared/claude-cli-path.ts +139 -0
  463. package/server/shared/default-user.ts +30 -0
  464. package/server/shared/frontmatter.ts +18 -0
  465. package/server/shared/interfaces.ts +111 -0
  466. package/server/shared/types.ts +406 -0
  467. package/server/shared/utils.ts +763 -0
  468. package/server/tsconfig.json +36 -0
  469. package/server/utils/colors.js +21 -0
  470. package/server/utils/commandParser.js +305 -0
  471. package/server/utils/gitConfig.js +34 -0
  472. package/server/utils/mcp-detector.js +147 -0
  473. package/server/utils/plugin-loader.js +457 -0
  474. package/server/utils/plugin-process-manager.js +184 -0
  475. package/server/utils/runtime-paths.js +37 -0
  476. package/server/utils/taskmaster-websocket.js +135 -0
  477. package/server/utils/url-detection.js +71 -0
  478. package/shared/modelConstants.js +107 -0
  479. package/shared/networkHosts.js +22 -0
@@ -0,0 +1,106 @@
1
+ /**
2
+ * User credentials repository.
3
+ *
4
+ * Manages external service tokens (GitHub, GitLab, Bitbucket, etc.)
5
+ * stored per-user. Each credential has a type discriminator so multiple
6
+ * credential kinds can coexist in the same table.
7
+ */
8
+
9
+ import { getConnection } from '@/modules/database/connection.js';
10
+ import type {
11
+ CreateCredentialResult,
12
+ CredentialPublicRow,
13
+ } from '@/shared/types.js';
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // Queries
17
+ // ---------------------------------------------------------------------------
18
+
19
+ export const credentialsDb = {
20
+ /** Stores a new credential and returns a safe (no raw value) result. */
21
+ createCredential(
22
+ userId: number,
23
+ credentialName: string,
24
+ credentialType: string,
25
+ credentialValue: string,
26
+ description: string | null = null
27
+ ): CreateCredentialResult {
28
+ const db = getConnection();
29
+ const result = db
30
+ .prepare(
31
+ 'INSERT INTO user_credentials (user_id, credential_name, credential_type, credential_value, description) VALUES (?, ?, ?, ?, ?)'
32
+ )
33
+ .run(userId, credentialName, credentialType, credentialValue, description);
34
+ return {
35
+ id: result.lastInsertRowid,
36
+ credentialName,
37
+ credentialType,
38
+ };
39
+ },
40
+
41
+ /**
42
+ * Lists credentials for a user (excluding raw values).
43
+ * Optionally filters by credential type (e.g. 'github_token').
44
+ */
45
+ getCredentials(
46
+ userId: number,
47
+ credentialType: string | null = null
48
+ ): CredentialPublicRow[] {
49
+ const db = getConnection();
50
+
51
+ if (credentialType) {
52
+ return db
53
+ .prepare(
54
+ 'SELECT id, credential_name, credential_type, description, created_at, is_active FROM user_credentials WHERE user_id = ? AND credential_type = ? ORDER BY created_at DESC'
55
+ )
56
+ .all(userId, credentialType) as CredentialPublicRow[];
57
+ }
58
+
59
+ return db
60
+ .prepare(
61
+ 'SELECT id, credential_name, credential_type, description, created_at, is_active FROM user_credentials WHERE user_id = ? ORDER BY created_at DESC'
62
+ )
63
+ .all(userId) as CredentialPublicRow[];
64
+ },
65
+
66
+ /**
67
+ * Returns the raw credential value for the most recent active
68
+ * credential of the given type, or null if none exists.
69
+ */
70
+ getActiveCredential(
71
+ userId: number,
72
+ credentialType: string
73
+ ): string | null {
74
+ const db = getConnection();
75
+ const row = db
76
+ .prepare(
77
+ 'SELECT credential_value FROM user_credentials WHERE user_id = ? AND credential_type = ? AND is_active = 1 ORDER BY created_at DESC LIMIT 1'
78
+ )
79
+ .get(userId, credentialType) as { credential_value: string } | undefined;
80
+ return row?.credential_value ?? null;
81
+ },
82
+
83
+ /** Permanently removes a credential. Returns true if a row was deleted. */
84
+ deleteCredential(userId: number, credentialId: number): boolean {
85
+ const db = getConnection();
86
+ const result = db
87
+ .prepare('DELETE FROM user_credentials WHERE id = ? AND user_id = ?')
88
+ .run(credentialId, userId);
89
+ return result.changes > 0;
90
+ },
91
+
92
+ /** Enables or disables a credential without deleting it. */
93
+ toggleCredential(
94
+ userId: number,
95
+ credentialId: number,
96
+ isActive: boolean
97
+ ): boolean {
98
+ const db = getConnection();
99
+ const result = db
100
+ .prepare(
101
+ 'UPDATE user_credentials SET is_active = ? WHERE id = ? AND user_id = ?'
102
+ )
103
+ .run(isActive ? 1 : 0, credentialId, userId);
104
+ return result.changes > 0;
105
+ },
106
+ };
@@ -0,0 +1,100 @@
1
+ /**
2
+ * GitHub tokens repository.
3
+ *
4
+ * Backward-compatible helper layer over generic credentials storage.
5
+ * Tokens are stored in `user_credentials` with `credential_type = 'github_token'`.
6
+ */
7
+
8
+ import { getConnection } from '@/modules/database/connection.js';
9
+ import { credentialsDb } from '@/modules/database/repositories/credentials.js';
10
+ import type {
11
+ CredentialPublicRow,
12
+ CreateCredentialResult,
13
+ } from '@/shared/types.js';
14
+
15
+ const GITHUB_TOKEN_TYPE = 'github_token';
16
+
17
+ type CredentialRow = {
18
+ id: number;
19
+ user_id: number;
20
+ credential_name: string;
21
+ credential_type: string;
22
+ credential_value: string;
23
+ description: string | null;
24
+ created_at: string;
25
+ is_active: number;
26
+ };
27
+
28
+ type GithubTokenLookup = CredentialRow & {
29
+ github_token: string;
30
+ };
31
+
32
+ export const githubTokensDb = {
33
+ /** Creates a GitHub token credential entry. */
34
+ createGithubToken(
35
+ userId: number,
36
+ tokenName: string,
37
+ githubToken: string,
38
+ description: string | null = null
39
+ ): CreateCredentialResult {
40
+ return credentialsDb.createCredential(
41
+ userId,
42
+ tokenName,
43
+ GITHUB_TOKEN_TYPE,
44
+ githubToken,
45
+ description
46
+ );
47
+ },
48
+
49
+ /** Returns all GitHub tokens (safe shape: no credential value). */
50
+ getGithubTokens(userId: number): CredentialPublicRow[] {
51
+ return credentialsDb.getCredentials(userId, GITHUB_TOKEN_TYPE);
52
+ },
53
+
54
+ /** Returns the most recent active GitHub token value for a user. */
55
+ getActiveGithubToken(userId: number): string | null {
56
+ return credentialsDb.getActiveCredential(userId, GITHUB_TOKEN_TYPE);
57
+ },
58
+
59
+ /**
60
+ * Returns a specific active GitHub token row by id/user, including
61
+ * a `github_token` compatibility field.
62
+ */
63
+ getGithubTokenById(userId: number, tokenId: number): GithubTokenLookup | null {
64
+ const db = getConnection();
65
+ const row = db
66
+ .prepare(
67
+ `SELECT *
68
+ FROM user_credentials
69
+ WHERE id = ? AND user_id = ? AND credential_type = ? AND is_active = 1`
70
+ )
71
+ .get(tokenId, userId, GITHUB_TOKEN_TYPE) as CredentialRow | undefined;
72
+
73
+ if (!row) return null;
74
+
75
+ return {
76
+ ...row,
77
+ github_token: row.credential_value,
78
+ };
79
+ },
80
+
81
+ /** Updates active state for a GitHub token. */
82
+ updateGithubToken(
83
+ userId: number,
84
+ tokenId: number,
85
+ isActive: boolean
86
+ ): boolean {
87
+ return credentialsDb.toggleCredential(userId, tokenId, isActive);
88
+ },
89
+
90
+ /** Deletes a GitHub token. */
91
+ deleteGithubToken(userId: number, tokenId: number): boolean {
92
+ return credentialsDb.deleteCredential(userId, tokenId);
93
+ },
94
+
95
+ // Legacy alias used by existing routes
96
+ toggleGithubToken(userId: number, tokenId: number, isActive: boolean): boolean {
97
+ return githubTokensDb.updateGithubToken(userId, tokenId, isActive);
98
+ },
99
+ };
100
+
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Notification preferences repository.
3
+ *
4
+ * Stores per-user notification channel/event preferences as JSON.
5
+ */
6
+
7
+ import { getConnection } from '@/modules/database/connection.js';
8
+
9
+ type NotificationPreferences = {
10
+ channels: {
11
+ inApp: boolean;
12
+ webPush: boolean;
13
+ };
14
+ events: {
15
+ actionRequired: boolean;
16
+ stop: boolean;
17
+ error: boolean;
18
+ };
19
+ };
20
+
21
+ const DEFAULT_NOTIFICATION_PREFERENCES: NotificationPreferences = {
22
+ channels: {
23
+ inApp: false,
24
+ webPush: false,
25
+ },
26
+ events: {
27
+ actionRequired: true,
28
+ stop: true,
29
+ error: true,
30
+ },
31
+ };
32
+
33
+ function normalizeNotificationPreferences(value: unknown): NotificationPreferences {
34
+ const source = value && typeof value === 'object' ? (value as Record<string, any>) : {};
35
+
36
+ return {
37
+ channels: {
38
+ inApp: source.channels?.inApp === true,
39
+ webPush: source.channels?.webPush === true,
40
+ },
41
+ events: {
42
+ actionRequired: source.events?.actionRequired !== false,
43
+ stop: source.events?.stop !== false,
44
+ error: source.events?.error !== false,
45
+ },
46
+ };
47
+ }
48
+
49
+ export const notificationPreferencesDb = {
50
+ /** Returns the normalized preferences for a user, creating defaults on first read. */
51
+ getNotificationPreferences(userId: number): NotificationPreferences {
52
+ const db = getConnection();
53
+ const row = db
54
+ .prepare(
55
+ 'SELECT preferences_json FROM user_notification_preferences WHERE user_id = ?'
56
+ )
57
+ .get(userId) as { preferences_json: string } | undefined;
58
+
59
+ if (!row) {
60
+ const defaults = normalizeNotificationPreferences(DEFAULT_NOTIFICATION_PREFERENCES);
61
+ db.prepare(
62
+ 'INSERT INTO user_notification_preferences (user_id, preferences_json, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP)'
63
+ ).run(userId, JSON.stringify(defaults));
64
+ return defaults;
65
+ }
66
+
67
+ let parsed: unknown;
68
+ try {
69
+ parsed = JSON.parse(row.preferences_json);
70
+ } catch {
71
+ parsed = DEFAULT_NOTIFICATION_PREFERENCES;
72
+ }
73
+ return normalizeNotificationPreferences(parsed);
74
+ },
75
+
76
+ /** Upserts normalized preferences for a user and returns the stored value. */
77
+ updateNotificationPreferences(
78
+ userId: number,
79
+ preferences: unknown
80
+ ): NotificationPreferences {
81
+ const normalized = normalizeNotificationPreferences(preferences);
82
+ const db = getConnection();
83
+
84
+ db.prepare(
85
+ `INSERT INTO user_notification_preferences (user_id, preferences_json, updated_at)
86
+ VALUES (?, ?, CURRENT_TIMESTAMP)
87
+ ON CONFLICT(user_id) DO UPDATE SET
88
+ preferences_json = excluded.preferences_json,
89
+ updated_at = CURRENT_TIMESTAMP`
90
+ ).run(userId, JSON.stringify(normalized));
91
+
92
+ return normalized;
93
+ },
94
+
95
+ // Legacy aliases used by existing services/routes
96
+ getPreferences(userId: number): NotificationPreferences {
97
+ return notificationPreferencesDb.getNotificationPreferences(userId);
98
+ },
99
+ updatePreferences(userId: number, preferences: unknown): NotificationPreferences {
100
+ return notificationPreferencesDb.updateNotificationPreferences(userId, preferences);
101
+ },
102
+ };
103
+
@@ -0,0 +1,78 @@
1
+ import assert from 'node:assert/strict';
2
+ import { mkdtemp, rm } from 'node:fs/promises';
3
+ import { tmpdir } from 'node:os';
4
+ import path from 'node:path';
5
+ import test from 'node:test';
6
+
7
+ import { closeConnection } from '@/modules/database/connection.js';
8
+ import { initializeDatabase } from '@/modules/database/init-db.js';
9
+ import { projectsDb } from '@/modules/database/repositories/projects.db.js';
10
+ import { userDb } from '@/modules/database/repositories/users.js';
11
+
12
+ async function withIsolatedDatabase(
13
+ runTest: (userId: number) => void | Promise<void>,
14
+ ): Promise<void> {
15
+ const previousDatabasePath = process.env.DATABASE_PATH;
16
+ const tempDirectory = await mkdtemp(path.join(tmpdir(), 'projects-db-'));
17
+ const databasePath = path.join(tempDirectory, 'auth.db');
18
+
19
+ closeConnection();
20
+ process.env.DATABASE_PATH = databasePath;
21
+ await initializeDatabase();
22
+
23
+ const created = userDb.createUser('test-user', 'hash');
24
+ const userId = Number(created.id);
25
+
26
+ try {
27
+ await runTest(userId);
28
+ } finally {
29
+ closeConnection();
30
+ if (previousDatabasePath === undefined) {
31
+ delete process.env.DATABASE_PATH;
32
+ } else {
33
+ process.env.DATABASE_PATH = previousDatabasePath;
34
+ }
35
+ await rm(tempDirectory, { recursive: true, force: true });
36
+ }
37
+ }
38
+
39
+ test('projectsDb.createProjectPath returns created for fresh paths', async () => {
40
+ await withIsolatedDatabase((userId) => {
41
+ const created = projectsDb.createProjectPath(userId, '/workspace/new-project');
42
+
43
+ assert.equal(created.outcome, 'created');
44
+ assert.ok(created.project);
45
+ assert.equal(created.project?.project_path, '/workspace/new-project');
46
+ assert.equal(created.project?.isArchived, 0);
47
+ });
48
+ });
49
+
50
+ test('projectsDb.createProjectPath returns reactivated_archived for archived duplicates', async () => {
51
+ await withIsolatedDatabase((userId) => {
52
+ const initial = projectsDb.createProjectPath(userId, '/workspace/archived-project', 'Archived Project');
53
+ assert.equal(initial.outcome, 'created');
54
+ assert.ok(initial.project);
55
+
56
+ projectsDb.updateProjectIsArchived(userId, '/workspace/archived-project', true);
57
+
58
+ const reused = projectsDb.createProjectPath(userId, '/workspace/archived-project', 'Renamed Project');
59
+ assert.equal(reused.outcome, 'reactivated_archived');
60
+ assert.ok(reused.project);
61
+ assert.equal(reused.project?.project_id, initial.project?.project_id);
62
+ assert.equal(reused.project?.isArchived, 0);
63
+ });
64
+ });
65
+
66
+ test('projectsDb.createProjectPath returns active_conflict for active duplicates', async () => {
67
+ await withIsolatedDatabase((userId) => {
68
+ const initial = projectsDb.createProjectPath(userId, '/workspace/active-project');
69
+ assert.equal(initial.outcome, 'created');
70
+ assert.ok(initial.project);
71
+
72
+ const conflict = projectsDb.createProjectPath(userId, '/workspace/active-project');
73
+ assert.equal(conflict.outcome, 'active_conflict');
74
+ assert.ok(conflict.project);
75
+ assert.equal(conflict.project?.project_id, initial.project?.project_id);
76
+ assert.equal(conflict.project?.isArchived, 0);
77
+ });
78
+ });
@@ -0,0 +1,210 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import path from 'node:path';
3
+
4
+ import { getConnection } from '@/modules/database/connection.js';
5
+ import type { CreateProjectPathResult, ProjectRepositoryRow } from '@/shared/types.js';
6
+ import { normalizeProjectPath } from '@/shared/utils.js';
7
+
8
+ function normalizeProjectDisplayName(projectPath: string, customProjectName: string | null): string {
9
+ const trimmedCustomName = typeof customProjectName === 'string' ? customProjectName.trim() : '';
10
+ if (trimmedCustomName.length > 0) {
11
+ return trimmedCustomName;
12
+ }
13
+
14
+ const directoryName = path.basename(projectPath);
15
+ return directoryName || projectPath;
16
+ }
17
+
18
+ export const projectsDb = {
19
+ createProjectPath(userId: number, projectPath: string, customProjectName: string | null = null): CreateProjectPathResult {
20
+ const db = getConnection();
21
+ const normalizedProjectPath = normalizeProjectPath(projectPath);
22
+ const normalizedProjectName = normalizeProjectDisplayName(normalizedProjectPath, customProjectName);
23
+ const attemptedId = randomUUID();
24
+ const row = db.prepare(`
25
+ INSERT INTO projects (project_id, user_id, project_path, custom_project_name, isArchived)
26
+ VALUES (?, ?, ?, ?, 0)
27
+ ON CONFLICT(user_id, project_path) DO UPDATE SET
28
+ isArchived = 0
29
+ WHERE projects.isArchived = 1
30
+ RETURNING project_id, project_path, custom_project_name, isStarred, isArchived
31
+ `).get(attemptedId, userId, normalizedProjectPath, normalizedProjectName) as ProjectRepositoryRow | undefined;
32
+
33
+ if (row) {
34
+ return {
35
+ outcome: row.project_id === attemptedId ? 'created' : 'reactivated_archived',
36
+ project: row,
37
+ };
38
+ }
39
+
40
+ const existingProject = projectsDb.getProjectPath(userId, normalizedProjectPath);
41
+ return {
42
+ outcome: 'active_conflict',
43
+ project: existingProject,
44
+ };
45
+ },
46
+
47
+ getProjectPath(userId: number, projectPath: string): ProjectRepositoryRow | null {
48
+ const db = getConnection();
49
+ const normalizedProjectPath = normalizeProjectPath(projectPath);
50
+ const row = db.prepare(`
51
+ SELECT project_id, project_path, custom_project_name, isStarred, isArchived
52
+ FROM projects
53
+ WHERE user_id = ? AND project_path = ?
54
+ `).get(userId, normalizedProjectPath) as ProjectRepositoryRow | undefined;
55
+
56
+ return row ?? null;
57
+ },
58
+
59
+ getProjectById(userId: number, projectId: string): ProjectRepositoryRow | null {
60
+ const db = getConnection();
61
+ const row = db.prepare(`
62
+ SELECT project_id, project_path, custom_project_name, isStarred, isArchived
63
+ FROM projects
64
+ WHERE user_id = ? AND project_id = ?
65
+ `).get(userId, projectId) as ProjectRepositoryRow | undefined;
66
+
67
+ return row ?? null;
68
+ },
69
+
70
+ /**
71
+ * Resolve the absolute project directory from a database project_id.
72
+ *
73
+ * This is the canonical lookup used after the projectName → projectId migration:
74
+ * API routes receive the DB-assigned `projectId` and must resolve the real folder
75
+ * path through this helper before touching the filesystem. Returns `null` when the
76
+ * project row does not exist so callers can respond with a 404.
77
+ */
78
+ getProjectPathById(userId: number, projectId: string): string | null {
79
+ const db = getConnection();
80
+ const row = db.prepare(`
81
+ SELECT project_path
82
+ FROM projects
83
+ WHERE user_id = ? AND project_id = ?
84
+ `).get(userId, projectId) as Pick<ProjectRepositoryRow, 'project_path'> | undefined;
85
+
86
+ return row?.project_path ?? null;
87
+ },
88
+
89
+ getProjectPaths(userId: number): ProjectRepositoryRow[] {
90
+ const db = getConnection();
91
+ return db.prepare(`
92
+ SELECT project_id, project_path, custom_project_name, isStarred, isArchived
93
+ FROM projects
94
+ WHERE user_id = ? AND isArchived = 0
95
+ `).all(userId) as ProjectRepositoryRow[];
96
+ },
97
+
98
+ /**
99
+ * Archived rows are queried separately so archive-focused UIs can present
100
+ * hidden workspaces without reintroducing them into the active sidebar list.
101
+ */
102
+ getArchivedProjectPaths(userId: number): ProjectRepositoryRow[] {
103
+ const db = getConnection();
104
+ return db.prepare(`
105
+ SELECT project_id, project_path, custom_project_name, isStarred, isArchived
106
+ FROM projects
107
+ WHERE user_id = ? AND isArchived = 1
108
+ `).all(userId) as ProjectRepositoryRow[];
109
+ },
110
+
111
+ getCustomProjectName(userId: number, projectPath: string): string | null {
112
+ const db = getConnection();
113
+ const normalizedProjectPath = normalizeProjectPath(projectPath);
114
+ const row = db.prepare(`
115
+ SELECT custom_project_name
116
+ FROM projects
117
+ WHERE user_id = ? AND project_path = ?
118
+ `).get(userId, normalizedProjectPath) as Pick<ProjectRepositoryRow, 'custom_project_name'> | undefined;
119
+
120
+ return row?.custom_project_name ?? null;
121
+ },
122
+
123
+ updateCustomProjectName(userId: number, projectPath: string, customProjectName: string | null): void {
124
+ const db = getConnection();
125
+ const normalizedProjectPath = normalizeProjectPath(projectPath);
126
+ db.prepare(`
127
+ INSERT INTO projects (project_id, user_id, project_path, custom_project_name)
128
+ VALUES (?, ?, ?, ?)
129
+ ON CONFLICT(user_id, project_path) DO UPDATE SET custom_project_name = excluded.custom_project_name
130
+ `).run(randomUUID(), userId, normalizedProjectPath, customProjectName);
131
+ },
132
+
133
+ updateCustomProjectNameById(userId: number, projectId: string, customProjectName: string | null): void {
134
+ const db = getConnection();
135
+ db.prepare(`
136
+ UPDATE projects
137
+ SET custom_project_name = ?
138
+ WHERE user_id = ? AND project_id = ?
139
+ `).run(customProjectName, userId, projectId);
140
+ },
141
+
142
+ updateProjectIsStarred(userId: number, projectPath: string, isStarred: boolean): void {
143
+ const db = getConnection();
144
+ const normalizedProjectPath = normalizeProjectPath(projectPath);
145
+ db.prepare(`
146
+ UPDATE projects
147
+ SET isStarred = ?
148
+ WHERE user_id = ? AND project_path = ?
149
+ `).run(isStarred ? 1 : 0, userId, normalizedProjectPath);
150
+ },
151
+
152
+ updateProjectIsStarredById(userId: number, projectId: string, isStarred: boolean): void {
153
+ const db = getConnection();
154
+ db.prepare(`
155
+ UPDATE projects
156
+ SET isStarred = ?
157
+ WHERE user_id = ? AND project_id = ?
158
+ `).run(isStarred ? 1 : 0, userId, projectId);
159
+ },
160
+
161
+ updateProjectIsArchived(userId: number, projectPath: string, isArchived: boolean): void {
162
+ const db = getConnection();
163
+ const normalizedProjectPath = normalizeProjectPath(projectPath);
164
+ db.prepare(`
165
+ UPDATE projects
166
+ SET isArchived = ?
167
+ WHERE user_id = ? AND project_path = ?
168
+ `).run(isArchived ? 1 : 0, userId, normalizedProjectPath);
169
+ },
170
+
171
+ updateProjectIsArchivedById(userId: number, projectId: string, isArchived: boolean): void {
172
+ const db = getConnection();
173
+ db.prepare(`
174
+ UPDATE projects
175
+ SET isArchived = ?
176
+ WHERE user_id = ? AND project_id = ?
177
+ `).run(isArchived ? 1 : 0, userId, projectId);
178
+ },
179
+
180
+ deleteProjectPath(userId: number, projectPath: string): void {
181
+ const db = getConnection();
182
+ const normalizedProjectPath = normalizeProjectPath(projectPath);
183
+ db.prepare(`
184
+ DELETE FROM projects
185
+ WHERE user_id = ? AND project_path = ?
186
+ `).run(userId, normalizedProjectPath);
187
+ },
188
+
189
+ deleteProjectById(userId: number, projectId: string): void {
190
+ const db = getConnection();
191
+ db.prepare(`
192
+ DELETE FROM projects
193
+ WHERE user_id = ? AND project_id = ?
194
+ `).run(userId, projectId);
195
+ },
196
+
197
+ isProjectPathUsedByOthers(userId: number, projectPath: string): { used: boolean; usernames: string[] } {
198
+ const db = getConnection();
199
+ const normalizedProjectPath = normalizeProjectPath(projectPath);
200
+ const rows = db.prepare(`
201
+ SELECT u.username FROM projects p
202
+ JOIN users u ON u.id = p.user_id
203
+ WHERE p.project_path = ? AND p.user_id != ? AND p.isArchived = 0 AND u.is_active = 1
204
+ `).all(normalizedProjectPath, userId) as Array<{ username: string }>;
205
+ return {
206
+ used: rows.length > 0,
207
+ usernames: rows.map(r => r.username),
208
+ };
209
+ },
210
+ };
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Push subscriptions repository.
3
+ *
4
+ * Persists browser push subscription endpoints and keys per user.
5
+ */
6
+
7
+ import { getConnection } from '@/modules/database/connection.js';
8
+
9
+ type PushSubscriptionLookupRow = {
10
+ endpoint: string;
11
+ keys_p256dh: string;
12
+ keys_auth: string;
13
+ };
14
+
15
+ export const pushSubscriptionsDb = {
16
+ /** Upserts a push subscription endpoint for a user. */
17
+ createPushSubscription(
18
+ userId: number,
19
+ endpoint: string,
20
+ keysP256dh: string,
21
+ keysAuth: string
22
+ ): void {
23
+ const db = getConnection();
24
+ db.prepare(
25
+ `INSERT INTO push_subscriptions (user_id, endpoint, keys_p256dh, keys_auth)
26
+ VALUES (?, ?, ?, ?)
27
+ ON CONFLICT(endpoint) DO UPDATE SET
28
+ user_id = excluded.user_id,
29
+ keys_p256dh = excluded.keys_p256dh,
30
+ keys_auth = excluded.keys_auth`
31
+ ).run(userId, endpoint, keysP256dh, keysAuth);
32
+ },
33
+
34
+ /** Returns all subscriptions for a user. */
35
+ getPushSubscriptions(userId: number): PushSubscriptionLookupRow[] {
36
+ const db = getConnection();
37
+ return db
38
+ .prepare(
39
+ 'SELECT endpoint, keys_p256dh, keys_auth FROM push_subscriptions WHERE user_id = ?'
40
+ )
41
+ .all(userId) as PushSubscriptionLookupRow[];
42
+ },
43
+
44
+ /** Deletes one subscription by endpoint. */
45
+ deletePushSubscription(endpoint: string): void {
46
+ const db = getConnection();
47
+ db.prepare('DELETE FROM push_subscriptions WHERE endpoint = ?').run(endpoint);
48
+ },
49
+
50
+ /** Deletes all subscriptions for a user. */
51
+ deletePushSubscriptionsForUser(userId: number): void {
52
+ const db = getConnection();
53
+ db.prepare('DELETE FROM push_subscriptions WHERE user_id = ?').run(userId);
54
+ },
55
+
56
+ // Legacy aliases used by existing services/routes
57
+ saveSubscription(
58
+ userId: number,
59
+ endpoint: string,
60
+ keysP256dh: string,
61
+ keysAuth: string
62
+ ): void {
63
+ pushSubscriptionsDb.createPushSubscription(
64
+ userId,
65
+ endpoint,
66
+ keysP256dh,
67
+ keysAuth
68
+ );
69
+ },
70
+ getSubscriptions(userId: number): PushSubscriptionLookupRow[] {
71
+ return pushSubscriptionsDb.getPushSubscriptions(userId);
72
+ },
73
+ removeSubscription(endpoint: string): void {
74
+ pushSubscriptionsDb.deletePushSubscription(endpoint);
75
+ },
76
+ removeAllForUser(userId: number): void {
77
+ pushSubscriptionsDb.deletePushSubscriptionsForUser(userId);
78
+ },
79
+ };
80
+