upfynai-code 2.6.0 → 2.6.1

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 (295) hide show
  1. package/README.md +123 -88
  2. package/bin/cli.js +63 -0
  3. package/package.json +48 -106
  4. package/src/auth.js +115 -0
  5. package/src/config.js +33 -0
  6. package/src/connect.js +314 -0
  7. package/src/launch.js +54 -0
  8. package/src/mcp.js +57 -0
  9. package/src/server.js +54 -0
  10. package/client/dist/api-docs.html +0 -879
  11. package/client/dist/assets/AppContent-C0CyP3g5.js +0 -513
  12. package/client/dist/assets/CanvasPanel-0u9QR7U-.js +0 -34
  13. package/client/dist/assets/CanvasPanel-WhZulBJw.css +0 -1
  14. package/client/dist/assets/DashboardPanel-Dgqw1yZk.js +0 -1
  15. package/client/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  16. package/client/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  17. package/client/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  18. package/client/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  19. package/client/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  20. package/client/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  21. package/client/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  22. package/client/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  23. package/client/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  24. package/client/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  25. package/client/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  26. package/client/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  27. package/client/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  28. package/client/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  29. package/client/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  30. package/client/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  31. package/client/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  32. package/client/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  33. package/client/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  34. package/client/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  35. package/client/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  36. package/client/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  37. package/client/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  38. package/client/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  39. package/client/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  40. package/client/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  41. package/client/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  42. package/client/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  43. package/client/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  44. package/client/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  45. package/client/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  46. package/client/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  47. package/client/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  48. package/client/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  49. package/client/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  50. package/client/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  51. package/client/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  52. package/client/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  53. package/client/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  54. package/client/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  55. package/client/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  56. package/client/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  57. package/client/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  58. package/client/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  59. package/client/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  60. package/client/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  61. package/client/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  62. package/client/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  63. package/client/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  64. package/client/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  65. package/client/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  66. package/client/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  67. package/client/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  68. package/client/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  69. package/client/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  70. package/client/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  71. package/client/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  72. package/client/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  73. package/client/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  74. package/client/dist/assets/LoginModal-CZDEzqjK.js +0 -19
  75. package/client/dist/assets/MarkdownPreview-CYdvwJaV.js +0 -1
  76. package/client/dist/assets/Onboarding-DR6NZ4Vz.js +0 -1
  77. package/client/dist/assets/SetupForm-D49gtWY4.js +0 -1
  78. package/client/dist/assets/Tableau10-B-NsZVaP.js +0 -1
  79. package/client/dist/assets/WorkflowsPanel-CqlbEJA_.js +0 -1
  80. package/client/dist/assets/_commonjs-dynamic-modules-TDtrdbi3.js +0 -1
  81. package/client/dist/assets/ar-SA-G6X2FPQ2-BWqa1yBH.js +0 -10
  82. package/client/dist/assets/arc-BegSKqEW.js +0 -1
  83. package/client/dist/assets/array-BKyUJesY.js +0 -1
  84. package/client/dist/assets/az-AZ-76LH7QW2-DrVlbZDP.js +0 -1
  85. package/client/dist/assets/bg-BG-XCXSNQG7-DdunjBgT.js +0 -5
  86. package/client/dist/assets/blockDiagram-38ab4fdb-BKMbwGHu.js +0 -118
  87. package/client/dist/assets/bn-BD-2XOGV67Q-_7DtmvwO.js +0 -5
  88. package/client/dist/assets/c4Diagram-3d4e48cf-hJuiHhSn.js +0 -10
  89. package/client/dist/assets/ca-ES-6MX7JW3Y-BFIrmojG.js +0 -8
  90. package/client/dist/assets/channel-Bur-rRTp.js +0 -1
  91. package/client/dist/assets/classDiagram-70f12bd4-BjiAf9cM.js +0 -2
  92. package/client/dist/assets/classDiagram-v2-f2320105-pwBewejc.js +0 -2
  93. package/client/dist/assets/clone-BtqXeoBJ.js +0 -1
  94. package/client/dist/assets/createText-2e5e7dd3-Dq_acOWe.js +0 -5
  95. package/client/dist/assets/cs-CZ-2BRQDIVT-B-x4F6TJ.js +0 -11
  96. package/client/dist/assets/da-DK-5WZEPLOC-Btlc8Dgn.js +0 -5
  97. package/client/dist/assets/de-DE-XR44H4JA-BVu3ZIoD.js +0 -8
  98. package/client/dist/assets/directory-open-01563666-DWU9wJ6I.js +0 -1
  99. package/client/dist/assets/directory-open-4ed118d0-CunoC1EB.js +0 -1
  100. package/client/dist/assets/edges-e0da2a9e-DH0wVTXR.js +0 -4
  101. package/client/dist/assets/el-GR-BZB4AONW-h2ll8_ZC.js +0 -10
  102. package/client/dist/assets/erDiagram-9861fffd-BYezLIR7.js +0 -51
  103. package/client/dist/assets/es-ES-U4NZUMDT-Cveiulwt.js +0 -9
  104. package/client/dist/assets/eu-ES-A7QVB2H4-DQluL2PY.js +0 -11
  105. package/client/dist/assets/fa-IR-HGAKTJCU-BJtcMBSv.js +0 -8
  106. package/client/dist/assets/fi-FI-Z5N7JZ37-D8NfbVXV.js +0 -6
  107. package/client/dist/assets/file-open-002ab408-DIuFHtCF.js +0 -1
  108. package/client/dist/assets/file-open-7c801643-684qeFg4.js +0 -1
  109. package/client/dist/assets/file-save-3189631c-C1wFhQhH.js +0 -1
  110. package/client/dist/assets/file-save-745eba88-Bb9F9Kg7.js +0 -1
  111. package/client/dist/assets/flowDb-956e92f1-scnUykhM.js +0 -10
  112. package/client/dist/assets/flowDiagram-66a62f08-jVyWsfyU.js +0 -4
  113. package/client/dist/assets/flowDiagram-v2-96b9c2cf-N6xgi25h.js +0 -1
  114. package/client/dist/assets/flowchart-elk-definition-4a651766-gKGX3HqR.js +0 -139
  115. package/client/dist/assets/fr-FR-RHASNOE6-vdj42kC6.js +0 -9
  116. package/client/dist/assets/ganttDiagram-c361ad54-C2CiWFUP.js +0 -257
  117. package/client/dist/assets/gitGraphDiagram-72cf32ee-C59Yz2LK.js +0 -70
  118. package/client/dist/assets/gl-ES-HMX3MZ6V-DQo0TzoP.js +0 -10
  119. package/client/dist/assets/graph-Dx_H43Kv.js +0 -1
  120. package/client/dist/assets/he-IL-6SHJWFNN-DKXK5e33.js +0 -10
  121. package/client/dist/assets/hi-IN-IWLTKZ5I-C2Qgqc0R.js +0 -4
  122. package/client/dist/assets/hu-HU-A5ZG7DT2-Ss-6vX0m.js +0 -7
  123. package/client/dist/assets/id-ID-SAP4L64H-D7Wsg1S2.js +0 -10
  124. package/client/dist/assets/image-blob-reduce.esm-D6s-rqMO.js +0 -7
  125. package/client/dist/assets/index-3862675e-u8Nv7hHC.js +0 -1
  126. package/client/dist/assets/index-BVowJdZF.js +0 -97
  127. package/client/dist/assets/index-ce18TYkg.js +0 -27
  128. package/client/dist/assets/index-kQoJx-bc.css +0 -1
  129. package/client/dist/assets/infoDiagram-f8f76790-LmoJYsxo.js +0 -7
  130. package/client/dist/assets/init-Gi6I4Gst.js +0 -1
  131. package/client/dist/assets/it-IT-JPQ66NNP-CAPTVl7M.js +0 -11
  132. package/client/dist/assets/ja-JP-DBVTYXUO-eNVPawR2.js +0 -8
  133. package/client/dist/assets/journeyDiagram-49397b02-BaJqehpR.js +0 -139
  134. package/client/dist/assets/kaa-6HZHGXH3-tpuNkKhS.js +0 -1
  135. package/client/dist/assets/kab-KAB-ZGHBKWFO-Dp83kx4x.js +0 -8
  136. package/client/dist/assets/kk-KZ-P5N5QNE5-B9IlC6YN.js +0 -1
  137. package/client/dist/assets/km-KH-HSX4SM5Z-B_KMYaMj.js +0 -11
  138. package/client/dist/assets/ko-KR-MTYHY66A-yebnUNdb.js +0 -9
  139. package/client/dist/assets/ku-TR-6OUDTVRD-BR6fh6-5.js +0 -9
  140. package/client/dist/assets/layout-DLl5Jwcl.js +0 -1
  141. package/client/dist/assets/line-FpB7omSK.js +0 -1
  142. package/client/dist/assets/linear-CkXqUFJ8.js +0 -1
  143. package/client/dist/assets/lt-LT-XHIRWOB4-SutZSWtR.js +0 -3
  144. package/client/dist/assets/lv-LV-5QDEKY6T-DuAxdcZL.js +0 -7
  145. package/client/dist/assets/mindmap-definition-fc14e90a-DyxXOExh.js +0 -425
  146. package/client/dist/assets/mr-IN-CRQNXWMA-DqDUWM_8.js +0 -13
  147. package/client/dist/assets/my-MM-5M5IBNSE-C40kMFMR.js +0 -1
  148. package/client/dist/assets/nb-NO-T6EIAALU-DVij32Ju.js +0 -10
  149. package/client/dist/assets/nl-NL-IS3SIHDZ-rT84mDYq.js +0 -8
  150. package/client/dist/assets/nn-NO-6E72VCQL-BBZXBW8V.js +0 -8
  151. package/client/dist/assets/oc-FR-POXYY2M6-DzjOugOf.js +0 -8
  152. package/client/dist/assets/ordinal-Cboi1Yqb.js +0 -1
  153. package/client/dist/assets/pa-IN-N4M65BXN-DD1iU8_F.js +0 -4
  154. package/client/dist/assets/path-CbwjOpE9.js +0 -1
  155. package/client/dist/assets/pdf-CE_K4jFx.js +0 -12
  156. package/client/dist/assets/pdf.worker-BA9kU3Pw.mjs +0 -61080
  157. package/client/dist/assets/percentages-BXMCSKIN-WVlHS4wx.js +0 -207
  158. package/client/dist/assets/pica-CQIY57Tf.js +0 -7
  159. package/client/dist/assets/pieDiagram-8a3498a8-Dd_85qBH.js +0 -35
  160. package/client/dist/assets/pl-PL-T2D74RX3-ukVXa48G.js +0 -9
  161. package/client/dist/assets/pt-BR-5N22H2LF-BibawarT.js +0 -9
  162. package/client/dist/assets/pt-PT-UZXXM6DQ-So3i9l9w.js +0 -9
  163. package/client/dist/assets/quadrantDiagram-120e2f19-C4dFVDEx.js +0 -7
  164. package/client/dist/assets/requirementDiagram-deff3bca-DrTO7yFl.js +0 -52
  165. package/client/dist/assets/ro-RO-JPDTUUEW-DY0Xq_Hd.js +0 -11
  166. package/client/dist/assets/roundRect-0PYZxl1G.js +0 -1
  167. package/client/dist/assets/ru-RU-B4JR7IUQ-B7u_Zvkd.js +0 -9
  168. package/client/dist/assets/sankeyDiagram-04a897e0-D24gfzuS.js +0 -8
  169. package/client/dist/assets/sequenceDiagram-704730f1-Dgji2XLQ.js +0 -122
  170. package/client/dist/assets/si-LK-N5RQ5JYF-OejsLzQ_.js +0 -1
  171. package/client/dist/assets/sk-SK-C5VTKIMK-_vy2Bt-M.js +0 -6
  172. package/client/dist/assets/sl-SI-NN7IZMDC-DKOl_u2M.js +0 -6
  173. package/client/dist/assets/stateDiagram-587899a1-CJ8eBaiU.js +0 -1
  174. package/client/dist/assets/stateDiagram-v2-d93cdb3a-C5K3l-Nt.js +0 -1
  175. package/client/dist/assets/styles-6aaf32cf-DAKE0jbx.js +0 -207
  176. package/client/dist/assets/styles-9a916d00-LFAJCgEy.js +0 -160
  177. package/client/dist/assets/styles-c10674c1-CllKO8NG.js +0 -116
  178. package/client/dist/assets/subset-shared.chunk-Uy-J87FQ.js +0 -84
  179. package/client/dist/assets/subset-worker.chunk-dvgDvqt9.js +0 -1
  180. package/client/dist/assets/sv-SE-XGPEYMSR-CDCB2ZV5.js +0 -10
  181. package/client/dist/assets/svgDrawCommon-08f97a94-CObOzbFQ.js +0 -1
  182. package/client/dist/assets/ta-IN-2NMHFXQM-DHUNdO69.js +0 -9
  183. package/client/dist/assets/th-TH-HPSO5L25-zI2hnBq3.js +0 -2
  184. package/client/dist/assets/timeline-definition-85554ec2-C2XHRmxK.js +0 -61
  185. package/client/dist/assets/tr-TR-DEFEU3FU-l-6Hu4-D.js +0 -7
  186. package/client/dist/assets/uk-UA-QMV73CPH-CqSOwrl7.js +0 -6
  187. package/client/dist/assets/vendor-codemirror-D_s0aGBu.js +0 -35
  188. package/client/dist/assets/vendor-i18n-DCFGyhQR.js +0 -1
  189. package/client/dist/assets/vendor-icons-Lb69KSFJ.js +0 -646
  190. package/client/dist/assets/vendor-markdown-BXEi_H3G.js +0 -298
  191. package/client/dist/assets/vendor-react-9mUTKBHH.js +0 -67
  192. package/client/dist/assets/vendor-syntax-DnmwQQJF.js +0 -16
  193. package/client/dist/assets/vendor-xterm-CZq1hqo1.js +0 -66
  194. package/client/dist/assets/vendor-xterm-qxJ8_QYu.css +0 -32
  195. package/client/dist/assets/vi-VN-M7AON7JQ-CUL8-mBZ.js +0 -5
  196. package/client/dist/assets/xychartDiagram-e933f94c-1fmf6slj.js +0 -7
  197. package/client/dist/assets/zh-CN-LNUGB5OW-CB5y5VVU.js +0 -10
  198. package/client/dist/assets/zh-HK-E62DVLB3-BHcrrEeJ.js +0 -1
  199. package/client/dist/assets/zh-TW-RAJ6MFWO-DoDUdkaJ.js +0 -9
  200. package/client/dist/clear-cache.html +0 -85
  201. package/client/dist/convert-icons.md +0 -53
  202. package/client/dist/favicon.png +0 -0
  203. package/client/dist/favicon.svg +0 -9
  204. package/client/dist/generate-icons.js +0 -49
  205. package/client/dist/icons/claude-ai-icon.svg +0 -1
  206. package/client/dist/icons/codex-white.svg +0 -3
  207. package/client/dist/icons/codex.svg +0 -3
  208. package/client/dist/icons/cursor-white.svg +0 -12
  209. package/client/dist/icons/cursor.svg +0 -1
  210. package/client/dist/icons/icon-128x128.png +0 -0
  211. package/client/dist/icons/icon-128x128.svg +0 -12
  212. package/client/dist/icons/icon-144x144.png +0 -0
  213. package/client/dist/icons/icon-144x144.svg +0 -12
  214. package/client/dist/icons/icon-152x152.png +0 -0
  215. package/client/dist/icons/icon-152x152.svg +0 -12
  216. package/client/dist/icons/icon-192x192.png +0 -0
  217. package/client/dist/icons/icon-192x192.svg +0 -12
  218. package/client/dist/icons/icon-384x384.png +0 -0
  219. package/client/dist/icons/icon-384x384.svg +0 -12
  220. package/client/dist/icons/icon-512x512.png +0 -0
  221. package/client/dist/icons/icon-512x512.svg +0 -12
  222. package/client/dist/icons/icon-72x72.png +0 -0
  223. package/client/dist/icons/icon-72x72.svg +0 -12
  224. package/client/dist/icons/icon-96x96.png +0 -0
  225. package/client/dist/icons/icon-96x96.svg +0 -12
  226. package/client/dist/icons/icon-template.svg +0 -12
  227. package/client/dist/index.html +0 -128
  228. package/client/dist/logo-128.png +0 -0
  229. package/client/dist/logo-256.png +0 -0
  230. package/client/dist/logo-32.png +0 -0
  231. package/client/dist/logo-512.png +0 -0
  232. package/client/dist/logo-64.png +0 -0
  233. package/client/dist/logo.svg +0 -17
  234. package/client/dist/manifest.json +0 -61
  235. package/client/dist/mcp-docs.html +0 -119
  236. package/client/dist/screenshots/cli-selection.png +0 -0
  237. package/client/dist/screenshots/desktop-main.png +0 -0
  238. package/client/dist/screenshots/mobile-chat.png +0 -0
  239. package/client/dist/screenshots/tools-modal.png +0 -0
  240. package/client/dist/sw.js +0 -19
  241. package/commands/upfynai-connect.md +0 -59
  242. package/commands/upfynai-disconnect.md +0 -31
  243. package/commands/upfynai-doctor.md +0 -99
  244. package/commands/upfynai-export.md +0 -49
  245. package/commands/upfynai-local.md +0 -82
  246. package/commands/upfynai-status.md +0 -75
  247. package/commands/upfynai-stop.md +0 -49
  248. package/commands/upfynai-uninstall.md +0 -58
  249. package/commands/upfynai.md +0 -69
  250. package/scripts/build-client.js +0 -17
  251. package/scripts/fix-node-pty.js +0 -67
  252. package/scripts/install-commands.js +0 -78
  253. package/server/claude-sdk.js +0 -714
  254. package/server/cli-ui.js +0 -785
  255. package/server/cli.js +0 -596
  256. package/server/constants/config.js +0 -31
  257. package/server/cursor-cli.js +0 -270
  258. package/server/database/auth.db +0 -0
  259. package/server/database/db.js +0 -822
  260. package/server/database/init.sql +0 -70
  261. package/server/index.js +0 -2738
  262. package/server/load-env.js +0 -26
  263. package/server/mcp-server.js +0 -621
  264. package/server/middleware/auth.js +0 -181
  265. package/server/openai-codex.js +0 -403
  266. package/server/openrouter.js +0 -137
  267. package/server/projects.js +0 -1742
  268. package/server/relay-client.js +0 -672
  269. package/server/routes/agent.js +0 -1226
  270. package/server/routes/auth.js +0 -266
  271. package/server/routes/cli-auth.js +0 -263
  272. package/server/routes/codex.js +0 -344
  273. package/server/routes/commands.js +0 -598
  274. package/server/routes/cursor.js +0 -807
  275. package/server/routes/dashboard.js +0 -205
  276. package/server/routes/git.js +0 -1151
  277. package/server/routes/mcp-utils.js +0 -48
  278. package/server/routes/mcp.js +0 -535
  279. package/server/routes/payments.js +0 -172
  280. package/server/routes/projects.js +0 -552
  281. package/server/routes/settings.js +0 -261
  282. package/server/routes/taskmaster.js +0 -1928
  283. package/server/routes/user.js +0 -106
  284. package/server/routes/vapi-chat.js +0 -94
  285. package/server/routes/voice.js +0 -194
  286. package/server/routes/webhooks.js +0 -166
  287. package/server/routes/workflows.js +0 -118
  288. package/server/sandbox.js +0 -120
  289. package/server/services/whisperService.js +0 -84
  290. package/server/services/workflowScheduler.js +0 -186
  291. package/server/utils/commandParser.js +0 -303
  292. package/server/utils/gitConfig.js +0 -24
  293. package/server/utils/mcp-detector.js +0 -198
  294. package/server/utils/taskmaster-websocket.js +0 -129
  295. package/shared/modelConstants.js +0 -96
