@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,864 @@
1
+ /**
2
+ * Claude SDK Integration
3
+ *
4
+ * This module provides SDK-based integration with Claude using the @anthropic-ai/claude-agent-sdk.
5
+ * It mirrors the interface of claude-cli.js but uses the SDK internally for better performance
6
+ * and maintainability.
7
+ *
8
+ * Key features:
9
+ * - Direct SDK integration without child processes
10
+ * - Session management with abort capability
11
+ * - Options mapping between CLI and SDK formats
12
+ * - WebSocket message streaming
13
+ */
14
+
15
+ import { query } from '@anthropic-ai/claude-agent-sdk';
16
+ import crypto from 'crypto';
17
+ import { promises as fs } from 'fs';
18
+ import path from 'path';
19
+ import os from 'os';
20
+ import { CLAUDE_MODELS } from '../shared/modelConstants.js';
21
+ import { resolveClaudeCodeExecutablePath } from './shared/claude-cli-path.js';
22
+ import {
23
+ createNotificationEvent,
24
+ notifyRunFailed,
25
+ notifyRunStopped,
26
+ notifyUserIfEnabled
27
+ } from './services/notification-orchestrator.js';
28
+ import { sessionsService } from './modules/providers/services/sessions.service.js';
29
+ import { providerAuthService } from './modules/providers/services/provider-auth.service.js';
30
+ import { createNormalizedMessage } from './shared/utils.js';
31
+
32
+ // Keys are namespaced as `${userId}:${sessionId}` to prevent cross-user session access.
33
+ const activeSessions = new Map();
34
+ const pendingToolApprovals = new Map();
35
+
36
+ function buildSessionKey(userId, sessionId) {
37
+ const safeUser = userId === null || userId === undefined ? 'unknown' : String(userId);
38
+ return `${safeUser}:${sessionId}`;
39
+ }
40
+
41
+ const TOOL_APPROVAL_TIMEOUT_MS = parseInt(process.env.CLAUDE_TOOL_APPROVAL_TIMEOUT_MS, 10) || 55000;
42
+
43
+ const TOOLS_REQUIRING_INTERACTION = new Set(['AskUserQuestion', 'ExitPlanMode']);
44
+
45
+ function createRequestId() {
46
+ if (typeof crypto.randomUUID === 'function') {
47
+ return crypto.randomUUID();
48
+ }
49
+ return crypto.randomBytes(16).toString('hex');
50
+ }
51
+
52
+ function waitForToolApproval(requestId, options = {}) {
53
+ const { timeoutMs = TOOL_APPROVAL_TIMEOUT_MS, signal, onCancel, metadata } = options;
54
+
55
+ return new Promise(resolve => {
56
+ let settled = false;
57
+
58
+ const finalize = (decision) => {
59
+ if (settled) return;
60
+ settled = true;
61
+ cleanup();
62
+ resolve(decision);
63
+ };
64
+
65
+ let timeout;
66
+
67
+ const cleanup = () => {
68
+ pendingToolApprovals.delete(requestId);
69
+ if (timeout) clearTimeout(timeout);
70
+ if (signal && abortHandler) {
71
+ signal.removeEventListener('abort', abortHandler);
72
+ }
73
+ };
74
+
75
+ // timeoutMs 0 = wait indefinitely (interactive tools)
76
+ if (timeoutMs > 0) {
77
+ timeout = setTimeout(() => {
78
+ onCancel?.('timeout');
79
+ finalize(null);
80
+ }, timeoutMs);
81
+ }
82
+
83
+ const abortHandler = () => {
84
+ onCancel?.('cancelled');
85
+ finalize({ cancelled: true });
86
+ };
87
+
88
+ if (signal) {
89
+ if (signal.aborted) {
90
+ onCancel?.('cancelled');
91
+ finalize({ cancelled: true });
92
+ return;
93
+ }
94
+ signal.addEventListener('abort', abortHandler, { once: true });
95
+ }
96
+
97
+ const resolver = (decision) => {
98
+ finalize(decision);
99
+ };
100
+ // Attach metadata for getPendingApprovalsForSession lookup
101
+ if (metadata) {
102
+ Object.assign(resolver, metadata);
103
+ }
104
+ pendingToolApprovals.set(requestId, resolver);
105
+ });
106
+ }
107
+
108
+ function resolveToolApproval(requestId, decision) {
109
+ const resolver = pendingToolApprovals.get(requestId);
110
+ if (resolver) {
111
+ resolver(decision);
112
+ }
113
+ }
114
+
115
+ // Match stored permission entries against a tool + input combo.
116
+ // This only supports exact tool names and the Bash(command:*) shorthand
117
+ // used by the UI; it intentionally does not implement full glob semantics,
118
+ // introduced to stay consistent with the UI's "Allow rule" format.
119
+ function matchesToolPermission(entry, toolName, input) {
120
+ if (!entry || !toolName) {
121
+ return false;
122
+ }
123
+
124
+ if (entry === toolName) {
125
+ return true;
126
+ }
127
+
128
+ const bashMatch = entry.match(/^Bash\((.+):\*\)$/);
129
+ if (toolName === 'Bash' && bashMatch) {
130
+ const allowedPrefix = bashMatch[1];
131
+ let command = '';
132
+
133
+ if (typeof input === 'string') {
134
+ command = input.trim();
135
+ } else if (input && typeof input === 'object' && typeof input.command === 'string') {
136
+ command = input.command.trim();
137
+ }
138
+
139
+ if (!command) {
140
+ return false;
141
+ }
142
+
143
+ return command.startsWith(allowedPrefix);
144
+ }
145
+
146
+ return false;
147
+ }
148
+
149
+ /**
150
+ * Maps CLI options to SDK-compatible options format
151
+ * @param {Object} options - CLI options
152
+ * @returns {Object} SDK-compatible options
153
+ */
154
+ function mapCliOptionsToSDK(options = {}) {
155
+ const { sessionId, cwd, toolsSettings, permissionMode } = options;
156
+
157
+ const sdkOptions = {};
158
+
159
+ // Forward all host env vars (e.g. ANTHROPIC_BASE_URL) to the subprocess.
160
+ // Since SDK 0.2.113, options.env replaces process.env instead of overlaying it.
161
+ sdkOptions.env = { ...process.env };
162
+
163
+ // Resolve the executable eagerly on Windows because the SDK uses raw child_process.spawn,
164
+ // which does not reliably follow npm's shell wrappers like cross-spawn does.
165
+ sdkOptions.pathToClaudeCodeExecutable = resolveClaudeCodeExecutablePath(process.env.CLAUDE_CLI_PATH);
166
+
167
+ // Map working directory
168
+ if (cwd) {
169
+ sdkOptions.cwd = cwd;
170
+ }
171
+
172
+ // Map permission mode
173
+ if (permissionMode && permissionMode !== 'default') {
174
+ sdkOptions.permissionMode = permissionMode;
175
+ }
176
+
177
+ // Map tool settings
178
+ const settings = toolsSettings || {
179
+ allowedTools: [],
180
+ disallowedTools: [],
181
+ skipPermissions: false
182
+ };
183
+
184
+ // Handle tool permissions
185
+ if (settings.skipPermissions && permissionMode !== 'plan') {
186
+ // When skipping permissions, use bypassPermissions mode
187
+ sdkOptions.permissionMode = 'bypassPermissions';
188
+ }
189
+
190
+ let allowedTools = [...(settings.allowedTools || [])];
191
+
192
+ // Add plan mode default tools
193
+ if (permissionMode === 'plan') {
194
+ const planModeTools = ['Read', 'Task', 'exit_plan_mode', 'TodoRead', 'TodoWrite', 'WebFetch', 'WebSearch'];
195
+ for (const tool of planModeTools) {
196
+ if (!allowedTools.includes(tool)) {
197
+ allowedTools.push(tool);
198
+ }
199
+ }
200
+ }
201
+
202
+ sdkOptions.allowedTools = allowedTools;
203
+
204
+ // Use the tools preset to make all default built-in tools available (including AskUserQuestion).
205
+ // This was introduced in SDK 0.1.57. Omitting this preserves existing behavior (all tools available),
206
+ // but being explicit ensures forward compatibility and clarity.
207
+ sdkOptions.tools = { type: 'preset', preset: 'claude_code' };
208
+
209
+ sdkOptions.disallowedTools = settings.disallowedTools || [];
210
+
211
+ // Map model (default to sonnet)
212
+ // Valid models: sonnet, opus, haiku, opusplan, sonnet[1m]
213
+ sdkOptions.model = options.model || CLAUDE_MODELS.DEFAULT;
214
+ // Model logged at query start below
215
+
216
+ // Map system prompt configuration
217
+ sdkOptions.systemPrompt = {
218
+ type: 'preset',
219
+ preset: 'claude_code' // Required to use CLAUDE.md
220
+ };
221
+
222
+ // Map setting sources for CLAUDE.md loading
223
+ // This loads CLAUDE.md from project, user (~/.config/claude/CLAUDE.md), and local directories
224
+ sdkOptions.settingSources = ['project', 'user', 'local'];
225
+
226
+ // Map resume session
227
+ if (sessionId) {
228
+ sdkOptions.resume = sessionId;
229
+ }
230
+
231
+ return sdkOptions;
232
+ }
233
+
234
+ /**
235
+ * Adds a session to the active sessions map
236
+ * @param {number|string|null} userId - User identifier
237
+ * @param {string} sessionId - Session identifier
238
+ * @param {Object} queryInstance - SDK query instance
239
+ * @param {Array<string>} tempImagePaths - Temp image file paths for cleanup
240
+ * @param {string} tempDir - Temp directory for cleanup
241
+ */
242
+ function addSession(userId, sessionId, queryInstance, tempImagePaths = [], tempDir = null, writer = null) {
243
+ const key = buildSessionKey(userId, sessionId);
244
+ activeSessions.set(key, {
245
+ userId,
246
+ instance: queryInstance,
247
+ startTime: Date.now(),
248
+ status: 'active',
249
+ tempImagePaths,
250
+ tempDir,
251
+ writer
252
+ });
253
+ }
254
+
255
+ /**
256
+ * Removes a session from the active sessions map
257
+ * @param {number|string|null} userId - User identifier
258
+ * @param {string} sessionId - Session identifier
259
+ */
260
+ function removeSession(userId, sessionId) {
261
+ activeSessions.delete(buildSessionKey(userId, sessionId));
262
+ }
263
+
264
+ /**
265
+ * Gets a session from the active sessions map
266
+ * @param {number|string|null} userId - User identifier
267
+ * @param {string} sessionId - Session identifier
268
+ * @returns {Object|undefined} Session data or undefined
269
+ */
270
+ function getSession(userId, sessionId) {
271
+ return activeSessions.get(buildSessionKey(userId, sessionId));
272
+ }
273
+
274
+ /**
275
+ * Gets all active session IDs for a user
276
+ * @param {number|string|null} userId - User identifier (null for all)
277
+ * @returns {Array<string>} Array of active session IDs
278
+ */
279
+ function getAllSessions(userId = null) {
280
+ if (userId === null) {
281
+ return Array.from(activeSessions.keys());
282
+ }
283
+ const prefix = `${String(userId)}:`;
284
+ const sessions = [];
285
+ for (const key of activeSessions.keys()) {
286
+ if (key.startsWith(prefix)) {
287
+ sessions.push(key.split(':').slice(1).join(':'));
288
+ }
289
+ }
290
+ return sessions;
291
+ }
292
+
293
+ /**
294
+ * Transforms SDK messages to WebSocket format expected by frontend
295
+ * @param {Object} sdkMessage - SDK message object
296
+ * @returns {Object} Transformed message ready for WebSocket
297
+ */
298
+ function transformMessage(sdkMessage) {
299
+ // Extract parent_tool_use_id for subagent tool grouping
300
+ if (sdkMessage.parent_tool_use_id) {
301
+ return {
302
+ ...sdkMessage,
303
+ parentToolUseId: sdkMessage.parent_tool_use_id
304
+ };
305
+ }
306
+ return sdkMessage;
307
+ }
308
+
309
+ /**
310
+ * Extracts token usage from SDK result messages
311
+ * @param {Object} resultMessage - SDK result message
312
+ * @returns {Object|null} Token budget object or null
313
+ */
314
+ function extractTokenBudget(resultMessage) {
315
+ if (resultMessage.type !== 'result' || !resultMessage.modelUsage) {
316
+ return null;
317
+ }
318
+
319
+ // Get the first model's usage data
320
+ const modelKey = Object.keys(resultMessage.modelUsage)[0];
321
+ const modelData = resultMessage.modelUsage[modelKey];
322
+
323
+ if (!modelData) {
324
+ return null;
325
+ }
326
+
327
+ // Use cumulative tokens if available (tracks total for the session)
328
+ // Otherwise fall back to per-request tokens
329
+ const inputTokens = modelData.cumulativeInputTokens || modelData.inputTokens || 0;
330
+ const outputTokens = modelData.cumulativeOutputTokens || modelData.outputTokens || 0;
331
+ const cacheReadTokens = modelData.cumulativeCacheReadInputTokens || modelData.cacheReadInputTokens || 0;
332
+ const cacheCreationTokens = modelData.cumulativeCacheCreationInputTokens || modelData.cacheCreationInputTokens || 0;
333
+
334
+ // Total used = input + output + cache tokens
335
+ const totalUsed = inputTokens + outputTokens + cacheReadTokens + cacheCreationTokens;
336
+
337
+ // Use configured context window budget from environment (default 160000)
338
+ // This is the user's budget limit, not the model's context window
339
+ const contextWindow = parseInt(process.env.CONTEXT_WINDOW) || 160000;
340
+
341
+ // Token calc logged via token-budget WS event
342
+
343
+ return {
344
+ used: totalUsed,
345
+ total: contextWindow
346
+ };
347
+ }
348
+
349
+ /**
350
+ * Handles image processing for SDK queries
351
+ * Saves base64 images to temporary files and returns modified prompt with file paths
352
+ * @param {string} command - Original user prompt
353
+ * @param {Array} images - Array of image objects with base64 data
354
+ * @param {string} cwd - Working directory for temp file creation
355
+ * @returns {Promise<Object>} {modifiedCommand, tempImagePaths, tempDir}
356
+ */
357
+ async function handleImages(command, images, cwd) {
358
+ const tempImagePaths = [];
359
+ let tempDir = null;
360
+
361
+ if (!images || images.length === 0) {
362
+ return { modifiedCommand: command, tempImagePaths, tempDir };
363
+ }
364
+
365
+ try {
366
+ // Create temp directory in the project directory
367
+ const workingDir = cwd || process.cwd();
368
+ tempDir = path.join(workingDir, '.tmp', 'images', Date.now().toString());
369
+ await fs.mkdir(tempDir, { recursive: true });
370
+
371
+ // Save each image to a temp file
372
+ for (const [index, image] of images.entries()) {
373
+ // Extract base64 data and mime type
374
+ const matches = image.data.match(/^data:([^;]+);base64,(.+)$/);
375
+ if (!matches) {
376
+ console.error('Invalid image data format');
377
+ continue;
378
+ }
379
+
380
+ const [, mimeType, base64Data] = matches;
381
+ const extension = mimeType.split('/')[1] || 'png';
382
+ const filename = `image_${index}.${extension}`;
383
+ const filepath = path.join(tempDir, filename);
384
+
385
+ // Write base64 data to file
386
+ await fs.writeFile(filepath, Buffer.from(base64Data, 'base64'));
387
+ tempImagePaths.push(filepath);
388
+ }
389
+
390
+ // Include the full image paths in the prompt
391
+ let modifiedCommand = command;
392
+ if (tempImagePaths.length > 0 && command && command.trim()) {
393
+ const imageNote = `\n\n[Images provided at the following paths:]\n${tempImagePaths.map((p, i) => `${i + 1}. ${p}`).join('\n')}`;
394
+ modifiedCommand = command + imageNote;
395
+ }
396
+
397
+ // Images processed
398
+ return { modifiedCommand, tempImagePaths, tempDir };
399
+ } catch (error) {
400
+ console.error('Error processing images for SDK:', error);
401
+ return { modifiedCommand: command, tempImagePaths, tempDir };
402
+ }
403
+ }
404
+
405
+ /**
406
+ * Cleans up temporary image files
407
+ * @param {Array<string>} tempImagePaths - Array of temp file paths to delete
408
+ * @param {string} tempDir - Temp directory to remove
409
+ */
410
+ async function cleanupTempFiles(tempImagePaths, tempDir) {
411
+ if (!tempImagePaths || tempImagePaths.length === 0) {
412
+ return;
413
+ }
414
+
415
+ try {
416
+ // Delete individual temp files
417
+ for (const imagePath of tempImagePaths) {
418
+ await fs.unlink(imagePath).catch(err =>
419
+ console.error(`Failed to delete temp image ${imagePath}:`, err)
420
+ );
421
+ }
422
+
423
+ // Delete temp directory
424
+ if (tempDir) {
425
+ await fs.rm(tempDir, { recursive: true, force: true }).catch(err =>
426
+ console.error(`Failed to delete temp directory ${tempDir}:`, err)
427
+ );
428
+ }
429
+
430
+ // Temp files cleaned
431
+ } catch (error) {
432
+ console.error('Error during temp file cleanup:', error);
433
+ }
434
+ }
435
+
436
+ /**
437
+ * Loads MCP server configurations from ~/.claude.json
438
+ * @param {string} cwd - Current working directory for project-specific configs
439
+ * @returns {Object|null} MCP servers object or null if none found
440
+ */
441
+ async function loadMcpConfig(cwd) {
442
+ try {
443
+ const claudeConfigPath = path.join(os.homedir(), '.claude.json');
444
+
445
+ // Check if config file exists
446
+ try {
447
+ await fs.access(claudeConfigPath);
448
+ } catch (error) {
449
+ // File doesn't exist, return null
450
+ // No config file
451
+ return null;
452
+ }
453
+
454
+ // Read and parse config file
455
+ let claudeConfig;
456
+ try {
457
+ const configContent = await fs.readFile(claudeConfigPath, 'utf8');
458
+ claudeConfig = JSON.parse(configContent);
459
+ } catch (error) {
460
+ console.error('Failed to parse ~/.claude.json:', error.message);
461
+ return null;
462
+ }
463
+
464
+ // Extract MCP servers (merge global and project-specific)
465
+ let mcpServers = {};
466
+
467
+ // Add global MCP servers
468
+ if (claudeConfig.mcpServers && typeof claudeConfig.mcpServers === 'object') {
469
+ mcpServers = { ...claudeConfig.mcpServers };
470
+ // Global MCP servers loaded
471
+ }
472
+
473
+ // Add/override with project-specific MCP servers
474
+ if (claudeConfig.claudeProjects && cwd) {
475
+ const projectConfig = claudeConfig.claudeProjects[cwd];
476
+ if (projectConfig && projectConfig.mcpServers && typeof projectConfig.mcpServers === 'object') {
477
+ mcpServers = { ...mcpServers, ...projectConfig.mcpServers };
478
+ // Project MCP servers merged
479
+ }
480
+ }
481
+
482
+ // Return null if no servers found
483
+ if (Object.keys(mcpServers).length === 0) {
484
+ return null;
485
+ }
486
+ return mcpServers;
487
+ } catch (error) {
488
+ console.error('Error loading MCP config:', error.message);
489
+ return null;
490
+ }
491
+ }
492
+
493
+ /**
494
+ * Executes a Claude query using the SDK
495
+ * @param {string} command - User prompt/command
496
+ * @param {Object} options - Query options
497
+ * @param {Object} ws - WebSocket connection
498
+ * @returns {Promise<void>}
499
+ */
500
+ async function queryClaudeSDK(command, options = {}, ws) {
501
+ const { sessionId, sessionSummary } = options;
502
+ const userId = ws?.userId || null;
503
+ let capturedSessionId = sessionId;
504
+ let sessionCreatedSent = false;
505
+ let tempImagePaths = [];
506
+ let tempDir = null;
507
+
508
+ const emitNotification = (event) => {
509
+ notifyUserIfEnabled({
510
+ userId: ws?.userId || null,
511
+ writer: ws,
512
+ event
513
+ });
514
+ };
515
+
516
+ try {
517
+ // Map CLI options to SDK format
518
+ const sdkOptions = mapCliOptionsToSDK(options);
519
+
520
+ // Load MCP configuration
521
+ const mcpServers = await loadMcpConfig(options.cwd);
522
+ if (mcpServers) {
523
+ sdkOptions.mcpServers = mcpServers;
524
+ }
525
+
526
+ // Handle images - save to temp files and modify prompt
527
+ const imageResult = await handleImages(command, options.images, options.cwd);
528
+ const finalCommand = imageResult.modifiedCommand;
529
+ tempImagePaths = imageResult.tempImagePaths;
530
+ tempDir = imageResult.tempDir;
531
+
532
+ sdkOptions.hooks = {
533
+ Notification: [{
534
+ matcher: '',
535
+ hooks: [async (input) => {
536
+ const message = typeof input?.message === 'string' ? input.message : 'Claude requires your attention.';
537
+ emitNotification(createNotificationEvent({
538
+ provider: 'claude',
539
+ sessionId: capturedSessionId || sessionId || null,
540
+ kind: 'action_required',
541
+ code: 'agent.notification',
542
+ meta: { message, sessionName: sessionSummary },
543
+ severity: 'warning',
544
+ requiresUserAction: true,
545
+ dedupeKey: `claude:hook:notification:${capturedSessionId || sessionId || 'none'}:${message}`
546
+ }));
547
+ return {};
548
+ }]
549
+ }]
550
+ };
551
+
552
+ // Caveat: in 'auto' and 'bypassPermissions' modes the SDK resolves approval
553
+ // at the permission-mode step and skips this callback, so interactive tools
554
+ // (AskUserQuestion, ExitPlanMode) won't reach the UI — the classifier/bypass
555
+ // auto-approves them and the model acts on a generated answer. Move these
556
+ // tools to a PreToolUse hook (runs before the mode check) if we need them
557
+ // to work in those modes.
558
+ sdkOptions.canUseTool = async (toolName, input, context) => {
559
+ const requiresInteraction = TOOLS_REQUIRING_INTERACTION.has(toolName);
560
+
561
+ if (!requiresInteraction) {
562
+ if (sdkOptions.permissionMode === 'bypassPermissions') {
563
+ return { behavior: 'allow', updatedInput: input };
564
+ }
565
+
566
+ const isDisallowed = (sdkOptions.disallowedTools || []).some(entry =>
567
+ matchesToolPermission(entry, toolName, input)
568
+ );
569
+ if (isDisallowed) {
570
+ return { behavior: 'deny', message: 'Tool disallowed by settings' };
571
+ }
572
+
573
+ const isAllowed = (sdkOptions.allowedTools || []).some(entry =>
574
+ matchesToolPermission(entry, toolName, input)
575
+ );
576
+ if (isAllowed) {
577
+ return { behavior: 'allow', updatedInput: input };
578
+ }
579
+ }
580
+
581
+ const requestId = createRequestId();
582
+ ws.send(createNormalizedMessage({ kind: 'permission_request', requestId, toolName, input, sessionId: capturedSessionId || sessionId || null, provider: 'claude' }));
583
+ emitNotification(createNotificationEvent({
584
+ provider: 'claude',
585
+ sessionId: capturedSessionId || sessionId || null,
586
+ kind: 'action_required',
587
+ code: 'permission.required',
588
+ meta: { toolName, sessionName: sessionSummary },
589
+ severity: 'warning',
590
+ requiresUserAction: true,
591
+ dedupeKey: `claude:permission:${capturedSessionId || sessionId || 'none'}:${requestId}`
592
+ }));
593
+
594
+ const decision = await waitForToolApproval(requestId, {
595
+ timeoutMs: requiresInteraction ? 0 : undefined,
596
+ signal: context?.signal,
597
+ metadata: {
598
+ _sessionId: capturedSessionId || sessionId || null,
599
+ _toolName: toolName,
600
+ _input: input,
601
+ _receivedAt: new Date(),
602
+ },
603
+ onCancel: (reason) => {
604
+ ws.send(createNormalizedMessage({ kind: 'permission_cancelled', requestId, reason, sessionId: capturedSessionId || sessionId || null, provider: 'claude' }));
605
+ }
606
+ });
607
+ if (!decision) {
608
+ return { behavior: 'deny', message: 'Permission request timed out' };
609
+ }
610
+
611
+ if (decision.cancelled) {
612
+ return { behavior: 'deny', message: 'Permission request cancelled' };
613
+ }
614
+
615
+ if (decision.allow) {
616
+ if (decision.rememberEntry && typeof decision.rememberEntry === 'string') {
617
+ if (!sdkOptions.allowedTools.includes(decision.rememberEntry)) {
618
+ sdkOptions.allowedTools.push(decision.rememberEntry);
619
+ }
620
+ if (Array.isArray(sdkOptions.disallowedTools)) {
621
+ sdkOptions.disallowedTools = sdkOptions.disallowedTools.filter(entry => entry !== decision.rememberEntry);
622
+ }
623
+ }
624
+ return { behavior: 'allow', updatedInput: decision.updatedInput ?? input };
625
+ }
626
+
627
+ return { behavior: 'deny', message: decision.message ?? 'User denied tool use' };
628
+ };
629
+
630
+ // Set stream-close timeout for interactive tools (Query constructor reads it synchronously). Claude Agent SDK has a default of 5s and this overrides it
631
+ const prevStreamTimeout = process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT;
632
+ process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT = '300000';
633
+
634
+ let queryInstance;
635
+ try {
636
+ queryInstance = query({
637
+ prompt: finalCommand,
638
+ options: sdkOptions
639
+ });
640
+ } catch (hookError) {
641
+ // Older/newer SDK versions may not accept hook shapes yet.
642
+ // Keep notification behavior operational via runtime events even if hook registration fails.
643
+ console.warn('Failed to initialize Claude query with hooks, retrying without hooks:', hookError?.message || hookError);
644
+ delete sdkOptions.hooks;
645
+ queryInstance = query({
646
+ prompt: finalCommand,
647
+ options: sdkOptions
648
+ });
649
+ }
650
+
651
+ // Restore immediately — Query constructor already captured the value
652
+ if (prevStreamTimeout !== undefined) {
653
+ process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT = prevStreamTimeout;
654
+ } else {
655
+ delete process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT;
656
+ }
657
+
658
+ // Track the query instance for abort capability
659
+ if (capturedSessionId) {
660
+ addSession(userId, capturedSessionId, queryInstance, tempImagePaths, tempDir, ws);
661
+ }
662
+
663
+ // Process streaming messages
664
+ console.log('Starting async generator loop for session:', capturedSessionId || 'NEW');
665
+ for await (const message of queryInstance) {
666
+ // Capture session ID from first message
667
+ if (message.session_id && !capturedSessionId) {
668
+
669
+ capturedSessionId = message.session_id;
670
+ addSession(userId, capturedSessionId, queryInstance, tempImagePaths, tempDir, ws);
671
+
672
+ // Set session ID on writer
673
+ if (ws.setSessionId && typeof ws.setSessionId === 'function') {
674
+ ws.setSessionId(capturedSessionId);
675
+ }
676
+
677
+ // Send session-created event only once for new sessions
678
+ if (!sessionId && !sessionCreatedSent) {
679
+ sessionCreatedSent = true;
680
+ ws.send(createNormalizedMessage({ kind: 'session_created', newSessionId: capturedSessionId, sessionId: capturedSessionId, provider: 'claude' }));
681
+ }
682
+ } else {
683
+ // session_id already captured
684
+ }
685
+
686
+ // Transform and normalize message via adapter
687
+ const transformedMessage = transformMessage(message);
688
+ const sid = capturedSessionId || sessionId || null;
689
+
690
+ // Use adapter to normalize SDK events into NormalizedMessage[]
691
+ const normalized = sessionsService.normalizeMessage('claude', transformedMessage, sid);
692
+ for (const msg of normalized) {
693
+ // Preserve parentToolUseId from SDK wrapper for subagent tool grouping
694
+ if (transformedMessage.parentToolUseId && !msg.parentToolUseId) {
695
+ msg.parentToolUseId = transformedMessage.parentToolUseId;
696
+ }
697
+ ws.send(msg);
698
+ }
699
+
700
+ // Extract and send token budget updates from result messages
701
+ if (message.type === 'result') {
702
+ const models = Object.keys(message.modelUsage || {});
703
+ if (models.length > 0) {
704
+ // Model info available in result message
705
+ }
706
+ const tokenBudgetData = extractTokenBudget(message);
707
+ if (tokenBudgetData) {
708
+ ws.send(createNormalizedMessage({ kind: 'status', text: 'token_budget', tokenBudget: tokenBudgetData, sessionId: capturedSessionId || sessionId || null, provider: 'claude' }));
709
+ }
710
+ }
711
+ }
712
+
713
+ // Clean up session on completion
714
+ if (capturedSessionId) {
715
+ removeSession(userId, capturedSessionId);
716
+ }
717
+
718
+ // Clean up temporary image files
719
+ await cleanupTempFiles(tempImagePaths, tempDir);
720
+
721
+ // Send completion event
722
+ ws.send(createNormalizedMessage({ kind: 'complete', exitCode: 0, isNewSession: !sessionId && !!command, sessionId: capturedSessionId, provider: 'claude' }));
723
+ notifyRunStopped({
724
+ userId: ws?.userId || null,
725
+ provider: 'claude',
726
+ sessionId: capturedSessionId || sessionId || null,
727
+ sessionName: sessionSummary,
728
+ stopReason: 'completed'
729
+ });
730
+ // Complete
731
+
732
+ } catch (error) {
733
+ console.error('SDK query error:', error);
734
+
735
+ // Clean up session on error
736
+ if (capturedSessionId) {
737
+ removeSession(userId, capturedSessionId);
738
+ }
739
+
740
+ // Clean up temporary image files on error
741
+ await cleanupTempFiles(tempImagePaths, tempDir);
742
+
743
+ // Check if Claude CLI is installed for a clearer error message
744
+ const installed = await providerAuthService.isProviderInstalled('claude');
745
+ const errorContent = !installed
746
+ ? 'Claude Code is not installed. Please install it first: https://docs.anthropic.com/en/docs/claude-code'
747
+ : error.message;
748
+
749
+ // Send error to WebSocket
750
+ ws.send(createNormalizedMessage({ kind: 'error', content: errorContent, sessionId: capturedSessionId || sessionId || null, provider: 'claude' }));
751
+ notifyRunFailed({
752
+ userId: ws?.userId || null,
753
+ provider: 'claude',
754
+ sessionId: capturedSessionId || sessionId || null,
755
+ sessionName: sessionSummary,
756
+ error
757
+ });
758
+ }
759
+ }
760
+
761
+ /**
762
+ * Aborts an active SDK session
763
+ * @param {string} sessionId - Session identifier
764
+ * @param {number|string|null} userId - User identifier for namespace
765
+ * @returns {boolean} True if session was aborted, false if not found
766
+ */
767
+ async function abortClaudeSDKSession(sessionId, userId = null) {
768
+ const session = getSession(userId, sessionId);
769
+
770
+ if (!session) {
771
+ console.log(`Session ${sessionId} not found for user ${userId}`);
772
+ return false;
773
+ }
774
+
775
+ try {
776
+ console.log(`Aborting SDK session: ${sessionId} (user: ${userId})`);
777
+
778
+ // Call interrupt() on the query instance
779
+ await session.instance.interrupt();
780
+
781
+ // Update session status
782
+ session.status = 'aborted';
783
+
784
+ // Clean up temporary image files
785
+ await cleanupTempFiles(session.tempImagePaths, session.tempDir);
786
+
787
+ // Clean up session
788
+ removeSession(userId, sessionId);
789
+
790
+ return true;
791
+ } catch (error) {
792
+ console.error(`Error aborting session ${sessionId}:`, error);
793
+ return false;
794
+ }
795
+ }
796
+
797
+ /**
798
+ * Checks if an SDK session is currently active
799
+ * @param {string} sessionId - Session identifier
800
+ * @param {number|string|null} userId - User identifier for namespace
801
+ * @returns {boolean} True if session is active
802
+ */
803
+ function isClaudeSDKSessionActive(sessionId, userId = null) {
804
+ const session = getSession(userId, sessionId);
805
+ return session && session.status === 'active';
806
+ }
807
+
808
+ /**
809
+ * Gets all active SDK session IDs
810
+ * @param {number|string|null} userId - User identifier (null for all)
811
+ * @returns {Array<string>} Array of active session IDs
812
+ */
813
+ function getActiveClaudeSDKSessions(userId = null) {
814
+ return getAllSessions(userId);
815
+ }
816
+
817
+ /**
818
+ * Get pending tool approvals for a specific session.
819
+ * @param {string} sessionId - The session ID
820
+ * @param {number|string|null} userId - User identifier for namespace
821
+ * @returns {Array} Array of pending permission request objects
822
+ */
823
+ function getPendingApprovalsForSession(sessionId, userId = null) {
824
+ const pending = [];
825
+ for (const [requestId, resolver] of pendingToolApprovals.entries()) {
826
+ if (resolver._sessionId === sessionId) {
827
+ pending.push({
828
+ requestId,
829
+ toolName: resolver._toolName || 'UnknownTool',
830
+ input: resolver._input,
831
+ context: resolver._context,
832
+ sessionId,
833
+ receivedAt: resolver._receivedAt || new Date(),
834
+ });
835
+ }
836
+ }
837
+ return pending;
838
+ }
839
+
840
+ /**
841
+ * Reconnect a session's WebSocketWriter to a new raw WebSocket.
842
+ * @param {string} sessionId - The session ID
843
+ * @param {Object} newRawWs - The new raw WebSocket connection
844
+ * @param {number|string|null} userId - User identifier for namespace
845
+ * @returns {boolean} True if writer was successfully reconnected
846
+ */
847
+ function reconnectSessionWriter(sessionId, newRawWs, userId = null) {
848
+ const session = getSession(userId, sessionId);
849
+ if (!session?.writer?.updateWebSocket) return false;
850
+ session.writer.updateWebSocket(newRawWs);
851
+ console.log(`[RECONNECT] Writer swapped for session ${sessionId} (user: ${userId})`);
852
+ return true;
853
+ }
854
+
855
+ // Export public API
856
+ export {
857
+ queryClaudeSDK,
858
+ abortClaudeSDKSession,
859
+ isClaudeSDKSessionActive,
860
+ getActiveClaudeSDKSessions,
861
+ resolveToolApproval,
862
+ getPendingApprovalsForSession,
863
+ reconnectSessionWriter
864
+ };