@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,123 @@
1
+ import express from 'express';
2
+ import { userDb } from '../modules/database/index.js';
3
+ import { authenticateToken } from '../middleware/auth.js';
4
+ import { getSystemGitConfig } from '../utils/gitConfig.js';
5
+ import { spawn } from 'child_process';
6
+
7
+ const router = express.Router();
8
+
9
+ function spawnAsync(command, args, options = {}) {
10
+ return new Promise((resolve, reject) => {
11
+ const child = spawn(command, args, { ...options, shell: false });
12
+ let stdout = '';
13
+ let stderr = '';
14
+ child.stdout.on('data', (data) => { stdout += data.toString(); });
15
+ child.stderr.on('data', (data) => { stderr += data.toString(); });
16
+ child.on('error', (error) => { reject(error); });
17
+ child.on('close', (code) => {
18
+ if (code === 0) { resolve({ stdout, stderr }); return; }
19
+ const error = new Error(`Command failed: ${command} ${args.join(' ')}`);
20
+ error.code = code;
21
+ error.stdout = stdout;
22
+ error.stderr = stderr;
23
+ reject(error);
24
+ });
25
+ });
26
+ }
27
+
28
+ router.get('/git-config', authenticateToken, async (req, res) => {
29
+ try {
30
+ const userId = req.user.id;
31
+ let gitConfig = userDb.getGitConfig(userId);
32
+
33
+ // If database is empty, try to get from system git config
34
+ if (!gitConfig || (!gitConfig.git_name && !gitConfig.git_email)) {
35
+ const systemConfig = await getSystemGitConfig();
36
+
37
+ // If system has values, save them to database for this user
38
+ if (systemConfig.git_name || systemConfig.git_email) {
39
+ userDb.updateGitConfig(userId, systemConfig.git_name, systemConfig.git_email);
40
+ gitConfig = systemConfig;
41
+ console.log(`Auto-populated git config from system for user ${userId}: ${systemConfig.git_name} <${systemConfig.git_email}>`);
42
+ }
43
+ }
44
+
45
+ res.json({
46
+ success: true,
47
+ gitName: gitConfig?.git_name || null,
48
+ gitEmail: gitConfig?.git_email || null
49
+ });
50
+ } catch (error) {
51
+ console.error('Error getting git config:', error);
52
+ res.status(500).json({ error: 'Failed to get git configuration' });
53
+ }
54
+ });
55
+
56
+ // Apply git config globally via git config --global
57
+ router.post('/git-config', authenticateToken, async (req, res) => {
58
+ try {
59
+ const userId = req.user.id;
60
+ const { gitName, gitEmail } = req.body;
61
+
62
+ if (!gitName || !gitEmail) {
63
+ return res.status(400).json({ error: 'Git name and email are required' });
64
+ }
65
+
66
+ // Validate email format
67
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
68
+ if (!emailRegex.test(gitEmail)) {
69
+ return res.status(400).json({ error: 'Invalid email format' });
70
+ }
71
+
72
+ userDb.updateGitConfig(userId, gitName, gitEmail);
73
+
74
+ try {
75
+ await spawnAsync('git', ['config', '--global', 'user.name', gitName]);
76
+ await spawnAsync('git', ['config', '--global', 'user.email', gitEmail]);
77
+ console.log(`Applied git config globally: ${gitName} <${gitEmail}>`);
78
+ } catch (gitError) {
79
+ console.error('Error applying git config:', gitError);
80
+ }
81
+
82
+ res.json({
83
+ success: true,
84
+ gitName,
85
+ gitEmail
86
+ });
87
+ } catch (error) {
88
+ console.error('Error updating git config:', error);
89
+ res.status(500).json({ error: 'Failed to update git configuration' });
90
+ }
91
+ });
92
+
93
+ router.post('/complete-onboarding', authenticateToken, async (req, res) => {
94
+ try {
95
+ const userId = req.user.id;
96
+ userDb.completeOnboarding(userId);
97
+
98
+ res.json({
99
+ success: true,
100
+ message: 'Onboarding completed successfully'
101
+ });
102
+ } catch (error) {
103
+ console.error('Error completing onboarding:', error);
104
+ res.status(500).json({ error: 'Failed to complete onboarding' });
105
+ }
106
+ });
107
+
108
+ router.get('/onboarding-status', authenticateToken, async (req, res) => {
109
+ try {
110
+ const userId = req.user.id;
111
+ const hasCompleted = userDb.hasCompletedOnboarding(userId);
112
+
113
+ res.json({
114
+ success: true,
115
+ hasCompletedOnboarding: hasCompleted
116
+ });
117
+ } catch (error) {
118
+ console.error('Error checking onboarding status:', error);
119
+ res.status(500).json({ error: 'Failed to check onboarding status' });
120
+ }
121
+ });
122
+
123
+ export default router;
@@ -0,0 +1,228 @@
1
+ import webPush from 'web-push';
2
+
3
+ import { notificationPreferencesDb, pushSubscriptionsDb, sessionsDb } from '../modules/database/index.js';
4
+
5
+ const KIND_TO_PREF_KEY = {
6
+ action_required: 'actionRequired',
7
+ stop: 'stop',
8
+ error: 'error'
9
+ };
10
+
11
+ const PROVIDER_LABELS = {
12
+ claude: 'Claude',
13
+ cursor: 'Cursor',
14
+ codex: 'Codex',
15
+ gemini: 'Gemini',
16
+ system: 'System'
17
+ };
18
+
19
+ const recentEventKeys = new Map();
20
+ const DEDUPE_WINDOW_MS = 20000;
21
+
22
+ const cleanupOldEventKeys = () => {
23
+ const now = Date.now();
24
+ for (const [key, timestamp] of recentEventKeys.entries()) {
25
+ if (now - timestamp > DEDUPE_WINDOW_MS) {
26
+ recentEventKeys.delete(key);
27
+ }
28
+ }
29
+ };
30
+
31
+ function shouldSendPush(preferences, event) {
32
+ const webPushEnabled = Boolean(preferences?.channels?.webPush);
33
+ const prefEventKey = KIND_TO_PREF_KEY[event.kind];
34
+ const eventEnabled = prefEventKey ? Boolean(preferences?.events?.[prefEventKey]) : true;
35
+
36
+ return webPushEnabled && eventEnabled;
37
+ }
38
+
39
+ function isDuplicate(event) {
40
+ cleanupOldEventKeys();
41
+ const key = event.dedupeKey || `${event.provider}:${event.kind || 'info'}:${event.code || 'generic'}:${event.sessionId || 'none'}`;
42
+ if (recentEventKeys.has(key)) {
43
+ return true;
44
+ }
45
+ recentEventKeys.set(key, Date.now());
46
+ return false;
47
+ }
48
+
49
+ function createNotificationEvent({
50
+ provider,
51
+ sessionId = null,
52
+ kind = 'info',
53
+ code = 'generic.info',
54
+ meta = {},
55
+ severity = 'info',
56
+ dedupeKey = null,
57
+ requiresUserAction = false
58
+ }) {
59
+ return {
60
+ provider,
61
+ sessionId,
62
+ kind,
63
+ code,
64
+ meta,
65
+ severity,
66
+ requiresUserAction,
67
+ dedupeKey,
68
+ createdAt: new Date().toISOString()
69
+ };
70
+ }
71
+
72
+ function normalizeErrorMessage(error) {
73
+ if (typeof error === 'string') {
74
+ return error;
75
+ }
76
+
77
+ if (error && typeof error.message === 'string') {
78
+ return error.message;
79
+ }
80
+
81
+ if (error == null) {
82
+ return 'Unknown error';
83
+ }
84
+
85
+ return String(error);
86
+ }
87
+
88
+ function normalizeSessionName(sessionName) {
89
+ if (typeof sessionName !== 'string') {
90
+ return null;
91
+ }
92
+
93
+ const normalized = sessionName.replace(/\s+/g, ' ').trim();
94
+ if (!normalized) {
95
+ return null;
96
+ }
97
+
98
+ return normalized.length > 80 ? `${normalized.slice(0, 77)}...` : normalized;
99
+ }
100
+
101
+ function resolveSessionName(userId, event) {
102
+ const explicitSessionName = normalizeSessionName(event.meta?.sessionName);
103
+ if (explicitSessionName) {
104
+ return explicitSessionName;
105
+ }
106
+
107
+ if (!event.sessionId || !event.provider) {
108
+ return null;
109
+ }
110
+
111
+ return normalizeSessionName(sessionsDb.getSessionName(userId, event.sessionId, event.provider));
112
+ }
113
+
114
+ function buildPushBody(userId, event) {
115
+ const CODE_MAP = {
116
+ 'permission.required': event.meta?.toolName
117
+ ? `Action Required: Tool "${event.meta.toolName}" needs approval`
118
+ : 'Action Required: A tool needs your approval',
119
+ 'run.stopped': event.meta?.stopReason || 'Run Stopped: The run has stopped',
120
+ 'run.failed': event.meta?.error ? `Run Failed: ${event.meta.error}` : 'Run Failed: The run encountered an error',
121
+ 'agent.notification': event.meta?.message ? String(event.meta.message) : 'You have a new notification',
122
+ 'push.enabled': 'Push notifications are now enabled!'
123
+ };
124
+ const providerLabel = PROVIDER_LABELS[event.provider] || 'Assistant';
125
+ const sessionName = resolveSessionName(userId, event);
126
+ const message = CODE_MAP[event.code] || 'You have a new notification';
127
+
128
+ return {
129
+ title: sessionName || 'CloudCLI',
130
+ body: `${providerLabel}: ${message}`,
131
+ data: {
132
+ sessionId: event.sessionId || null,
133
+ code: event.code,
134
+ provider: event.provider || null,
135
+ sessionName,
136
+ tag: `${event.provider || 'assistant'}:${event.sessionId || 'none'}:${event.code}`
137
+ }
138
+ };
139
+ }
140
+
141
+ async function sendWebPush(userId, event) {
142
+ const subscriptions = pushSubscriptionsDb.getSubscriptions(userId);
143
+ if (!subscriptions.length) return;
144
+
145
+ const payload = JSON.stringify(buildPushBody(userId, event));
146
+
147
+ const results = await Promise.allSettled(
148
+ subscriptions.map((sub) =>
149
+ webPush.sendNotification(
150
+ {
151
+ endpoint: sub.endpoint,
152
+ keys: {
153
+ p256dh: sub.keys_p256dh,
154
+ auth: sub.keys_auth
155
+ }
156
+ },
157
+ payload
158
+ )
159
+ )
160
+ );
161
+
162
+ // Clean up gone subscriptions (410 Gone or 404)
163
+ results.forEach((result, index) => {
164
+ if (result.status === 'rejected') {
165
+ const statusCode = result.reason?.statusCode;
166
+ if (statusCode === 410 || statusCode === 404) {
167
+ pushSubscriptionsDb.removeSubscription(subscriptions[index].endpoint);
168
+ }
169
+ }
170
+ });
171
+ }
172
+
173
+ function notifyUserIfEnabled({ userId, event }) {
174
+ if (!userId || !event) {
175
+ return;
176
+ }
177
+
178
+ const preferences = notificationPreferencesDb.getPreferences(userId);
179
+ if (!shouldSendPush(preferences, event)) {
180
+ return;
181
+ }
182
+ if (isDuplicate(event)) {
183
+ return;
184
+ }
185
+
186
+ sendWebPush(userId, event).catch((err) => {
187
+ console.error('Web push send error:', err);
188
+ });
189
+ }
190
+
191
+ function notifyRunStopped({ userId, provider, sessionId = null, stopReason = 'completed', sessionName = null }) {
192
+ notifyUserIfEnabled({
193
+ userId,
194
+ event: createNotificationEvent({
195
+ provider,
196
+ sessionId,
197
+ kind: 'stop',
198
+ code: 'run.stopped',
199
+ meta: { stopReason, sessionName },
200
+ severity: 'info',
201
+ dedupeKey: `${provider}:run:stop:${sessionId || 'none'}:${stopReason}`
202
+ })
203
+ });
204
+ }
205
+
206
+ function notifyRunFailed({ userId, provider, sessionId = null, error, sessionName = null }) {
207
+ const errorMessage = normalizeErrorMessage(error);
208
+
209
+ notifyUserIfEnabled({
210
+ userId,
211
+ event: createNotificationEvent({
212
+ provider,
213
+ sessionId,
214
+ kind: 'error',
215
+ code: 'run.failed',
216
+ meta: { error: errorMessage, sessionName },
217
+ severity: 'error',
218
+ dedupeKey: `${provider}:run:error:${sessionId || 'none'}:${errorMessage}`
219
+ })
220
+ });
221
+ }
222
+
223
+ export {
224
+ createNotificationEvent,
225
+ notifyUserIfEnabled,
226
+ notifyRunStopped,
227
+ notifyRunFailed
228
+ };
@@ -0,0 +1,36 @@
1
+ import webPush from 'web-push';
2
+ import { getConnection } from '../modules/database/connection.js';
3
+
4
+ let cachedKeys = null;
5
+ const db = getConnection();
6
+
7
+ function ensureVapidKeys() {
8
+ if (cachedKeys) return cachedKeys;
9
+
10
+ const row = db.prepare('SELECT public_key, private_key FROM vapid_keys ORDER BY id DESC LIMIT 1').get();
11
+ if (row) {
12
+ cachedKeys = { publicKey: row.public_key, privateKey: row.private_key };
13
+ return cachedKeys;
14
+ }
15
+
16
+ const keys = webPush.generateVAPIDKeys();
17
+ db.prepare('INSERT INTO vapid_keys (public_key, private_key) VALUES (?, ?)').run(keys.publicKey, keys.privateKey);
18
+ cachedKeys = keys;
19
+ return cachedKeys;
20
+ }
21
+
22
+ function getPublicKey() {
23
+ return ensureVapidKeys().publicKey;
24
+ }
25
+
26
+ function configureWebPush() {
27
+ const keys = ensureVapidKeys();
28
+ webPush.setVapidDetails(
29
+ 'mailto:noreply@claudecodeui.local',
30
+ keys.publicKey,
31
+ keys.privateKey
32
+ );
33
+ console.log('Web Push notifications configured');
34
+ }
35
+
36
+ export { ensureVapidKeys, getPublicKey, configureWebPush };
@@ -0,0 +1,248 @@
1
+ import { promises as fs } from 'fs';
2
+ import path from 'path';
3
+ import os from 'os';
4
+
5
+ // Map keys are namespaced by user id so the same session id can belong to
6
+ // different users without colliding.
7
+ function buildSessionKey(userId, sessionId) {
8
+ const safeUser = userId === null || userId === undefined ? 'unknown' : String(userId);
9
+ return `${safeUser}:${sessionId}`;
10
+ }
11
+
12
+ class SessionManager {
13
+ constructor() {
14
+ // Store sessions in memory keyed by `${userId}:${sessionId}`
15
+ this.sessions = new Map();
16
+ this.maxSessions = 100;
17
+ this.sessionsDir = path.join(os.homedir(), '.gemini', 'sessions');
18
+ this.ready = this.init();
19
+ }
20
+
21
+ async init() {
22
+ await this.initSessionsDir();
23
+ await this.loadSessions();
24
+ }
25
+
26
+ async initSessionsDir() {
27
+ try {
28
+ await fs.mkdir(this.sessionsDir, { recursive: true });
29
+ } catch (error) {
30
+ // console.error('Error creating sessions directory:', error);
31
+ }
32
+ }
33
+
34
+ // Create a new session
35
+ createSession(userId, sessionId, projectPath) {
36
+ const session = {
37
+ id: sessionId,
38
+ userId: userId ?? null,
39
+ projectPath: projectPath,
40
+ messages: [],
41
+ createdAt: new Date(),
42
+ lastActivity: new Date()
43
+ };
44
+
45
+ // Evict oldest session from memory if we exceed limit
46
+ if (this.sessions.size >= this.maxSessions) {
47
+ const oldestKey = this.sessions.keys().next().value;
48
+ if (oldestKey) this.sessions.delete(oldestKey);
49
+ }
50
+
51
+ const key = buildSessionKey(userId, sessionId);
52
+ this.sessions.set(key, session);
53
+ this.saveSession(userId, sessionId);
54
+
55
+ return session;
56
+ }
57
+
58
+ // Add a message to session
59
+ addMessage(userId, sessionId, role, content) {
60
+ const key = buildSessionKey(userId, sessionId);
61
+ let session = this.sessions.get(key);
62
+
63
+ if (!session) {
64
+ // Create session if it doesn't exist
65
+ session = this.createSession(userId, sessionId, '');
66
+ }
67
+
68
+ const message = {
69
+ role: role, // 'user' or 'assistant'
70
+ content: content,
71
+ timestamp: new Date()
72
+ };
73
+
74
+ session.messages.push(message);
75
+ session.lastActivity = new Date();
76
+
77
+ this.saveSession(userId, sessionId);
78
+
79
+ return session;
80
+ }
81
+
82
+ // Get session by ID
83
+ getSession(userId, sessionId) {
84
+ return this.sessions.get(buildSessionKey(userId, sessionId));
85
+ }
86
+
87
+ // Get all sessions for a project (scoped to user)
88
+ getProjectSessions(userId, projectPath) {
89
+ const sessions = [];
90
+ const userPrefix = `${userId === null || userId === undefined ? 'unknown' : String(userId)}:`;
91
+
92
+ for (const [key, session] of this.sessions) {
93
+ if (!key.startsWith(userPrefix)) {
94
+ continue;
95
+ }
96
+ if (session.projectPath === projectPath) {
97
+ sessions.push({
98
+ id: session.id,
99
+ summary: this.getSessionSummary(session),
100
+ messageCount: session.messages.length,
101
+ lastActivity: session.lastActivity
102
+ });
103
+ }
104
+ }
105
+
106
+ return sessions.sort((a, b) =>
107
+ new Date(b.lastActivity) - new Date(a.lastActivity)
108
+ );
109
+ }
110
+
111
+ // Get session summary
112
+ getSessionSummary(session) {
113
+ if (session.messages.length === 0) {
114
+ return 'New Session';
115
+ }
116
+
117
+ // Find first user message
118
+ const firstUserMessage = session.messages.find(m => m.role === 'user');
119
+ if (firstUserMessage) {
120
+ const content = firstUserMessage.content;
121
+ return content.length > 50 ? content.substring(0, 50) + '...' : content;
122
+ }
123
+
124
+ return 'New Session';
125
+ }
126
+
127
+ // Build conversation context for Gemini
128
+ buildConversationContext(userId, sessionId, maxMessages = 10) {
129
+ const session = this.sessions.get(buildSessionKey(userId, sessionId));
130
+
131
+ if (!session || session.messages.length === 0) {
132
+ return '';
133
+ }
134
+
135
+ // Get last N messages for context
136
+ const recentMessages = session.messages.slice(-maxMessages);
137
+
138
+ let context = 'Here is the conversation history:\n\n';
139
+
140
+ for (const msg of recentMessages) {
141
+ if (msg.role === 'user') {
142
+ context += `User: ${msg.content}\n`;
143
+ } else {
144
+ context += `Assistant: ${msg.content}\n`;
145
+ }
146
+ }
147
+
148
+ context += '\nBased on the conversation history above, please answer the following:\n';
149
+
150
+ return context;
151
+ }
152
+
153
+ // Prevent path traversal; persisted file name embeds the user id so each
154
+ // user keeps a separate on-disk record of the same session id.
155
+ _safeFilePath(userId, sessionId) {
156
+ const safeId = String(sessionId).replace(/[/\\]|\.\./g, '');
157
+ const safeUser = userId === null || userId === undefined
158
+ ? 'unknown'
159
+ : String(userId).replace(/[/\\]|\.\./g, '');
160
+ return path.join(this.sessionsDir, `${safeUser}-${safeId}.json`);
161
+ }
162
+
163
+ // Save session to disk
164
+ async saveSession(userId, sessionId) {
165
+ const session = this.sessions.get(buildSessionKey(userId, sessionId));
166
+ if (!session) return;
167
+
168
+ try {
169
+ const filePath = this._safeFilePath(userId, sessionId);
170
+ await fs.writeFile(filePath, JSON.stringify(session, null, 2));
171
+ } catch (error) {
172
+ // console.error('Error saving session:', error);
173
+ }
174
+ }
175
+
176
+ // Load sessions from disk
177
+ async loadSessions() {
178
+ try {
179
+ const files = await fs.readdir(this.sessionsDir);
180
+
181
+ for (const file of files) {
182
+ if (file.endsWith('.json')) {
183
+ try {
184
+ const filePath = path.join(this.sessionsDir, file);
185
+ const data = await fs.readFile(filePath, 'utf8');
186
+ const session = JSON.parse(data);
187
+
188
+ // Convert dates
189
+ session.createdAt = new Date(session.createdAt);
190
+ session.lastActivity = new Date(session.lastActivity);
191
+ session.messages.forEach(msg => {
192
+ msg.timestamp = new Date(msg.timestamp);
193
+ });
194
+
195
+ // Legacy files without `userId` get bucketed under "unknown" so
196
+ // pre-existing sessions remain reachable until they are rewritten.
197
+ const userId = session.userId ?? null;
198
+ const key = buildSessionKey(userId, session.id);
199
+ this.sessions.set(key, session);
200
+ } catch (error) {
201
+ // console.error(`Error loading session ${file}:`, error);
202
+ }
203
+ }
204
+ }
205
+
206
+ // Enforce eviction after loading to prevent massive memory usage
207
+ while (this.sessions.size > this.maxSessions) {
208
+ const oldestKey = this.sessions.keys().next().value;
209
+ if (oldestKey) this.sessions.delete(oldestKey);
210
+ }
211
+ } catch (error) {
212
+ // console.error('Error loading sessions:', error);
213
+ }
214
+ }
215
+
216
+ // Delete a session
217
+ async deleteSession(userId, sessionId) {
218
+ this.sessions.delete(buildSessionKey(userId, sessionId));
219
+
220
+ try {
221
+ const filePath = this._safeFilePath(userId, sessionId);
222
+ await fs.unlink(filePath);
223
+ } catch (error) {
224
+ // console.error('Error deleting session file:', error);
225
+ }
226
+ }
227
+
228
+ // Get session messages for display
229
+ getSessionMessages(userId, sessionId) {
230
+ const session = this.sessions.get(buildSessionKey(userId, sessionId));
231
+ if (!session) return [];
232
+
233
+ return session.messages.map(msg => ({
234
+ type: 'message',
235
+ message: {
236
+ role: msg.role,
237
+ content: msg.content
238
+ },
239
+ timestamp: msg.timestamp.toISOString()
240
+ }));
241
+ }
242
+ }
243
+
244
+ // Singleton instance
245
+ const sessionManager = new SessionManager();
246
+
247
+ export const ready = sessionManager.ready;
248
+ export default sessionManager;
@@ -0,0 +1,61 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+
4
+ import {
5
+ resolveClaudeCodeExecutablePath,
6
+ type ResolveClaudeCodeExecutablePathDependencies,
7
+ } from '@/shared/claude-cli-path.js';
8
+
9
+ test('resolveClaudeCodeExecutablePath resolves the npm Claude wrapper to its native exe on Windows', () => {
10
+ const wrapperDir = 'C:\\nvm4w\\nodejs';
11
+ const nativePath = `${wrapperDir}\\node_modules\\@anthropic-ai\\claude-code\\bin\\claude.exe`;
12
+ const execFileSync =
13
+ (() => `${wrapperDir}\\claude\r\n${wrapperDir}\\claude.cmd\r\n`) as unknown as ResolveClaudeCodeExecutablePathDependencies['execFileSync'];
14
+ const readFileSync = (() => '') as unknown as ResolveClaudeCodeExecutablePathDependencies['readFileSync'];
15
+
16
+ const resolved = resolveClaudeCodeExecutablePath('claude', {
17
+ platform: 'win32',
18
+ execFileSync,
19
+ existsSync: (candidate) => candidate === nativePath,
20
+ readFileSync,
21
+ });
22
+
23
+ assert.equal(resolved, nativePath);
24
+ });
25
+
26
+ test('resolveClaudeCodeExecutablePath keeps an explicit JavaScript launcher path unchanged', () => {
27
+ const scriptPath = 'C:\\tools\\claude.js';
28
+
29
+ const resolved = resolveClaudeCodeExecutablePath(scriptPath, {
30
+ platform: 'win32',
31
+ });
32
+
33
+ assert.equal(resolved, scriptPath);
34
+ });
35
+
36
+ test('resolveClaudeCodeExecutablePath can parse a wrapper file path containing letters r and n before claude.exe', () => {
37
+ const wrapperPath = 'C:\\tools\\claude';
38
+ const nativePath = 'C:\\tools\\custom\\bin\\node_modules\\@anthropic-ai\\claude-code\\bin\\claude.exe';
39
+ const readFileSync = (() => `exec "$basedir/custom/bin/node_modules/@anthropic-ai/claude-code/bin/claude.exe" "$@"`) as unknown as ResolveClaudeCodeExecutablePathDependencies['readFileSync'];
40
+
41
+ const resolved = resolveClaudeCodeExecutablePath(wrapperPath, {
42
+ platform: 'win32',
43
+ existsSync: (candidate) => candidate === nativePath,
44
+ readFileSync,
45
+ });
46
+
47
+ assert.equal(resolved, nativePath);
48
+ });
49
+
50
+ test('resolveClaudeCodeExecutablePath falls back to the configured command when PATH lookup fails', () => {
51
+ const execFileSync = (() => {
52
+ throw new Error('not found');
53
+ }) as unknown as ResolveClaudeCodeExecutablePathDependencies['execFileSync'];
54
+
55
+ const resolved = resolveClaudeCodeExecutablePath('claude', {
56
+ platform: 'win32',
57
+ execFileSync,
58
+ });
59
+
60
+ assert.equal(resolved, 'claude');
61
+ });