@@ -1,822 +0,0 @@
1
- import { createClient } from '@libsql/client';
2
- import path from 'path';
3
- import fs from 'fs';
4
- import crypto from 'crypto';
5
- import { fileURLToPath } from 'url';
6
- import { dirname } from 'path';
7
-
8
- const __filename = fileURLToPath(import.meta.url);
9
- const __dirname = dirname(__filename);
10
-
11
- // ANSI color codes
12
- const colors = { reset: '\x1b[0m', bright: '\x1b[1m', cyan: '\x1b[36m', dim: '\x1b[2m' };
13
- const c = {
14
- info: (t) => `${colors.cyan}${t}${colors.reset}`,
15
- bright: (t) => `${colors.bright}${t}${colors.reset}`,
16
- dim: (t) => `${colors.dim}${t}${colors.reset}`,
17
- };
18
-
19
- // Database URL resolution (lazy — resolved on first access)
20
- let _db = null;
21
- let _dbUrl = null;
22
- let _dbAuthToken = null;
23
-
24
- function resolveDbConfig() {
25
- if (_dbUrl) return;
26
-
27
- if (process.env.TURSO_DATABASE_URL) {
28
- _dbUrl = process.env.TURSO_DATABASE_URL.trim();
29
- _dbAuthToken = process.env.TURSO_AUTH_TOKEN?.trim();
30
- console.log(`${c.info('[DB]')} Using Turso: ${_dbUrl}`);
31
- } else if (process.env.DATABASE_PATH) {
32
- const dbPath = process.env.DATABASE_PATH.trim();
33
- try { if (!fs.existsSync(path.dirname(dbPath))) fs.mkdirSync(path.dirname(dbPath), { recursive: true }); } catch { /* Vercel read-only */ }
34
- _dbUrl = `file:${dbPath}`;
35
- console.log(`${c.info('[DB]')} Using custom path: ${dbPath}`);
36
- } else if (process.env.VERCEL) {
37
- _dbUrl = 'file:/tmp/auth.db';
38
- console.warn(`${c.info('[DB]')} WARNING: Ephemeral /tmp on Vercel. Set TURSO_DATABASE_URL for persistent data.`);
39
- } else {
40
- _dbUrl = `file:${path.join(__dirname, 'auth.db')}`;
41
- console.log(`${c.info('[DB]')} Using local: ${_dbUrl}`);
42
- }
43
- }
44
-
45
- function getDb() {
46
- if (!_db) {
47
- resolveDbConfig();
48
- _db = createClient({ url: _dbUrl, ...(_dbAuthToken ? { authToken: _dbAuthToken } : {}) });
49
- }
50
- return _db;
51
- }
52
-
53
- // Lazy Proxy — defers createClient() until first DB call
54
- const db = new Proxy({}, {
55
- get(_, prop) {
56
- const client = getDb();
57
- const val = client[prop];
58
- return typeof val === 'function' ? val.bind(client) : val;
59
- }
60
- });
61
-
62
- // ─── Schema ─────────────────────────────────────────────────────────────────────
63
-
64
- const INIT_SQL = `
65
- CREATE TABLE IF NOT EXISTS users (
66
- id INTEGER PRIMARY KEY AUTOINCREMENT,
67
- username TEXT UNIQUE NOT NULL,
68
- password_hash TEXT NOT NULL,
69
- first_name TEXT,
70
- last_name TEXT,
71
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
72
- last_login DATETIME,
73
- is_active BOOLEAN DEFAULT 1,
74
- email TEXT,
75
- phone TEXT,
76
- git_name TEXT,
77
- git_email TEXT,
78
- has_completed_onboarding BOOLEAN DEFAULT 0,
79
- access_override TEXT DEFAULT NULL,
80
- user_code TEXT UNIQUE
81
- );
82
-
83
- CREATE INDEX IF NOT EXISTS idx_users_username ON users(username);
84
- CREATE INDEX IF NOT EXISTS idx_users_active ON users(is_active);
85
- CREATE INDEX IF NOT EXISTS idx_users_user_code ON users(user_code);
86
-
87
- CREATE TABLE IF NOT EXISTS api_keys (
88
- id INTEGER PRIMARY KEY AUTOINCREMENT,
89
- user_id INTEGER NOT NULL,
90
- key_name TEXT NOT NULL,
91
- api_key TEXT UNIQUE NOT NULL,
92
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
93
- last_used DATETIME,
94
- is_active BOOLEAN DEFAULT 1,
95
- FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
96
- );
97
-
98
- CREATE INDEX IF NOT EXISTS idx_api_keys_key ON api_keys(api_key);
99
- CREATE INDEX IF NOT EXISTS idx_api_keys_user_id ON api_keys(user_id);
100
- CREATE INDEX IF NOT EXISTS idx_api_keys_active ON api_keys(is_active);
101
-
102
- CREATE TABLE IF NOT EXISTS user_credentials (
103
- id INTEGER PRIMARY KEY AUTOINCREMENT,
104
- user_id INTEGER NOT NULL,
105
- credential_name TEXT NOT NULL,
106
- credential_type TEXT NOT NULL,
107
- credential_value TEXT NOT NULL,
108
- description TEXT,
109
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
110
- is_active BOOLEAN DEFAULT 1,
111
- FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
112
- );
113
-
114
- CREATE INDEX IF NOT EXISTS idx_user_credentials_user_id ON user_credentials(user_id);
115
- CREATE INDEX IF NOT EXISTS idx_user_credentials_type ON user_credentials(credential_type);
116
- CREATE INDEX IF NOT EXISTS idx_user_credentials_active ON user_credentials(is_active);
117
-
118
- CREATE TABLE IF NOT EXISTS relay_tokens (
119
- id INTEGER PRIMARY KEY AUTOINCREMENT,
120
- user_id INTEGER NOT NULL,
121
- token TEXT UNIQUE NOT NULL,
122
- name TEXT NOT NULL DEFAULT 'default',
123
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
124
- last_connected DATETIME,
125
- is_active BOOLEAN DEFAULT 1,
126
- FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
127
- );
128
-
129
- CREATE INDEX IF NOT EXISTS idx_relay_tokens_token ON relay_tokens(token);
130
- CREATE INDEX IF NOT EXISTS idx_relay_tokens_user_id ON relay_tokens(user_id);
131
- CREATE INDEX IF NOT EXISTS idx_relay_tokens_active ON relay_tokens(is_active);
132
-
133
- CREATE TABLE IF NOT EXISTS subscriptions (
134
- id INTEGER PRIMARY KEY AUTOINCREMENT,
135
- user_id INTEGER NOT NULL,
136
- plan_id TEXT NOT NULL,
137
- status TEXT NOT NULL DEFAULT 'active',
138
- amount INTEGER NOT NULL,
139
- currency TEXT NOT NULL DEFAULT 'INR',
140
- starts_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
141
- expires_at DATETIME NOT NULL,
142
- razorpay_order_id TEXT,
143
- razorpay_payment_id TEXT,
144
- razorpay_signature TEXT,
145
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
146
- updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
147
- FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
148
- );
149
-
150
- CREATE INDEX IF NOT EXISTS idx_subscriptions_user_id ON subscriptions(user_id);
151
- CREATE INDEX IF NOT EXISTS idx_subscriptions_status ON subscriptions(status);
152
- CREATE INDEX IF NOT EXISTS idx_subscriptions_expires ON subscriptions(expires_at);
153
-
154
- CREATE TABLE IF NOT EXISTS payments (
155
- id INTEGER PRIMARY KEY AUTOINCREMENT,
156
- user_id INTEGER NOT NULL,
157
- subscription_id INTEGER,
158
- plan_id TEXT NOT NULL,
159
- amount INTEGER NOT NULL,
160
- currency TEXT NOT NULL DEFAULT 'INR',
161
- status TEXT NOT NULL DEFAULT 'pending',
162
- razorpay_order_id TEXT UNIQUE,
163
- razorpay_payment_id TEXT,
164
- razorpay_signature TEXT,
165
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
166
- FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
167
- FOREIGN KEY (subscription_id) REFERENCES subscriptions(id) ON DELETE SET NULL
168
- );
169
-
170
- CREATE INDEX IF NOT EXISTS idx_payments_user_id ON payments(user_id);
171
- CREATE INDEX IF NOT EXISTS idx_payments_order_id ON payments(razorpay_order_id);
172
- CREATE INDEX IF NOT EXISTS idx_payments_status ON payments(status);
173
-
174
- CREATE TABLE IF NOT EXISTS webhooks (
175
- id INTEGER PRIMARY KEY AUTOINCREMENT,
176
- user_id INTEGER NOT NULL,
177
- name TEXT NOT NULL,
178
- url TEXT NOT NULL,
179
- method TEXT NOT NULL DEFAULT 'POST',
180
- headers TEXT DEFAULT '{}',
181
- description TEXT,
182
- is_active BOOLEAN DEFAULT 1,
183
- last_triggered DATETIME,
184
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
185
- updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
186
- FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
187
- );
188
-
189
- CREATE INDEX IF NOT EXISTS idx_webhooks_user_id ON webhooks(user_id);
190
- CREATE INDEX IF NOT EXISTS idx_webhooks_active ON webhooks(is_active);
191
-
192
- CREATE TABLE IF NOT EXISTS workflows (
193
- id INTEGER PRIMARY KEY AUTOINCREMENT,
194
- user_id INTEGER NOT NULL,
195
- name TEXT NOT NULL,
196
- description TEXT,
197
- steps TEXT NOT NULL DEFAULT '[]',
198
- schedule TEXT DEFAULT NULL,
199
- schedule_enabled BOOLEAN DEFAULT 0,
200
- schedule_timezone TEXT DEFAULT 'UTC',
201
- is_active BOOLEAN DEFAULT 1,
202
- last_run DATETIME,
203
- next_run DATETIME,
204
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
205
- updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
206
- FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
207
- );
208
-
209
- CREATE INDEX IF NOT EXISTS idx_workflows_user_id ON workflows(user_id);
210
- CREATE INDEX IF NOT EXISTS idx_workflows_active ON workflows(is_active);
211
-
212
- CREATE TABLE IF NOT EXISTS workflow_runs (
213
- id INTEGER PRIMARY KEY AUTOINCREMENT,
214
- workflow_id INTEGER NOT NULL,
215
- user_id INTEGER NOT NULL,
216
- status TEXT NOT NULL DEFAULT 'pending',
217
- steps_completed INTEGER DEFAULT 0,
218
- total_steps INTEGER DEFAULT 0,
219
- result TEXT,
220
- error TEXT,
221
- started_at DATETIME DEFAULT CURRENT_TIMESTAMP,
222
- completed_at DATETIME,
223
- FOREIGN KEY (workflow_id) REFERENCES workflows(id) ON DELETE CASCADE,
224
- FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
225
- );
226
-
227
- CREATE INDEX IF NOT EXISTS idx_workflow_runs_workflow_id ON workflow_runs(workflow_id);
228
- CREATE INDEX IF NOT EXISTS idx_workflow_runs_user_id ON workflow_runs(user_id);
229
- CREATE INDEX IF NOT EXISTS idx_workflow_runs_status ON workflow_runs(status);
230
-
231
- CREATE TABLE IF NOT EXISTS user_sandboxes (
232
- id INTEGER PRIMARY KEY AUTOINCREMENT,
233
- user_id INTEGER NOT NULL UNIQUE,
234
- sandbox_path TEXT NOT NULL,
235
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
236
- last_accessed DATETIME DEFAULT CURRENT_TIMESTAMP,
237
- disk_usage_bytes INTEGER DEFAULT 0,
238
- is_active BOOLEAN DEFAULT 1,
239
- FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
240
- );
241
-
242
- CREATE INDEX IF NOT EXISTS idx_user_sandboxes_user_id ON user_sandboxes(user_id);
243
- CREATE INDEX IF NOT EXISTS idx_user_sandboxes_active ON user_sandboxes(is_active);
244
- `;
245
-
246
- // ─── Migrations ─────────────────────────────────────────────────────────────────
247
-
248
- const runMigrations = async () => {
249
- try {
250
- const result = await db.execute("PRAGMA table_info(users)");
251
- const cols = result.rows.map(r => r.name);
252
-
253
- const addCol = async (col, def) => {
254
- if (!cols.includes(col)) {
255
- await db.execute(`ALTER TABLE users ADD COLUMN ${col} ${def}`);
256
- }
257
- };
258
-
259
- await addCol('git_name', 'TEXT');
260
- await addCol('git_email', 'TEXT');
261
- await addCol('has_completed_onboarding', 'BOOLEAN DEFAULT 0');
262
- await addCol('email', 'TEXT');
263
- await addCol('phone', 'TEXT');
264
- await addCol('first_name', 'TEXT');
265
- await addCol('last_name', 'TEXT');
266
- await addCol('access_override', 'TEXT DEFAULT NULL');
267
- await addCol('user_code', 'TEXT');
268
- try { await db.execute('CREATE UNIQUE INDEX IF NOT EXISTS idx_users_user_code ON users(user_code)'); } catch { /* ignore */ }
269
-
270
- // Backfill user_code (upc-001, upc-002, ...) for users missing it
271
- try {
272
- const noCode = await db.execute("SELECT id FROM users WHERE user_code IS NULL ORDER BY id ASC");
273
- for (const row of noCode.rows) {
274
- const code = `upc-${String(row.id).padStart(3, '0')}`;
275
- await db.execute({ sql: 'UPDATE users SET user_code = ? WHERE id = ?', args: [code, row.id] });
276
- }
277
- if (noCode.rows.length > 0) console.log(`${c.info('[DB]')} Assigned user_code to ${noCode.rows.length} users`);
278
- } catch (e) { console.warn('[DB] user_code backfill:', e.message); }
279
-
280
- // Migrate old ck_ API keys → up-cli- prefix
281
- try {
282
- const migrated = await db.execute("UPDATE api_keys SET api_key = 'up-cli-' || SUBSTR(api_key, 4) WHERE api_key LIKE 'ck_%'");
283
- if (migrated.rowsAffected > 0) console.log(`${c.info('[DB]')} Migrated ${migrated.rowsAffected} API keys: ck_ → up-cli-`);
284
- } catch { /* empty table or already migrated */ }
285
-
286
- // Backfill: ensure every user has records in all tables (relay_tokens, api_keys, subscriptions, payments, access_override)
287
- try {
288
- const allUsers = await db.execute('SELECT id, username, access_override FROM users WHERE is_active = 1');
289
- let backfilled = 0;
290
- for (const user of allUsers.rows) {
291
- // Relay token
292
- const tokens = await db.execute({ sql: 'SELECT id FROM relay_tokens WHERE user_id = ? LIMIT 1', args: [user.id] });
293
- if (tokens.rows.length === 0) {
294
- const token = 'upfyn_' + crypto.randomBytes(32).toString('hex');
295
- await db.execute({ sql: 'INSERT INTO relay_tokens (user_id, token, name) VALUES (?, ?, ?)', args: [user.id, token, 'default'] });
296
- backfilled++;
297
- }
298
- // API key
299
- const keys = await db.execute({ sql: 'SELECT id FROM api_keys WHERE user_id = ? LIMIT 1', args: [user.id] });
300
- if (keys.rows.length === 0) {
301
- const apiKey = 'up-cli-' + crypto.randomBytes(32).toString('hex');
302
- await db.execute({ sql: 'INSERT INTO api_keys (user_id, key_name, api_key) VALUES (?, ?, ?)', args: [user.id, 'default', apiKey] });
303
- backfilled++;
304
- }
305
- // Yearly subscription (if none exists)
306
- const subs = await db.execute({ sql: 'SELECT id FROM subscriptions WHERE user_id = ? LIMIT 1', args: [user.id] });
307
- if (subs.rows.length === 0) {
308
- const now = new Date();
309
- const expiresAt = new Date(now.getTime() + 365 * 24 * 60 * 60 * 1000).toISOString();
310
- const subResult = await db.execute({
311
- sql: `INSERT INTO subscriptions (user_id, plan_id, status, amount, currency, starts_at, expires_at) VALUES (?, 'yearly', 'active', 49900, 'INR', ?, ?)`,
312
- args: [user.id, now.toISOString(), expiresAt]
313
- });
314
- // Payment record for the subscription
315
- const subId = Number(subResult.lastInsertRowid);
316
- await db.execute({
317
- sql: `INSERT INTO payments (user_id, subscription_id, plan_id, amount, currency, status, razorpay_order_id) VALUES (?, ?, 'yearly', 49900, 'INR', 'paid', ?)`,
318
- args: [user.id, subId, `seed_order_${user.id}_${Date.now()}`]
319
- });
320
- backfilled++;
321
- }
322
- // Set access_override = 'paid' if not already set
323
- if (!user.access_override || user.access_override !== 'paid') {
324
- await db.execute({ sql: "UPDATE users SET access_override = 'paid' WHERE id = ?", args: [user.id] });
325
- backfilled++;
326
- }
327
- }
328
- if (backfilled > 0) console.log(`${c.info('[DB]')} Backfilled ${backfilled} records for existing users`);
329
- } catch (e) { console.warn('[DB] Backfill warning:', e.message); }
330
-
331
- console.log(`${c.info('[DB]')} Migrations complete`);
332
- } catch (error) {
333
- console.error('Migration error:', error.message);
334
- throw error;
335
- }
336
- };
337
-
338
- // ─── Initialize ─────────────────────────────────────────────────────────────────
339
-
340
- const initializeDatabase = async () => {
341
- try {
342
- try { await db.execute('PRAGMA foreign_keys = ON'); } catch (e) { console.warn('[DB] PRAGMA foreign_keys not supported:', e.message); }
343
-
344
- const stmts = INIT_SQL.split(';').map(s => s.trim()).filter(s => s.length > 0 && !s.startsWith('--'));
345
- for (const stmt of stmts) {
346
- try { await db.execute(stmt); } catch (e) { console.error('[DB] Statement failed:', stmt.substring(0, 80), e.message); throw e; }
347
- }
348
-
349
- console.log(`${c.info('[DB]')} Initialized`);
350
- await runMigrations();
351
- } catch (error) {
352
- console.error('DB init error:', error.message);
353
- throw error;
354
- }
355
- };
356
-
357
- // ─── Helpers ────────────────────────────────────────────────────────────────────
358
-
359
- const getRow = (result) => result.rows.length > 0 ? result.rows[0] : null;
360
- const ensureForeignKeys = async () => { try { await db.execute('PRAGMA foreign_keys = ON'); } catch { /* Turso */ } };
361
-
362
- // ─── User DB ────────────────────────────────────────────────────────────────────
363
-
364
- const userDb = {
365
- hasUsers: async () => {
366
- const result = await db.execute('SELECT COUNT(*) as count FROM users');
367
- return Number(getRow(result).count) > 0;
368
- },
369
-
370
- createUser: async (username, passwordHash, email = null, phone = null, firstName = null, lastName = null) => {
371
- const result = await db.execute({
372
- sql: 'INSERT INTO users (username, password_hash, email, phone, first_name, last_name) VALUES (?, ?, ?, ?, ?, ?)',
373
- args: [username, passwordHash, email, phone, firstName, lastName]
374
- });
375
- const userId = Number(result.lastInsertRowid);
376
- // Auto-assign user_code (upc-001, upc-002, ...)
377
- const userCode = `upc-${String(userId).padStart(3, '0')}`;
378
- try { await db.execute({ sql: 'UPDATE users SET user_code = ? WHERE id = ?', args: [userCode, userId] }); } catch { /* non-critical */ }
379
- return { id: userId, username, first_name: firstName, last_name: lastName, user_code: userCode };
380
- },
381
-
382
- getUserByUsername: async (username) => {
383
- const lower = (username || '').toLowerCase();
384
- const result = await db.execute({
385
- sql: 'SELECT * FROM users WHERE (LOWER(username) = ? OR LOWER(email) = ? OR phone = ? OR LOWER(user_code) = ?) AND is_active = 1',
386
- args: [lower, lower, username, lower]
387
- });
388
- return getRow(result);
389
- },
390
-
391
- updateLastLogin: async (userId) => {
392
- await db.execute({ sql: 'UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE id = ?', args: [userId] });
393
- },
394
-
395
- getUserById: async (userId) => {
396
- const result = await db.execute({
397
- sql: 'SELECT id, username, first_name, last_name, email, phone, created_at, last_login, access_override, user_code FROM users WHERE id = ? AND is_active = 1',
398
- args: [userId]
399
- });
400
- return getRow(result);
401
- },
402
-
403
- getFirstUser: async () => {
404
- const result = await db.execute('SELECT id, username, first_name, last_name, email, phone, created_at, last_login, access_override, user_code FROM users WHERE is_active = 1 LIMIT 1');
405
- return getRow(result);
406
- },
407
-
408
- updateGitConfig: async (userId, gitName, gitEmail) => {
409
- await db.execute({ sql: 'UPDATE users SET git_name = ?, git_email = ? WHERE id = ?', args: [gitName, gitEmail, userId] });
410
- },
411
-
412
- getGitConfig: async (userId) => {
413
- const result = await db.execute({ sql: 'SELECT git_name, git_email FROM users WHERE id = ?', args: [userId] });
414
- return getRow(result);
415
- },
416
-
417
- completeOnboarding: async (userId) => {
418
- await db.execute({ sql: 'UPDATE users SET has_completed_onboarding = 1 WHERE id = ?', args: [userId] });
419
- },
420
-
421
- hasCompletedOnboarding: async (userId) => {
422
- const result = await db.execute({ sql: 'SELECT has_completed_onboarding FROM users WHERE id = ?', args: [userId] });
423
- const row = getRow(result);
424
- return row?.has_completed_onboarding === 1;
425
- },
426
-
427
- setAccessOverride: async (userId, value) => {
428
- await db.execute({
429
- sql: 'UPDATE users SET access_override = ? WHERE id = ?',
430
- args: [value, userId]
431
- });
432
- }
433
- };
434
-
435
- // ─── API Keys DB ────────────────────────────────────────────────────────────────
436
-
437
- const apiKeysDb = {
438
- generateApiKey: () => 'up-cli-' + crypto.randomBytes(32).toString('hex'),
439
-
440
- createApiKey: async (userId, keyName) => {
441
- const apiKey = apiKeysDb.generateApiKey();
442
- const result = await db.execute({
443
- sql: 'INSERT INTO api_keys (user_id, key_name, api_key) VALUES (?, ?, ?)',
444
- args: [userId, keyName, apiKey]
445
- });
446
- return { id: Number(result.lastInsertRowid), keyName, apiKey };
447
- },
448
-
449
- getApiKeys: async (userId) => {
450
- const result = await db.execute({
451
- sql: 'SELECT id, key_name, api_key, created_at, last_used, is_active FROM api_keys WHERE user_id = ? ORDER BY created_at DESC',
452
- args: [userId]
453
- });
454
- return result.rows;
455
- },
456
-
457
- validateApiKey: async (apiKey) => {
458
- const result = await db.execute({
459
- sql: `SELECT u.id, u.username, ak.id as api_key_id
460
- FROM api_keys ak JOIN users u ON ak.user_id = u.id
461
- WHERE ak.api_key = ? AND ak.is_active = 1 AND u.is_active = 1`,
462
- args: [apiKey]
463
- });
464
- const row = getRow(result);
465
- if (row) {
466
- await db.execute({ sql: 'UPDATE api_keys SET last_used = CURRENT_TIMESTAMP WHERE id = ?', args: [row.api_key_id] });
467
- }
468
- return row;
469
- },
470
-
471
- deleteApiKey: async (userId, apiKeyId) => {
472
- await ensureForeignKeys();
473
- const result = await db.execute({ sql: 'DELETE FROM api_keys WHERE id = ? AND user_id = ?', args: [apiKeyId, userId] });
474
- return result.rowsAffected > 0;
475
- },
476
-
477
- toggleApiKey: async (userId, apiKeyId, isActive) => {
478
- const result = await db.execute({
479
- sql: 'UPDATE api_keys SET is_active = ? WHERE id = ? AND user_id = ?',
480
- args: [isActive ? 1 : 0, apiKeyId, userId]
481
- });
482
- return result.rowsAffected > 0;
483
- }
484
- };
485
-
486
- // ─── Credentials DB ─────────────────────────────────────────────────────────────
487
-
488
- const credentialsDb = {
489
- createCredential: async (userId, credentialName, credentialType, credentialValue, description = null) => {
490
- const result = await db.execute({
491
- sql: 'INSERT INTO user_credentials (user_id, credential_name, credential_type, credential_value, description) VALUES (?, ?, ?, ?, ?)',
492
- args: [userId, credentialName, credentialType, credentialValue, description]
493
- });
494
- return { id: Number(result.lastInsertRowid), credentialName, credentialType };
495
- },
496
-
497
- getCredentials: async (userId, credentialType = null) => {
498
- let sql = 'SELECT id, credential_name, credential_type, description, created_at, is_active FROM user_credentials WHERE user_id = ?';
499
- const args = [userId];
500
- if (credentialType) { sql += ' AND credential_type = ?'; args.push(credentialType); }
501
- sql += ' ORDER BY created_at DESC';
502
- return (await db.execute({ sql, args })).rows;
503
- },
504
-
505
- getActiveCredential: async (userId, credentialType) => {
506
- const result = await db.execute({
507
- sql: 'SELECT credential_value FROM user_credentials WHERE user_id = ? AND credential_type = ? AND is_active = 1 ORDER BY created_at DESC LIMIT 1',
508
- args: [userId, credentialType]
509
- });
510
- return getRow(result)?.credential_value || null;
511
- },
512
-
513
- deleteCredential: async (userId, credentialId) => {
514
- await ensureForeignKeys();
515
- const result = await db.execute({ sql: 'DELETE FROM user_credentials WHERE id = ? AND user_id = ?', args: [credentialId, userId] });
516
- return result.rowsAffected > 0;
517
- },
518
-
519
- toggleCredential: async (userId, credentialId, isActive) => {
520
- const result = await db.execute({
521
- sql: 'UPDATE user_credentials SET is_active = ? WHERE id = ? AND user_id = ?',
522
- args: [isActive ? 1 : 0, credentialId, userId]
523
- });
524
- return result.rowsAffected > 0;
525
- }
526
- };
527
-
528
- // Backward compat
529
- const githubTokensDb = {
530
- createGithubToken: (userId, tokenName, githubToken, description = null) => credentialsDb.createCredential(userId, tokenName, 'github_token', githubToken, description),
531
- getGithubTokens: (userId) => credentialsDb.getCredentials(userId, 'github_token'),
532
- getActiveGithubToken: (userId) => credentialsDb.getActiveCredential(userId, 'github_token'),
533
- deleteGithubToken: (userId, tokenId) => credentialsDb.deleteCredential(userId, tokenId),
534
- toggleGithubToken: (userId, tokenId, isActive) => credentialsDb.toggleCredential(userId, tokenId, isActive)
535
- };
536
-
537
- // ─── Plan Durations ─────────────────────────────────────────────────────────────
538
-
539
- const PLAN_DURATIONS = {
540
- monthly: 30,
541
- 'half-yearly': 180,
542
- yearly: 365,
543
- };
544
-
545
- // ─── Subscription DB ────────────────────────────────────────────────────────────
546
-
547
- const subscriptionDb = {
548
- getActiveSub: async (userId) => {
549
- const result = await db.execute({
550
- sql: `SELECT * FROM subscriptions WHERE user_id = ? AND status = 'active' AND expires_at > CURRENT_TIMESTAMP ORDER BY expires_at DESC LIMIT 1`,
551
- args: [userId]
552
- });
553
- return getRow(result);
554
- },
555
-
556
- getAllSubs: async (userId) => {
557
- const result = await db.execute({
558
- sql: 'SELECT * FROM subscriptions WHERE user_id = ? ORDER BY created_at DESC',
559
- args: [userId]
560
- });
561
- return result.rows;
562
- },
563
-
564
- createSub: async (userId, planId, amount, currency, razorpayOrderId, razorpayPaymentId, razorpaySignature) => {
565
- const days = PLAN_DURATIONS[planId] || 30;
566
-
567
- // Check if user has an active sub — extend from its expiry, otherwise start now
568
- const existing = await subscriptionDb.getActiveSub(userId);
569
- const startsAt = existing ? existing.expires_at : new Date().toISOString();
570
- const startsDate = new Date(startsAt);
571
- const expiresAt = new Date(startsDate.getTime() + days * 24 * 60 * 60 * 1000).toISOString();
572
-
573
- const result = await db.execute({
574
- sql: `INSERT INTO subscriptions (user_id, plan_id, status, amount, currency, starts_at, expires_at, razorpay_order_id, razorpay_payment_id, razorpay_signature) VALUES (?, ?, 'active', ?, ?, ?, ?, ?, ?, ?)`,
575
- args: [userId, planId, amount, currency, startsAt, expiresAt, razorpayOrderId, razorpayPaymentId, razorpaySignature]
576
- });
577
- return { id: Number(result.lastInsertRowid), planId, status: 'active', startsAt, expiresAt };
578
- },
579
-
580
- cancelSub: async (userId, subId) => {
581
- const result = await db.execute({
582
- sql: `UPDATE subscriptions SET status = 'cancelled', updated_at = CURRENT_TIMESTAMP WHERE id = ? AND user_id = ?`,
583
- args: [subId, userId]
584
- });
585
- return result.rowsAffected > 0;
586
- },
587
-
588
- expireOverdue: async () => {
589
- const result = await db.execute(
590
- `UPDATE subscriptions SET status = 'expired', updated_at = CURRENT_TIMESTAMP WHERE status = 'active' AND expires_at <= CURRENT_TIMESTAMP`
591
- );
592
- return result.rowsAffected;
593
- },
594
- };
595
-
596
- // ─── Payment DB ─────────────────────────────────────────────────────────────────
597
-
598
- const paymentDb = {
599
- createPayment: async (userId, planId, amount, currency, razorpayOrderId) => {
600
- const result = await db.execute({
601
- sql: `INSERT INTO payments (user_id, plan_id, amount, currency, razorpay_order_id, status) VALUES (?, ?, ?, ?, ?, 'pending')`,
602
- args: [userId, planId, amount, currency, razorpayOrderId]
603
- });
604
- return { id: Number(result.lastInsertRowid), razorpayOrderId };
605
- },
606
-
607
- getByOrderId: async (razorpayOrderId) => {
608
- const result = await db.execute({
609
- sql: 'SELECT * FROM payments WHERE razorpay_order_id = ?',
610
- args: [razorpayOrderId]
611
- });
612
- return getRow(result);
613
- },
614
-
615
- markPaid: async (razorpayOrderId, razorpayPaymentId, razorpaySignature, subscriptionId) => {
616
- await db.execute({
617
- sql: `UPDATE payments SET status = 'paid', razorpay_payment_id = ?, razorpay_signature = ?, subscription_id = ? WHERE razorpay_order_id = ?`,
618
- args: [razorpayPaymentId, razorpaySignature, subscriptionId, razorpayOrderId]
619
- });
620
- },
621
-
622
- markFailed: async (razorpayOrderId) => {
623
- await db.execute({
624
- sql: `UPDATE payments SET status = 'failed' WHERE razorpay_order_id = ?`,
625
- args: [razorpayOrderId]
626
- });
627
- },
628
-
629
- getUserPayments: async (userId) => {
630
- const result = await db.execute({
631
- sql: 'SELECT * FROM payments WHERE user_id = ? ORDER BY created_at DESC',
632
- args: [userId]
633
- });
634
- return result.rows;
635
- },
636
- };
637
-
638
- // ─── Relay Tokens DB ────────────────────────────────────────────────────────────
639
-
640
- const relayTokensDb = {
641
- generateToken: () => 'upfyn_' + crypto.randomBytes(32).toString('hex'),
642
-
643
- createToken: async (userId, name = 'default') => {
644
- const token = relayTokensDb.generateToken();
645
- await db.execute({ sql: 'INSERT INTO relay_tokens (user_id, token, name) VALUES (?, ?, ?)', args: [userId, token, name] });
646
- return { token, name };
647
- },
648
-
649
- validateToken: async (token) => {
650
- try {
651
- const result = await db.execute({
652
- sql: `SELECT rt.id, rt.user_id, rt.name, u.id as uid, u.username
653
- FROM relay_tokens rt JOIN users u ON rt.user_id = u.id
654
- WHERE rt.token = ? AND rt.is_active = 1 AND u.is_active = 1`,
655
- args: [token]
656
- });
657
- const row = getRow(result);
658
- if (row) await db.execute({ sql: 'UPDATE relay_tokens SET last_connected = CURRENT_TIMESTAMP WHERE id = ?', args: [row.id] });
659
- return row;
660
- } catch { return null; }
661
- },
662
-
663
- getTokens: async (userId) => {
664
- return (await db.execute({ sql: 'SELECT id, name, token, created_at, last_connected, is_active FROM relay_tokens WHERE user_id = ? ORDER BY created_at DESC', args: [userId] })).rows;
665
- },
666
-
667
- deleteToken: async (userId, tokenId) => {
668
- await ensureForeignKeys();
669
- return (await db.execute({ sql: 'DELETE FROM relay_tokens WHERE id = ? AND user_id = ?', args: [tokenId, userId] })).rowsAffected > 0;
670
- },
671
-
672
- toggleToken: async (userId, tokenId, isActive) => {
673
- return (await db.execute({ sql: 'UPDATE relay_tokens SET is_active = ? WHERE id = ? AND user_id = ?', args: [isActive ? 1 : 0, tokenId, userId] })).rowsAffected > 0;
674
- }
675
- };
676
-
677
- // ─── Webhook DB ──────────────────────────────────────────────────────────────
678
-
679
- const webhookDb = {
680
- getAll: async (userId) => {
681
- const result = await db.execute({
682
- sql: 'SELECT * FROM webhooks WHERE user_id = ? ORDER BY created_at DESC',
683
- args: [userId]
684
- });
685
- return result.rows;
686
- },
687
-
688
- getById: async (id, userId) => {
689
- const result = await db.execute({
690
- sql: 'SELECT * FROM webhooks WHERE id = ? AND user_id = ?',
691
- args: [id, userId]
692
- });
693
- return getRow(result);
694
- },
695
-
696
- create: async (userId, { name, url, method, headers, description }) => {
697
- const result = await db.execute({
698
- sql: 'INSERT INTO webhooks (user_id, name, url, method, headers, description) VALUES (?, ?, ?, ?, ?, ?)',
699
- args: [userId, name, url, method || 'POST', headers || '{}', description || null]
700
- });
701
- return { id: Number(result.lastInsertRowid), name, url, method: method || 'POST' };
702
- },
703
-
704
- update: async (id, userId, { name, url, method, headers, description }) => {
705
- const result = await db.execute({
706
- sql: 'UPDATE webhooks SET name = ?, url = ?, method = ?, headers = ?, description = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ? AND user_id = ?',
707
- args: [name, url, method, headers || '{}', description || null, id, userId]
708
- });
709
- return result.rowsAffected > 0;
710
- },
711
-
712
- delete: async (id, userId) => {
713
- await ensureForeignKeys();
714
- const result = await db.execute({
715
- sql: 'DELETE FROM webhooks WHERE id = ? AND user_id = ?',
716
- args: [id, userId]
717
- });
718
- return result.rowsAffected > 0;
719
- },
720
-
721
- updateLastTriggered: async (id) => {
722
- await db.execute({
723
- sql: 'UPDATE webhooks SET last_triggered = CURRENT_TIMESTAMP WHERE id = ?',
724
- args: [id]
725
- });
726
- }
727
- };
728
-
729
- // ─── Workflow DB ─────────────────────────────────────────────────────────────
730
-
731
- const workflowDb = {
732
- getAll: async (userId) => {
733
- const result = await db.execute({
734
- sql: 'SELECT * FROM workflows WHERE user_id = ? ORDER BY created_at DESC',
735
- args: [userId]
736
- });
737
- return result.rows;
738
- },
739
-
740
- getById: async (id, userId) => {
741
- const result = await db.execute({
742
- sql: 'SELECT * FROM workflows WHERE id = ? AND user_id = ?',
743
- args: [id, userId]
744
- });
745
- return getRow(result);
746
- },
747
-
748
- create: async (userId, { name, description, steps, schedule, schedule_enabled, schedule_timezone }) => {
749
- const result = await db.execute({
750
- sql: 'INSERT INTO workflows (user_id, name, description, steps, schedule, schedule_enabled, schedule_timezone) VALUES (?, ?, ?, ?, ?, ?, ?)',
751
- args: [userId, name, description || null, JSON.stringify(steps || []), schedule || null, schedule_enabled ? 1 : 0, schedule_timezone || 'UTC']
752
- });
753
- return { id: Number(result.lastInsertRowid), name };
754
- },
755
-
756
- update: async (id, userId, { name, description, steps, schedule, schedule_enabled, schedule_timezone }) => {
757
- const result = await db.execute({
758
- sql: 'UPDATE workflows SET name = ?, description = ?, steps = ?, schedule = ?, schedule_enabled = ?, schedule_timezone = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ? AND user_id = ?',
759
- args: [name, description || null, JSON.stringify(steps || []), schedule || null, schedule_enabled ? 1 : 0, schedule_timezone || 'UTC', id, userId]
760
- });
761
- return result.rowsAffected > 0;
762
- },
763
-
764
- delete: async (id, userId) => {
765
- await ensureForeignKeys();
766
- const result = await db.execute({
767
- sql: 'DELETE FROM workflows WHERE id = ? AND user_id = ?',
768
- args: [id, userId]
769
- });
770
- return result.rowsAffected > 0;
771
- },
772
-
773
- updateLastRun: async (id) => {
774
- await db.execute({
775
- sql: 'UPDATE workflows SET last_run = CURRENT_TIMESTAMP WHERE id = ?',
776
- args: [id]
777
- });
778
- },
779
-
780
- getScheduled: async () => {
781
- const result = await db.execute(
782
- 'SELECT * FROM workflows WHERE schedule IS NOT NULL AND schedule_enabled = 1 AND is_active = 1'
783
- );
784
- return result.rows;
785
- },
786
-
787
- updateNextRun: async (id, nextRun) => {
788
- await db.execute({
789
- sql: 'UPDATE workflows SET next_run = ? WHERE id = ?',
790
- args: [nextRun, id]
791
- });
792
- },
793
-
794
- createRun: async (workflowId, userId, totalSteps) => {
795
- const result = await db.execute({
796
- sql: 'INSERT INTO workflow_runs (workflow_id, user_id, status, total_steps) VALUES (?, ?, ?, ?)',
797
- args: [workflowId, userId, 'running', totalSteps]
798
- });
799
- return { id: Number(result.lastInsertRowid) };
800
- },
801
-
802
- updateRun: async (runId, { status, stepsCompleted, result: runResult, error }) => {
803
- const sets = ['status = ?'];
804
- const args = [status];
805
- if (stepsCompleted !== undefined) { sets.push('steps_completed = ?'); args.push(stepsCompleted); }
806
- if (runResult !== undefined) { sets.push('result = ?'); args.push(typeof runResult === 'string' ? runResult : JSON.stringify(runResult)); }
807
- if (error !== undefined) { sets.push('error = ?'); args.push(error); }
808
- if (status === 'completed' || status === 'failed') { sets.push('completed_at = CURRENT_TIMESTAMP'); }
809
- args.push(runId);
810
- await db.execute({ sql: `UPDATE workflow_runs SET ${sets.join(', ')} WHERE id = ?`, args });
811
- },
812
-
813
- getRuns: async (workflowId, userId) => {
814
- const result = await db.execute({
815
- sql: 'SELECT * FROM workflow_runs WHERE workflow_id = ? AND user_id = ? ORDER BY started_at DESC LIMIT 20',
816
- args: [workflowId, userId]
817
- });
818
- return result.rows;
819
- }
820
- };
821
-
822
- export { db, initializeDatabase, userDb, apiKeysDb, credentialsDb, relayTokensDb, githubTokensDb, subscriptionDb, paymentDb, webhookDb, workflowDb, PLAN_DURATIONS };