@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,42 @@
1
+ import { getConnection } from '@/modules/database/connection.js';
2
+
3
+ type ScanStateRow = {
4
+ last_scanned_at: string;
5
+ };
6
+
7
+ export const scanStateDb = {
8
+ getLastScannedAt() {
9
+ const db = getConnection();
10
+
11
+ const row = db
12
+ .prepare(`SELECT last_scanned_at FROM scan_state WHERE id = 1`)
13
+ .get() as ScanStateRow;
14
+
15
+ if (!row) {
16
+ return null; // Before any scan, the row is undefined.
17
+ }
18
+
19
+ let lastScannedDate: Date | null = null;
20
+ const lastScannedStr = row.last_scanned_at;
21
+
22
+ if (lastScannedStr) {
23
+ // SQLite CURRENT_TIMESTAMP returns UTC in "YYYY-MM-DD HH:MM:SS" format.
24
+ // Replace space with 'T' and append 'Z' to parse reliably in JS across all platforms.
25
+ lastScannedDate = new Date(lastScannedStr.replace(' ', 'T') + 'Z');
26
+ }
27
+
28
+ return lastScannedDate;
29
+ },
30
+
31
+ updateLastScannedAt(scannedAt: Date = new Date()) {
32
+ const db = getConnection();
33
+ const sqliteTimestamp = scannedAt.toISOString().slice(0, 19).replace('T', ' ');
34
+
35
+ db.prepare(`
36
+ INSERT INTO scan_state (id, last_scanned_at)
37
+ VALUES (1, ?)
38
+ ON CONFLICT (id)
39
+ DO UPDATE SET last_scanned_at = excluded.last_scanned_at
40
+ `).run(sqliteTimestamp);
41
+ }
42
+ };
@@ -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 { sessionsDb } from '@/modules/database/repositories/sessions.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(), 'sessions-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('session archive queries hide archived rows from active project views', async () => {
40
+ await withIsolatedDatabase((userId) => {
41
+ sessionsDb.createSession(userId, 'session-active', 'claude', '/workspace/demo-project', 'Active Session');
42
+ sessionsDb.createSession(userId, 'session-archived', 'claude', '/workspace/demo-project', 'Archived Session');
43
+ sessionsDb.updateSessionIsArchived(userId, 'session-archived', true);
44
+
45
+ const activeSessions = sessionsDb.getAllSessions(userId);
46
+ const archivedSessions = sessionsDb.getArchivedSessions(userId);
47
+ const activeProjectSessions = sessionsDb.getSessionsByProjectPath(userId, '/workspace/demo-project');
48
+ const allProjectSessions = sessionsDb.getSessionsByProjectPathIncludingArchived(userId, '/workspace/demo-project');
49
+
50
+ assert.deepEqual(activeSessions.map((session) => session.session_id), ['session-active']);
51
+ assert.deepEqual(archivedSessions.map((session) => session.session_id), ['session-archived']);
52
+ assert.deepEqual(activeProjectSessions.map((session) => session.session_id), ['session-active']);
53
+ assert.deepEqual(
54
+ allProjectSessions.map((session) => session.session_id).sort(),
55
+ ['session-active', 'session-archived'],
56
+ );
57
+ assert.equal(sessionsDb.countSessionsByProjectPath(userId, '/workspace/demo-project'), 1);
58
+ });
59
+ });
60
+
61
+ test('createSession reactivates archived rows when the session becomes active again', async () => {
62
+ await withIsolatedDatabase((userId) => {
63
+ sessionsDb.createSession(userId, 'session-reused', 'claude', '/workspace/demo-project', 'First Name');
64
+ sessionsDb.updateSessionIsArchived(userId, 'session-reused', true);
65
+
66
+ sessionsDb.createSession(userId, 'session-reused', 'claude', '/workspace/demo-project', 'Updated Name');
67
+
68
+ const activeSessions = sessionsDb.getAllSessions(userId);
69
+ const archivedSessions = sessionsDb.getArchivedSessions(userId);
70
+ const restoredSession = sessionsDb.getSessionById(userId, 'session-reused');
71
+
72
+ assert.equal(activeSessions.length, 1);
73
+ assert.equal(activeSessions[0]?.session_id, 'session-reused');
74
+ assert.equal(activeSessions[0]?.custom_name, 'Updated Name');
75
+ assert.equal(archivedSessions.length, 0);
76
+ assert.equal(restoredSession?.isArchived, 0);
77
+ });
78
+ });
@@ -0,0 +1,230 @@
1
+ import { getConnection } from '@/modules/database/connection.js';
2
+ import { projectsDb } from '@/modules/database/repositories/projects.db.js';
3
+ import { normalizeProjectPath } from '@/shared/utils.js';
4
+
5
+ type SessionRow = {
6
+ session_id: string;
7
+ user_id: number;
8
+ provider: string;
9
+ project_path: string | null;
10
+ jsonl_path: string | null;
11
+ custom_name: string | null;
12
+ isArchived: number;
13
+ created_at: string;
14
+ updated_at: string;
15
+ };
16
+
17
+ type SessionMetadataLookupRow = Pick<
18
+ SessionRow,
19
+ 'session_id' | 'user_id' | 'provider' | 'project_path' | 'jsonl_path' | 'custom_name' | 'isArchived' | 'created_at' | 'updated_at'
20
+ >;
21
+
22
+ function normalizeTimestamp(value?: string): string | null {
23
+ if (!value) return null;
24
+
25
+ const parsed = new Date(value);
26
+ if (Number.isNaN(parsed.getTime())) {
27
+ return null;
28
+ }
29
+
30
+ return parsed.toISOString();
31
+ }
32
+
33
+ function normalizeProjectPathForProvider(provider: string, projectPath: string): string {
34
+ void provider;
35
+ return normalizeProjectPath(projectPath);
36
+ }
37
+
38
+ export const sessionsDb = {
39
+ createSession(
40
+ userId: number,
41
+ sessionId: string,
42
+ provider: string,
43
+ projectPath: string,
44
+ customName?: string,
45
+ createdAt?: string,
46
+ updatedAt?: string,
47
+ jsonlPath?: string | null
48
+ ): string {
49
+ const db = getConnection();
50
+ const createdAtValue = normalizeTimestamp(createdAt);
51
+ const updatedAtValue = normalizeTimestamp(updatedAt);
52
+ const normalizedProjectPath = normalizeProjectPathForProvider(provider, projectPath);
53
+
54
+ // First, ensure the project path is recorded in the projects table,
55
+ // since it's a foreign key in the sessions table.
56
+ projectsDb.createProjectPath(userId, normalizedProjectPath);
57
+
58
+ db.prepare(
59
+ `INSERT INTO sessions (session_id, user_id, provider, custom_name, project_path, jsonl_path, isArchived, created_at, updated_at)
60
+ VALUES (?, ?, ?, ?, ?, ?, 0, COALESCE(?, CURRENT_TIMESTAMP), COALESCE(?, CURRENT_TIMESTAMP))
61
+ ON CONFLICT(user_id, session_id) DO UPDATE SET
62
+ provider = excluded.provider,
63
+ updated_at = excluded.updated_at,
64
+ project_path = excluded.project_path,
65
+ jsonl_path = excluded.jsonl_path,
66
+ isArchived = 0,
67
+ custom_name = COALESCE(excluded.custom_name, sessions.custom_name)`
68
+ ).run(
69
+ sessionId,
70
+ userId,
71
+ provider,
72
+ customName ?? null,
73
+ normalizedProjectPath,
74
+ jsonlPath ?? null,
75
+ createdAtValue,
76
+ updatedAtValue
77
+ );
78
+
79
+ return sessionId;
80
+ },
81
+
82
+ updateSessionCustomName(userId: number, sessionId: string, customName: string): void {
83
+ const db = getConnection();
84
+ db.prepare(
85
+ `UPDATE sessions
86
+ SET custom_name = ?
87
+ WHERE user_id = ? AND session_id = ?`
88
+ ).run(customName, userId, sessionId);
89
+ },
90
+
91
+ getSessionById(userId: number, sessionId: string): SessionMetadataLookupRow | null {
92
+ const db = getConnection();
93
+ const row = db
94
+ .prepare(
95
+ `SELECT session_id, user_id, provider, project_path, jsonl_path, custom_name, isArchived, created_at, updated_at
96
+ FROM sessions
97
+ WHERE user_id = ? AND session_id = ?
98
+ ORDER BY updated_at DESC
99
+ LIMIT 1`
100
+ )
101
+ .get(userId, sessionId) as SessionMetadataLookupRow | undefined;
102
+
103
+ return row ?? null;
104
+ },
105
+
106
+ getAllSessions(userId: number): SessionRow[] {
107
+ const db = getConnection();
108
+ return db
109
+ .prepare(
110
+ `SELECT session_id, user_id, provider, project_path, jsonl_path, custom_name, isArchived, created_at, updated_at
111
+ FROM sessions
112
+ WHERE user_id = ? AND isArchived = 0`
113
+ )
114
+ .all(userId) as SessionRow[];
115
+ },
116
+
117
+ /**
118
+ * Archived rows are intentionally queried separately so the caller can render
119
+ * them in a dedicated view without reintroducing them into active session lists.
120
+ */
121
+ getArchivedSessions(userId: number): SessionRow[] {
122
+ const db = getConnection();
123
+ return db
124
+ .prepare(
125
+ `SELECT session_id, user_id, provider, project_path, jsonl_path, custom_name, isArchived, created_at, updated_at
126
+ FROM sessions
127
+ WHERE user_id = ? AND isArchived = 1
128
+ ORDER BY datetime(COALESCE(updated_at, created_at)) DESC, session_id DESC`
129
+ )
130
+ .all(userId) as SessionRow[];
131
+ },
132
+
133
+ getSessionsByProjectPath(userId: number, projectPath: string): SessionRow[] {
134
+ const db = getConnection();
135
+ const normalizedProjectPath = normalizeProjectPath(projectPath);
136
+ return db
137
+ .prepare(
138
+ `SELECT session_id, user_id, provider, project_path, jsonl_path, custom_name, isArchived, created_at, updated_at
139
+ FROM sessions
140
+ WHERE user_id = ? AND project_path = ?
141
+ AND isArchived = 0`
142
+ )
143
+ .all(userId, normalizedProjectPath) as SessionRow[];
144
+ },
145
+
146
+ /**
147
+ * Permanent project deletion must see every session row for the path,
148
+ * including archived ones, so their transcript files can be cleaned up.
149
+ */
150
+ getSessionsByProjectPathIncludingArchived(userId: number, projectPath: string): SessionRow[] {
151
+ const db = getConnection();
152
+ const normalizedProjectPath = normalizeProjectPath(projectPath);
153
+ return db
154
+ .prepare(
155
+ `SELECT session_id, user_id, provider, project_path, jsonl_path, custom_name, isArchived, created_at, updated_at
156
+ FROM sessions
157
+ WHERE user_id = ? AND project_path = ?`
158
+ )
159
+ .all(userId, normalizedProjectPath) as SessionRow[];
160
+ },
161
+
162
+ getSessionsByProjectPathPage(userId: number, projectPath: string, limit: number, offset: number): SessionRow[] {
163
+ const db = getConnection();
164
+ const normalizedProjectPath = normalizeProjectPath(projectPath);
165
+ return db
166
+ .prepare(
167
+ `SELECT session_id, user_id, provider, project_path, jsonl_path, custom_name, isArchived, created_at, updated_at
168
+ FROM sessions
169
+ WHERE user_id = ? AND project_path = ?
170
+ AND isArchived = 0
171
+ ORDER BY datetime(COALESCE(updated_at, created_at)) DESC, session_id DESC
172
+ LIMIT ? OFFSET ?`
173
+ )
174
+ .all(userId, normalizedProjectPath, limit, offset) as SessionRow[];
175
+ },
176
+
177
+ countSessionsByProjectPath(userId: number, projectPath: string): number {
178
+ const db = getConnection();
179
+ const normalizedProjectPath = normalizeProjectPath(projectPath);
180
+ const row = db
181
+ .prepare(
182
+ `SELECT COUNT(*) AS count
183
+ FROM sessions
184
+ WHERE user_id = ? AND project_path = ?
185
+ AND isArchived = 0`
186
+ )
187
+ .get(userId, normalizedProjectPath) as { count: number } | undefined;
188
+
189
+ return Number(row?.count ?? 0);
190
+ },
191
+
192
+ deleteSessionsByProjectPath(userId: number, projectPath: string): void {
193
+ const db = getConnection();
194
+ const normalizedProjectPath = normalizeProjectPath(projectPath);
195
+ db.prepare(`DELETE FROM sessions WHERE user_id = ? AND project_path = ?`).run(userId, normalizedProjectPath);
196
+ },
197
+
198
+ getSessionName(userId: number, sessionId: string, provider: string): string | null {
199
+ const db = getConnection();
200
+ const row = db
201
+ .prepare(
202
+ `SELECT custom_name
203
+ FROM sessions
204
+ WHERE user_id = ? AND session_id = ? AND provider = ?`
205
+ )
206
+ .get(userId, sessionId, provider) as { custom_name: string | null } | undefined;
207
+
208
+ return row?.custom_name ?? null;
209
+ },
210
+
211
+ /**
212
+ * Soft-delete and restore both use the same flag update so callers keep the
213
+ * row, metadata, and file path intact while toggling visibility.
214
+ */
215
+ updateSessionIsArchived(userId: number, sessionId: string, isArchived: boolean): void {
216
+ const db = getConnection();
217
+ db.prepare(
218
+ `UPDATE sessions
219
+ SET isArchived = ?
220
+ WHERE user_id = ? AND session_id = ?`
221
+ ).run(isArchived ? 1 : 0, userId, sessionId);
222
+ },
223
+
224
+ deleteSessionById(userId: number, sessionId: string): boolean {
225
+ const db = getConnection();
226
+ return (
227
+ db.prepare('DELETE FROM sessions WHERE user_id = ? AND session_id = ?').run(userId, sessionId).changes > 0
228
+ );
229
+ },
230
+ };
@@ -0,0 +1,186 @@
1
+ /**
2
+ * User repository.
3
+ *
4
+ * Provides typed CRUD operations for the `users` table.
5
+ */
6
+
7
+ import { getConnection } from '@/modules/database/connection.js';
8
+
9
+ type UserRow = {
10
+ id: number;
11
+ username: string;
12
+ password_hash: string;
13
+ created_at: string;
14
+ last_login: string | null;
15
+ is_active: number;
16
+ git_name: string | null;
17
+ git_email: string | null;
18
+ has_completed_onboarding: number;
19
+ is_admin: number;
20
+ };
21
+
22
+ type UserPublicRow = {
23
+ id: number;
24
+ username: string;
25
+ created_at: string;
26
+ last_login: string | null;
27
+ isAdmin: boolean;
28
+ };
29
+
30
+ type UserGitConfig = {
31
+ git_name: string | null;
32
+ git_email: string | null;
33
+ };
34
+
35
+ type CreateUserOptions = {
36
+ isAdmin?: boolean;
37
+ };
38
+
39
+ type CreateUserResult = {
40
+ id: number;
41
+ username: string;
42
+ isAdmin: boolean;
43
+ };
44
+
45
+ const normalizePublicUser = (
46
+ row:
47
+ | Pick<UserRow, 'id' | 'username' | 'created_at' | 'last_login' | 'is_admin'>
48
+ | undefined,
49
+ ): UserPublicRow | undefined => {
50
+ if (!row) {
51
+ return undefined;
52
+ }
53
+
54
+ return {
55
+ id: row.id,
56
+ username: row.username,
57
+ created_at: row.created_at,
58
+ last_login: row.last_login,
59
+ isAdmin: row.is_admin === 1,
60
+ };
61
+ };
62
+
63
+ export const userDb = {
64
+ /** Returns true if at least one user exists in the database. */
65
+ hasUsers(): boolean {
66
+ const db = getConnection();
67
+ const row = db.prepare('SELECT COUNT(*) as count FROM users').get() as {
68
+ count: number;
69
+ };
70
+ return row.count > 0;
71
+ },
72
+
73
+ /** Inserts a new user and returns the created public fields. */
74
+ createUser(username: string, passwordHash: string, options: CreateUserOptions = {}): CreateUserResult {
75
+ const db = getConnection();
76
+ const isAdmin = options.isAdmin ?? false;
77
+ const result = db
78
+ .prepare('INSERT INTO users (username, password_hash, is_admin) VALUES (?, ?, ?)')
79
+ .run(username, passwordHash, isAdmin ? 1 : 0);
80
+
81
+ return {
82
+ id: Number(result.lastInsertRowid),
83
+ username,
84
+ isAdmin,
85
+ };
86
+ },
87
+
88
+ /** Looks up an active user by username, including password hash for auth verification. */
89
+ getUserByUsername(username: string): UserRow | undefined {
90
+ const db = getConnection();
91
+ return db
92
+ .prepare('SELECT * FROM users WHERE username = ? AND is_active = 1')
93
+ .get(username) as UserRow | undefined;
94
+ },
95
+
96
+ /** Updates the last_login timestamp. Non-fatal — logs but does not throw. */
97
+ updateLastLogin(userId: number): void {
98
+ try {
99
+ const db = getConnection();
100
+ db.prepare('UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE id = ?').run(userId);
101
+ } catch (err) {
102
+ const message = err instanceof Error ? err.message : String(err);
103
+ console.error('Failed to update last login', { error: message });
104
+ }
105
+ },
106
+
107
+ /** Returns public user fields by ID (no password hash). */
108
+ getUserById(userId: number): UserPublicRow | undefined {
109
+ const db = getConnection();
110
+ const row = db
111
+ .prepare(
112
+ 'SELECT id, username, created_at, last_login, is_admin FROM users WHERE id = ? AND is_active = 1'
113
+ )
114
+ .get(userId) as Pick<UserRow, 'id' | 'username' | 'created_at' | 'last_login' | 'is_admin'> | undefined;
115
+
116
+ return normalizePublicUser(row);
117
+ },
118
+
119
+ /** Returns the first active user. Used for single-user mode lookups. */
120
+ getFirstUser(): UserPublicRow | undefined {
121
+ const db = getConnection();
122
+ const row = db
123
+ .prepare(
124
+ 'SELECT id, username, created_at, last_login, is_admin FROM users WHERE is_active = 1 ORDER BY id ASC LIMIT 1'
125
+ )
126
+ .get() as Pick<UserRow, 'id' | 'username' | 'created_at' | 'last_login' | 'is_admin'> | undefined;
127
+
128
+ return normalizePublicUser(row);
129
+ },
130
+
131
+ listUsers(): UserPublicRow[] {
132
+ const db = getConnection();
133
+ const rows = db
134
+ .prepare(
135
+ 'SELECT id, username, created_at, last_login, is_admin FROM users WHERE is_active = 1 ORDER BY id ASC'
136
+ )
137
+ .all() as Array<Pick<UserRow, 'id' | 'username' | 'created_at' | 'last_login' | 'is_admin'>>;
138
+
139
+ return rows.map((row) => normalizePublicUser(row)).filter(Boolean) as UserPublicRow[];
140
+ },
141
+
142
+ deleteUser(userId: number): boolean {
143
+ const db = getConnection();
144
+ const result = db.prepare('DELETE FROM users WHERE id = ?').run(userId);
145
+ return result.changes > 0;
146
+ },
147
+
148
+ /** Stores the user's preferred git name and email. */
149
+ updateGitConfig(userId: number, gitName: string, gitEmail: string): void {
150
+ const db = getConnection();
151
+ db.prepare('UPDATE users SET git_name = ?, git_email = ? WHERE id = ?').run(
152
+ gitName,
153
+ gitEmail,
154
+ userId,
155
+ );
156
+ },
157
+
158
+ /** Retrieves the user's git identity (name + email). */
159
+ getGitConfig(userId: number): UserGitConfig | undefined {
160
+ const db = getConnection();
161
+ return db
162
+ .prepare('SELECT git_name, git_email FROM users WHERE id = ?')
163
+ .get(userId) as UserGitConfig | undefined;
164
+ },
165
+
166
+ /** Marks onboarding as complete for the given user. */
167
+ completeOnboarding(userId: number): void {
168
+ const db = getConnection();
169
+ db.prepare('UPDATE users SET has_completed_onboarding = 1 WHERE id = ?').run(userId);
170
+ },
171
+
172
+ /** Returns true if the user has finished the onboarding flow. */
173
+ hasCompletedOnboarding(userId: number): boolean {
174
+ const db = getConnection();
175
+ const row = db
176
+ .prepare('SELECT has_completed_onboarding FROM users WHERE id = ?')
177
+ .get(userId) as { has_completed_onboarding: number } | undefined;
178
+ return row?.has_completed_onboarding === 1;
179
+ },
180
+
181
+ /** Updates the password hash for a given user. */
182
+ updatePassword(userId: number, passwordHash: string): void {
183
+ const db = getConnection();
184
+ db.prepare('UPDATE users SET password_hash = ? WHERE id = ?').run(passwordHash, userId);
185
+ },
186
+ };
@@ -0,0 +1,57 @@
1
+ /**
2
+ * VAPID keys repository.
3
+ *
4
+ * Stores and retrieves the Web Push VAPID key pair.
5
+ */
6
+
7
+ import { getConnection } from '@/modules/database/connection.js';
8
+
9
+ type VapidKeyRow = {
10
+ public_key: string;
11
+ private_key: string;
12
+ };
13
+
14
+ type VapidKeyPair = {
15
+ publicKey: string;
16
+ privateKey: string;
17
+ };
18
+
19
+ export const vapidKeysDb = {
20
+ /** Returns the latest stored VAPID key pair, or null when unset. */
21
+ getVapidKeys(): VapidKeyPair | null {
22
+ const db = getConnection();
23
+ const row = db
24
+ .prepare(
25
+ 'SELECT public_key, private_key FROM vapid_keys ORDER BY id DESC LIMIT 1'
26
+ )
27
+ .get() as Pick<VapidKeyRow, 'public_key' | 'private_key'> | undefined;
28
+
29
+ if (!row) return null;
30
+ return {
31
+ publicKey: row.public_key,
32
+ privateKey: row.private_key,
33
+ };
34
+ },
35
+
36
+ /** Persists a new VAPID key pair. */
37
+ createVapidKeys(publicKey: string, privateKey: string): void {
38
+ const db = getConnection();
39
+ db.prepare(
40
+ 'INSERT INTO vapid_keys (public_key, private_key) VALUES (?, ?)'
41
+ ).run(publicKey, privateKey);
42
+ },
43
+
44
+ /** Replaces all existing keys with a fresh pair. */
45
+ updateVapidKeys(publicKey: string, privateKey: string): void {
46
+ const db = getConnection();
47
+ db.prepare('DELETE FROM vapid_keys').run();
48
+ vapidKeysDb.createVapidKeys(publicKey, privateKey);
49
+ },
50
+
51
+ /** Deletes all VAPID key rows. */
52
+ deleteVapidKeys(): void {
53
+ const db = getConnection();
54
+ db.prepare('DELETE FROM vapid_keys').run();
55
+ },
56
+ };
57
+