@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,419 @@
1
+ import { APP_CONFIG_TABLE_SCHEMA_SQL, LAST_SCANNED_AT_SQL, PROJECTS_TABLE_SCHEMA_SQL, PUSH_SUBSCRIPTIONS_TABLE_SCHEMA_SQL, SESSIONS_TABLE_SCHEMA_SQL, USER_NOTIFICATION_PREFERENCES_TABLE_SCHEMA_SQL, VAPID_KEYS_TABLE_SCHEMA_SQL, } from '../../modules/database/schema.js';
2
+ const SQLITE_UUID_SQL = `
3
+ lower(hex(randomblob(4))) || '-' ||
4
+ lower(hex(randomblob(2))) || '-' ||
5
+ lower(hex(randomblob(2))) || '-' ||
6
+ lower(hex(randomblob(2))) || '-' ||
7
+ lower(hex(randomblob(6)))
8
+ `;
9
+ const addColumnToTableIfNotExists = (db, tableName, columnNames, columnName, columnType) => {
10
+ if (!columnNames.includes(columnName)) {
11
+ console.log(`Running migration: Adding ${columnName} column to ${tableName} table`);
12
+ db.exec(`ALTER TABLE ${tableName} ADD COLUMN ${columnName} ${columnType}`);
13
+ }
14
+ };
15
+ const tableExists = (db, tableName) => Boolean(db
16
+ .prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?")
17
+ .get(tableName));
18
+ const getTableInfo = (db, tableName) => db.prepare(`PRAGMA table_info(${tableName})`).all();
19
+ const migrateLegacySessionNames = (db) => {
20
+ const hasLegacySessionNamesTable = tableExists(db, 'session_names');
21
+ const hasSessionsTable = tableExists(db, 'sessions');
22
+ if (!hasLegacySessionNamesTable) {
23
+ return;
24
+ }
25
+ if (hasSessionsTable) {
26
+ console.log('Running migration: Merging session_names into sessions');
27
+ db.exec(`
28
+ INSERT INTO sessions (session_id, user_id, provider, custom_name, created_at, updated_at)
29
+ SELECT
30
+ session_id,
31
+ COALESCE(
32
+ (SELECT id FROM users WHERE is_active = 1 ORDER BY id LIMIT 1),
33
+ 1
34
+ ),
35
+ COALESCE(provider, 'claude'),
36
+ custom_name,
37
+ COALESCE(created_at, CURRENT_TIMESTAMP),
38
+ COALESCE(updated_at, CURRENT_TIMESTAMP)
39
+ FROM session_names
40
+ WHERE true
41
+ ON CONFLICT(user_id, session_id) DO UPDATE SET
42
+ provider = excluded.provider,
43
+ custom_name = COALESCE(excluded.custom_name, sessions.custom_name),
44
+ created_at = COALESCE(sessions.created_at, excluded.created_at),
45
+ updated_at = COALESCE(excluded.updated_at, sessions.updated_at)
46
+ `);
47
+ db.exec('DROP TABLE session_names');
48
+ return;
49
+ }
50
+ console.log('Running migration: Renaming session_names table to sessions');
51
+ db.exec('ALTER TABLE session_names RENAME TO sessions');
52
+ };
53
+ const migrateLegacyWorkspaceTableIntoProjects = (db) => {
54
+ db.exec(PROJECTS_TABLE_SCHEMA_SQL);
55
+ if (!tableExists(db, 'workspace_original_paths')) {
56
+ return;
57
+ }
58
+ console.log('Running migration: Migrating workspace_original_paths data into projects');
59
+ db.exec(`
60
+ INSERT INTO projects (project_id, user_id, project_path, custom_project_name, isStarred, isArchived)
61
+ SELECT
62
+ CASE
63
+ WHEN workspace_id IS NULL OR trim(workspace_id) = ''
64
+ THEN ${SQLITE_UUID_SQL}
65
+ ELSE workspace_id
66
+ END,
67
+ COALESCE(
68
+ (SELECT id FROM users WHERE is_active = 1 ORDER BY id LIMIT 1),
69
+ 1
70
+ ),
71
+ workspace_path,
72
+ custom_workspace_name,
73
+ COALESCE(isStarred, 0),
74
+ 0
75
+ FROM workspace_original_paths
76
+ WHERE workspace_path IS NOT NULL AND trim(workspace_path) <> ''
77
+ ON CONFLICT(user_id, project_path) DO UPDATE SET
78
+ custom_project_name = COALESCE(projects.custom_project_name, excluded.custom_project_name),
79
+ isStarred = COALESCE(projects.isStarred, excluded.isStarred)
80
+ `);
81
+ };
82
+ const rebuildProjectsTableWithPrimaryKeySchema = (db) => {
83
+ const hasProjectsTable = tableExists(db, 'projects');
84
+ if (!hasProjectsTable) {
85
+ db.exec(PROJECTS_TABLE_SCHEMA_SQL);
86
+ return;
87
+ }
88
+ const projectsTableInfo = getTableInfo(db, 'projects');
89
+ const columnNames = projectsTableInfo.map((column) => column.name);
90
+ const hasProjectIdPrimaryKey = projectsTableInfo.some((column) => column.name === 'project_id' && column.pk === 1);
91
+ const shouldRebuild = !hasProjectIdPrimaryKey || !columnNames.includes('user_id');
92
+ if (!shouldRebuild) {
93
+ addColumnToTableIfNotExists(db, 'projects', columnNames, 'custom_project_name', 'TEXT DEFAULT NULL');
94
+ addColumnToTableIfNotExists(db, 'projects', columnNames, 'isStarred', 'BOOLEAN DEFAULT 0');
95
+ addColumnToTableIfNotExists(db, 'projects', columnNames, 'isArchived', 'BOOLEAN DEFAULT 0');
96
+ db.exec(`
97
+ UPDATE projects
98
+ SET project_id = ${SQLITE_UUID_SQL}
99
+ WHERE project_id IS NULL OR trim(project_id) = ''
100
+ `);
101
+ return;
102
+ }
103
+ console.log('Running migration: Rebuilding projects table to enforce user_id + project_id primary key schema');
104
+ const projectPathExpression = columnNames.includes('project_path')
105
+ ? 'project_path'
106
+ : columnNames.includes('workspace_path')
107
+ ? 'workspace_path'
108
+ : 'NULL';
109
+ const userIdExpression = columnNames.includes('user_id')
110
+ ? 'user_id'
111
+ : 'COALESCE((SELECT id FROM users WHERE is_active = 1 ORDER BY id LIMIT 1), 1)';
112
+ const customProjectNameExpression = columnNames.includes('custom_project_name')
113
+ ? 'custom_project_name'
114
+ : columnNames.includes('custom_workspace_name')
115
+ ? 'custom_workspace_name'
116
+ : 'NULL';
117
+ const isStarredExpression = columnNames.includes('isStarred') ? 'COALESCE(isStarred, 0)' : '0';
118
+ const isArchivedExpression = columnNames.includes('isArchived') ? 'COALESCE(isArchived, 0)' : '0';
119
+ const projectIdExpression = columnNames.includes('project_id')
120
+ ? `CASE
121
+ WHEN project_id IS NULL OR trim(project_id) = ''
122
+ THEN ${SQLITE_UUID_SQL}
123
+ ELSE project_id
124
+ END`
125
+ : SQLITE_UUID_SQL;
126
+ db.exec('PRAGMA foreign_keys = OFF');
127
+ try {
128
+ db.exec('BEGIN TRANSACTION');
129
+ db.exec('DROP TABLE IF EXISTS projects__new');
130
+ db.exec(`
131
+ CREATE TABLE projects__new (
132
+ project_id TEXT PRIMARY KEY NOT NULL,
133
+ user_id INTEGER NOT NULL,
134
+ project_path TEXT NOT NULL,
135
+ custom_project_name TEXT DEFAULT NULL,
136
+ isStarred BOOLEAN DEFAULT 0,
137
+ isArchived BOOLEAN DEFAULT 0,
138
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
139
+ UNIQUE(user_id, project_path)
140
+ )
141
+ `);
142
+ db.exec(`
143
+ WITH source_rows AS (
144
+ SELECT
145
+ ${userIdExpression} AS user_id,
146
+ ${projectPathExpression} AS project_path,
147
+ ${customProjectNameExpression} AS custom_project_name,
148
+ ${isStarredExpression} AS isStarred,
149
+ ${isArchivedExpression} AS isArchived,
150
+ ${projectIdExpression} AS candidate_project_id,
151
+ rowid AS source_rowid
152
+ FROM projects
153
+ WHERE ${projectPathExpression} IS NOT NULL AND trim(${projectPathExpression}) <> ''
154
+ ),
155
+ deduped_paths AS (
156
+ SELECT
157
+ user_id,
158
+ project_path,
159
+ custom_project_name,
160
+ isStarred,
161
+ isArchived,
162
+ candidate_project_id,
163
+ source_rowid,
164
+ ROW_NUMBER() OVER (PARTITION BY user_id, project_path ORDER BY source_rowid) AS project_path_rank
165
+ FROM source_rows
166
+ ),
167
+ prepared_rows AS (
168
+ SELECT
169
+ CASE
170
+ WHEN ROW_NUMBER() OVER (PARTITION BY candidate_project_id ORDER BY source_rowid) = 1
171
+ THEN candidate_project_id
172
+ ELSE ${SQLITE_UUID_SQL}
173
+ END AS project_id,
174
+ user_id,
175
+ project_path,
176
+ custom_project_name,
177
+ isStarred,
178
+ isArchived
179
+ FROM deduped_paths
180
+ WHERE project_path_rank = 1
181
+ )
182
+ INSERT INTO projects__new (
183
+ project_id,
184
+ user_id,
185
+ project_path,
186
+ custom_project_name,
187
+ isStarred,
188
+ isArchived
189
+ )
190
+ SELECT
191
+ project_id,
192
+ user_id,
193
+ project_path,
194
+ custom_project_name,
195
+ isStarred,
196
+ isArchived
197
+ FROM prepared_rows
198
+ `);
199
+ db.exec('DROP TABLE projects');
200
+ db.exec('ALTER TABLE projects__new RENAME TO projects');
201
+ db.exec('COMMIT');
202
+ }
203
+ catch (migrationError) {
204
+ db.exec('ROLLBACK');
205
+ throw migrationError;
206
+ }
207
+ finally {
208
+ db.exec('PRAGMA foreign_keys = ON');
209
+ }
210
+ };
211
+ const rebuildSessionsTableWithProjectSchema = (db) => {
212
+ const hasSessions = tableExists(db, 'sessions');
213
+ if (!hasSessions) {
214
+ db.exec(SESSIONS_TABLE_SCHEMA_SQL);
215
+ return;
216
+ }
217
+ const sessionsTableInfo = getTableInfo(db, 'sessions');
218
+ const columnNames = sessionsTableInfo.map((column) => column.name);
219
+ const primaryKeyColumns = sessionsTableInfo
220
+ .filter((column) => column.pk > 0)
221
+ .sort((a, b) => a.pk - b.pk)
222
+ .map((column) => column.name);
223
+ const shouldRebuild = !columnNames.includes('project_path') ||
224
+ !columnNames.includes('user_id') ||
225
+ primaryKeyColumns.length !== 2 ||
226
+ primaryKeyColumns[0] !== 'user_id' ||
227
+ primaryKeyColumns[1] !== 'session_id' ||
228
+ !columnNames.includes('provider');
229
+ if (!shouldRebuild) {
230
+ addColumnToTableIfNotExists(db, 'sessions', columnNames, 'jsonl_path', 'TEXT');
231
+ addColumnToTableIfNotExists(db, 'sessions', columnNames, 'isArchived', 'BOOLEAN DEFAULT 0');
232
+ addColumnToTableIfNotExists(db, 'sessions', columnNames, 'created_at', 'DATETIME');
233
+ addColumnToTableIfNotExists(db, 'sessions', columnNames, 'updated_at', 'DATETIME');
234
+ db.exec('UPDATE sessions SET isArchived = COALESCE(isArchived, 0)');
235
+ db.exec('UPDATE sessions SET created_at = COALESCE(created_at, CURRENT_TIMESTAMP)');
236
+ db.exec('UPDATE sessions SET updated_at = COALESCE(updated_at, CURRENT_TIMESTAMP)');
237
+ return;
238
+ }
239
+ console.log('Running migration: Rebuilding sessions table to project-based schema');
240
+ const projectPathExpression = columnNames.includes('project_path')
241
+ ? 'project_path'
242
+ : columnNames.includes('workspace_path')
243
+ ? 'workspace_path'
244
+ : 'NULL';
245
+ const providerExpression = columnNames.includes('provider')
246
+ ? "COALESCE(provider, 'claude')"
247
+ : "'claude'";
248
+ const userIdExpression = columnNames.includes('user_id')
249
+ ? 'user_id'
250
+ : 'COALESCE((SELECT id FROM users WHERE is_active = 1 ORDER BY id LIMIT 1), 1)';
251
+ const customNameExpression = columnNames.includes('custom_name')
252
+ ? 'custom_name'
253
+ : 'NULL';
254
+ const jsonlPathExpression = columnNames.includes('jsonl_path')
255
+ ? 'jsonl_path'
256
+ : 'NULL';
257
+ const isArchivedExpression = columnNames.includes('isArchived')
258
+ ? 'COALESCE(isArchived, 0)'
259
+ : '0';
260
+ const createdAtExpression = columnNames.includes('created_at')
261
+ ? 'COALESCE(created_at, CURRENT_TIMESTAMP)'
262
+ : 'CURRENT_TIMESTAMP';
263
+ const updatedAtExpression = columnNames.includes('updated_at')
264
+ ? 'COALESCE(updated_at, CURRENT_TIMESTAMP)'
265
+ : 'CURRENT_TIMESTAMP';
266
+ db.exec('PRAGMA foreign_keys = OFF');
267
+ try {
268
+ db.exec('BEGIN TRANSACTION');
269
+ db.exec('DROP TABLE IF EXISTS sessions__new');
270
+ db.exec(`
271
+ CREATE TABLE sessions__new (
272
+ session_id TEXT NOT NULL,
273
+ user_id INTEGER NOT NULL,
274
+ provider TEXT NOT NULL DEFAULT 'claude',
275
+ custom_name TEXT,
276
+ project_path TEXT,
277
+ jsonl_path TEXT,
278
+ isArchived BOOLEAN DEFAULT 0,
279
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
280
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
281
+ PRIMARY KEY (user_id, session_id),
282
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
283
+ FOREIGN KEY (user_id, project_path) REFERENCES projects(user_id, project_path)
284
+ ON DELETE CASCADE
285
+ ON UPDATE CASCADE
286
+ )
287
+ `);
288
+ db.exec(`
289
+ WITH source_rows AS (
290
+ SELECT
291
+ session_id,
292
+ ${userIdExpression} AS user_id,
293
+ ${providerExpression} AS provider,
294
+ ${customNameExpression} AS custom_name,
295
+ ${projectPathExpression} AS project_path,
296
+ ${jsonlPathExpression} AS jsonl_path,
297
+ ${isArchivedExpression} AS isArchived,
298
+ ${createdAtExpression} AS created_at,
299
+ ${updatedAtExpression} AS updated_at,
300
+ rowid AS source_rowid
301
+ FROM sessions
302
+ WHERE session_id IS NOT NULL AND trim(session_id) <> ''
303
+ ),
304
+ ranked_rows AS (
305
+ SELECT
306
+ session_id,
307
+ user_id,
308
+ provider,
309
+ custom_name,
310
+ project_path,
311
+ jsonl_path,
312
+ isArchived,
313
+ created_at,
314
+ updated_at,
315
+ ROW_NUMBER() OVER (
316
+ PARTITION BY user_id, session_id
317
+ ORDER BY datetime(COALESCE(updated_at, created_at)) DESC, source_rowid DESC
318
+ ) AS session_rank
319
+ FROM source_rows
320
+ )
321
+ INSERT INTO sessions__new (
322
+ session_id,
323
+ user_id,
324
+ provider,
325
+ custom_name,
326
+ project_path,
327
+ jsonl_path,
328
+ isArchived,
329
+ created_at,
330
+ updated_at
331
+ )
332
+ SELECT
333
+ session_id,
334
+ user_id,
335
+ provider,
336
+ custom_name,
337
+ project_path,
338
+ jsonl_path,
339
+ isArchived,
340
+ created_at,
341
+ updated_at
342
+ FROM ranked_rows
343
+ WHERE session_rank = 1
344
+ `);
345
+ db.exec('DROP TABLE sessions');
346
+ db.exec('ALTER TABLE sessions__new RENAME TO sessions');
347
+ db.exec('COMMIT');
348
+ }
349
+ catch (migrationError) {
350
+ db.exec('ROLLBACK');
351
+ throw migrationError;
352
+ }
353
+ finally {
354
+ db.exec('PRAGMA foreign_keys = ON');
355
+ }
356
+ };
357
+ const ensureProjectsForSessionPaths = (db) => {
358
+ if (!tableExists(db, 'sessions')) {
359
+ return;
360
+ }
361
+ db.exec(`
362
+ INSERT INTO projects (project_id, user_id, project_path, custom_project_name, isStarred, isArchived)
363
+ SELECT
364
+ ${SQLITE_UUID_SQL},
365
+ user_id,
366
+ project_path,
367
+ NULL,
368
+ 0,
369
+ 0
370
+ FROM sessions
371
+ WHERE project_path IS NOT NULL AND trim(project_path) <> ''
372
+ ON CONFLICT(user_id, project_path) DO NOTHING
373
+ `);
374
+ };
375
+ export const runMigrations = (db) => {
376
+ try {
377
+ const usersTableInfo = db.prepare('PRAGMA table_info(users)').all();
378
+ const userColumnNames = usersTableInfo.map((column) => column.name);
379
+ addColumnToTableIfNotExists(db, 'users', userColumnNames, 'git_name', 'TEXT');
380
+ addColumnToTableIfNotExists(db, 'users', userColumnNames, 'git_email', 'TEXT');
381
+ addColumnToTableIfNotExists(db, 'users', userColumnNames, 'has_completed_onboarding', 'BOOLEAN DEFAULT 0');
382
+ addColumnToTableIfNotExists(db, 'users', userColumnNames, 'is_admin', 'BOOLEAN DEFAULT 0');
383
+ db.exec("UPDATE users SET is_admin = 1 WHERE id = 1 AND is_active = 1");
384
+ db.exec(APP_CONFIG_TABLE_SCHEMA_SQL);
385
+ db.exec(USER_NOTIFICATION_PREFERENCES_TABLE_SCHEMA_SQL);
386
+ db.exec(VAPID_KEYS_TABLE_SCHEMA_SQL);
387
+ db.exec(PUSH_SUBSCRIPTIONS_TABLE_SCHEMA_SQL);
388
+ db.exec('CREATE INDEX IF NOT EXISTS idx_push_subscriptions_user_id ON push_subscriptions(user_id)');
389
+ db.exec(PROJECTS_TABLE_SCHEMA_SQL);
390
+ rebuildProjectsTableWithPrimaryKeySchema(db);
391
+ migrateLegacyWorkspaceTableIntoProjects(db);
392
+ rebuildSessionsTableWithProjectSchema(db);
393
+ migrateLegacySessionNames(db);
394
+ ensureProjectsForSessionPaths(db);
395
+ db.exec('CREATE INDEX IF NOT EXISTS idx_session_ids_lookup ON sessions(user_id, session_id)');
396
+ db.exec('CREATE INDEX IF NOT EXISTS idx_sessions_user_id ON sessions(user_id)');
397
+ db.exec('CREATE INDEX IF NOT EXISTS idx_sessions_project_path ON sessions(user_id, project_path)');
398
+ db.exec('CREATE INDEX IF NOT EXISTS idx_sessions_is_archived ON sessions(isArchived)');
399
+ db.exec('CREATE INDEX IF NOT EXISTS idx_projects_user_id ON projects(user_id)');
400
+ db.exec('CREATE INDEX IF NOT EXISTS idx_projects_user_path ON projects(user_id, project_path)');
401
+ db.exec('CREATE INDEX IF NOT EXISTS idx_projects_is_starred ON projects(isStarred)');
402
+ db.exec('CREATE INDEX IF NOT EXISTS idx_projects_is_archived ON projects(isArchived)');
403
+ db.exec('DROP INDEX IF EXISTS idx_session_names_lookup');
404
+ db.exec('DROP INDEX IF EXISTS idx_sessions_workspace_path');
405
+ db.exec('DROP INDEX IF EXISTS idx_workspace_original_paths_is_starred');
406
+ db.exec('DROP INDEX IF EXISTS idx_workspace_original_paths_workspace_id');
407
+ if (tableExists(db, 'workspace_original_paths')) {
408
+ console.log('Running migration: Dropping legacy workspace_original_paths table');
409
+ db.exec('DROP TABLE workspace_original_paths');
410
+ }
411
+ db.exec(LAST_SCANNED_AT_SQL);
412
+ console.log('Database migrations completed successfully');
413
+ }
414
+ catch (error) {
415
+ console.error('Error running migrations:', error.message);
416
+ throw error;
417
+ }
418
+ };
419
+ //# sourceMappingURL=migrations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../../../server/modules/database/migrations.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * API keys repository.
3
+ *
4
+ * Manages API keys used for external/programmatic access to the backend.
5
+ * Keys are prefixed with `ck_` and tied to a user via foreign key.
6
+ */
7
+ import crypto from 'crypto';
8
+ import { getConnection } from '../../../modules/database/connection.js';
9
+ // ---------------------------------------------------------------------------
10
+ // Helpers
11
+ // ---------------------------------------------------------------------------
12
+ /** Generates a cryptographically random API key with the `ck_` prefix. */
13
+ function generateApiKey() {
14
+ return 'ck_' + crypto.randomBytes(32).toString('hex');
15
+ }
16
+ // ---------------------------------------------------------------------------
17
+ // Queries
18
+ // ---------------------------------------------------------------------------
19
+ export const apiKeysDb = {
20
+ generateApiKey,
21
+ /** Creates a new API key for the given user and returns it for one-time display. */
22
+ createApiKey(userId, keyName) {
23
+ const db = getConnection();
24
+ const apiKey = generateApiKey();
25
+ const result = db
26
+ .prepare('INSERT INTO api_keys (user_id, key_name, api_key) VALUES (?, ?, ?)')
27
+ .run(userId, keyName, apiKey);
28
+ return { id: result.lastInsertRowid, keyName, apiKey };
29
+ },
30
+ /** Lists all API keys for a user, most recent first. */
31
+ getApiKeys(userId) {
32
+ const db = getConnection();
33
+ return db
34
+ .prepare('SELECT id, key_name, api_key, created_at, last_used, is_active FROM api_keys WHERE user_id = ? ORDER BY created_at DESC')
35
+ .all(userId);
36
+ },
37
+ /**
38
+ * Validates an API key and resolves the owning user.
39
+ * If the key is valid, its `last_used` timestamp is updated as a side effect.
40
+ * Returns undefined when the key is invalid or the user is inactive.
41
+ */
42
+ validateApiKey(apiKey) {
43
+ const db = getConnection();
44
+ const row = db
45
+ .prepare(`SELECT u.id, u.username, ak.id as api_key_id
46
+ FROM api_keys ak
47
+ JOIN users u ON ak.user_id = u.id
48
+ WHERE ak.api_key = ? AND ak.is_active = 1 AND u.is_active = 1`)
49
+ .get(apiKey);
50
+ if (row) {
51
+ db.prepare('UPDATE api_keys SET last_used = CURRENT_TIMESTAMP WHERE id = ?').run(row.api_key_id);
52
+ }
53
+ return row;
54
+ },
55
+ /** Permanently removes an API key. Returns true if a row was deleted. */
56
+ deleteApiKey(userId, apiKeyId) {
57
+ const db = getConnection();
58
+ const result = db
59
+ .prepare('DELETE FROM api_keys WHERE id = ? AND user_id = ?')
60
+ .run(apiKeyId, userId);
61
+ return result.changes > 0;
62
+ },
63
+ /** Enables or disables an API key without deleting it. */
64
+ toggleApiKey(userId, apiKeyId, isActive) {
65
+ const db = getConnection();
66
+ const result = db
67
+ .prepare('UPDATE api_keys SET is_active = ? WHERE id = ? AND user_id = ?')
68
+ .run(isActive ? 1 : 0, apiKeyId, userId);
69
+ return result.changes > 0;
70
+ },
71
+ };
72
+ //# sourceMappingURL=api-keys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-keys.js","sourceRoot":"","sources":["../../../../../server/modules/database/repositories/api-keys.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAuBjE,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,0EAA0E;AAC1E,SAAS,cAAc;IACrB,OAAO,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACxD,CAAC;AAED,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,cAAc;IAEd,oFAAoF;IACpF,YAAY,CAAC,MAAc,EAAE,OAAe;QAC1C,MAAM,EAAE,GAAG,aAAa,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,cAAc,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,EAAE;aACd,OAAO,CACN,oEAAoE,CACrE;aACA,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QAChC,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,eAAe,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IACzD,CAAC;IAED,wDAAwD;IACxD,UAAU,CAAC,MAAc;QACvB,MAAM,EAAE,GAAG,aAAa,EAAE,CAAC;QAC3B,OAAO,EAAE;aACN,OAAO,CACN,yHAAyH,CAC1H;aACA,GAAG,CAAC,MAAM,CAAgB,CAAC;IAChC,CAAC;IAED;;;;OAIG;IACH,cAAc,CAAC,MAAc;QAC3B,MAAM,EAAE,GAAG,aAAa,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG,EAAE;aACX,OAAO,CACN;;;uEAG+D,CAChE;aACA,GAAG,CAAC,MAAM,CAAoC,CAAC;QAElD,IAAI,GAAG,EAAE,CAAC;YACR,EAAE,CAAC,OAAO,CACR,gEAAgE,CACjE,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACxB,CAAC;QAED,OAAO,GAAG,CAAC;IACb,CAAC;IAED,yEAAyE;IACzE,YAAY,CAAC,MAAc,EAAE,QAAgB;QAC3C,MAAM,EAAE,GAAG,aAAa,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,EAAE;aACd,OAAO,CAAC,mDAAmD,CAAC;aAC5D,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QACzB,OAAO,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC;IAC5B,CAAC;IAED,0DAA0D;IAC1D,YAAY,CACV,MAAc,EACd,QAAgB,EAChB,QAAiB;QAEjB,MAAM,EAAE,GAAG,aAAa,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,EAAE;aACd,OAAO,CACN,gEAAgE,CACjE;aACA,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC3C,OAAO,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC;IAC5B,CAAC;CACF,CAAC"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * App config repository.
3
+ *
4
+ * Key-value store for application-level configuration that persists
5
+ * across restarts (JWT secret, feature flags, etc.). Values are always
6
+ * stored as strings; callers handle parsing.
7
+ */
8
+ import crypto from 'crypto';
9
+ import { getConnection } from '../../../modules/database/connection.js';
10
+ // ---------------------------------------------------------------------------
11
+ // Queries
12
+ // ---------------------------------------------------------------------------
13
+ export const appConfigDb = {
14
+ /** Returns the stored value for a config key, or null if missing. */
15
+ get(key) {
16
+ try {
17
+ const db = getConnection();
18
+ const row = db
19
+ .prepare('SELECT value FROM app_config WHERE key = ?')
20
+ .get(key);
21
+ return row?.value ?? null;
22
+ }
23
+ catch {
24
+ // Swallow errors so early-startup reads (e.g. JWT secret) do not crash.
25
+ return null;
26
+ }
27
+ },
28
+ /** Inserts or updates a config key (upsert). */
29
+ set(key, value) {
30
+ const db = getConnection();
31
+ db.prepare('INSERT INTO app_config (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value').run(key, value);
32
+ },
33
+ /**
34
+ * Returns the JWT signing secret, generating and persisting one
35
+ * if it does not already exist. This ensures the secret survives
36
+ * server restarts while being created automatically on first boot.
37
+ */
38
+ getOrCreateJwtSecret() {
39
+ let secret = appConfigDb.get('jwt_secret');
40
+ if (!secret) {
41
+ secret = crypto.randomBytes(64).toString('hex');
42
+ appConfigDb.set('jwt_secret', secret);
43
+ }
44
+ return secret;
45
+ },
46
+ };
47
+ //# sourceMappingURL=app-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-config.js","sourceRoot":"","sources":["../../../../../server/modules/database/repositories/app-config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAEjE,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,qEAAqE;IACrE,GAAG,CAAC,GAAW;QACb,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,aAAa,EAAE,CAAC;YAC3B,MAAM,GAAG,GAAG,EAAE;iBACX,OAAO,CAAC,4CAA4C,CAAC;iBACrD,GAAG,CAAC,GAAG,CAAkC,CAAC;YAC7C,OAAO,GAAG,EAAE,KAAK,IAAI,IAAI,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,wEAAwE;YACxE,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,gDAAgD;IAChD,GAAG,CAAC,GAAW,EAAE,KAAa;QAC5B,MAAM,EAAE,GAAG,aAAa,EAAE,CAAC;QAC3B,EAAE,CAAC,OAAO,CACR,yGAAyG,CAC1G,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACpB,CAAC;IAED;;;;OAIG;IACH,oBAAoB;QAClB,IAAI,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAC3C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YAChD,WAAW,CAAC,GAAG,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QACxC,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAC"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * User credentials repository.
3
+ *
4
+ * Manages external service tokens (GitHub, GitLab, Bitbucket, etc.)
5
+ * stored per-user. Each credential has a type discriminator so multiple
6
+ * credential kinds can coexist in the same table.
7
+ */
8
+ import { getConnection } from '../../../modules/database/connection.js';
9
+ // ---------------------------------------------------------------------------
10
+ // Queries
11
+ // ---------------------------------------------------------------------------
12
+ export const credentialsDb = {
13
+ /** Stores a new credential and returns a safe (no raw value) result. */
14
+ createCredential(userId, credentialName, credentialType, credentialValue, description = null) {
15
+ const db = getConnection();
16
+ const result = db
17
+ .prepare('INSERT INTO user_credentials (user_id, credential_name, credential_type, credential_value, description) VALUES (?, ?, ?, ?, ?)')
18
+ .run(userId, credentialName, credentialType, credentialValue, description);
19
+ return {
20
+ id: result.lastInsertRowid,
21
+ credentialName,
22
+ credentialType,
23
+ };
24
+ },
25
+ /**
26
+ * Lists credentials for a user (excluding raw values).
27
+ * Optionally filters by credential type (e.g. 'github_token').
28
+ */
29
+ getCredentials(userId, credentialType = null) {
30
+ const db = getConnection();
31
+ if (credentialType) {
32
+ return db
33
+ .prepare('SELECT id, credential_name, credential_type, description, created_at, is_active FROM user_credentials WHERE user_id = ? AND credential_type = ? ORDER BY created_at DESC')
34
+ .all(userId, credentialType);
35
+ }
36
+ return db
37
+ .prepare('SELECT id, credential_name, credential_type, description, created_at, is_active FROM user_credentials WHERE user_id = ? ORDER BY created_at DESC')
38
+ .all(userId);
39
+ },
40
+ /**
41
+ * Returns the raw credential value for the most recent active
42
+ * credential of the given type, or null if none exists.
43
+ */
44
+ getActiveCredential(userId, credentialType) {
45
+ const db = getConnection();
46
+ const row = db
47
+ .prepare('SELECT credential_value FROM user_credentials WHERE user_id = ? AND credential_type = ? AND is_active = 1 ORDER BY created_at DESC LIMIT 1')
48
+ .get(userId, credentialType);
49
+ return row?.credential_value ?? null;
50
+ },
51
+ /** Permanently removes a credential. Returns true if a row was deleted. */
52
+ deleteCredential(userId, credentialId) {
53
+ const db = getConnection();
54
+ const result = db
55
+ .prepare('DELETE FROM user_credentials WHERE id = ? AND user_id = ?')
56
+ .run(credentialId, userId);
57
+ return result.changes > 0;
58
+ },
59
+ /** Enables or disables a credential without deleting it. */
60
+ toggleCredential(userId, credentialId, isActive) {
61
+ const db = getConnection();
62
+ const result = db
63
+ .prepare('UPDATE user_credentials SET is_active = ? WHERE id = ? AND user_id = ?')
64
+ .run(isActive ? 1 : 0, credentialId, userId);
65
+ return result.changes > 0;
66
+ },
67
+ };
68
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../../../../server/modules/database/repositories/credentials.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAMjE,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,wEAAwE;IACxE,gBAAgB,CACd,MAAc,EACd,cAAsB,EACtB,cAAsB,EACtB,eAAuB,EACvB,cAA6B,IAAI;QAEjC,MAAM,EAAE,GAAG,aAAa,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,EAAE;aACd,OAAO,CACN,gIAAgI,CACjI;aACA,GAAG,CAAC,MAAM,EAAE,cAAc,EAAE,cAAc,EAAE,eAAe,EAAE,WAAW,CAAC,CAAC;QAC7E,OAAO;YACL,EAAE,EAAE,MAAM,CAAC,eAAe;YAC1B,cAAc;YACd,cAAc;SACf,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,cAAc,CACZ,MAAc,EACd,iBAAgC,IAAI;QAEpC,MAAM,EAAE,GAAG,aAAa,EAAE,CAAC;QAE3B,IAAI,cAAc,EAAE,CAAC;YACnB,OAAO,EAAE;iBACN,OAAO,CACN,0KAA0K,CAC3K;iBACA,GAAG,CAAC,MAAM,EAAE,cAAc,CAA0B,CAAC;QAC1D,CAAC;QAED,OAAO,EAAE;aACN,OAAO,CACN,kJAAkJ,CACnJ;aACA,GAAG,CAAC,MAAM,CAA0B,CAAC;IAC1C,CAAC;IAED;;;OAGG;IACH,mBAAmB,CACjB,MAAc,EACd,cAAsB;QAEtB,MAAM,EAAE,GAAG,aAAa,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG,EAAE;aACX,OAAO,CACN,4IAA4I,CAC7I;aACA,GAAG,CAAC,MAAM,EAAE,cAAc,CAA6C,CAAC;QAC3E,OAAO,GAAG,EAAE,gBAAgB,IAAI,IAAI,CAAC;IACvC,CAAC;IAED,2EAA2E;IAC3E,gBAAgB,CAAC,MAAc,EAAE,YAAoB;QACnD,MAAM,EAAE,GAAG,aAAa,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,EAAE;aACd,OAAO,CAAC,2DAA2D,CAAC;aACpE,GAAG,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QAC7B,OAAO,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC;IAC5B,CAAC;IAED,4DAA4D;IAC5D,gBAAgB,CACd,MAAc,EACd,YAAoB,EACpB,QAAiB;QAEjB,MAAM,EAAE,GAAG,aAAa,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,EAAE;aACd,OAAO,CACN,wEAAwE,CACzE;aACA,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC;QAC/C,OAAO,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC;IAC5B,CAAC;CACF,CAAC"}