upfynai-code 0.1.0 → 2.2.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.
- package/client/dist/api-docs.html +879 -0
- package/client/dist/assets/AppContent-CTSHQdyq.js +513 -0
- package/client/dist/assets/CanvasPanel-Cig0Mo9s.js +6 -0
- package/client/dist/assets/CanvasPanel-q4HEqNtV.css +1 -0
- package/client/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/client/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/client/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/client/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/client/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/client/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/client/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/client/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/client/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/client/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/client/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/client/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/client/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/client/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/client/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/client/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/client/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/client/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/client/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/client/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/client/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/client/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/client/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/client/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/client/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/client/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/client/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/client/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/client/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/client/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/client/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/client/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/client/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/client/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/client/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/client/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/client/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/client/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/client/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/client/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/client/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/client/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/client/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/client/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/client/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/client/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/client/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/client/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/client/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/client/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/client/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/client/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/client/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/client/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/client/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/client/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/client/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/client/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/client/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/client/dist/assets/LoginModal-silya-zP.js +11 -0
- package/client/dist/assets/MarkdownPreview-B3c7OEj6.js +1 -0
- package/client/dist/assets/Onboarding-Coxo6mFA.js +1 -0
- package/client/dist/assets/SetupForm-BzYOsbji.js +1 -0
- package/client/dist/assets/Tableau10-B-NsZVaP.js +1 -0
- package/client/dist/assets/_commonjs-dynamic-modules-TDtrdbi3.js +1 -0
- package/client/dist/assets/ar-SA-G6X2FPQ2-Bmw2-hDt.js +10 -0
- package/client/dist/assets/arc-BMqY7_Ci.js +1 -0
- package/client/dist/assets/array-BKyUJesY.js +1 -0
- package/client/dist/assets/az-AZ-76LH7QW2-Dh1le_qs.js +1 -0
- package/client/dist/assets/bg-BG-XCXSNQG7-Cbav8Z9z.js +5 -0
- package/client/dist/assets/blockDiagram-38ab4fdb-ChHJxsXw.js +118 -0
- package/client/dist/assets/bn-BD-2XOGV67Q-DCNjOaWz.js +5 -0
- package/client/dist/assets/c4Diagram-3d4e48cf-b8Xue4Z6.js +10 -0
- package/client/dist/assets/ca-ES-6MX7JW3Y-Dl_vM7NS.js +8 -0
- package/client/dist/assets/channel-CSnvHe_M.js +1 -0
- package/client/dist/assets/classDiagram-70f12bd4-BheP7Ggo.js +2 -0
- package/client/dist/assets/classDiagram-v2-f2320105-xtym7GEZ.js +2 -0
- package/client/dist/assets/clone-B75abXxS.js +1 -0
- package/client/dist/assets/createText-2e5e7dd3-_n4jI_fO.js +5 -0
- package/client/dist/assets/cs-CZ-2BRQDIVT-ftsKDdz4.js +11 -0
- package/client/dist/assets/da-DK-5WZEPLOC-DAjdwGRO.js +5 -0
- package/client/dist/assets/de-DE-XR44H4JA-BJXczHGT.js +8 -0
- package/client/dist/assets/directory-open-01563666-DWU9wJ6I.js +1 -0
- package/client/dist/assets/directory-open-4ed118d0-CunoC1EB.js +1 -0
- package/client/dist/assets/edges-e0da2a9e-CfPZr4YM.js +4 -0
- package/client/dist/assets/el-GR-BZB4AONW-DW2p_uy7.js +10 -0
- package/client/dist/assets/erDiagram-9861fffd-CF33V-Of.js +51 -0
- package/client/dist/assets/es-ES-U4NZUMDT-DLOIGnrl.js +9 -0
- package/client/dist/assets/eu-ES-A7QVB2H4-LJXbf89m.js +11 -0
- package/client/dist/assets/fa-IR-HGAKTJCU-Dvx65fgW.js +8 -0
- package/client/dist/assets/fi-FI-Z5N7JZ37-EoL65BQh.js +6 -0
- package/client/dist/assets/file-open-002ab408-DIuFHtCF.js +1 -0
- package/client/dist/assets/file-open-7c801643-684qeFg4.js +1 -0
- package/client/dist/assets/file-save-3189631c-C1wFhQhH.js +1 -0
- package/client/dist/assets/file-save-745eba88-Bb9F9Kg7.js +1 -0
- package/client/dist/assets/flowDb-956e92f1-HgoXVy2H.js +10 -0
- package/client/dist/assets/flowDiagram-66a62f08-tffoET0H.js +4 -0
- package/client/dist/assets/flowDiagram-v2-96b9c2cf-Byc3JCHh.js +1 -0
- package/client/dist/assets/flowchart-elk-definition-4a651766-DJbI2dpv.js +139 -0
- package/client/dist/assets/fr-FR-RHASNOE6-DNk_jdDs.js +9 -0
- package/client/dist/assets/ganttDiagram-c361ad54-2XX670FU.js +257 -0
- package/client/dist/assets/gitGraphDiagram-72cf32ee-CcUfruAo.js +70 -0
- package/client/dist/assets/gl-ES-HMX3MZ6V-dxzFjZlG.js +10 -0
- package/client/dist/assets/graph-BSbiMSBC.js +1 -0
- package/client/dist/assets/he-IL-6SHJWFNN-Cogsfdt1.js +10 -0
- package/client/dist/assets/hi-IN-IWLTKZ5I-L6wbgi4F.js +4 -0
- package/client/dist/assets/hu-HU-A5ZG7DT2-DSA6ZDsH.js +7 -0
- package/client/dist/assets/id-ID-SAP4L64H-BK_vGGS6.js +10 -0
- package/client/dist/assets/image-blob-reduce.esm-BLtmMM_J.js +2 -0
- package/client/dist/assets/index-3862675e-Bv32HUgT.js +1 -0
- package/client/dist/assets/index-B8wwD_Xo.css +1 -0
- package/client/dist/assets/index-BPwf8Fw3.js +27 -0
- package/client/dist/assets/index-D1urGMYu.js +95 -0
- package/client/dist/assets/infoDiagram-f8f76790-w4mR4pxn.js +7 -0
- package/client/dist/assets/init-Gi6I4Gst.js +1 -0
- package/client/dist/assets/it-IT-JPQ66NNP-BLdHYMhn.js +11 -0
- package/client/dist/assets/ja-JP-DBVTYXUO-B_vmexl_.js +8 -0
- package/client/dist/assets/journeyDiagram-49397b02-D9nmO17e.js +139 -0
- package/client/dist/assets/kaa-6HZHGXH3-5s-3jl6F.js +1 -0
- package/client/dist/assets/kab-KAB-ZGHBKWFO-2QaVDuSf.js +8 -0
- package/client/dist/assets/kk-KZ-P5N5QNE5-CTC52Vbi.js +1 -0
- package/client/dist/assets/km-KH-HSX4SM5Z-DxawH8UZ.js +11 -0
- package/client/dist/assets/ko-KR-MTYHY66A-CmosEM8_.js +9 -0
- package/client/dist/assets/ku-TR-6OUDTVRD-DbiLen4y.js +9 -0
- package/client/dist/assets/layout-jmt3H9tA.js +1 -0
- package/client/dist/assets/line-JTlRayUJ.js +1 -0
- package/client/dist/assets/linear-DJeB5p7x.js +1 -0
- package/client/dist/assets/lt-LT-XHIRWOB4-CH15wrjA.js +3 -0
- package/client/dist/assets/lv-LV-5QDEKY6T-dhgfPuCQ.js +7 -0
- package/client/dist/assets/mindmap-definition-fc14e90a-BOOrexmz.js +415 -0
- package/client/dist/assets/mr-IN-CRQNXWMA-3Gi6iq7A.js +13 -0
- package/client/dist/assets/my-MM-5M5IBNSE-CpH4rdJj.js +1 -0
- package/client/dist/assets/nb-NO-T6EIAALU-Du6iiGql.js +10 -0
- package/client/dist/assets/nl-NL-IS3SIHDZ-BGvsd1MT.js +8 -0
- package/client/dist/assets/nn-NO-6E72VCQL-B-odvJZW.js +8 -0
- package/client/dist/assets/oc-FR-POXYY2M6-COC8xNjo.js +8 -0
- package/client/dist/assets/ordinal-Cboi1Yqb.js +1 -0
- package/client/dist/assets/pa-IN-N4M65BXN-CE21PUQH.js +4 -0
- package/client/dist/assets/path-CbwjOpE9.js +1 -0
- package/client/dist/assets/pdf-TYrZqVzP.js +12 -0
- package/client/dist/assets/pdf.worker-BA9kU3Pw.mjs +61080 -0
- package/client/dist/assets/percentages-BXMCSKIN-C9GT0OD3.js +199 -0
- package/client/dist/assets/pica-VkdyTzi8.js +2 -0
- package/client/dist/assets/pieDiagram-8a3498a8-Cvfh7Qr5.js +35 -0
- package/client/dist/assets/pl-PL-T2D74RX3-D4xFVSoT.js +9 -0
- package/client/dist/assets/pt-BR-5N22H2LF-CCq257gA.js +9 -0
- package/client/dist/assets/pt-PT-UZXXM6DQ-1l8gt5vA.js +9 -0
- package/client/dist/assets/quadrantDiagram-120e2f19-BA0js1aD.js +7 -0
- package/client/dist/assets/requirementDiagram-deff3bca-B0QNFfIn.js +52 -0
- package/client/dist/assets/ro-RO-JPDTUUEW-yosBW01E.js +11 -0
- package/client/dist/assets/roundRect-mAH3dD0p.js +1 -0
- package/client/dist/assets/ru-RU-B4JR7IUQ-8LkEJUix.js +9 -0
- package/client/dist/assets/sankeyDiagram-04a897e0-D4T9eCXn.js +8 -0
- package/client/dist/assets/sequenceDiagram-704730f1-CfBUTCrO.js +122 -0
- package/client/dist/assets/si-LK-N5RQ5JYF-D8rjbqtd.js +1 -0
- package/client/dist/assets/sk-SK-C5VTKIMK-Bg14sAzN.js +6 -0
- package/client/dist/assets/sl-SI-NN7IZMDC-CMTib6Zs.js +6 -0
- package/client/dist/assets/stateDiagram-587899a1-BGgvmVSZ.js +1 -0
- package/client/dist/assets/stateDiagram-v2-d93cdb3a-Qn3DpYuO.js +1 -0
- package/client/dist/assets/styles-6aaf32cf-IdVZLPrD.js +207 -0
- package/client/dist/assets/styles-9a916d00-BAC3L45X.js +160 -0
- package/client/dist/assets/styles-c10674c1-COhXxX8c.js +116 -0
- package/client/dist/assets/subset-shared.chunk-BWHnFai4.js +22 -0
- package/client/dist/assets/subset-worker.chunk-C8QUSruZ.js +1 -0
- package/client/dist/assets/sv-SE-XGPEYMSR-C1425rOF.js +10 -0
- package/client/dist/assets/svgDrawCommon-08f97a94-Cfk-fgnN.js +1 -0
- package/client/dist/assets/ta-IN-2NMHFXQM-BHHo1zpF.js +9 -0
- package/client/dist/assets/th-TH-HPSO5L25-CZVzm_WT.js +2 -0
- package/client/dist/assets/timeline-definition-85554ec2-VAvuJith.js +61 -0
- package/client/dist/assets/tr-TR-DEFEU3FU-DE1lclCq.js +7 -0
- package/client/dist/assets/uk-UA-QMV73CPH-D4lJZ85O.js +6 -0
- package/client/dist/assets/vendor-codemirror-BARtJV1V.js +16 -0
- package/client/dist/assets/vendor-codemirror-langs-52_y1wip.js +20 -0
- package/client/dist/assets/vendor-i18n-ByAl-gdx.js +1 -0
- package/client/dist/assets/vendor-icons-D33IkSIf.js +1 -0
- package/client/dist/assets/vendor-markdown-CIVH08vJ.js +298 -0
- package/client/dist/assets/vendor-react-CHoMc7ka.js +8 -0
- package/client/dist/assets/vendor-syntax-Djb62v3a.js +9 -0
- package/client/dist/assets/vendor-xterm-DBb3RXlu.js +66 -0
- package/client/dist/assets/vendor-xterm-DrlLKa8f.css +1 -0
- package/client/dist/assets/vi-VN-M7AON7JQ-Dgc_SShk.js +5 -0
- package/client/dist/assets/xychartDiagram-e933f94c-BeyVBJhb.js +7 -0
- package/client/dist/assets/zh-CN-LNUGB5OW-MH4Yh8in.js +10 -0
- package/client/dist/assets/zh-HK-E62DVLB3-D4XHehjx.js +1 -0
- package/client/dist/assets/zh-TW-RAJ6MFWO--efj3evj.js +9 -0
- package/client/dist/clear-cache.html +85 -0
- package/client/dist/convert-icons.md +53 -0
- package/client/dist/favicon.png +0 -0
- package/client/dist/favicon.svg +9 -0
- package/client/dist/generate-icons.js +49 -0
- package/client/dist/icons/claude-ai-icon.svg +1 -0
- package/client/dist/icons/codex-white.svg +3 -0
- package/client/dist/icons/codex.svg +3 -0
- package/client/dist/icons/cursor-white.svg +12 -0
- package/client/dist/icons/cursor.svg +1 -0
- package/client/dist/icons/icon-128x128.png +0 -0
- package/client/dist/icons/icon-128x128.svg +12 -0
- package/client/dist/icons/icon-144x144.png +0 -0
- package/client/dist/icons/icon-144x144.svg +12 -0
- package/client/dist/icons/icon-152x152.png +0 -0
- package/client/dist/icons/icon-152x152.svg +12 -0
- package/client/dist/icons/icon-192x192.png +0 -0
- package/client/dist/icons/icon-192x192.svg +12 -0
- package/client/dist/icons/icon-384x384.png +0 -0
- package/client/dist/icons/icon-384x384.svg +12 -0
- package/client/dist/icons/icon-512x512.png +0 -0
- package/client/dist/icons/icon-512x512.svg +12 -0
- package/client/dist/icons/icon-72x72.png +0 -0
- package/client/dist/icons/icon-72x72.svg +12 -0
- package/client/dist/icons/icon-96x96.png +0 -0
- package/client/dist/icons/icon-96x96.svg +12 -0
- package/client/dist/icons/icon-template.svg +12 -0
- package/client/dist/index.html +128 -0
- package/client/dist/logo-128.png +0 -0
- package/client/dist/logo-256.png +0 -0
- package/client/dist/logo-32.png +0 -0
- package/client/dist/logo-512.png +0 -0
- package/client/dist/logo-64.png +0 -0
- package/client/dist/logo.svg +17 -0
- package/client/dist/manifest.json +61 -0
- package/client/dist/mcp-docs.html +119 -0
- package/client/dist/screenshots/cli-selection.png +0 -0
- package/client/dist/screenshots/desktop-main.png +0 -0
- package/client/dist/screenshots/mobile-chat.png +0 -0
- package/client/dist/screenshots/tools-modal.png +0 -0
- package/client/dist/sw.js +19 -0
- package/commands/upfynai-connect.md +46 -0
- package/commands/upfynai-disconnect.md +31 -0
- package/commands/upfynai-doctor.md +99 -0
- package/commands/upfynai-export.md +49 -0
- package/commands/upfynai-local.md +82 -0
- package/commands/upfynai-status.md +75 -0
- package/commands/upfynai-stop.md +49 -0
- package/commands/upfynai-uninstall.md +58 -0
- package/commands/upfynai.md +50 -0
- package/package.json +106 -47
- package/scripts/build-client.js +17 -0
- package/scripts/fix-node-pty.js +67 -0
- package/scripts/install-commands.js +78 -0
- package/server/claude-sdk.js +714 -0
- package/server/cli.js +419 -0
- package/server/constants/config.js +5 -0
- package/server/cursor-cli.js +270 -0
- package/server/database/auth.db +0 -0
- package/server/database/db.js +606 -0
- package/server/database/init.sql +70 -0
- package/server/index.js +2269 -0
- package/server/load-env.js +26 -0
- package/server/mcp-server.js +620 -0
- package/server/middleware/auth.js +158 -0
- package/server/openai-codex.js +403 -0
- package/server/projects.js +1849 -0
- package/server/relay-client.js +314 -0
- package/server/routes/agent.js +1231 -0
- package/server/routes/auth.js +220 -0
- package/server/routes/cli-auth.js +263 -0
- package/server/routes/codex.js +344 -0
- package/server/routes/commands.js +601 -0
- package/server/routes/cursor.js +808 -0
- package/server/routes/git.js +1165 -0
- package/server/routes/mcp-utils.js +48 -0
- package/server/routes/mcp.js +552 -0
- package/server/routes/payments.js +172 -0
- package/server/routes/projects.js +549 -0
- package/server/routes/settings.js +178 -0
- package/server/routes/taskmaster.js +1964 -0
- package/server/routes/user.js +106 -0
- package/server/utils/commandParser.js +303 -0
- package/server/utils/gitConfig.js +24 -0
- package/server/utils/mcp-detector.js +198 -0
- package/server/utils/taskmaster-websocket.js +129 -0
- package/shared/modelConstants.js +67 -0
- package/LICENSE +0 -22
- package/bin/cli.js +0 -86
- package/dist/assets/CanvasPanel-B48gAKVY.js +0 -538
- package/dist/assets/CanvasPanel-B48gAKVY.js.map +0 -1
- package/dist/assets/CanvasPanel-BsOG3EVs.css +0 -1
- package/dist/assets/index-CEhTwG68.css +0 -1
- package/dist/assets/index-GqAGWpJI.js +0 -70
- package/dist/assets/index-GqAGWpJI.js.map +0 -1
- package/dist/index.html +0 -18
- package/index.html +0 -17
- package/src/App.tsx +0 -226
- package/src/components/canvas/CanvasPanel.tsx +0 -62
- package/src/components/canvas/layout/graph-builder.ts +0 -136
- package/src/components/canvas/shapes/CompactionNodeShape.tsx +0 -76
- package/src/components/canvas/shapes/SessionNodeShape.tsx +0 -93
- package/src/components/canvas/shapes/StatuslineWidgetShape.tsx +0 -125
- package/src/components/canvas/shapes/TextResponseNodeShape.tsx +0 -86
- package/src/components/canvas/shapes/ToolCallNodeShape.tsx +0 -107
- package/src/components/canvas/shapes/ToolResultNodeShape.tsx +0 -87
- package/src/components/canvas/shapes/shared-styles.ts +0 -35
- package/src/components/chat/ChatPanel.tsx +0 -96
- package/src/components/chat/InputBar.tsx +0 -81
- package/src/components/chat/MessageList.tsx +0 -130
- package/src/components/chat/PermissionDialog.tsx +0 -70
- package/src/components/layout/FolderSelector.tsx +0 -152
- package/src/components/layout/ModelSelector.tsx +0 -65
- package/src/components/layout/SessionManager.tsx +0 -115
- package/src/components/statusline/StatuslineBar.tsx +0 -114
- package/src/main.tsx +0 -10
- package/src/server/claude-session.ts +0 -156
- package/src/server/index.ts +0 -149
- package/src/services/stream-consumer.ts +0 -330
- package/src/statusline-core/bin/statusline.sh +0 -121
- package/src/statusline-core/commands/sls-config.md +0 -42
- package/src/statusline-core/commands/sls-doctor.md +0 -35
- package/src/statusline-core/commands/sls-help.md +0 -48
- package/src/statusline-core/commands/sls-layout.md +0 -38
- package/src/statusline-core/commands/sls-preview.md +0 -34
- package/src/statusline-core/commands/sls-theme.md +0 -40
- package/src/statusline-core/installer.js +0 -228
- package/src/statusline-core/layouts/compact.sh +0 -21
- package/src/statusline-core/layouts/full.sh +0 -62
- package/src/statusline-core/layouts/standard.sh +0 -39
- package/src/statusline-core/lib/core.sh +0 -389
- package/src/statusline-core/lib/helpers.sh +0 -81
- package/src/statusline-core/lib/json-parser.sh +0 -71
- package/src/statusline-core/themes/catppuccin.sh +0 -32
- package/src/statusline-core/themes/default.sh +0 -37
- package/src/statusline-core/themes/gruvbox.sh +0 -32
- package/src/statusline-core/themes/nord.sh +0 -32
- package/src/statusline-core/themes/tokyo-night.sh +0 -32
- package/src/store/canvas-store.ts +0 -50
- package/src/store/chat-store.ts +0 -60
- package/src/store/permission-store.ts +0 -29
- package/src/store/session-store.ts +0 -52
- package/src/store/statusline-store.ts +0 -160
- package/src/styles/global.css +0 -117
- package/src/themes/index.ts +0 -149
- package/src/types/canvas-graph.ts +0 -24
- package/src/types/sdk-messages.ts +0 -156
- package/src/types/statusline-fields.ts +0 -67
- package/src/vite-env.d.ts +0 -1
- package/tsconfig.json +0 -26
- package/vite.config.ts +0 -24
|
@@ -0,0 +1,606 @@
|
|
|
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
|
+
|
|
175
|
+
// ─── Migrations ─────────────────────────────────────────────────────────────────
|
|
176
|
+
|
|
177
|
+
const runMigrations = async () => {
|
|
178
|
+
try {
|
|
179
|
+
const result = await db.execute("PRAGMA table_info(users)");
|
|
180
|
+
const cols = result.rows.map(r => r.name);
|
|
181
|
+
|
|
182
|
+
const addCol = async (col, def) => {
|
|
183
|
+
if (!cols.includes(col)) {
|
|
184
|
+
await db.execute(`ALTER TABLE users ADD COLUMN ${col} ${def}`);
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
await addCol('git_name', 'TEXT');
|
|
189
|
+
await addCol('git_email', 'TEXT');
|
|
190
|
+
await addCol('has_completed_onboarding', 'BOOLEAN DEFAULT 0');
|
|
191
|
+
await addCol('email', 'TEXT');
|
|
192
|
+
await addCol('phone', 'TEXT');
|
|
193
|
+
await addCol('first_name', 'TEXT');
|
|
194
|
+
await addCol('last_name', 'TEXT');
|
|
195
|
+
await addCol('access_override', 'TEXT DEFAULT NULL');
|
|
196
|
+
await addCol('user_code', 'TEXT');
|
|
197
|
+
try { await db.execute('CREATE UNIQUE INDEX IF NOT EXISTS idx_users_user_code ON users(user_code)'); } catch { /* ignore */ }
|
|
198
|
+
|
|
199
|
+
// Backfill user_code (upc-001, upc-002, ...) for users missing it
|
|
200
|
+
try {
|
|
201
|
+
const noCode = await db.execute("SELECT id FROM users WHERE user_code IS NULL ORDER BY id ASC");
|
|
202
|
+
for (const row of noCode.rows) {
|
|
203
|
+
const code = `upc-${String(row.id).padStart(3, '0')}`;
|
|
204
|
+
await db.execute({ sql: 'UPDATE users SET user_code = ? WHERE id = ?', args: [code, row.id] });
|
|
205
|
+
}
|
|
206
|
+
if (noCode.rows.length > 0) console.log(`${c.info('[DB]')} Assigned user_code to ${noCode.rows.length} users`);
|
|
207
|
+
} catch (e) { console.warn('[DB] user_code backfill:', e.message); }
|
|
208
|
+
|
|
209
|
+
// Migrate old ck_ API keys → up-cli- prefix
|
|
210
|
+
try {
|
|
211
|
+
const migrated = await db.execute("UPDATE api_keys SET api_key = 'up-cli-' || SUBSTR(api_key, 4) WHERE api_key LIKE 'ck_%'");
|
|
212
|
+
if (migrated.rowsAffected > 0) console.log(`${c.info('[DB]')} Migrated ${migrated.rowsAffected} API keys: ck_ → up-cli-`);
|
|
213
|
+
} catch { /* empty table or already migrated */ }
|
|
214
|
+
|
|
215
|
+
// Backfill: ensure every user has records in all tables (relay_tokens, api_keys, subscriptions, payments, access_override)
|
|
216
|
+
try {
|
|
217
|
+
const allUsers = await db.execute('SELECT id, username, access_override FROM users WHERE is_active = 1');
|
|
218
|
+
let backfilled = 0;
|
|
219
|
+
for (const user of allUsers.rows) {
|
|
220
|
+
// Relay token
|
|
221
|
+
const tokens = await db.execute({ sql: 'SELECT id FROM relay_tokens WHERE user_id = ? LIMIT 1', args: [user.id] });
|
|
222
|
+
if (tokens.rows.length === 0) {
|
|
223
|
+
const token = 'upfyn_' + crypto.randomBytes(32).toString('hex');
|
|
224
|
+
await db.execute({ sql: 'INSERT INTO relay_tokens (user_id, token, name) VALUES (?, ?, ?)', args: [user.id, token, 'default'] });
|
|
225
|
+
backfilled++;
|
|
226
|
+
}
|
|
227
|
+
// API key
|
|
228
|
+
const keys = await db.execute({ sql: 'SELECT id FROM api_keys WHERE user_id = ? LIMIT 1', args: [user.id] });
|
|
229
|
+
if (keys.rows.length === 0) {
|
|
230
|
+
const apiKey = 'up-cli-' + crypto.randomBytes(32).toString('hex');
|
|
231
|
+
await db.execute({ sql: 'INSERT INTO api_keys (user_id, key_name, api_key) VALUES (?, ?, ?)', args: [user.id, 'default', apiKey] });
|
|
232
|
+
backfilled++;
|
|
233
|
+
}
|
|
234
|
+
// Yearly subscription (if none exists)
|
|
235
|
+
const subs = await db.execute({ sql: 'SELECT id FROM subscriptions WHERE user_id = ? LIMIT 1', args: [user.id] });
|
|
236
|
+
if (subs.rows.length === 0) {
|
|
237
|
+
const now = new Date();
|
|
238
|
+
const expiresAt = new Date(now.getTime() + 365 * 24 * 60 * 60 * 1000).toISOString();
|
|
239
|
+
const subResult = await db.execute({
|
|
240
|
+
sql: `INSERT INTO subscriptions (user_id, plan_id, status, amount, currency, starts_at, expires_at) VALUES (?, 'yearly', 'active', 49900, 'INR', ?, ?)`,
|
|
241
|
+
args: [user.id, now.toISOString(), expiresAt]
|
|
242
|
+
});
|
|
243
|
+
// Payment record for the subscription
|
|
244
|
+
const subId = Number(subResult.lastInsertRowid);
|
|
245
|
+
await db.execute({
|
|
246
|
+
sql: `INSERT INTO payments (user_id, subscription_id, plan_id, amount, currency, status, razorpay_order_id) VALUES (?, ?, 'yearly', 49900, 'INR', 'paid', ?)`,
|
|
247
|
+
args: [user.id, subId, `seed_order_${user.id}_${Date.now()}`]
|
|
248
|
+
});
|
|
249
|
+
backfilled++;
|
|
250
|
+
}
|
|
251
|
+
// Set access_override = 'paid' if not already set
|
|
252
|
+
if (!user.access_override || user.access_override !== 'paid') {
|
|
253
|
+
await db.execute({ sql: "UPDATE users SET access_override = 'paid' WHERE id = ?", args: [user.id] });
|
|
254
|
+
backfilled++;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (backfilled > 0) console.log(`${c.info('[DB]')} Backfilled ${backfilled} records for existing users`);
|
|
258
|
+
} catch (e) { console.warn('[DB] Backfill warning:', e.message); }
|
|
259
|
+
|
|
260
|
+
console.log(`${c.info('[DB]')} Migrations complete`);
|
|
261
|
+
} catch (error) {
|
|
262
|
+
console.error('Migration error:', error.message);
|
|
263
|
+
throw error;
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
// ─── Initialize ─────────────────────────────────────────────────────────────────
|
|
268
|
+
|
|
269
|
+
const initializeDatabase = async () => {
|
|
270
|
+
try {
|
|
271
|
+
try { await db.execute('PRAGMA foreign_keys = ON'); } catch (e) { console.warn('[DB] PRAGMA foreign_keys not supported:', e.message); }
|
|
272
|
+
|
|
273
|
+
const stmts = INIT_SQL.split(';').map(s => s.trim()).filter(s => s.length > 0 && !s.startsWith('--'));
|
|
274
|
+
for (const stmt of stmts) {
|
|
275
|
+
try { await db.execute(stmt); } catch (e) { console.error('[DB] Statement failed:', stmt.substring(0, 80), e.message); throw e; }
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
console.log(`${c.info('[DB]')} Initialized`);
|
|
279
|
+
await runMigrations();
|
|
280
|
+
} catch (error) {
|
|
281
|
+
console.error('DB init error:', error.message);
|
|
282
|
+
throw error;
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────────
|
|
287
|
+
|
|
288
|
+
const getRow = (result) => result.rows.length > 0 ? result.rows[0] : null;
|
|
289
|
+
const ensureForeignKeys = async () => { try { await db.execute('PRAGMA foreign_keys = ON'); } catch { /* Turso */ } };
|
|
290
|
+
|
|
291
|
+
// ─── User DB ────────────────────────────────────────────────────────────────────
|
|
292
|
+
|
|
293
|
+
const userDb = {
|
|
294
|
+
hasUsers: async () => {
|
|
295
|
+
const result = await db.execute('SELECT COUNT(*) as count FROM users');
|
|
296
|
+
return Number(getRow(result).count) > 0;
|
|
297
|
+
},
|
|
298
|
+
|
|
299
|
+
createUser: async (username, passwordHash, email = null, phone = null, firstName = null, lastName = null) => {
|
|
300
|
+
const result = await db.execute({
|
|
301
|
+
sql: 'INSERT INTO users (username, password_hash, email, phone, first_name, last_name) VALUES (?, ?, ?, ?, ?, ?)',
|
|
302
|
+
args: [username, passwordHash, email, phone, firstName, lastName]
|
|
303
|
+
});
|
|
304
|
+
const userId = Number(result.lastInsertRowid);
|
|
305
|
+
// Auto-assign user_code (upc-001, upc-002, ...)
|
|
306
|
+
const userCode = `upc-${String(userId).padStart(3, '0')}`;
|
|
307
|
+
try { await db.execute({ sql: 'UPDATE users SET user_code = ? WHERE id = ?', args: [userCode, userId] }); } catch { /* non-critical */ }
|
|
308
|
+
return { id: userId, username, first_name: firstName, last_name: lastName, user_code: userCode };
|
|
309
|
+
},
|
|
310
|
+
|
|
311
|
+
getUserByUsername: async (username) => {
|
|
312
|
+
const lower = (username || '').toLowerCase();
|
|
313
|
+
const result = await db.execute({
|
|
314
|
+
sql: 'SELECT * FROM users WHERE (LOWER(username) = ? OR LOWER(email) = ? OR phone = ?) AND is_active = 1',
|
|
315
|
+
args: [lower, lower, username]
|
|
316
|
+
});
|
|
317
|
+
return getRow(result);
|
|
318
|
+
},
|
|
319
|
+
|
|
320
|
+
updateLastLogin: async (userId) => {
|
|
321
|
+
await db.execute({ sql: 'UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE id = ?', args: [userId] });
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
getUserById: async (userId) => {
|
|
325
|
+
const result = await db.execute({
|
|
326
|
+
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',
|
|
327
|
+
args: [userId]
|
|
328
|
+
});
|
|
329
|
+
return getRow(result);
|
|
330
|
+
},
|
|
331
|
+
|
|
332
|
+
getFirstUser: async () => {
|
|
333
|
+
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');
|
|
334
|
+
return getRow(result);
|
|
335
|
+
},
|
|
336
|
+
|
|
337
|
+
updateGitConfig: async (userId, gitName, gitEmail) => {
|
|
338
|
+
await db.execute({ sql: 'UPDATE users SET git_name = ?, git_email = ? WHERE id = ?', args: [gitName, gitEmail, userId] });
|
|
339
|
+
},
|
|
340
|
+
|
|
341
|
+
getGitConfig: async (userId) => {
|
|
342
|
+
const result = await db.execute({ sql: 'SELECT git_name, git_email FROM users WHERE id = ?', args: [userId] });
|
|
343
|
+
return getRow(result);
|
|
344
|
+
},
|
|
345
|
+
|
|
346
|
+
completeOnboarding: async (userId) => {
|
|
347
|
+
await db.execute({ sql: 'UPDATE users SET has_completed_onboarding = 1 WHERE id = ?', args: [userId] });
|
|
348
|
+
},
|
|
349
|
+
|
|
350
|
+
hasCompletedOnboarding: async (userId) => {
|
|
351
|
+
const result = await db.execute({ sql: 'SELECT has_completed_onboarding FROM users WHERE id = ?', args: [userId] });
|
|
352
|
+
const row = getRow(result);
|
|
353
|
+
return row?.has_completed_onboarding === 1;
|
|
354
|
+
},
|
|
355
|
+
|
|
356
|
+
setAccessOverride: async (userId, value) => {
|
|
357
|
+
await db.execute({
|
|
358
|
+
sql: 'UPDATE users SET access_override = ? WHERE id = ?',
|
|
359
|
+
args: [value, userId]
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
// ─── API Keys DB ────────────────────────────────────────────────────────────────
|
|
365
|
+
|
|
366
|
+
const apiKeysDb = {
|
|
367
|
+
generateApiKey: () => 'up-cli-' + crypto.randomBytes(32).toString('hex'),
|
|
368
|
+
|
|
369
|
+
createApiKey: async (userId, keyName) => {
|
|
370
|
+
const apiKey = apiKeysDb.generateApiKey();
|
|
371
|
+
const result = await db.execute({
|
|
372
|
+
sql: 'INSERT INTO api_keys (user_id, key_name, api_key) VALUES (?, ?, ?)',
|
|
373
|
+
args: [userId, keyName, apiKey]
|
|
374
|
+
});
|
|
375
|
+
return { id: Number(result.lastInsertRowid), keyName, apiKey };
|
|
376
|
+
},
|
|
377
|
+
|
|
378
|
+
getApiKeys: async (userId) => {
|
|
379
|
+
const result = await db.execute({
|
|
380
|
+
sql: 'SELECT id, key_name, api_key, created_at, last_used, is_active FROM api_keys WHERE user_id = ? ORDER BY created_at DESC',
|
|
381
|
+
args: [userId]
|
|
382
|
+
});
|
|
383
|
+
return result.rows;
|
|
384
|
+
},
|
|
385
|
+
|
|
386
|
+
validateApiKey: async (apiKey) => {
|
|
387
|
+
const result = await db.execute({
|
|
388
|
+
sql: `SELECT u.id, u.username, ak.id as api_key_id
|
|
389
|
+
FROM api_keys ak JOIN users u ON ak.user_id = u.id
|
|
390
|
+
WHERE ak.api_key = ? AND ak.is_active = 1 AND u.is_active = 1`,
|
|
391
|
+
args: [apiKey]
|
|
392
|
+
});
|
|
393
|
+
const row = getRow(result);
|
|
394
|
+
if (row) {
|
|
395
|
+
await db.execute({ sql: 'UPDATE api_keys SET last_used = CURRENT_TIMESTAMP WHERE id = ?', args: [row.api_key_id] });
|
|
396
|
+
}
|
|
397
|
+
return row;
|
|
398
|
+
},
|
|
399
|
+
|
|
400
|
+
deleteApiKey: async (userId, apiKeyId) => {
|
|
401
|
+
await ensureForeignKeys();
|
|
402
|
+
const result = await db.execute({ sql: 'DELETE FROM api_keys WHERE id = ? AND user_id = ?', args: [apiKeyId, userId] });
|
|
403
|
+
return result.rowsAffected > 0;
|
|
404
|
+
},
|
|
405
|
+
|
|
406
|
+
toggleApiKey: async (userId, apiKeyId, isActive) => {
|
|
407
|
+
const result = await db.execute({
|
|
408
|
+
sql: 'UPDATE api_keys SET is_active = ? WHERE id = ? AND user_id = ?',
|
|
409
|
+
args: [isActive ? 1 : 0, apiKeyId, userId]
|
|
410
|
+
});
|
|
411
|
+
return result.rowsAffected > 0;
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
// ─── Credentials DB ─────────────────────────────────────────────────────────────
|
|
416
|
+
|
|
417
|
+
const credentialsDb = {
|
|
418
|
+
createCredential: async (userId, credentialName, credentialType, credentialValue, description = null) => {
|
|
419
|
+
const result = await db.execute({
|
|
420
|
+
sql: 'INSERT INTO user_credentials (user_id, credential_name, credential_type, credential_value, description) VALUES (?, ?, ?, ?, ?)',
|
|
421
|
+
args: [userId, credentialName, credentialType, credentialValue, description]
|
|
422
|
+
});
|
|
423
|
+
return { id: Number(result.lastInsertRowid), credentialName, credentialType };
|
|
424
|
+
},
|
|
425
|
+
|
|
426
|
+
getCredentials: async (userId, credentialType = null) => {
|
|
427
|
+
let sql = 'SELECT id, credential_name, credential_type, description, created_at, is_active FROM user_credentials WHERE user_id = ?';
|
|
428
|
+
const args = [userId];
|
|
429
|
+
if (credentialType) { sql += ' AND credential_type = ?'; args.push(credentialType); }
|
|
430
|
+
sql += ' ORDER BY created_at DESC';
|
|
431
|
+
return (await db.execute({ sql, args })).rows;
|
|
432
|
+
},
|
|
433
|
+
|
|
434
|
+
getActiveCredential: async (userId, credentialType) => {
|
|
435
|
+
const result = await db.execute({
|
|
436
|
+
sql: 'SELECT credential_value FROM user_credentials WHERE user_id = ? AND credential_type = ? AND is_active = 1 ORDER BY created_at DESC LIMIT 1',
|
|
437
|
+
args: [userId, credentialType]
|
|
438
|
+
});
|
|
439
|
+
return getRow(result)?.credential_value || null;
|
|
440
|
+
},
|
|
441
|
+
|
|
442
|
+
deleteCredential: async (userId, credentialId) => {
|
|
443
|
+
await ensureForeignKeys();
|
|
444
|
+
const result = await db.execute({ sql: 'DELETE FROM user_credentials WHERE id = ? AND user_id = ?', args: [credentialId, userId] });
|
|
445
|
+
return result.rowsAffected > 0;
|
|
446
|
+
},
|
|
447
|
+
|
|
448
|
+
toggleCredential: async (userId, credentialId, isActive) => {
|
|
449
|
+
const result = await db.execute({
|
|
450
|
+
sql: 'UPDATE user_credentials SET is_active = ? WHERE id = ? AND user_id = ?',
|
|
451
|
+
args: [isActive ? 1 : 0, credentialId, userId]
|
|
452
|
+
});
|
|
453
|
+
return result.rowsAffected > 0;
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
// Backward compat
|
|
458
|
+
const githubTokensDb = {
|
|
459
|
+
createGithubToken: (userId, tokenName, githubToken, description = null) => credentialsDb.createCredential(userId, tokenName, 'github_token', githubToken, description),
|
|
460
|
+
getGithubTokens: (userId) => credentialsDb.getCredentials(userId, 'github_token'),
|
|
461
|
+
getActiveGithubToken: (userId) => credentialsDb.getActiveCredential(userId, 'github_token'),
|
|
462
|
+
deleteGithubToken: (userId, tokenId) => credentialsDb.deleteCredential(userId, tokenId),
|
|
463
|
+
toggleGithubToken: (userId, tokenId, isActive) => credentialsDb.toggleCredential(userId, tokenId, isActive)
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
// ─── Plan Durations ─────────────────────────────────────────────────────────────
|
|
467
|
+
|
|
468
|
+
const PLAN_DURATIONS = {
|
|
469
|
+
monthly: 30,
|
|
470
|
+
'half-yearly': 180,
|
|
471
|
+
yearly: 365,
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
// ─── Subscription DB ────────────────────────────────────────────────────────────
|
|
475
|
+
|
|
476
|
+
const subscriptionDb = {
|
|
477
|
+
getActiveSub: async (userId) => {
|
|
478
|
+
const result = await db.execute({
|
|
479
|
+
sql: `SELECT * FROM subscriptions WHERE user_id = ? AND status = 'active' AND expires_at > CURRENT_TIMESTAMP ORDER BY expires_at DESC LIMIT 1`,
|
|
480
|
+
args: [userId]
|
|
481
|
+
});
|
|
482
|
+
return getRow(result);
|
|
483
|
+
},
|
|
484
|
+
|
|
485
|
+
getAllSubs: async (userId) => {
|
|
486
|
+
const result = await db.execute({
|
|
487
|
+
sql: 'SELECT * FROM subscriptions WHERE user_id = ? ORDER BY created_at DESC',
|
|
488
|
+
args: [userId]
|
|
489
|
+
});
|
|
490
|
+
return result.rows;
|
|
491
|
+
},
|
|
492
|
+
|
|
493
|
+
createSub: async (userId, planId, amount, currency, razorpayOrderId, razorpayPaymentId, razorpaySignature) => {
|
|
494
|
+
const days = PLAN_DURATIONS[planId] || 30;
|
|
495
|
+
|
|
496
|
+
// Check if user has an active sub — extend from its expiry, otherwise start now
|
|
497
|
+
const existing = await subscriptionDb.getActiveSub(userId);
|
|
498
|
+
const startsAt = existing ? existing.expires_at : new Date().toISOString();
|
|
499
|
+
const startsDate = new Date(startsAt);
|
|
500
|
+
const expiresAt = new Date(startsDate.getTime() + days * 24 * 60 * 60 * 1000).toISOString();
|
|
501
|
+
|
|
502
|
+
const result = await db.execute({
|
|
503
|
+
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', ?, ?, ?, ?, ?, ?, ?)`,
|
|
504
|
+
args: [userId, planId, amount, currency, startsAt, expiresAt, razorpayOrderId, razorpayPaymentId, razorpaySignature]
|
|
505
|
+
});
|
|
506
|
+
return { id: Number(result.lastInsertRowid), planId, status: 'active', startsAt, expiresAt };
|
|
507
|
+
},
|
|
508
|
+
|
|
509
|
+
cancelSub: async (userId, subId) => {
|
|
510
|
+
const result = await db.execute({
|
|
511
|
+
sql: `UPDATE subscriptions SET status = 'cancelled', updated_at = CURRENT_TIMESTAMP WHERE id = ? AND user_id = ?`,
|
|
512
|
+
args: [subId, userId]
|
|
513
|
+
});
|
|
514
|
+
return result.rowsAffected > 0;
|
|
515
|
+
},
|
|
516
|
+
|
|
517
|
+
expireOverdue: async () => {
|
|
518
|
+
const result = await db.execute(
|
|
519
|
+
`UPDATE subscriptions SET status = 'expired', updated_at = CURRENT_TIMESTAMP WHERE status = 'active' AND expires_at <= CURRENT_TIMESTAMP`
|
|
520
|
+
);
|
|
521
|
+
return result.rowsAffected;
|
|
522
|
+
},
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
// ─── Payment DB ─────────────────────────────────────────────────────────────────
|
|
526
|
+
|
|
527
|
+
const paymentDb = {
|
|
528
|
+
createPayment: async (userId, planId, amount, currency, razorpayOrderId) => {
|
|
529
|
+
const result = await db.execute({
|
|
530
|
+
sql: `INSERT INTO payments (user_id, plan_id, amount, currency, razorpay_order_id, status) VALUES (?, ?, ?, ?, ?, 'pending')`,
|
|
531
|
+
args: [userId, planId, amount, currency, razorpayOrderId]
|
|
532
|
+
});
|
|
533
|
+
return { id: Number(result.lastInsertRowid), razorpayOrderId };
|
|
534
|
+
},
|
|
535
|
+
|
|
536
|
+
getByOrderId: async (razorpayOrderId) => {
|
|
537
|
+
const result = await db.execute({
|
|
538
|
+
sql: 'SELECT * FROM payments WHERE razorpay_order_id = ?',
|
|
539
|
+
args: [razorpayOrderId]
|
|
540
|
+
});
|
|
541
|
+
return getRow(result);
|
|
542
|
+
},
|
|
543
|
+
|
|
544
|
+
markPaid: async (razorpayOrderId, razorpayPaymentId, razorpaySignature, subscriptionId) => {
|
|
545
|
+
await db.execute({
|
|
546
|
+
sql: `UPDATE payments SET status = 'paid', razorpay_payment_id = ?, razorpay_signature = ?, subscription_id = ? WHERE razorpay_order_id = ?`,
|
|
547
|
+
args: [razorpayPaymentId, razorpaySignature, subscriptionId, razorpayOrderId]
|
|
548
|
+
});
|
|
549
|
+
},
|
|
550
|
+
|
|
551
|
+
markFailed: async (razorpayOrderId) => {
|
|
552
|
+
await db.execute({
|
|
553
|
+
sql: `UPDATE payments SET status = 'failed' WHERE razorpay_order_id = ?`,
|
|
554
|
+
args: [razorpayOrderId]
|
|
555
|
+
});
|
|
556
|
+
},
|
|
557
|
+
|
|
558
|
+
getUserPayments: async (userId) => {
|
|
559
|
+
const result = await db.execute({
|
|
560
|
+
sql: 'SELECT * FROM payments WHERE user_id = ? ORDER BY created_at DESC',
|
|
561
|
+
args: [userId]
|
|
562
|
+
});
|
|
563
|
+
return result.rows;
|
|
564
|
+
},
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
// ─── Relay Tokens DB ────────────────────────────────────────────────────────────
|
|
568
|
+
|
|
569
|
+
const relayTokensDb = {
|
|
570
|
+
generateToken: () => 'upfyn_' + crypto.randomBytes(32).toString('hex'),
|
|
571
|
+
|
|
572
|
+
createToken: async (userId, name = 'default') => {
|
|
573
|
+
const token = relayTokensDb.generateToken();
|
|
574
|
+
await db.execute({ sql: 'INSERT INTO relay_tokens (user_id, token, name) VALUES (?, ?, ?)', args: [userId, token, name] });
|
|
575
|
+
return { token, name };
|
|
576
|
+
},
|
|
577
|
+
|
|
578
|
+
validateToken: async (token) => {
|
|
579
|
+
try {
|
|
580
|
+
const result = await db.execute({
|
|
581
|
+
sql: `SELECT rt.id, rt.user_id, rt.name, u.id as uid, u.username
|
|
582
|
+
FROM relay_tokens rt JOIN users u ON rt.user_id = u.id
|
|
583
|
+
WHERE rt.token = ? AND rt.is_active = 1 AND u.is_active = 1`,
|
|
584
|
+
args: [token]
|
|
585
|
+
});
|
|
586
|
+
const row = getRow(result);
|
|
587
|
+
if (row) await db.execute({ sql: 'UPDATE relay_tokens SET last_connected = CURRENT_TIMESTAMP WHERE id = ?', args: [row.id] });
|
|
588
|
+
return row;
|
|
589
|
+
} catch { return null; }
|
|
590
|
+
},
|
|
591
|
+
|
|
592
|
+
getTokens: async (userId) => {
|
|
593
|
+
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;
|
|
594
|
+
},
|
|
595
|
+
|
|
596
|
+
deleteToken: async (userId, tokenId) => {
|
|
597
|
+
await ensureForeignKeys();
|
|
598
|
+
return (await db.execute({ sql: 'DELETE FROM relay_tokens WHERE id = ? AND user_id = ?', args: [tokenId, userId] })).rowsAffected > 0;
|
|
599
|
+
},
|
|
600
|
+
|
|
601
|
+
toggleToken: async (userId, tokenId, isActive) => {
|
|
602
|
+
return (await db.execute({ sql: 'UPDATE relay_tokens SET is_active = ? WHERE id = ? AND user_id = ?', args: [isActive ? 1 : 0, tokenId, userId] })).rowsAffected > 0;
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
|
|
606
|
+
export { db, initializeDatabase, userDb, apiKeysDb, credentialsDb, relayTokensDb, githubTokensDb, subscriptionDb, paymentDb, PLAN_DURATIONS };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
-- Initialize authentication database
|
|
2
|
+
PRAGMA foreign_keys = ON;
|
|
3
|
+
|
|
4
|
+
-- Users table (single user system)
|
|
5
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
6
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
7
|
+
username TEXT UNIQUE NOT NULL,
|
|
8
|
+
password_hash TEXT NOT NULL,
|
|
9
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
10
|
+
last_login DATETIME,
|
|
11
|
+
is_active BOOLEAN DEFAULT 1,
|
|
12
|
+
email TEXT,
|
|
13
|
+
phone TEXT,
|
|
14
|
+
git_name TEXT,
|
|
15
|
+
git_email TEXT,
|
|
16
|
+
has_completed_onboarding BOOLEAN DEFAULT 0
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
-- Indexes for performance
|
|
20
|
+
CREATE INDEX IF NOT EXISTS idx_users_username ON users(username);
|
|
21
|
+
CREATE INDEX IF NOT EXISTS idx_users_active ON users(is_active);
|
|
22
|
+
|
|
23
|
+
-- API Keys table for external API access
|
|
24
|
+
CREATE TABLE IF NOT EXISTS api_keys (
|
|
25
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
26
|
+
user_id INTEGER NOT NULL,
|
|
27
|
+
key_name TEXT NOT NULL,
|
|
28
|
+
api_key TEXT UNIQUE NOT NULL,
|
|
29
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
30
|
+
last_used DATETIME,
|
|
31
|
+
is_active BOOLEAN DEFAULT 1,
|
|
32
|
+
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
CREATE INDEX IF NOT EXISTS idx_api_keys_key ON api_keys(api_key);
|
|
36
|
+
CREATE INDEX IF NOT EXISTS idx_api_keys_user_id ON api_keys(user_id);
|
|
37
|
+
CREATE INDEX IF NOT EXISTS idx_api_keys_active ON api_keys(is_active);
|
|
38
|
+
|
|
39
|
+
-- User credentials table for storing various tokens/credentials (GitHub, GitLab, etc.)
|
|
40
|
+
CREATE TABLE IF NOT EXISTS user_credentials (
|
|
41
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
42
|
+
user_id INTEGER NOT NULL,
|
|
43
|
+
credential_name TEXT NOT NULL,
|
|
44
|
+
credential_type TEXT NOT NULL, -- 'github_token', 'gitlab_token', 'bitbucket_token', etc.
|
|
45
|
+
credential_value TEXT NOT NULL,
|
|
46
|
+
description TEXT,
|
|
47
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
48
|
+
is_active BOOLEAN DEFAULT 1,
|
|
49
|
+
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
CREATE INDEX IF NOT EXISTS idx_user_credentials_user_id ON user_credentials(user_id);
|
|
53
|
+
CREATE INDEX IF NOT EXISTS idx_user_credentials_type ON user_credentials(credential_type);
|
|
54
|
+
CREATE INDEX IF NOT EXISTS idx_user_credentials_active ON user_credentials(is_active);
|
|
55
|
+
|
|
56
|
+
-- Relay tokens for local machine ↔ server connections
|
|
57
|
+
CREATE TABLE IF NOT EXISTS relay_tokens (
|
|
58
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
59
|
+
user_id INTEGER NOT NULL,
|
|
60
|
+
token TEXT UNIQUE NOT NULL,
|
|
61
|
+
name TEXT NOT NULL DEFAULT 'default',
|
|
62
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
63
|
+
last_connected DATETIME,
|
|
64
|
+
is_active BOOLEAN DEFAULT 1,
|
|
65
|
+
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
CREATE INDEX IF NOT EXISTS idx_relay_tokens_token ON relay_tokens(token);
|
|
69
|
+
CREATE INDEX IF NOT EXISTS idx_relay_tokens_user_id ON relay_tokens(user_id);
|
|
70
|
+
CREATE INDEX IF NOT EXISTS idx_relay_tokens_active ON relay_tokens(is_active);
|