upfynai-code 3.0.3 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. package/README.md +66 -91
  2. package/bin/cli.js +191 -0
  3. package/{client/dist → dist/client}/api-docs.html +838 -838
  4. package/{client/dist/assets/AppContent-Bvg0CPCO.js → dist/client/assets/AppContent-BofJquUs.js} +43 -43
  5. package/dist/client/assets/BrowserPanel-CSvD4jOX.js +2 -0
  6. package/dist/client/assets/CanvasFullScreen-onRfarpc.js +1 -0
  7. package/dist/client/assets/CanvasWorkspace-DvGKdL-k.js +259 -0
  8. package/dist/client/assets/DashboardPanel-DqAHbXDO.js +1 -0
  9. package/dist/client/assets/FileTree-BE0h-9M9.js +1 -0
  10. package/{client/dist/assets/GitPanel-RtyZUIWS.js → dist/client/assets/GitPanel-DdeJ0bp5.js} +2 -2
  11. package/{client/dist/assets/LoginModal-BWep8a6g.js → dist/client/assets/LoginModal-BP0pCTrH.js} +3 -3
  12. package/{client/dist/assets/MarkdownPreview-DHmk3qzu.js → dist/client/assets/MarkdownPreview-CESjI261.js} +1 -1
  13. package/dist/client/assets/MermaidBlock-D0rfEhrT.js +2 -0
  14. package/dist/client/assets/Onboarding-B2zQy-_6.js +1 -0
  15. package/dist/client/assets/SetupForm-Be7-WBe-.js +1 -0
  16. package/dist/client/assets/WorkflowsPanel-CusLbVJ6.js +1 -0
  17. package/{client/dist/assets/index-C5ptjuTl.js → dist/client/assets/index-BQy15irW.js} +25 -25
  18. package/dist/client/assets/index-CS0fDqEC.js +1 -0
  19. package/dist/client/assets/index-DYLSCCCp.css +1 -0
  20. package/dist/client/assets/vendor-canvas-QWTduIvM.js +23 -0
  21. package/{client/dist/assets/vendor-codemirror-CbtmxxaB.js → dist/client/assets/vendor-codemirror-D2ALgpaX.js} +1 -1
  22. package/{client/dist/assets/vendor-icons-BaD0x9SL.js → dist/client/assets/vendor-icons-kix3Gb31.js} +178 -138
  23. package/{client/dist/assets/vendor-mermaid-CH7SGc99.js → dist/client/assets/vendor-mermaid-CS3J4_Bz.js} +329 -326
  24. package/{client/dist/assets/vendor-syntax-DuHI9Ok6.js → dist/client/assets/vendor-syntax-LS_Nt30I.js} +1 -1
  25. package/{client/dist → dist/client}/clear-cache.html +85 -85
  26. package/dist/client/favicon.png +0 -0
  27. package/dist/client/favicon.svg +15 -0
  28. package/{client/dist → dist/client}/index.html +17 -17
  29. package/{client/dist → dist/client}/manifest.json +15 -15
  30. package/{client/dist → dist/client}/mcp-docs.html +108 -108
  31. package/{client/dist → dist/client}/offline.html +84 -84
  32. package/{client/dist → dist/client}/sw.js +82 -82
  33. package/package.json +55 -104
  34. package/scripts/postinstall.js +9 -0
  35. package/scripts/prepublish.js +77 -0
  36. package/src/animation.js +228 -0
  37. package/src/auth.js +142 -0
  38. package/src/config.js +40 -0
  39. package/src/connect.js +416 -0
  40. package/src/launch.js +81 -0
  41. package/src/mcp.js +57 -0
  42. package/src/permissions.js +140 -0
  43. package/src/persistent-shell.js +261 -0
  44. package/src/server.js +54 -0
  45. package/client/dist/assets/CanvasFullScreen-BdiJ35aq.js +0 -1
  46. package/client/dist/assets/CanvasWorkspace-Bk9R9_e0.js +0 -163
  47. package/client/dist/assets/DashboardPanel-CblJfTGi.js +0 -1
  48. package/client/dist/assets/FileTree-BDUnBheV.js +0 -1
  49. package/client/dist/assets/MermaidBlock-BuBc_G-F.js +0 -2
  50. package/client/dist/assets/Onboarding-Drnlt75a.js +0 -1
  51. package/client/dist/assets/SetupForm-CtCKitZG.js +0 -1
  52. package/client/dist/assets/WorkflowsPanel-B2mIXDvD.js +0 -1
  53. package/client/dist/assets/index-BFuqS0tY.css +0 -1
  54. package/client/dist/assets/vendor-canvas-D39yWul6.js +0 -49
  55. package/client/dist/favicon.png +0 -0
  56. package/client/dist/favicon.svg +0 -5
  57. package/commands/upfynai-connect.md +0 -59
  58. package/commands/upfynai-disconnect.md +0 -31
  59. package/commands/upfynai-doctor.md +0 -99
  60. package/commands/upfynai-export.md +0 -49
  61. package/commands/upfynai-local.md +0 -82
  62. package/commands/upfynai-status.md +0 -75
  63. package/commands/upfynai-stop.md +0 -49
  64. package/commands/upfynai-uninstall.md +0 -58
  65. package/commands/upfynai.md +0 -69
  66. package/scripts/build-client.js +0 -17
  67. package/scripts/fix-node-pty.js +0 -67
  68. package/scripts/install-commands.js +0 -78
  69. package/server/agent-loop.js +0 -242
  70. package/server/auto-compact.js +0 -99
  71. package/server/claude-sdk.js +0 -797
  72. package/server/cli-ui.js +0 -798
  73. package/server/cli.js +0 -751
  74. package/server/constants/config.js +0 -31
  75. package/server/cursor-cli.js +0 -270
  76. package/server/database/auth.db +0 -0
  77. package/server/database/db.js +0 -1451
  78. package/server/database/init.sql +0 -70
  79. package/server/index.js +0 -3814
  80. package/server/load-env.js +0 -26
  81. package/server/mcp-server.js +0 -621
  82. package/server/middleware/auth.js +0 -181
  83. package/server/middleware/relayHelpers.js +0 -44
  84. package/server/middleware/sandboxRouter.js +0 -174
  85. package/server/openai-codex.js +0 -403
  86. package/server/openrouter.js +0 -137
  87. package/server/projects.js +0 -1807
  88. package/server/provider-factory.js +0 -174
  89. package/server/relay-client.js +0 -390
  90. package/server/routes/agent.js +0 -1234
  91. package/server/routes/auth.js +0 -559
  92. package/server/routes/canvas.js +0 -53
  93. package/server/routes/cli-auth.js +0 -263
  94. package/server/routes/codex.js +0 -396
  95. package/server/routes/commands.js +0 -707
  96. package/server/routes/composio.js +0 -176
  97. package/server/routes/cursor.js +0 -770
  98. package/server/routes/dashboard.js +0 -295
  99. package/server/routes/git.js +0 -1208
  100. package/server/routes/keys.js +0 -34
  101. package/server/routes/mcp-utils.js +0 -48
  102. package/server/routes/mcp.js +0 -661
  103. package/server/routes/payments.js +0 -227
  104. package/server/routes/projects.js +0 -655
  105. package/server/routes/sessions.js +0 -146
  106. package/server/routes/settings.js +0 -261
  107. package/server/routes/taskmaster.js +0 -1928
  108. package/server/routes/user.js +0 -106
  109. package/server/routes/vapi-chat.js +0 -624
  110. package/server/routes/voice.js +0 -235
  111. package/server/routes/webhooks.js +0 -166
  112. package/server/routes/workflows.js +0 -312
  113. package/server/sandbox.js +0 -120
  114. package/server/services/composio.js +0 -204
  115. package/server/services/sessionRegistry.js +0 -139
  116. package/server/services/whisperService.js +0 -84
  117. package/server/services/workflowScheduler.js +0 -211
  118. package/server/tests/relay-flow.test.js +0 -570
  119. package/server/tests/sessions.test.js +0 -259
  120. package/server/utils/commandParser.js +0 -303
  121. package/server/utils/email.js +0 -66
  122. package/server/utils/gitConfig.js +0 -24
  123. package/server/utils/mcp-detector.js +0 -198
  124. package/server/utils/taskmaster-websocket.js +0 -129
  125. package/shared/integrationCatalog.d.ts +0 -12
  126. package/shared/integrationCatalog.js +0 -172
  127. package/shared/modelConstants.js +0 -96
  128. /package/{shared → dist}/agents/claude.js +0 -0
  129. /package/{shared → dist}/agents/codex.js +0 -0
  130. /package/{shared → dist}/agents/cursor.js +0 -0
  131. /package/{shared → dist}/agents/detect.js +0 -0
  132. /package/{shared → dist}/agents/exec.js +0 -0
  133. /package/{shared → dist}/agents/files.js +0 -0
  134. /package/{shared → dist}/agents/git.js +0 -0
  135. /package/{shared → dist}/agents/gitagent.js +0 -0
  136. /package/{shared → dist}/agents/index.js +0 -0
  137. /package/{shared → dist}/agents/shell.js +0 -0
  138. /package/{shared → dist}/agents/utils.js +0 -0
  139. /package/{client/dist → dist/client}/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  140. /package/{client/dist → dist/client}/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  141. /package/{client/dist → dist/client}/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  142. /package/{client/dist → dist/client}/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  143. /package/{client/dist → dist/client}/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  144. /package/{client/dist → dist/client}/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  145. /package/{client/dist → dist/client}/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  146. /package/{client/dist → dist/client}/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  147. /package/{client/dist → dist/client}/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  148. /package/{client/dist → dist/client}/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  149. /package/{client/dist → dist/client}/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  150. /package/{client/dist → dist/client}/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  151. /package/{client/dist → dist/client}/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  152. /package/{client/dist → dist/client}/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  153. /package/{client/dist → dist/client}/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  154. /package/{client/dist → dist/client}/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  155. /package/{client/dist → dist/client}/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  156. /package/{client/dist → dist/client}/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  157. /package/{client/dist → dist/client}/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  158. /package/{client/dist → dist/client}/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  159. /package/{client/dist → dist/client}/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  160. /package/{client/dist → dist/client}/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  161. /package/{client/dist → dist/client}/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  162. /package/{client/dist → dist/client}/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  163. /package/{client/dist → dist/client}/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  164. /package/{client/dist → dist/client}/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  165. /package/{client/dist → dist/client}/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  166. /package/{client/dist → dist/client}/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  167. /package/{client/dist → dist/client}/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  168. /package/{client/dist → dist/client}/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  169. /package/{client/dist → dist/client}/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  170. /package/{client/dist → dist/client}/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  171. /package/{client/dist → dist/client}/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  172. /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  173. /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  174. /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  175. /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  176. /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  177. /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  178. /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  179. /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  180. /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  181. /package/{client/dist → dist/client}/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  182. /package/{client/dist → dist/client}/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  183. /package/{client/dist → dist/client}/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  184. /package/{client/dist → dist/client}/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  185. /package/{client/dist → dist/client}/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  186. /package/{client/dist → dist/client}/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  187. /package/{client/dist → dist/client}/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  188. /package/{client/dist → dist/client}/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  189. /package/{client/dist → dist/client}/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  190. /package/{client/dist → dist/client}/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  191. /package/{client/dist → dist/client}/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  192. /package/{client/dist → dist/client}/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  193. /package/{client/dist → dist/client}/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  194. /package/{client/dist → dist/client}/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  195. /package/{client/dist → dist/client}/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  196. /package/{client/dist → dist/client}/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  197. /package/{client/dist → dist/client}/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  198. /package/{client/dist → dist/client}/assets/PreviewPanel-CqCa92Tf.js +0 -0
  199. /package/{client/dist → dist/client}/assets/pdf-CE_K4jFx.js +0 -0
  200. /package/{client/dist → dist/client}/assets/vendor-canvas-BZV40eAE.css +0 -0
  201. /package/{client/dist → dist/client}/assets/vendor-diff-DNQpbhrT.js +0 -0
  202. /package/{client/dist → dist/client}/assets/vendor-i18n-DCFGyhQR.js +0 -0
  203. /package/{client/dist → dist/client}/assets/vendor-markdown-CimbIo6Y.js +0 -0
  204. /package/{client/dist → dist/client}/assets/vendor-react-96lCPsRK.js +0 -0
  205. /package/{client/dist → dist/client}/assets/vendor-xterm-CZq1hqo1.js +0 -0
  206. /package/{client/dist → dist/client}/assets/vendor-xterm-qxJ8_QYu.css +0 -0
  207. /package/{client/dist → dist/client}/convert-icons.md +0 -0
  208. /package/{client/dist → dist/client}/generate-icons.js +0 -0
  209. /package/{client/dist → dist/client}/icons/claude-ai-icon.svg +0 -0
  210. /package/{client/dist → dist/client}/icons/codex-white.svg +0 -0
  211. /package/{client/dist → dist/client}/icons/codex.svg +0 -0
  212. /package/{client/dist → dist/client}/icons/cursor-white.svg +0 -0
  213. /package/{client/dist → dist/client}/icons/cursor.svg +0 -0
  214. /package/{client/dist → dist/client}/icons/icon-128x128.png +0 -0
  215. /package/{client/dist → dist/client}/icons/icon-128x128.svg +0 -0
  216. /package/{client/dist → dist/client}/icons/icon-144x144.png +0 -0
  217. /package/{client/dist → dist/client}/icons/icon-144x144.svg +0 -0
  218. /package/{client/dist → dist/client}/icons/icon-152x152.png +0 -0
  219. /package/{client/dist → dist/client}/icons/icon-152x152.svg +0 -0
  220. /package/{client/dist → dist/client}/icons/icon-192x192.png +0 -0
  221. /package/{client/dist → dist/client}/icons/icon-192x192.svg +0 -0
  222. /package/{client/dist → dist/client}/icons/icon-384x384.png +0 -0
  223. /package/{client/dist → dist/client}/icons/icon-384x384.svg +0 -0
  224. /package/{client/dist → dist/client}/icons/icon-512x512.png +0 -0
  225. /package/{client/dist → dist/client}/icons/icon-512x512.svg +0 -0
  226. /package/{client/dist → dist/client}/icons/icon-72x72.png +0 -0
  227. /package/{client/dist → dist/client}/icons/icon-72x72.svg +0 -0
  228. /package/{client/dist → dist/client}/icons/icon-96x96.png +0 -0
  229. /package/{client/dist → dist/client}/icons/icon-96x96.svg +0 -0
  230. /package/{client/dist → dist/client}/icons/icon-template.svg +0 -0
  231. /package/{client/dist → dist/client}/logo-128.png +0 -0
  232. /package/{client/dist → dist/client}/logo-256.png +0 -0
  233. /package/{client/dist → dist/client}/logo-32.png +0 -0
  234. /package/{client/dist → dist/client}/logo-512.png +0 -0
  235. /package/{client/dist → dist/client}/logo-64.png +0 -0
  236. /package/{client/dist → dist/client}/logo.svg +0 -0
  237. /package/{client/dist → dist/client}/screenshots/cli-selection.png +0 -0
  238. /package/{client/dist → dist/client}/screenshots/desktop-main.png +0 -0
  239. /package/{client/dist → dist/client}/screenshots/mobile-chat.png +0 -0
  240. /package/{client/dist → dist/client}/screenshots/tools-modal.png +0 -0
  241. /package/{shared → dist}/gitagent/index.js +0 -0
  242. /package/{shared → dist}/gitagent/parser.js +0 -0
  243. /package/{shared → dist}/gitagent/prompt-builder.js +0 -0
@@ -1,295 +0,0 @@
1
- import { Router } from 'express';
2
- import { getProjects, getSessions } from '../projects.js';
3
- import { userDb, subscriptionDb, paymentDb, apiKeysDb, credentialsDb, relayTokensDb, webhookDb, workflowDb } from '../database/db.js';
4
-
5
- const router = Router();
6
-
7
- // AI provider credential types
8
- const AI_PROVIDER_TYPES = ['anthropic_key', 'openai_key', 'openrouter_key', 'google_key'];
9
- const PROVIDER_LABELS = {
10
- anthropic_key: 'Anthropic',
11
- openai_key: 'OpenAI',
12
- openrouter_key: 'OpenRouter',
13
- google_key: 'Google',
14
- };
15
-
16
- // Per-user stats cache (60s TTL)
17
- const statsCache = new Map();
18
- const CACHE_TTL = 60_000;
19
-
20
- function getCachedStats(userId) {
21
- const entry = statsCache.get(userId);
22
- if (entry && Date.now() - entry.ts < CACHE_TTL) return entry.data;
23
- return null;
24
- }
25
- function setCachedStats(userId, data) {
26
- statsCache.set(userId, { data, ts: Date.now() });
27
- }
28
- function invalidateStatsCache(userId) {
29
- statsCache.delete(userId);
30
- }
31
-
32
- // Periodic expiry of overdue subs (every 5 min, not per-request)
33
- let lastExpireRun = 0;
34
- async function maybeExpireOverdue() {
35
- if (Date.now() - lastExpireRun < 300_000) return;
36
- lastExpireRun = Date.now();
37
- try { await subscriptionDb.expireOverdue(); } catch { /* non-critical */ }
38
- }
39
-
40
- /**
41
- * GET /api/dashboard/user-stats — Per-user dashboard data from Turso
42
- * Aggregates: account, subscription, payments, API keys, AI providers, relay tokens, webhooks, workflows
43
- */
44
- router.get('/user-stats', async (req, res) => {
45
- try {
46
- const userId = req.user.id;
47
-
48
- // Return cached stats if fresh (< 60s)
49
- const cached = getCachedStats(userId);
50
- if (cached) return res.json(cached);
51
-
52
- // Run all DB queries in parallel
53
- const [
54
- fullUser,
55
- activeSub,
56
- allSubs,
57
- payments,
58
- apiKeys,
59
- credentials,
60
- relayTokens,
61
- webhooks,
62
- workflows,
63
- ] = await Promise.all([
64
- userDb.getUserById(userId),
65
- subscriptionDb.getActiveSub(userId).catch(() => null),
66
- subscriptionDb.getAllSubs(userId).catch(() => []),
67
- paymentDb.getUserPayments(userId).catch(() => []),
68
- apiKeysDb.getApiKeys(userId).catch(() => []),
69
- credentialsDb.getCredentials(userId).catch(() => []),
70
- relayTokensDb.getTokens(userId).catch(() => []),
71
- webhookDb.getAll(userId).catch(() => []),
72
- workflowDb.getAll(userId).catch(() => []),
73
- ]);
74
-
75
- // Expire overdue subs periodically (not per-request)
76
- maybeExpireOverdue();
77
-
78
- // Account
79
- const account = fullUser ? {
80
- username: fullUser.username,
81
- email: fullUser.email,
82
- phone: fullUser.phone,
83
- firstName: fullUser.first_name,
84
- lastName: fullUser.last_name,
85
- userCode: fullUser.user_code,
86
- createdAt: fullUser.created_at,
87
- lastLogin: fullUser.last_login,
88
- accessOverride: fullUser.access_override,
89
- } : null;
90
-
91
- // Subscription
92
- const subscription = {
93
- active: activeSub ? {
94
- id: activeSub.id,
95
- planId: activeSub.plan_id,
96
- status: activeSub.status,
97
- startsAt: activeSub.starts_at,
98
- expiresAt: activeSub.expires_at,
99
- amount: activeSub.amount,
100
- currency: activeSub.currency || 'INR',
101
- } : null,
102
- history: allSubs.map(s => ({
103
- id: s.id,
104
- planId: s.plan_id,
105
- status: s.status,
106
- startsAt: s.starts_at,
107
- expiresAt: s.expires_at,
108
- amount: s.amount,
109
- currency: s.currency || 'INR',
110
- createdAt: s.created_at,
111
- })),
112
- };
113
-
114
- // Payments
115
- const paidPayments = payments.filter(p => p.status === 'paid');
116
- const totalAmountPaise = paidPayments.reduce((sum, p) => sum + (Number(p.amount) || 0), 0);
117
- const paymentData = {
118
- total: payments.length,
119
- paid: paidPayments.length,
120
- totalAmountPaise,
121
- recent: payments.slice(0, 10).map(p => ({
122
- id: p.id,
123
- planId: p.plan_id,
124
- amount: p.amount,
125
- currency: p.currency || 'INR',
126
- status: p.status,
127
- createdAt: p.created_at,
128
- })),
129
- };
130
-
131
- // API Keys (include full list for dashboard display)
132
- const activeKeys = apiKeys.filter(k => k.is_active);
133
- const apiKeyData = {
134
- total: apiKeys.length,
135
- active: activeKeys.length,
136
- keys: apiKeys.map(k => ({
137
- id: k.id,
138
- keyName: k.key_name || 'default',
139
- keyPrefix: k.api_key ? k.api_key.slice(0, 12) : '',
140
- keySuffix: k.api_key ? k.api_key.slice(-6) : '',
141
- fullKey: k.api_key,
142
- isActive: !!k.is_active,
143
- createdAt: k.created_at,
144
- lastUsed: k.last_used,
145
- })),
146
- };
147
-
148
- // AI Providers (filter credentials by AI provider types)
149
- const aiCreds = credentials.filter(c => AI_PROVIDER_TYPES.includes(c.credential_type));
150
- const activeAiCreds = aiCreds.filter(c => c.is_active);
151
- const aiProviderData = {
152
- total: aiCreds.length,
153
- active: activeAiCreds.length,
154
- providers: activeAiCreds.map(c => PROVIDER_LABELS[c.credential_type] || c.credential_type),
155
- };
156
-
157
- // Relay Tokens
158
- const activeTokens = relayTokens.filter(t => t.is_active);
159
- const lastConnected = relayTokens
160
- .filter(t => t.last_connected)
161
- .sort((a, b) => new Date(b.last_connected) - new Date(a.last_connected))[0]?.last_connected || null;
162
- const relayData = {
163
- total: relayTokens.length,
164
- active: activeTokens.length,
165
- lastConnected,
166
- };
167
-
168
- // Webhooks
169
- const activeWebhooks = webhooks.filter(w => w.is_active);
170
- const webhookData = {
171
- total: webhooks.length,
172
- active: activeWebhooks.length,
173
- };
174
-
175
- // Workflows
176
- const activeWorkflows = workflows.filter(w => w.is_active);
177
- const workflowData = {
178
- total: workflows.length,
179
- active: activeWorkflows.length,
180
- };
181
-
182
- const result = {
183
- account,
184
- subscription,
185
- payments: paymentData,
186
- apiKeys: apiKeyData,
187
- aiProviders: aiProviderData,
188
- relayTokens: relayData,
189
- webhooks: webhookData,
190
- workflows: workflowData,
191
- };
192
-
193
- setCachedStats(userId, result);
194
- res.json(result);
195
- } catch (error) {
196
- res.status(500).json({ error: 'Failed to fetch user stats' });
197
- }
198
- });
199
-
200
- /**
201
- * GET /api/dashboard/stats — Dashboard usage analytics (local projects)
202
- * Returns session counts, provider breakdown, and today's activity.
203
- */
204
- router.get('/stats', async (req, res) => {
205
- try {
206
- const userId = req.user?.id || req.user?.userId;
207
- const projects = await getProjects(null, userId);
208
- let totalSessions = 0;
209
- let todaySessions = 0;
210
- const providers = {};
211
- const today = new Date().toISOString().slice(0, 10);
212
-
213
- for (const project of projects) {
214
- // Count sessions per provider
215
- for (const provider of ['claude', 'cursor', 'codex']) {
216
- try {
217
- const sessions = await getSessions(project.name, provider);
218
- if (sessions && sessions.length) {
219
- totalSessions += sessions.length;
220
- providers[provider] = (providers[provider] || 0) + sessions.length;
221
-
222
- // Count today's sessions
223
- for (const s of sessions) {
224
- const created = s.created_at || s.createdAt || '';
225
- if (created.startsWith(today)) {
226
- todaySessions++;
227
- }
228
- }
229
- }
230
- } catch (e) {
231
- // Provider not available for this project
232
- }
233
- }
234
- }
235
-
236
- res.json({
237
- total: totalSessions,
238
- today: todaySessions,
239
- providers,
240
- projectCount: projects.length,
241
- });
242
- } catch (error) {
243
- res.status(500).json({ error: 'Failed to fetch dashboard stats' });
244
- }
245
- });
246
-
247
- /**
248
- * POST /api/dashboard/api-keys — Create a new API key
249
- */
250
- router.post('/api-keys', async (req, res) => {
251
- try {
252
- const userId = req.user.id;
253
- const keyName = req.body.keyName || 'default';
254
- const result = await apiKeysDb.createApiKey(userId, keyName);
255
- invalidateStatsCache(userId);
256
- res.json({ success: true, key: result });
257
- } catch (error) {
258
- res.status(500).json({ error: 'Failed to create API key' });
259
- }
260
- });
261
-
262
- /**
263
- * DELETE /api/dashboard/api-keys/:id — Delete a specific API key
264
- */
265
- router.delete('/api-keys/:id', async (req, res) => {
266
- try {
267
- const userId = req.user.id;
268
- const keyId = Number(req.params.id);
269
- const deleted = await apiKeysDb.deleteApiKey(userId, keyId);
270
- if (!deleted) return res.status(404).json({ error: 'API key not found' });
271
- invalidateStatsCache(userId);
272
- res.json({ success: true });
273
- } catch (error) {
274
- res.status(500).json({ error: 'Failed to delete API key' });
275
- }
276
- });
277
-
278
- /**
279
- * PATCH /api/dashboard/api-keys/:id/toggle — Toggle API key active/inactive
280
- */
281
- router.patch('/api-keys/:id/toggle', async (req, res) => {
282
- try {
283
- const userId = req.user.id;
284
- const keyId = Number(req.params.id);
285
- const isActive = !!req.body.isActive;
286
- const toggled = await apiKeysDb.toggleApiKey(userId, keyId, isActive);
287
- invalidateStatsCache(userId);
288
- if (!toggled) return res.status(404).json({ error: 'API key not found' });
289
- res.json({ success: true });
290
- } catch (error) {
291
- res.status(500).json({ error: 'Failed to toggle API key' });
292
- }
293
- });
294
-
295
- export default router;