cognova 0.2.13 → 0.2.15

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 (386) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/_nuxt/-mGU8_Hv.js +1 -0
  3. package/.output/public/_nuxt/0GqCtQ9S.js +1 -0
  4. package/.output/public/_nuxt/{DFpmIy6J.js → 1wCoXZ14.js} +1 -1
  5. package/.output/public/_nuxt/2J1KxdHv.js +1 -0
  6. package/.output/public/_nuxt/6VDUZECX.js +1 -0
  7. package/.output/public/_nuxt/{C7m9zGMG.js → 8WMJQbWG.js} +1 -1
  8. package/.output/public/_nuxt/9Rw88G6h.js +1 -0
  9. package/.output/public/_nuxt/{BjsKx597.js → B-yW4SD3.js} +1 -1
  10. package/.output/public/_nuxt/B20jNAhm.js +1 -0
  11. package/.output/public/_nuxt/B3OpTH_e.js +1 -0
  12. package/.output/public/_nuxt/B9Y3_B5G.js +1 -0
  13. package/.output/public/_nuxt/B9_vmatf.js +1 -0
  14. package/.output/public/_nuxt/B9o6sFST.js +1 -0
  15. package/.output/public/_nuxt/{CseYuM6E.js → BAY3_Hrq.js} +1 -1
  16. package/.output/public/_nuxt/{B_9zJTqe.js → BD5rPkc2.js} +3 -3
  17. package/.output/public/_nuxt/BGYoW3kO.js +1 -0
  18. package/.output/public/_nuxt/BHUy5EAj.js +1 -0
  19. package/.output/public/_nuxt/BLdMRpJD.js +1 -0
  20. package/.output/public/_nuxt/{D6tVZcRs.js → BOCn_SGf.js} +12 -12
  21. package/.output/public/_nuxt/BQi8LIrn.js +1 -0
  22. package/.output/public/_nuxt/BS1t375R.js +1 -0
  23. package/.output/public/_nuxt/BTGeP1bA.js +1 -0
  24. package/.output/public/_nuxt/{pdYvpiud.js → BVYYP4eQ.js} +5 -5
  25. package/.output/public/_nuxt/BWQ7zE01.js +1 -0
  26. package/.output/public/_nuxt/BWqJInD3.js +1 -0
  27. package/.output/public/_nuxt/BXdQ9ASJ.js +1 -0
  28. package/.output/public/_nuxt/{DQhm-UPG.js → BXxZokD_.js} +2 -2
  29. package/.output/public/_nuxt/BZ7DAtPi.js +1 -0
  30. package/.output/public/_nuxt/BZm9w7en.js +1 -0
  31. package/.output/public/_nuxt/B_hNqC64.js +1 -0
  32. package/.output/public/_nuxt/Be47YE2L.js +1 -0
  33. package/.output/public/_nuxt/BgIS9_ti.js +39 -0
  34. package/.output/public/_nuxt/BgavRUl_.js +1 -0
  35. package/.output/public/_nuxt/Bh0yGTzr.js +1 -0
  36. package/.output/public/_nuxt/BhhMUror.js +1 -0
  37. package/.output/public/_nuxt/Bj5e7QPz.js +1 -0
  38. package/.output/public/_nuxt/{9kAKMaPC.js → Bnn59-FS.js} +1 -1
  39. package/.output/public/_nuxt/BoCU3mWu.js +1 -0
  40. package/.output/public/_nuxt/Br6epbzY.js +1 -0
  41. package/.output/public/_nuxt/Br81Sr2R.js +1 -0
  42. package/.output/public/_nuxt/BrkEOLHu.js +9 -0
  43. package/.output/public/_nuxt/BtrNCc2S.js +1 -0
  44. package/.output/public/_nuxt/BxsQlbgl.js +1 -0
  45. package/.output/public/_nuxt/BzrhNhdT.js +1 -0
  46. package/.output/public/_nuxt/{B-DMcEU9.js → C-78dDNd.js} +1 -1
  47. package/.output/public/_nuxt/C1oaoi1p.js +1 -0
  48. package/.output/public/_nuxt/C2-_hcwB.js +1 -0
  49. package/.output/public/_nuxt/{TxMdabnL.js → C2Gh3qqr.js} +1 -1
  50. package/.output/public/_nuxt/C2OHaW6l.js +1 -0
  51. package/.output/public/_nuxt/C3szZmvA.js +1 -0
  52. package/.output/public/_nuxt/C3xzleKP.js +1 -0
  53. package/.output/public/_nuxt/C4sG6rdW.js +1 -0
  54. package/.output/public/_nuxt/C5Cu9Fx6.js +1 -0
  55. package/.output/public/_nuxt/{LRO7YHoH.js → C5gfF7J1.js} +1 -1
  56. package/.output/public/_nuxt/C6Eoiclo.js +1 -0
  57. package/.output/public/_nuxt/C7hFztZL.js +1 -0
  58. package/.output/public/_nuxt/CAVUAxNU.js +2 -0
  59. package/.output/public/_nuxt/{CiNxIhaq.js → CCeM5lW2.js} +1 -1
  60. package/.output/public/_nuxt/CDcQ31Eh.js +1 -0
  61. package/.output/public/_nuxt/CHbHVtCy.js +1 -0
  62. package/.output/public/_nuxt/CHnVFnBW.js +1 -0
  63. package/.output/public/_nuxt/CLNcmNQS.js +1 -0
  64. package/.output/public/_nuxt/{D7N3EZ-I.js → CPwCTHun.js} +1 -1
  65. package/.output/public/_nuxt/{CFmvNggn.js → CS8SdmH2.js} +1 -1
  66. package/.output/public/_nuxt/CTGm_mc8.js +1 -0
  67. package/.output/public/_nuxt/{DNcOC5zj.js → CTQFGHjS.js} +1 -1
  68. package/.output/public/_nuxt/{B0nzD8Sk.js → CVVtVzXC.js} +1 -1
  69. package/.output/public/_nuxt/CZNOaqeX.js +1 -0
  70. package/.output/public/_nuxt/C_A_NkeC.js +2 -0
  71. package/.output/public/_nuxt/{CGplHfmR.js → C_doutX-.js} +1 -1
  72. package/.output/public/_nuxt/{BrX-PnSK.js → CbDLhrNA.js} +2 -2
  73. package/.output/public/_nuxt/CePKCqSb.js +1 -0
  74. package/.output/public/_nuxt/Cet4T_yO.js +1 -0
  75. package/.output/public/_nuxt/{C6OSRpRd.js → CjJPkhbc.js} +1 -1
  76. package/.output/public/_nuxt/CkZT4LGq.js +1 -0
  77. package/.output/public/_nuxt/CkrW8sGL.js +1 -0
  78. package/.output/public/_nuxt/Co0UxVsx.js +1 -0
  79. package/.output/public/_nuxt/CqS0zXYQ.js +1 -0
  80. package/.output/public/_nuxt/CskGv_Km.js +1 -0
  81. package/.output/public/_nuxt/{DuzpliIL.js → CuvkTsqs.js} +1 -1
  82. package/.output/public/_nuxt/{B2wI_pYg.js → CxMuZEwX.js} +1 -1
  83. package/.output/public/_nuxt/{9O1MXkck.js → CxSijT71.js} +8 -8
  84. package/.output/public/_nuxt/Cywo6tZI.js +1 -0
  85. package/.output/public/_nuxt/D1reIfpX.js +1 -0
  86. package/.output/public/_nuxt/D1uV2uir.js +1 -0
  87. package/.output/public/_nuxt/{BWuL2CzO.js → D2edadpd.js} +1 -1
  88. package/.output/public/_nuxt/D3cEeQh7.js +1 -0
  89. package/.output/public/_nuxt/D41A2mE2.js +1 -0
  90. package/.output/public/_nuxt/D49P2RhG.js +1 -0
  91. package/.output/public/_nuxt/D867g6Ds.js +1 -0
  92. package/.output/public/_nuxt/DEFWIjWl.js +1 -0
  93. package/.output/public/_nuxt/DFTEBTzZ.js +1 -0
  94. package/.output/public/_nuxt/{-HePz7lp.js → DHRexj9X.js} +2 -2
  95. package/.output/public/_nuxt/DL4EpgqQ.js +1 -0
  96. package/.output/public/_nuxt/DLHnP6tw.js +1 -0
  97. package/.output/public/_nuxt/DM20WaLq.js +1 -0
  98. package/.output/public/_nuxt/{B8DFEjxA.js → DNAMjHbb.js} +1 -1
  99. package/.output/public/_nuxt/{CLkkdThv.js → DP8t9VWs.js} +24 -24
  100. package/.output/public/_nuxt/DTR31rjp.js +1 -0
  101. package/.output/public/_nuxt/{Bc8bTnkd.js → DTlSLSo1.js} +3 -3
  102. package/.output/public/_nuxt/{DowkFZ2V.js → DVdnhfXT.js} +1 -1
  103. package/.output/public/_nuxt/{DvHS7_h2.js → DZikIgHK.js} +1 -1
  104. package/.output/public/_nuxt/D_YGqvw_.js +1 -0
  105. package/.output/public/_nuxt/D_k1YwXm.js +1 -0
  106. package/.output/public/_nuxt/Daf2MbWG.js +7 -0
  107. package/.output/public/_nuxt/DcJ0GstJ.js +1 -0
  108. package/.output/public/_nuxt/DcYKh6QA.js +1 -0
  109. package/.output/public/_nuxt/DhN3_DYH.js +1 -0
  110. package/.output/public/_nuxt/DhSvBSt3.js +1 -0
  111. package/.output/public/_nuxt/DhlIWT8K.js +1 -0
  112. package/.output/public/_nuxt/DjsqyRqt.js +1 -0
  113. package/.output/public/_nuxt/DpSM97eH.js +1 -0
  114. package/.output/public/_nuxt/DqR9SdeU.js +1 -0
  115. package/.output/public/_nuxt/DqmQtg6Y.js +1 -0
  116. package/.output/public/_nuxt/Dsuag_iW.js +1 -0
  117. package/.output/public/_nuxt/Dtr_0IbE.js +1 -0
  118. package/.output/public/_nuxt/DwrmFEZN.js +1 -0
  119. package/.output/public/_nuxt/DxE8r3r4.js +1 -0
  120. package/.output/public/_nuxt/G_CsVlOR.js +1 -0
  121. package/.output/public/_nuxt/Gyw_2f6x.js +1 -0
  122. package/.output/public/_nuxt/HStC1ZyE.js +1 -0
  123. package/.output/public/_nuxt/Hh9mv0Ek.js +1 -0
  124. package/.output/public/_nuxt/LR0kCjbe.js +1 -0
  125. package/.output/public/_nuxt/MYeLvJGV.js +1 -0
  126. package/.output/public/_nuxt/NKs0EGrh.js +1 -0
  127. package/.output/public/_nuxt/PY5EGujn.js +1 -0
  128. package/.output/public/_nuxt/PamJNol9.js +1 -0
  129. package/.output/public/_nuxt/RlvwEbHv.js +1 -0
  130. package/.output/public/_nuxt/{BghuwIGa.js → TNpdJOlc.js} +1 -1
  131. package/.output/public/_nuxt/TSYrPbLR.js +1 -0
  132. package/.output/public/_nuxt/U-eBrbR4.js +1 -0
  133. package/.output/public/_nuxt/UuqXScSw.js +1 -0
  134. package/.output/public/_nuxt/V04-MrLN.js +1 -0
  135. package/.output/public/_nuxt/X0o5GcBb.js +1 -0
  136. package/.output/public/_nuxt/XQNzmQy9.js +1 -0
  137. package/.output/public/_nuxt/Y59XKprr.js +1 -0
  138. package/.output/public/_nuxt/YZQjZgsH.js +1 -0
  139. package/.output/public/_nuxt/_-TGPiLQ.js +1 -0
  140. package/.output/public/_nuxt/builds/latest.json +1 -1
  141. package/.output/public/_nuxt/builds/meta/7c42ae85-1cae-45f0-8cb9-ad3a10165c2c.json +1 -0
  142. package/.output/public/_nuxt/dashboard.ByJQ-Wzh.css +1 -0
  143. package/.output/public/_nuxt/entry.ZJxWCrU6.css +1 -0
  144. package/.output/public/_nuxt/{D12qPplu.js → hCeWb_38.js} +1 -1
  145. package/.output/public/_nuxt/{sBzyvWMJ.js → hrPTRWjM.js} +1 -1
  146. package/.output/public/_nuxt/jC4pIK1p.js +1 -0
  147. package/.output/public/_nuxt/jTT1R-Vz.js +1 -0
  148. package/.output/public/_nuxt/{CRRw0Thu.js → kkAMoB43.js} +1 -1
  149. package/.output/public/_nuxt/{h_bXa-f2.js → mzM4BGtv.js} +1 -1
  150. package/.output/public/_nuxt/o-s8JBm2.js +1 -0
  151. package/.output/public/_nuxt/pwJTkIQ4.js +1 -0
  152. package/.output/public/_nuxt/{BL1mkqgd.js → rIgg2Arf.js} +3 -3
  153. package/.output/public/_nuxt/subdxgSQ.js +1 -0
  154. package/.output/public/_nuxt/t8B72leH.js +1 -0
  155. package/.output/public/_nuxt/vCFAHwdt.js +1 -0
  156. package/.output/public/_nuxt/z-MP0bB7.js +1 -0
  157. package/.output/server/chunks/build/CodeIcon-CWD5HcV7.mjs +1 -1
  158. package/.output/server/chunks/build/DropdownMenu-BBrV9nXz.mjs +1 -1
  159. package/.output/server/chunks/build/{EditorToolbar-DIfb5arC.mjs → EditorToolbar-B9OwSI5e.mjs} +3 -3
  160. package/.output/server/chunks/build/{EditorToolbar-DIfb5arC.mjs.map → EditorToolbar-B9OwSI5e.mjs.map} +1 -1
  161. package/.output/server/chunks/build/Img-CWLmvN1t.mjs +1 -1
  162. package/.output/server/chunks/build/MDC-Dx0YPDhe.mjs +1 -1
  163. package/.output/server/chunks/build/Select-BB1oLrCD.mjs +1 -1
  164. package/.output/server/chunks/build/SelectMenu-DPssg6zD.mjs +1 -1
  165. package/.output/server/chunks/build/Table-DCwTlhCj.mjs +1 -1
  166. package/.output/server/chunks/build/Tooltip-TRyl6dje.mjs +1 -1
  167. package/.output/server/chunks/build/Tree-DUhXKd8y.mjs +1 -1
  168. package/.output/server/chunks/build/{_id_-DN00UDdO.mjs → _id_-CwBeklIM.mjs} +296 -430
  169. package/.output/server/chunks/build/_id_-CwBeklIM.mjs.map +1 -0
  170. package/.output/server/chunks/build/{_name_-BnS_KEfX.mjs → _name_-D7MtW4Vu.mjs} +2 -2
  171. package/.output/server/chunks/build/{_name_-BnS_KEfX.mjs.map → _name_-D7MtW4Vu.mjs.map} +1 -1
  172. package/.output/server/chunks/build/{_uuid_-DfJaumTE.mjs → _uuid_-lvG92ein.mjs} +4 -4
  173. package/.output/server/chunks/build/{_uuid_-DfJaumTE.mjs.map → _uuid_-lvG92ein.mjs.map} +1 -1
  174. package/.output/server/chunks/build/account-LDZ4TL6x.mjs +513 -0
  175. package/.output/server/chunks/build/account-LDZ4TL6x.mjs.map +1 -0
  176. package/.output/server/chunks/build/app-CGhRZPNT.mjs +252 -0
  177. package/.output/server/chunks/build/app-CGhRZPNT.mjs.map +1 -0
  178. package/.output/server/chunks/build/{chat-m4-n9vC6.mjs → chat-BphuYhvA.mjs} +18 -27
  179. package/.output/server/chunks/build/chat-BphuYhvA.mjs.map +1 -0
  180. package/.output/server/chunks/build/client.precomputed.mjs +1 -1
  181. package/.output/server/chunks/build/cookie-C_iulBi6.mjs +1 -1
  182. package/.output/server/chunks/build/{dashboard-CiVTAZuF.mjs → dashboard-nDujDyOg.mjs} +55 -16
  183. package/.output/server/chunks/build/{dashboard-CiVTAZuF.mjs.map → dashboard-nDujDyOg.mjs.map} +1 -1
  184. package/.output/server/chunks/build/{docs-ChGwOPg5.mjs → docs-Bwucwp0r.mjs} +664 -592
  185. package/.output/server/chunks/build/docs-Bwucwp0r.mjs.map +1 -0
  186. package/.output/server/chunks/build/fetch-BB7Qzkwe.mjs +1 -1
  187. package/.output/server/chunks/build/{hooks-D328DcO6.mjs → hooks-D6PmTth8.mjs} +19 -29
  188. package/.output/server/chunks/build/hooks-D6PmTth8.mjs.map +1 -0
  189. package/.output/server/chunks/build/{index-CAnGTRlu.mjs → index-CJkn2jiM.mjs} +2 -2
  190. package/.output/server/chunks/build/{index-CAnGTRlu.mjs.map → index-CJkn2jiM.mjs.map} +1 -1
  191. package/.output/server/chunks/build/index-CxDxc9fm.mjs +1 -1
  192. package/.output/server/chunks/build/index-Cz9Y6NwN.mjs +723 -0
  193. package/.output/server/chunks/build/index-Cz9Y6NwN.mjs.map +1 -0
  194. package/.output/server/chunks/build/index-hwhuKhuZ.mjs +76 -0
  195. package/.output/server/chunks/build/index-hwhuKhuZ.mjs.map +1 -0
  196. package/.output/server/chunks/build/integrations-D5JWURiJ.mjs +1499 -0
  197. package/.output/server/chunks/build/integrations-D5JWURiJ.mjs.map +1 -0
  198. package/.output/server/chunks/build/{library-Aeg7_Lsb.mjs → library-DxRdgP6X.mjs} +2 -2
  199. package/.output/server/chunks/build/{library-Aeg7_Lsb.mjs.map → library-DxRdgP6X.mjs.map} +1 -1
  200. package/.output/server/chunks/build/{login-DnnElTl2.mjs → login-BmER_VoU.mjs} +2 -2
  201. package/.output/server/chunks/build/{login-DnnElTl2.mjs.map → login-BmER_VoU.mjs.map} +1 -1
  202. package/.output/server/chunks/build/manage-secrets-BWzqc_-f.mjs +669 -0
  203. package/.output/server/chunks/build/manage-secrets-BWzqc_-f.mjs.map +1 -0
  204. package/.output/server/chunks/build/server.mjs +65 -34
  205. package/.output/server/chunks/build/server.mjs.map +1 -1
  206. package/.output/server/chunks/build/settings-neokAVE5.mjs +154 -0
  207. package/.output/server/chunks/build/settings-neokAVE5.mjs.map +1 -0
  208. package/.output/server/chunks/build/styles.mjs +2 -2
  209. package/.output/server/chunks/build/{tasks-DnAFqbtt.mjs → tasks-CnpIQNpS.mjs} +27 -37
  210. package/.output/server/chunks/build/tasks-CnpIQNpS.mjs.map +1 -0
  211. package/.output/server/chunks/build/{useAgents-DHEXiFSc.mjs → useAgents-nmbkB9-_.mjs} +5 -26
  212. package/.output/server/chunks/build/useAgents-nmbkB9-_.mjs.map +1 -0
  213. package/.output/server/chunks/build/{view-n2sYa4Zh.mjs → view-mb0ISqfM.mjs} +3 -3
  214. package/.output/server/chunks/build/{view-n2sYa4Zh.mjs.map → view-mb0ISqfM.mjs.map} +1 -1
  215. package/.output/server/chunks/nitro/nitro.mjs +1000 -858
  216. package/.output/server/chunks/nitro/nitro.mjs.map +1 -1
  217. package/.output/server/chunks/routes/api/index.get.mjs +17 -2
  218. package/.output/server/chunks/routes/api/index.get.mjs.map +1 -1
  219. package/.output/server/chunks/routes/api/index.get3.mjs +21 -3
  220. package/.output/server/chunks/routes/api/index.get3.mjs.map +1 -1
  221. package/.output/server/chunks/routes/api/index.get4.mjs +10 -9
  222. package/.output/server/chunks/routes/api/index.get4.mjs.map +1 -1
  223. package/.output/server/chunks/routes/api/index.get9.mjs +10 -8
  224. package/.output/server/chunks/routes/api/index.get9.mjs.map +1 -1
  225. package/.output/server/chunks/routes/api/memory/search.get.mjs +10 -5
  226. package/.output/server/chunks/routes/api/memory/search.get.mjs.map +1 -1
  227. package/.output/server/chunks/routes/renderer.mjs +1 -1
  228. package/.output/server/index.mjs +1 -1
  229. package/.output/server/package.json +1 -1
  230. package/app/components/AssistantPanel.client.vue +16 -314
  231. package/app/components/agents/AgentCard.vue +111 -0
  232. package/app/components/agents/AgentInfoCard.vue +54 -0
  233. package/app/components/agents/AgentRunHistory.vue +103 -0
  234. package/app/components/agents/AgentRunModal.vue +1 -24
  235. package/app/components/agents/AgentStatsCards.vue +1 -26
  236. package/app/components/assistant/AssistantChat.vue +156 -0
  237. package/app/components/assistant/AssistantTerminal.client.vue +178 -0
  238. package/app/components/chat/ConversationList.vue +2 -12
  239. package/app/components/files/FileTree.vue +23 -202
  240. package/app/components/files/FileTreeModals.vue +226 -0
  241. package/app/components/hooks/HookStatsCards.vue +1 -6
  242. package/app/components/hooks/ToolBreakdownTable.vue +1 -6
  243. package/app/components/search/DashboardSearch.vue +38 -1
  244. package/app/components/tasks/TaskCard.vue +2 -7
  245. package/app/components/tasks/TaskDetail.vue +4 -9
  246. package/app/composables/useSearch.ts +21 -11
  247. package/app/layouts/dashboard.vue +1 -1
  248. package/app/pages/agents/[id].vue +6 -197
  249. package/app/pages/agents/index.vue +9 -142
  250. package/app/pages/settings/account.vue +243 -0
  251. package/app/pages/settings/app.vue +230 -0
  252. package/app/pages/settings/index.vue +12 -0
  253. package/app/pages/settings/integrations.vue +794 -0
  254. package/app/pages/settings/manage-secrets.vue +295 -0
  255. package/app/pages/settings.vue +20 -1581
  256. package/dist/cli/index.js +676 -212
  257. package/package.json +1 -1
  258. package/server/api/agents/index.get.ts +20 -1
  259. package/server/api/conversations/index.get.ts +23 -5
  260. package/server/api/documents/index.get.ts +11 -9
  261. package/server/api/memory/search.get.ts +11 -6
  262. package/server/api/tasks/index.get.ts +11 -9
  263. package/server/drizzle/migrations/0016_full_text_search.sql +127 -0
  264. package/server/drizzle/migrations/meta/_journal.json +7 -0
  265. package/shared/types/index.ts +7 -1
  266. package/shared/utils/formatting.ts +105 -0
  267. package/.output/public/_nuxt/4ALIM-zZ.js +0 -1
  268. package/.output/public/_nuxt/5sUbkh_6.js +0 -16
  269. package/.output/public/_nuxt/6bB8Ev7a.js +0 -1
  270. package/.output/public/_nuxt/7rT1DCe2.js +0 -1
  271. package/.output/public/_nuxt/B2PqR2Vu.js +0 -1
  272. package/.output/public/_nuxt/B5FuOh7E.js +0 -1
  273. package/.output/public/_nuxt/B6om4MW2.js +0 -1
  274. package/.output/public/_nuxt/B7ikW4eF.js +0 -1
  275. package/.output/public/_nuxt/B9kWNcU7.js +0 -1
  276. package/.output/public/_nuxt/BAZ8sewA.js +0 -1
  277. package/.output/public/_nuxt/BCPwL4ma.js +0 -1
  278. package/.output/public/_nuxt/BCrlmHh_.js +0 -1
  279. package/.output/public/_nuxt/BD-AFJWW.js +0 -1
  280. package/.output/public/_nuxt/BGcQMReV.js +0 -1
  281. package/.output/public/_nuxt/BNJJSRKh.js +0 -1
  282. package/.output/public/_nuxt/BNeeERBt.js +0 -1
  283. package/.output/public/_nuxt/BSJb5amZ.js +0 -1
  284. package/.output/public/_nuxt/BSsaE9Db.js +0 -1
  285. package/.output/public/_nuxt/BVO3XpHT.js +0 -1
  286. package/.output/public/_nuxt/B_Lr1vlV.js +0 -1
  287. package/.output/public/_nuxt/Bl0qBhCG.js +0 -1
  288. package/.output/public/_nuxt/Bl43dkDq.js +0 -1
  289. package/.output/public/_nuxt/BnMTFF5R.js +0 -1
  290. package/.output/public/_nuxt/BtV7oNn3.js +0 -1
  291. package/.output/public/_nuxt/BvFYnRMR.js +0 -1
  292. package/.output/public/_nuxt/BvTUBUNN.js +0 -1
  293. package/.output/public/_nuxt/ByLsRRoQ.js +0 -1
  294. package/.output/public/_nuxt/C1LcEMwB.js +0 -1
  295. package/.output/public/_nuxt/C2IiXp9N.js +0 -2
  296. package/.output/public/_nuxt/C796kFT9.js +0 -1
  297. package/.output/public/_nuxt/C8AtdrMH.js +0 -1
  298. package/.output/public/_nuxt/CAWbHTgy.js +0 -1
  299. package/.output/public/_nuxt/CJPrV25_.js +0 -1
  300. package/.output/public/_nuxt/CKKEWapF.js +0 -1
  301. package/.output/public/_nuxt/CNjv28J2.js +0 -1
  302. package/.output/public/_nuxt/CT8s4rqa.js +0 -1
  303. package/.output/public/_nuxt/CZSOrxUy.js +0 -1
  304. package/.output/public/_nuxt/CaNEL9JC.js +0 -39
  305. package/.output/public/_nuxt/CbTLg9oX.js +0 -1
  306. package/.output/public/_nuxt/CfCXpVn5.js +0 -1
  307. package/.output/public/_nuxt/ChmNlvWR.js +0 -1
  308. package/.output/public/_nuxt/Ck0QYdxe.js +0 -1
  309. package/.output/public/_nuxt/Ckb0pu4-.js +0 -1
  310. package/.output/public/_nuxt/CkwPRs1D.js +0 -1
  311. package/.output/public/_nuxt/CmEJg8C6.js +0 -2
  312. package/.output/public/_nuxt/CmL_vMJD.js +0 -1
  313. package/.output/public/_nuxt/Cq8zPxkT.js +0 -1
  314. package/.output/public/_nuxt/CxRnB4NC.js +0 -1
  315. package/.output/public/_nuxt/D0zMyG8n.js +0 -1
  316. package/.output/public/_nuxt/D1XYB283.js +0 -1
  317. package/.output/public/_nuxt/D2NxqyzW.js +0 -1
  318. package/.output/public/_nuxt/D5xyrvFu.js +0 -1
  319. package/.output/public/_nuxt/D8iBB0Py.js +0 -1
  320. package/.output/public/_nuxt/D8oIH8m1.js +0 -1
  321. package/.output/public/_nuxt/DB2zZev7.js +0 -1
  322. package/.output/public/_nuxt/DBrOAqkm.js +0 -1
  323. package/.output/public/_nuxt/DDXDFweK.js +0 -1
  324. package/.output/public/_nuxt/DFZkCmLQ.js +0 -1
  325. package/.output/public/_nuxt/DKZmgHNU.js +0 -1
  326. package/.output/public/_nuxt/DR8tGJhL.js +0 -1
  327. package/.output/public/_nuxt/DVAEVTjy.js +0 -1
  328. package/.output/public/_nuxt/DVqrPkCJ.js +0 -1
  329. package/.output/public/_nuxt/DWZ9ieYa.js +0 -1
  330. package/.output/public/_nuxt/DWj6VsZv.js +0 -1
  331. package/.output/public/_nuxt/DXJ9PxCv.js +0 -1
  332. package/.output/public/_nuxt/D_ziPROx.js +0 -1
  333. package/.output/public/_nuxt/Da2vwoYG.js +0 -1
  334. package/.output/public/_nuxt/Ddz3HcBB.js +0 -1
  335. package/.output/public/_nuxt/DgYei0bK.js +0 -1
  336. package/.output/public/_nuxt/Di_3GV_R.js +0 -1
  337. package/.output/public/_nuxt/Djog1f_e.js +0 -1
  338. package/.output/public/_nuxt/Dkap2-Pj.js +0 -7
  339. package/.output/public/_nuxt/DlJe_sHk.js +0 -1
  340. package/.output/public/_nuxt/DlPOQpH_.js +0 -1
  341. package/.output/public/_nuxt/DmYWMtVA.js +0 -1
  342. package/.output/public/_nuxt/DmxKU7sH.js +0 -1
  343. package/.output/public/_nuxt/DsodHRrQ.js +0 -1
  344. package/.output/public/_nuxt/DwSY6_2U.js +0 -1
  345. package/.output/public/_nuxt/DypJaSm-.js +0 -1
  346. package/.output/public/_nuxt/DzKaXH6P.js +0 -1
  347. package/.output/public/_nuxt/FK-jSuOT.js +0 -1
  348. package/.output/public/_nuxt/Fc2JsQt4.js +0 -1
  349. package/.output/public/_nuxt/IYCBGuRS.js +0 -1
  350. package/.output/public/_nuxt/NrRzUJNY.js +0 -1
  351. package/.output/public/_nuxt/OeVS0Rsb.js +0 -1
  352. package/.output/public/_nuxt/OlF0QnKF.js +0 -1
  353. package/.output/public/_nuxt/QuAoqY7t.js +0 -1
  354. package/.output/public/_nuxt/RlXHkmOd.js +0 -9
  355. package/.output/public/_nuxt/WZnvL9Dh.js +0 -1
  356. package/.output/public/_nuxt/ZvG7a1W3.js +0 -1
  357. package/.output/public/_nuxt/aPNZBVrG.js +0 -1
  358. package/.output/public/_nuxt/builds/meta/495aca73-bfa5-456e-b96a-f02e009c72d3.json +0 -1
  359. package/.output/public/_nuxt/dashboard.BxCOkJwt.css +0 -1
  360. package/.output/public/_nuxt/entry.C_udkZt9.css +0 -1
  361. package/.output/public/_nuxt/evY81hM3.js +0 -1
  362. package/.output/public/_nuxt/i6QBzaEi.js +0 -1
  363. package/.output/public/_nuxt/iTVKJ1kw.js +0 -1
  364. package/.output/public/_nuxt/j92uR3uR.js +0 -1
  365. package/.output/public/_nuxt/jdUkeAj_.js +0 -1
  366. package/.output/public/_nuxt/jeRvcMHU.js +0 -1
  367. package/.output/public/_nuxt/kuv0zpKq.js +0 -1
  368. package/.output/public/_nuxt/l3lKQs7b.js +0 -1
  369. package/.output/public/_nuxt/qIgZ7OXa.js +0 -1
  370. package/.output/public/_nuxt/qcVwZv8e.js +0 -1
  371. package/.output/public/_nuxt/ru8fqqWs.js +0 -1
  372. package/.output/public/_nuxt/sgmhgpCS.js +0 -1
  373. package/.output/public/_nuxt/y2jv0da-.js +0 -1
  374. package/.output/public/_nuxt/yyS4-kAX.js +0 -1
  375. package/.output/public/_nuxt/zIoBLBHF.js +0 -1
  376. package/.output/public/_nuxt/zpazwPgx.js +0 -1
  377. package/.output/server/chunks/build/_id_-DN00UDdO.mjs.map +0 -1
  378. package/.output/server/chunks/build/chat-m4-n9vC6.mjs.map +0 -1
  379. package/.output/server/chunks/build/docs-ChGwOPg5.mjs.map +0 -1
  380. package/.output/server/chunks/build/hooks-D328DcO6.mjs.map +0 -1
  381. package/.output/server/chunks/build/index-C9PuieXh.mjs +0 -820
  382. package/.output/server/chunks/build/index-C9PuieXh.mjs.map +0 -1
  383. package/.output/server/chunks/build/settings-B2KXoGcz.mjs +0 -3232
  384. package/.output/server/chunks/build/settings-B2KXoGcz.mjs.map +0 -1
  385. package/.output/server/chunks/build/tasks-DnAFqbtt.mjs.map +0 -1
  386. package/.output/server/chunks/build/useAgents-DHEXiFSc.mjs.map +0 -1
@@ -1,702 +1,26 @@
1
1
  <script setup lang="ts">
2
- import type { NotificationPreferences, NotificationResource, NotificationAction, Bridge, BridgePlatform } from '~~/shared/types'
3
- import { defaultNotificationPreferences } from '~~/shared/utils/notification-defaults'
4
-
5
- interface Secret {
6
- id: string
7
- key: string
8
- description: string | null
9
- createdAt: string
10
- updatedAt: string
11
- }
12
-
13
2
  definePageMeta({
14
3
  layout: 'dashboard',
15
4
  middleware: 'auth'
16
5
  })
17
6
 
18
- const { user, updateProfile, changeEmail, changePassword } = useAuth()
19
- const toast = useToast()
20
- const { updatePreferences } = useNotificationBus()
21
-
22
- // Profile form state
23
- const profileState = reactive({
24
- name: ''
25
- })
26
- const profileLoading = ref(false)
27
-
28
- // Email form state
29
- const emailState = reactive({
30
- newEmail: ''
31
- })
32
- const emailLoading = ref(false)
33
-
34
- // Password form state
35
- const passwordState = reactive({
36
- currentPassword: '',
37
- newPassword: '',
38
- confirmPassword: ''
39
- })
40
- const passwordLoading = ref(false)
41
-
42
- // Secrets state
43
- const secretsData = ref<Secret[]>([])
44
- const secretsLoading = ref(false)
45
- const secretModal = ref(false)
46
- const secretDeleteConfirm = ref(false)
47
- const secretToDelete = ref<Secret | null>(null)
48
- const secretSaving = ref(false)
49
- const editingSecret = ref<Secret | null>(null)
7
+ const route = useRoute()
50
8
 
51
- const secretForm = reactive({
52
- key: '',
53
- value: '',
54
- description: ''
55
- })
56
-
57
- // Initialize forms with current user data
58
- watch(() => user.value, (u) => {
59
- if (u) {
60
- profileState.name = u.name || ''
61
- emailState.newEmail = u.email || ''
62
- }
63
- }, { immediate: true })
64
-
65
- // Fetch secrets
66
- async function fetchSecrets() {
67
- secretsLoading.value = true
68
- try {
69
- const { data } = await $fetch<{ data: Secret[] }>('/api/secrets')
70
- secretsData.value = data
71
- } catch {
72
- toast.add({
73
- title: 'Failed to load secrets',
74
- color: 'error'
75
- })
76
- }
77
- secretsLoading.value = false
78
- }
79
-
80
- // Tab items
81
9
  const tabs = [
82
- { label: 'Account', icon: 'i-lucide-user', value: 'account', slot: 'account' },
83
- { label: 'Secrets', icon: 'i-lucide-key', value: 'secrets', slot: 'secrets' },
84
- { label: 'Integrations', icon: 'i-lucide-plug', value: 'integrations', slot: 'integrations' },
85
- { label: 'App', icon: 'i-lucide-settings', value: 'app', slot: 'app' }
86
- ]
87
-
88
- // Secrets table columns
89
- const secretColumns = [
90
- { accessorKey: 'key', header: 'Key' },
91
- { accessorKey: 'description', header: 'Description' },
92
- { accessorKey: 'updatedAt', header: 'Last Updated' },
93
- { accessorKey: 'actions', header: '' }
94
- ]
95
-
96
- function openCreateSecret() {
97
- editingSecret.value = null
98
- secretForm.key = ''
99
- secretForm.value = ''
100
- secretForm.description = ''
101
- secretModal.value = true
102
- }
103
-
104
- function openEditSecret(secret: Secret) {
105
- editingSecret.value = secret
106
- secretForm.key = secret.key
107
- secretForm.value = ''
108
- secretForm.description = secret.description || ''
109
- secretModal.value = true
110
- }
111
-
112
- function confirmDeleteSecret(secret: Secret) {
113
- secretToDelete.value = secret
114
- secretDeleteConfirm.value = true
115
- }
116
-
117
- async function handleSecretSubmit() {
118
- if (!secretForm.key || (!editingSecret.value && !secretForm.value)) {
119
- toast.add({
120
- title: 'Missing fields',
121
- description: editingSecret.value ? 'Key is required.' : 'Key and value are required.',
122
- color: 'error'
123
- })
124
- return
125
- }
126
-
127
- secretSaving.value = true
128
- try {
129
- if (editingSecret.value) {
130
- await $fetch(`/api/secrets/${editingSecret.value.key}`, {
131
- method: 'PUT',
132
- body: {
133
- value: secretForm.value || undefined,
134
- description: secretForm.description || undefined
135
- }
136
- })
137
- toast.add({
138
- title: 'Secret updated',
139
- color: 'success'
140
- })
141
- } else {
142
- await $fetch('/api/secrets', {
143
- method: 'POST',
144
- body: {
145
- key: secretForm.key,
146
- value: secretForm.value,
147
- description: secretForm.description || undefined
148
- }
149
- })
150
- toast.add({
151
- title: 'Secret created',
152
- color: 'success'
153
- })
154
- }
155
- secretModal.value = false
156
- await fetchSecrets()
157
- } catch (err: unknown) {
158
- const error = err as { data?: { message?: string } }
159
- toast.add({
160
- title: editingSecret.value ? 'Failed to update secret' : 'Failed to create secret',
161
- description: error.data?.message || 'An error occurred',
162
- color: 'error'
163
- })
164
- }
165
- secretSaving.value = false
166
- }
167
-
168
- async function handleDeleteSecret() {
169
- if (!secretToDelete.value) return
170
-
171
- try {
172
- await $fetch(`/api/secrets/${secretToDelete.value.key}`, {
173
- method: 'DELETE'
174
- })
175
- toast.add({
176
- title: 'Secret deleted',
177
- color: 'success'
178
- })
179
- secretDeleteConfirm.value = false
180
- secretToDelete.value = null
181
- await fetchSecrets()
182
- } catch {
183
- toast.add({
184
- title: 'Failed to delete secret',
185
- color: 'error'
186
- })
187
- }
188
- }
189
-
190
- function formatDate(dateStr: string) {
191
- return new Date(dateStr).toLocaleDateString('en-US', {
192
- month: 'short',
193
- day: 'numeric',
194
- year: 'numeric'
195
- })
196
- }
197
-
198
- // === App URL ===
199
- const appUrl = ref('')
200
- const appUrlSaving = ref(false)
201
-
202
- async function loadAppUrl() {
203
- try {
204
- const { data } = await $fetch<{ data: { key: string, value: string } }>('/api/secrets/APP_URL')
205
- appUrl.value = data.value || ''
206
- } catch {
207
- appUrl.value = ''
208
- }
209
- }
210
-
211
- async function saveAppUrl() {
212
- appUrlSaving.value = true
213
- try {
214
- if (appUrl.value) {
215
- // Upsert the APP_URL secret
216
- const exists = secretsData.value.some(s => s.key === 'APP_URL')
217
- if (exists) {
218
- await $fetch('/api/secrets/APP_URL', {
219
- method: 'PUT',
220
- body: { value: appUrl.value, description: 'Public URL for webhooks and auth callbacks' }
221
- })
222
- } else {
223
- await $fetch('/api/secrets', {
224
- method: 'POST',
225
- body: { key: 'APP_URL', value: appUrl.value, description: 'Public URL for webhooks and auth callbacks' }
226
- })
227
- }
228
- } else {
229
- // Remove the secret if cleared
230
- const exists = secretsData.value.some(s => s.key === 'APP_URL')
231
- if (exists)
232
- await $fetch('/api/secrets/APP_URL', { method: 'DELETE' })
233
- }
234
- toast.add({ title: 'Public URL saved', description: 'Restart running integrations for changes to take effect.', color: 'success' })
235
- await fetchSecrets()
236
- } catch {
237
- toast.add({ title: 'Failed to save URL', color: 'error' })
238
- }
239
- appUrlSaving.value = false
240
- }
241
-
242
- // === Notification Preferences ===
243
- const notifPrefs = ref<NotificationPreferences>({ ...defaultNotificationPreferences })
244
- const notifLoading = ref(false)
245
- const notifSaving = ref(false)
246
- const expandedResources = ref<Set<string>>(new Set())
247
-
248
- const resourceConfig: { key: NotificationResource, label: string, icon: string, subtypes: NotificationAction[] }[] = [
249
- { key: 'task', label: 'Tasks', icon: 'i-lucide-check-square', subtypes: ['create', 'edit', 'delete', 'restore'] },
250
- { key: 'project', label: 'Projects', icon: 'i-lucide-folder', subtypes: ['create', 'edit', 'delete'] },
251
- { key: 'agent', label: 'Agents', icon: 'i-lucide-bot', subtypes: ['create', 'edit', 'delete', 'run', 'complete', 'fail', 'cancel'] },
252
- { key: 'document', label: 'Documents', icon: 'i-lucide-file-text', subtypes: ['edit', 'delete', 'restore'] },
253
- { key: 'memory', label: 'Memories', icon: 'i-lucide-brain', subtypes: ['create', 'delete'] },
254
- { key: 'reminder', label: 'Reminders', icon: 'i-lucide-bell', subtypes: ['create'] },
255
- { key: 'secret', label: 'Secrets', icon: 'i-lucide-key-round', subtypes: ['create', 'edit', 'delete'] },
256
- { key: 'hook', label: 'Hooks', icon: 'i-lucide-webhook', subtypes: ['create'] },
257
- { key: 'conversation', label: 'Conversations', icon: 'i-lucide-message-square', subtypes: ['delete'] },
258
- { key: 'bridge', label: 'Bridges', icon: 'i-lucide-plug', subtypes: ['create', 'edit', 'delete', 'complete', 'fail'] }
10
+ { label: 'Account', icon: 'i-lucide-user', value: 'account' },
11
+ { label: 'Secrets', icon: 'i-lucide-key', value: 'secrets' },
12
+ { label: 'Integrations', icon: 'i-lucide-plug', value: 'integrations' },
13
+ { label: 'App', icon: 'i-lucide-settings', value: 'app' }
259
14
  ]
260
15
 
261
- async function loadNotificationPrefs() {
262
- notifLoading.value = true
263
- try {
264
- const { data } = await $fetch<{ data: { notifications: NotificationPreferences } }>('/api/settings')
265
- notifPrefs.value = { ...defaultNotificationPreferences, ...data.notifications }
266
- } catch {
267
- notifPrefs.value = { ...defaultNotificationPreferences }
268
- }
269
- notifLoading.value = false
270
- }
271
-
272
- async function saveNotificationPrefs() {
273
- notifSaving.value = true
274
- try {
275
- await $fetch('/api/settings', {
276
- method: 'PUT',
277
- body: { notifications: notifPrefs.value }
278
- })
279
- updatePreferences(notifPrefs.value)
280
- toast.add({ title: 'Notification preferences saved', color: 'success' })
281
- } catch {
282
- toast.add({ title: 'Failed to save preferences', color: 'error' })
283
- }
284
- notifSaving.value = false
285
- }
286
-
287
- function toggleResourceExpand(key: string) {
288
- if (expandedResources.value.has(key))
289
- expandedResources.value.delete(key)
290
- else
291
- expandedResources.value.add(key)
292
- }
293
-
294
- function setResourceEnabled(key: NotificationResource, enabled: boolean) {
295
- notifPrefs.value[key] = { ...notifPrefs.value[key], enabled }
296
- }
297
-
298
- function setSubtypeEnabled(key: NotificationResource, subtype: NotificationAction, enabled: boolean) {
299
- const current = notifPrefs.value[key]
300
- notifPrefs.value[key] = {
301
- ...current,
302
- subtypes: { ...current?.subtypes, [subtype]: enabled }
303
- }
304
- }
305
-
306
- // === Integrations / Bridges ===
307
- const bridgesData = ref<Bridge[]>([])
308
- const bridgesLoading = ref(false)
309
- const bridgeModal = ref(false)
310
- const bridgeSaving = ref(false)
311
- const bridgeDeleteConfirm = ref(false)
312
- const bridgeToDelete = ref<Bridge | null>(null)
313
-
314
- const bridgeForm = reactive({
315
- platform: 'telegram' as BridgePlatform,
316
- name: ''
16
+ const activeTab = computed(() => {
17
+ const path = route.path
18
+ const segment = path.split('/').pop()
19
+ return tabs.some(t => t.value === segment) ? segment : 'account'
317
20
  })
318
21
 
319
- // Bridge config modal state
320
- const bridgeConfigModal = ref(false)
321
- const editingBridge = ref<Bridge | null>(null)
322
- const bridgeConfigSaving = ref(false)
323
-
324
- const bridgeConfigForm = reactive({
325
- name: '',
326
- // Telegram
327
- botUsername: '',
328
- allowedChatIds: '',
329
- // Discord
330
- listenMode: 'mentions' as 'mentions' | 'dm' | 'all',
331
- guildId: '',
332
- channelId: '',
333
- // iMessage
334
- strategy: 'imsg' as 'imsg' | 'bluebubbles',
335
- allowedNumbers: '',
336
- blueBubblesUrl: '',
337
- // Google
338
- enabledServices: [] as string[],
339
- account: '',
340
- // Email
341
- imapHost: '',
342
- imapPort: '',
343
- smtpHost: '',
344
- smtpPort: '',
345
- emailAddress: ''
346
- })
347
-
348
- const discordListenModeOptions = [
349
- { value: 'mentions', label: 'Mentions & DMs' },
350
- { value: 'dm', label: 'DMs only' },
351
- { value: 'all', label: 'All messages' }
352
- ]
353
-
354
- const imessageStrategyOptions = [
355
- { value: 'imsg', label: 'Local (imsg CLI)' },
356
- { value: 'bluebubbles', label: 'BlueBubbles' }
357
- ]
358
-
359
- const googleServiceOptions = [
360
- { value: 'gmail', label: 'Gmail' },
361
- { value: 'calendar', label: 'Calendar' },
362
- { value: 'drive', label: 'Drive' },
363
- { value: 'contacts', label: 'Contacts' },
364
- { value: 'tasks', label: 'Tasks' }
365
- ]
366
-
367
- const platformNamePlaceholders: Record<BridgePlatform, string> = {
368
- telegram: 'My Telegram Bot',
369
- discord: 'My Discord Bot',
370
- imessage: 'iMessage Bridge',
371
- google: 'Google Suite',
372
- email: 'Work Email'
373
- }
374
-
375
- // Secrets required to create a bridge (blocks creation if missing)
376
- const platformRequiredSecrets: Record<BridgePlatform, string[]> = {
377
- telegram: ['TELEGRAM_BOT_TOKEN'],
378
- discord: ['DISCORD_BOT_TOKEN'],
379
- imessage: [],
380
- google: [],
381
- email: []
382
- }
383
-
384
- const platformOptions: { value: BridgePlatform, label: string, icon: string }[] = [
385
- { value: 'telegram', label: 'Telegram', icon: 'i-lucide-send' },
386
- { value: 'discord', label: 'Discord', icon: 'i-lucide-message-circle' },
387
- { value: 'imessage', label: 'iMessage', icon: 'i-lucide-smartphone' },
388
- { value: 'google', label: 'Google Suite', icon: 'i-lucide-mail' },
389
- { value: 'email', label: 'Email (IMAP)', icon: 'i-lucide-at-sign' }
390
- ]
391
-
392
- function getMissingSecrets(platform: BridgePlatform): string[] {
393
- const required = platformRequiredSecrets[platform]
394
- if (!required.length) return []
395
- const existing = new Set(secretsData.value.map(s => s.key))
396
- return required.filter(k => !existing.has(k))
397
- }
398
-
399
- const healthColors: Record<string, string> = {
400
- connected: 'text-success',
401
- disconnected: 'text-dimmed',
402
- error: 'text-error',
403
- unconfigured: 'text-dimmed'
404
- }
405
-
406
- async function fetchBridges() {
407
- bridgesLoading.value = true
408
- try {
409
- const { data } = await $fetch<{ data: Bridge[] }>('/api/bridges')
410
- bridgesData.value = data
411
- } catch {
412
- toast.add({ title: 'Failed to load integrations', color: 'error' })
413
- }
414
- bridgesLoading.value = false
415
- }
416
-
417
- function openBridgeConfig(bridge: Bridge) {
418
- editingBridge.value = bridge
419
- bridgeConfigForm.name = bridge.name
420
-
421
- const config = bridge.config ? JSON.parse(bridge.config) : {}
422
-
423
- // Reset all fields
424
- bridgeConfigForm.botUsername = ''
425
- bridgeConfigForm.allowedChatIds = ''
426
- bridgeConfigForm.listenMode = 'mentions'
427
- bridgeConfigForm.guildId = ''
428
- bridgeConfigForm.channelId = ''
429
- bridgeConfigForm.strategy = 'imsg'
430
- bridgeConfigForm.allowedNumbers = ''
431
- bridgeConfigForm.blueBubblesUrl = ''
432
- bridgeConfigForm.enabledServices = []
433
- bridgeConfigForm.account = ''
434
- bridgeConfigForm.imapHost = ''
435
- bridgeConfigForm.imapPort = ''
436
- bridgeConfigForm.smtpHost = ''
437
- bridgeConfigForm.smtpPort = ''
438
- bridgeConfigForm.emailAddress = ''
439
-
440
- // Populate platform-specific fields from config
441
- switch (bridge.platform) {
442
- case 'telegram':
443
- bridgeConfigForm.botUsername = config.botUsername || ''
444
- bridgeConfigForm.allowedChatIds = (config.allowedChatIds || []).join(', ')
445
- break
446
- case 'discord':
447
- bridgeConfigForm.listenMode = config.listenMode || 'mentions'
448
- bridgeConfigForm.guildId = config.guildId || ''
449
- bridgeConfigForm.channelId = config.channelId || ''
450
- break
451
- case 'imessage':
452
- bridgeConfigForm.strategy = config.strategy || 'imsg'
453
- bridgeConfigForm.allowedNumbers = (config.allowedNumbers || []).join(', ')
454
- bridgeConfigForm.blueBubblesUrl = config.blueBubblesUrl || ''
455
- break
456
- case 'google':
457
- bridgeConfigForm.enabledServices = config.enabledServices || []
458
- bridgeConfigForm.account = config.account || ''
459
- break
460
- case 'email':
461
- bridgeConfigForm.imapHost = config.imapHost || ''
462
- bridgeConfigForm.imapPort = config.imapPort?.toString() || ''
463
- bridgeConfigForm.smtpHost = config.smtpHost || ''
464
- bridgeConfigForm.smtpPort = config.smtpPort?.toString() || ''
465
- bridgeConfigForm.emailAddress = config.emailAddress || ''
466
- break
467
- }
468
-
469
- bridgeConfigModal.value = true
470
- }
471
-
472
- async function handleBridgeConfigSave() {
473
- if (!editingBridge.value) return
474
-
475
- const platform = editingBridge.value.platform
476
- let config: Record<string, unknown> = {}
477
-
478
- // Preserve existing config (like webhookSecret) and merge new fields
479
- if (editingBridge.value.config)
480
- config = JSON.parse(editingBridge.value.config)
481
-
482
- switch (platform) {
483
- case 'telegram':
484
- config.botUsername = bridgeConfigForm.botUsername || undefined
485
- config.allowedChatIds = bridgeConfigForm.allowedChatIds
486
- ? bridgeConfigForm.allowedChatIds.split(',').map(s => s.trim()).filter(Boolean)
487
- : undefined
488
- break
489
- case 'discord':
490
- config.listenMode = bridgeConfigForm.listenMode
491
- config.guildId = bridgeConfigForm.guildId || undefined
492
- config.channelId = bridgeConfigForm.channelId || undefined
493
- break
494
- case 'imessage':
495
- config.strategy = bridgeConfigForm.strategy
496
- config.allowedNumbers = bridgeConfigForm.allowedNumbers
497
- ? bridgeConfigForm.allowedNumbers.split(',').map(s => s.trim()).filter(Boolean)
498
- : undefined
499
- config.blueBubblesUrl = bridgeConfigForm.blueBubblesUrl || undefined
500
- break
501
- case 'google':
502
- config.enabledServices = bridgeConfigForm.enabledServices
503
- config.account = bridgeConfigForm.account || undefined
504
- break
505
- case 'email':
506
- config.imapHost = bridgeConfigForm.imapHost || undefined
507
- config.imapPort = bridgeConfigForm.imapPort ? Number(bridgeConfigForm.imapPort) : undefined
508
- config.smtpHost = bridgeConfigForm.smtpHost || undefined
509
- config.smtpPort = bridgeConfigForm.smtpPort ? Number(bridgeConfigForm.smtpPort) : undefined
510
- config.emailAddress = bridgeConfigForm.emailAddress || undefined
511
- break
512
- }
513
-
514
- bridgeConfigSaving.value = true
515
- try {
516
- await $fetch(`/api/bridges/${editingBridge.value.id}` as string, {
517
- method: 'PUT',
518
- body: { name: bridgeConfigForm.name, config }
519
- })
520
- toast.add({ title: 'Integration updated', color: 'success' })
521
- bridgeConfigModal.value = false
522
- await fetchBridges()
523
- } catch (err: unknown) {
524
- const error = err as { data?: { message?: string } }
525
- toast.add({ title: 'Failed to update integration', description: error.data?.message, color: 'error' })
526
- }
527
- bridgeConfigSaving.value = false
528
- }
529
-
530
- function openCreateBridge() {
531
- bridgeForm.platform = 'telegram'
532
- bridgeForm.name = ''
533
- bridgeModal.value = true
534
- }
535
-
536
- async function handleBridgeCreate() {
537
- if (!bridgeForm.name) {
538
- toast.add({ title: 'Name is required', color: 'error' })
539
- return
540
- }
541
- const missing = getMissingSecrets(bridgeForm.platform)
542
- if (missing.length) {
543
- toast.add({
544
- title: 'Missing required secrets',
545
- description: `Add ${missing.join(', ')} in the Secrets tab first.`,
546
- color: 'error'
547
- })
548
- return
549
- }
550
- bridgeSaving.value = true
551
- try {
552
- await $fetch('/api/bridges', {
553
- method: 'POST',
554
- body: { platform: bridgeForm.platform, name: bridgeForm.name }
555
- })
556
- toast.add({ title: 'Integration created', color: 'success' })
557
- bridgeModal.value = false
558
- await fetchBridges()
559
- } catch (err: unknown) {
560
- const error = err as { data?: { message?: string } }
561
- toast.add({ title: 'Failed to create integration', description: error.data?.message, color: 'error' })
562
- }
563
- bridgeSaving.value = false
564
- }
565
-
566
- async function toggleBridge(bridge: Bridge) {
567
- try {
568
- await $fetch(`/api/bridges/${bridge.id}` as string, {
569
- method: 'PUT',
570
- body: { enabled: !bridge.enabled }
571
- })
572
- await fetchBridges()
573
- } catch {
574
- toast.add({ title: 'Failed to update integration', color: 'error' })
575
- }
576
- }
577
-
578
- function confirmDeleteBridge(bridge: Bridge) {
579
- bridgeToDelete.value = bridge
580
- bridgeDeleteConfirm.value = true
581
- }
582
-
583
- async function handleDeleteBridge() {
584
- if (!bridgeToDelete.value) return
585
- try {
586
- await $fetch(`/api/bridges/${bridgeToDelete.value.id}` as string, { method: 'DELETE' })
587
- toast.add({ title: 'Integration deleted', color: 'success' })
588
- bridgeDeleteConfirm.value = false
589
- bridgeToDelete.value = null
590
- await fetchBridges()
591
- } catch {
592
- toast.add({ title: 'Failed to delete integration', color: 'error' })
593
- }
594
- }
595
-
596
- // Load secrets + notification prefs + bridges + app url when component mounts
597
- onMounted(() => {
598
- fetchSecrets()
599
- loadNotificationPrefs()
600
- fetchBridges()
601
- loadAppUrl()
602
- })
603
-
604
- // Form handlers
605
- async function handleProfileSubmit() {
606
- profileLoading.value = true
607
- const result = await updateProfile({
608
- name: profileState.name
609
- })
610
-
611
- if (result.error) {
612
- toast.add({
613
- title: 'Update failed',
614
- description: result.error.message,
615
- color: 'error'
616
- })
617
- } else {
618
- toast.add({
619
- title: 'Profile updated',
620
- description: 'Your name has been updated successfully.',
621
- color: 'success'
622
- })
623
- }
624
- profileLoading.value = false
625
- }
626
-
627
- async function handleEmailSubmit() {
628
- if (!emailState.newEmail) {
629
- toast.add({
630
- title: 'Email required',
631
- description: 'Please enter a new email address.',
632
- color: 'error'
633
- })
634
- return
635
- }
636
-
637
- emailLoading.value = true
638
- const result = await changeEmail(emailState.newEmail)
639
-
640
- if (result.error) {
641
- toast.add({
642
- title: 'Email change failed',
643
- description: result.error.message,
644
- color: 'error'
645
- })
646
- } else {
647
- toast.add({
648
- title: 'Email updated',
649
- description: 'Your email has been changed successfully.',
650
- color: 'success'
651
- })
652
- }
653
- emailLoading.value = false
654
- }
655
-
656
- async function handlePasswordSubmit() {
657
- // Client-side validation
658
- if (passwordState.newPassword !== passwordState.confirmPassword) {
659
- toast.add({
660
- title: 'Passwords do not match',
661
- description: 'Please make sure your new passwords match.',
662
- color: 'error'
663
- })
664
- return
665
- }
666
-
667
- if (!passwordState.currentPassword || !passwordState.newPassword) {
668
- toast.add({
669
- title: 'Missing fields',
670
- description: 'Please fill in all password fields.',
671
- color: 'error'
672
- })
673
- return
674
- }
675
-
676
- passwordLoading.value = true
677
- const result = await changePassword({
678
- currentPassword: passwordState.currentPassword,
679
- newPassword: passwordState.newPassword
680
- })
681
-
682
- if (result.error) {
683
- toast.add({
684
- title: 'Password change failed',
685
- description: result.error.message,
686
- color: 'error'
687
- })
688
- } else {
689
- toast.add({
690
- title: 'Password changed',
691
- description: 'Your password has been changed successfully.',
692
- color: 'success'
693
- })
694
- // Clear form
695
- passwordState.currentPassword = ''
696
- passwordState.newPassword = ''
697
- passwordState.confirmPassword = ''
698
- }
699
- passwordLoading.value = false
22
+ function navigateTab(value: string | number) {
23
+ navigateTo(`/settings/${value}`)
700
24
  }
701
25
  </script>
702
26
 
@@ -712,900 +36,15 @@ async function handlePasswordSubmit() {
712
36
  </UDashboardNavbar>
713
37
 
714
38
  <div class="p-6">
715
- <ClientOnly>
716
- <UTabs
717
- :items="tabs"
718
- default-value="account"
719
- class="w-full mx-auto"
720
- :ui="{ list: 'max-w-xl' }"
721
- >
722
- <!-- Account Tab -->
723
- <template #account>
724
- <div class="space-y-8 max-w-2xl mx-auto py-6">
725
- <!-- Profile Section -->
726
- <div>
727
- <h3 class="text-lg font-semibold mb-4">
728
- Profile
729
- </h3>
730
- <UForm
731
- :state="profileState"
732
- class="space-y-4"
733
- @submit="handleProfileSubmit"
734
- >
735
- <UFormField
736
- label="Name"
737
- name="name"
738
- >
739
- <UInput
740
- v-model="profileState.name"
741
- placeholder="Your name"
742
- class="w-full"
743
- />
744
- </UFormField>
745
- <UButton
746
- type="submit"
747
- :loading="profileLoading"
748
- >
749
- Save Name
750
- </UButton>
751
- </UForm>
752
- </div>
753
-
754
- <USeparator />
755
-
756
- <!-- Email Section -->
757
- <div>
758
- <h3 class="text-lg font-semibold mb-4">
759
- Email Address
760
- </h3>
761
- <UForm
762
- :state="emailState"
763
- class="space-y-4"
764
- @submit="handleEmailSubmit"
765
- >
766
- <UFormField
767
- label="Email"
768
- name="newEmail"
769
- >
770
- <UInput
771
- v-model="emailState.newEmail"
772
- type="email"
773
- placeholder="your@email.com"
774
- class="w-full"
775
- />
776
- </UFormField>
777
- <UButton
778
- type="submit"
779
- :loading="emailLoading"
780
- >
781
- Change Email
782
- </UButton>
783
- </UForm>
784
- </div>
785
-
786
- <USeparator />
787
-
788
- <!-- Password Section -->
789
- <div>
790
- <h3 class="text-lg font-semibold mb-4">
791
- Change Password
792
- </h3>
793
- <UForm
794
- :state="passwordState"
795
- class="space-y-4"
796
- @submit="handlePasswordSubmit"
797
- >
798
- <UFormField
799
- label="Current Password"
800
- name="currentPassword"
801
- >
802
- <UInput
803
- v-model="passwordState.currentPassword"
804
- type="password"
805
- class="w-full"
806
- />
807
- </UFormField>
808
- <UFormField
809
- label="New Password"
810
- name="newPassword"
811
- >
812
- <UInput
813
- v-model="passwordState.newPassword"
814
- type="password"
815
- class="w-full"
816
- />
817
- </UFormField>
818
- <UFormField
819
- label="Confirm New Password"
820
- name="confirmPassword"
821
- >
822
- <UInput
823
- v-model="passwordState.confirmPassword"
824
- type="password"
825
- class="w-full"
826
- />
827
- </UFormField>
828
- <UButton
829
- type="submit"
830
- :loading="passwordLoading"
831
- >
832
- Change Password
833
- </UButton>
834
- </UForm>
835
- </div>
836
- </div>
837
- </template>
838
-
839
- <!-- Secrets Tab -->
840
- <template #secrets>
841
- <div class="py-6">
842
- <div class="flex items-center justify-between mb-6">
843
- <div>
844
- <h3 class="text-lg font-semibold">
845
- Secrets
846
- </h3>
847
- <p class="text-sm text-dimmed">
848
- Encrypted key-value store for skills and integrations.
849
- </p>
850
- </div>
851
- <UButton
852
- icon="i-lucide-plus"
853
- @click="openCreateSecret"
854
- >
855
- Add Secret
856
- </UButton>
857
- </div>
858
-
859
- <UTable
860
- :columns="secretColumns"
861
- :data="secretsData"
862
- :loading="secretsLoading"
863
- >
864
- <template #key-cell="{ row }">
865
- <code class="text-sm bg-elevated px-2 py-0.5 rounded">{{ row.original.key }}</code>
866
- </template>
867
- <template #description-cell="{ row }">
868
- <span class="text-dimmed">{{ row.original.description || '—' }}</span>
869
- </template>
870
- <template #updatedAt-cell="{ row }">
871
- <span class="text-dimmed text-sm">{{ formatDate(row.original.updatedAt) }}</span>
872
- </template>
873
- <template #actions-cell="{ row }">
874
- <div class="flex gap-2 justify-end">
875
- <UButton
876
- variant="ghost"
877
- icon="i-lucide-pencil"
878
- size="xs"
879
- @click="openEditSecret(row.original)"
880
- />
881
- <UButton
882
- variant="ghost"
883
- color="error"
884
- icon="i-lucide-trash-2"
885
- size="xs"
886
- @click="confirmDeleteSecret(row.original)"
887
- />
888
- </div>
889
- </template>
890
- <template #empty>
891
- <div class="py-12 text-center">
892
- <UIcon
893
- name="i-lucide-key"
894
- class="size-12 mx-auto mb-4 text-dimmed"
895
- />
896
- <p class="text-dimmed">
897
- No secrets yet. Add one to get started.
898
- </p>
899
- </div>
900
- </template>
901
- </UTable>
902
- </div>
903
- </template>
904
-
905
- <!-- Integrations Tab -->
906
- <template #integrations>
907
- <div class="py-6">
908
- <div class="flex items-center justify-between mb-6">
909
- <div>
910
- <h3 class="text-lg font-semibold">
911
- Integrations
912
- </h3>
913
- <p class="text-sm text-dimmed">
914
- Connect external platforms to the message bridge.
915
- </p>
916
- </div>
917
- <UButton
918
- icon="i-lucide-plus"
919
- @click="openCreateBridge"
920
- >
921
- Add Integration
922
- </UButton>
923
- </div>
924
-
925
- <div
926
- v-if="bridgesLoading"
927
- class="space-y-3"
928
- >
929
- <USkeleton
930
- v-for="i in 3"
931
- :key="i"
932
- class="h-16 w-full"
933
- />
934
- </div>
935
-
936
- <div
937
- v-else-if="bridgesData.length === 0"
938
- class="py-12 text-center"
939
- >
940
- <UIcon
941
- name="i-lucide-plug"
942
- class="size-12 mx-auto mb-4 text-dimmed"
943
- />
944
- <p class="text-dimmed">
945
- No integrations configured.
946
- </p>
947
- <p class="text-sm text-dimmed mt-1">
948
- Connect Telegram, Discord, iMessage, or Google Suite.
949
- </p>
950
- </div>
951
-
952
- <div
953
- v-else
954
- class="space-y-3"
955
- >
956
- <div
957
- v-for="bridge in bridgesData"
958
- :key="bridge.id"
959
- class="border border-default rounded-lg px-4 py-3 cursor-pointer hover:bg-elevated/50 transition-colors"
960
- @click="openBridgeConfig(bridge)"
961
- >
962
- <div class="flex items-center justify-between">
963
- <div class="flex items-center gap-3">
964
- <UIcon
965
- :name="platformOptions.find(p => p.value === bridge.platform)?.icon || 'i-lucide-plug'"
966
- class="size-5 text-dimmed"
967
- />
968
- <div>
969
- <span class="font-medium">{{ bridge.name }}</span>
970
- <span class="text-sm text-dimmed ml-2">{{ platformOptions.find(p => p.value === bridge.platform)?.label }}</span>
971
- </div>
972
- </div>
973
- <div class="flex items-center gap-3">
974
- <span
975
- class="text-xs capitalize"
976
- :class="healthColors[bridge.healthStatus] || 'text-dimmed'"
977
- >
978
- {{ bridge.healthStatus }}
979
- </span>
980
- <USwitch
981
- :model-value="bridge.enabled"
982
- @click.stop
983
- @update:model-value="toggleBridge(bridge)"
984
- />
985
- <UButton
986
- variant="ghost"
987
- color="error"
988
- icon="i-lucide-trash-2"
989
- size="xs"
990
- @click.stop="confirmDeleteBridge(bridge)"
991
- />
992
- </div>
993
- </div>
994
- <p
995
- v-if="bridge.healthMessage"
996
- class="text-xs text-dimmed mt-1 pl-8"
997
- >
998
- {{ bridge.healthMessage }}
999
- </p>
1000
- </div>
1001
- </div>
1002
- </div>
1003
- </template>
1004
-
1005
- <!-- App Tab -->
1006
- <template #app>
1007
- <div class="max-w-2xl mx-auto py-6">
1008
- <!-- Public URL -->
1009
- <div class="mb-8">
1010
- <h3 class="text-lg font-semibold mb-1">
1011
- Public URL
1012
- </h3>
1013
- <p class="text-sm text-dimmed mb-4">
1014
- The publicly accessible URL for this instance. Used for Telegram webhooks, auth callbacks, and other integrations that need to reach your server.
1015
- </p>
1016
- <div class="flex gap-2">
1017
- <UInput
1018
- v-model="appUrl"
1019
- placeholder="https://example.com"
1020
- class="flex-1"
1021
- />
1022
- <UButton
1023
- :loading="appUrlSaving"
1024
- @click="saveAppUrl"
1025
- >
1026
- Save
1027
- </UButton>
1028
- </div>
1029
- <p class="text-xs text-dimmed mt-2">
1030
- Leave empty to use long-polling for Telegram instead of webhooks. Changes require restarting running integrations.
1031
- </p>
1032
- </div>
1033
-
1034
- <USeparator class="mb-8" />
1035
-
1036
- <!-- Notification Preferences -->
1037
- <div class="mb-6">
1038
- <h3 class="text-lg font-semibold mb-1">
1039
- Notification Preferences
1040
- </h3>
1041
- <p class="text-sm text-dimmed">
1042
- Choose which resource changes show toast notifications.
1043
- </p>
1044
- </div>
1045
-
1046
- <div
1047
- v-if="notifLoading"
1048
- class="space-y-3"
1049
- >
1050
- <USkeleton
1051
- v-for="i in 5"
1052
- :key="i"
1053
- class="h-12 w-full"
1054
- />
1055
- </div>
1056
-
1057
- <div
1058
- v-else
1059
- class="space-y-2"
1060
- >
1061
- <div
1062
- v-for="rc in resourceConfig"
1063
- :key="rc.key"
1064
- class="border border-default rounded-lg"
1065
- >
1066
- <div class="flex items-center justify-between px-4 py-3">
1067
- <div class="flex items-center gap-3">
1068
- <UButton
1069
- variant="ghost"
1070
- size="xs"
1071
- :icon="expandedResources.has(rc.key) ? 'i-lucide-chevron-down' : 'i-lucide-chevron-right'"
1072
- @click="toggleResourceExpand(rc.key)"
1073
- />
1074
- <UIcon
1075
- :name="rc.icon"
1076
- class="size-5 text-dimmed"
1077
- />
1078
- <span class="font-medium">{{ rc.label }}</span>
1079
- </div>
1080
- <USwitch
1081
- :model-value="notifPrefs[rc.key]?.enabled ?? false"
1082
- @update:model-value="(v: boolean) => setResourceEnabled(rc.key, v)"
1083
- />
1084
- </div>
1085
-
1086
- <div
1087
- v-if="expandedResources.has(rc.key) && notifPrefs[rc.key]?.enabled"
1088
- class="border-t border-default px-4 py-3 space-y-2 bg-elevated/50"
1089
- >
1090
- <div
1091
- v-for="subtype in rc.subtypes"
1092
- :key="subtype"
1093
- class="flex items-center justify-between pl-11"
1094
- >
1095
- <span class="text-sm text-dimmed capitalize">{{ subtype }}</span>
1096
- <USwitch
1097
- :model-value="notifPrefs[rc.key]?.subtypes?.[subtype] !== false"
1098
- size="sm"
1099
- @update:model-value="(v: boolean) => setSubtypeEnabled(rc.key, subtype, v)"
1100
- />
1101
- </div>
1102
- </div>
1103
- </div>
1104
- </div>
1105
-
1106
- <div class="mt-6">
1107
- <UButton
1108
- :loading="notifSaving"
1109
- @click="saveNotificationPrefs"
1110
- >
1111
- Save Preferences
1112
- </UButton>
1113
- </div>
1114
- </div>
1115
- </template>
1116
- </UTabs>
1117
-
1118
- <template #fallback>
1119
- <div class="space-y-8 max-w-xl mx-auto py-6">
1120
- <USkeleton class="h-10 w-full" />
1121
- <div class="space-y-4">
1122
- <USkeleton class="h-5 w-20" />
1123
- <USkeleton class="h-10 w-full" />
1124
- <USkeleton class="h-10 w-28" />
1125
- </div>
1126
- </div>
1127
- </template>
1128
- </ClientOnly>
39
+ <UTabs
40
+ :items="tabs"
41
+ :model-value="activeTab"
42
+ :content="false"
43
+ class="w-full mx-auto"
44
+ :ui="{ list: 'max-w-xl' }"
45
+ @update:model-value="navigateTab"
46
+ />
47
+ <NuxtPage />
1129
48
  </div>
1130
-
1131
- <!-- Secret Create/Edit Modal -->
1132
- <UModal v-model:open="secretModal">
1133
- <template #header>
1134
- <h3 class="text-lg font-semibold">
1135
- {{ editingSecret ? 'Edit Secret' : 'Add Secret' }}
1136
- </h3>
1137
- </template>
1138
- <template #body>
1139
- <UForm
1140
- :state="secretForm"
1141
- class="space-y-4"
1142
- @submit="handleSecretSubmit"
1143
- >
1144
- <UFormField
1145
- label="Key"
1146
- name="key"
1147
- :hint="editingSecret ? '' : 'SCREAMING_SNAKE_CASE'"
1148
- >
1149
- <UInput
1150
- v-model="secretForm.key"
1151
- :disabled="!!editingSecret"
1152
- placeholder="MY_API_KEY"
1153
- class="w-full font-mono"
1154
- />
1155
- </UFormField>
1156
- <UFormField
1157
- label="Value"
1158
- name="value"
1159
- :hint="editingSecret ? 'Leave empty to keep current value' : ''"
1160
- >
1161
- <UInput
1162
- v-model="secretForm.value"
1163
- type="password"
1164
- :placeholder="editingSecret ? '••••••••' : 'Secret value'"
1165
- class="w-full"
1166
- />
1167
- </UFormField>
1168
- <UFormField
1169
- label="Description"
1170
- name="description"
1171
- >
1172
- <UInput
1173
- v-model="secretForm.description"
1174
- placeholder="Optional description"
1175
- class="w-full"
1176
- />
1177
- </UFormField>
1178
- <div class="flex justify-end gap-2 pt-4">
1179
- <UButton
1180
- variant="ghost"
1181
- @click="secretModal = false"
1182
- >
1183
- Cancel
1184
- </UButton>
1185
- <UButton
1186
- type="submit"
1187
- :loading="secretSaving"
1188
- >
1189
- {{ editingSecret ? 'Update' : 'Create' }}
1190
- </UButton>
1191
- </div>
1192
- </UForm>
1193
- </template>
1194
- </UModal>
1195
-
1196
- <!-- Delete Confirmation Modal -->
1197
- <UModal v-model:open="secretDeleteConfirm">
1198
- <template #header>
1199
- <h3 class="text-lg font-semibold">
1200
- Delete Secret
1201
- </h3>
1202
- </template>
1203
- <template #body>
1204
- <p class="mb-4">
1205
- Are you sure you want to delete <code class="bg-elevated px-2 py-0.5 rounded">{{ secretToDelete?.key }}</code>?
1206
- </p>
1207
- <p class="text-sm text-dimmed">
1208
- This action cannot be undone. Any skills using this secret will stop working.
1209
- </p>
1210
- <div class="flex justify-end gap-2 pt-6">
1211
- <UButton
1212
- variant="ghost"
1213
- @click="secretDeleteConfirm = false"
1214
- >
1215
- Cancel
1216
- </UButton>
1217
- <UButton
1218
- color="error"
1219
- @click="handleDeleteSecret"
1220
- >
1221
- Delete
1222
- </UButton>
1223
- </div>
1224
- </template>
1225
- </UModal>
1226
-
1227
- <!-- Bridge Create Modal -->
1228
- <UModal v-model:open="bridgeModal">
1229
- <template #header>
1230
- <h3 class="text-lg font-semibold">
1231
- Add Integration
1232
- </h3>
1233
- </template>
1234
- <template #body>
1235
- <div class="space-y-4">
1236
- <UFormField
1237
- label="Platform"
1238
- name="platform"
1239
- >
1240
- <USelect
1241
- v-model="bridgeForm.platform"
1242
- :items="platformOptions"
1243
- value-key="value"
1244
- class="w-full"
1245
- />
1246
- </UFormField>
1247
-
1248
- <!-- Missing secrets warning -->
1249
- <div
1250
- v-if="getMissingSecrets(bridgeForm.platform).length"
1251
- class="flex items-start gap-2 rounded-lg bg-error/10 border border-error/20 p-3 text-sm"
1252
- >
1253
- <UIcon
1254
- name="i-lucide-alert-triangle"
1255
- class="size-4 mt-0.5 text-error shrink-0"
1256
- />
1257
- <div>
1258
- <p class="font-medium text-error">
1259
- Missing required secrets
1260
- </p>
1261
- <p class="text-dimmed mt-1">
1262
- Add the following in Settings &rarr; Secrets before creating:
1263
- </p>
1264
- <ul class="mt-1 space-y-0.5">
1265
- <li
1266
- v-for="key in getMissingSecrets(bridgeForm.platform)"
1267
- :key="key"
1268
- >
1269
- <code class="text-xs bg-elevated px-1.5 py-0.5 rounded">{{ key }}</code>
1270
- </li>
1271
- </ul>
1272
- </div>
1273
- </div>
1274
-
1275
- <UFormField
1276
- label="Name"
1277
- name="name"
1278
- >
1279
- <UInput
1280
- v-model="bridgeForm.name"
1281
- :placeholder="platformNamePlaceholders[bridgeForm.platform]"
1282
- class="w-full"
1283
- />
1284
- </UFormField>
1285
- <div class="flex justify-end gap-2 pt-4">
1286
- <UButton
1287
- variant="ghost"
1288
- @click="bridgeModal = false"
1289
- >
1290
- Cancel
1291
- </UButton>
1292
- <UButton
1293
- :loading="bridgeSaving"
1294
- :disabled="getMissingSecrets(bridgeForm.platform).length > 0"
1295
- @click="handleBridgeCreate"
1296
- >
1297
- Create
1298
- </UButton>
1299
- </div>
1300
- </div>
1301
- </template>
1302
- </UModal>
1303
-
1304
- <!-- Bridge Delete Confirmation Modal -->
1305
- <UModal v-model:open="bridgeDeleteConfirm">
1306
- <template #header>
1307
- <h3 class="text-lg font-semibold">
1308
- Delete Integration
1309
- </h3>
1310
- </template>
1311
- <template #body>
1312
- <p class="mb-4">
1313
- Are you sure you want to delete <strong>{{ bridgeToDelete?.name }}</strong>?
1314
- </p>
1315
- <p class="text-sm text-dimmed">
1316
- This will remove the integration and all associated message history.
1317
- </p>
1318
- <div class="flex justify-end gap-2 pt-6">
1319
- <UButton
1320
- variant="ghost"
1321
- @click="bridgeDeleteConfirm = false"
1322
- >
1323
- Cancel
1324
- </UButton>
1325
- <UButton
1326
- color="error"
1327
- @click="handleDeleteBridge"
1328
- >
1329
- Delete
1330
- </UButton>
1331
- </div>
1332
- </template>
1333
- </UModal>
1334
-
1335
- <!-- Bridge Config Modal -->
1336
- <UModal v-model:open="bridgeConfigModal">
1337
- <template #header>
1338
- <div class="flex items-center gap-2">
1339
- <UIcon
1340
- :name="platformOptions.find(p => p.value === editingBridge?.platform)?.icon || 'i-lucide-plug'"
1341
- class="size-5"
1342
- />
1343
- <h3 class="text-lg font-semibold">
1344
- Configure {{ editingBridge?.name }}
1345
- </h3>
1346
- </div>
1347
- </template>
1348
- <template #body>
1349
- <div class="space-y-4">
1350
- <!-- Secret hint -->
1351
- <div
1352
- v-if="editingBridge && platformRequiredSecrets[editingBridge.platform].length"
1353
- class="flex items-start gap-2 rounded-lg bg-elevated p-3 text-sm"
1354
- >
1355
- <UIcon
1356
- name="i-lucide-info"
1357
- class="size-4 mt-0.5 text-dimmed shrink-0"
1358
- />
1359
- <span class="text-dimmed">
1360
- Requires
1361
- <template
1362
- v-for="(key, i) in platformRequiredSecrets[editingBridge.platform]"
1363
- :key="key"
1364
- >
1365
- <code class="bg-elevated px-1 py-0.5 rounded text-xs">{{ key }}</code><template v-if="i < platformRequiredSecrets[editingBridge.platform].length - 1">, </template>
1366
- </template>
1367
- in the Secrets tab.
1368
- </span>
1369
- </div>
1370
-
1371
- <!-- Common: Name -->
1372
- <UFormField
1373
- label="Name"
1374
- name="name"
1375
- >
1376
- <UInput
1377
- v-model="bridgeConfigForm.name"
1378
- placeholder="Integration name"
1379
- class="w-full"
1380
- />
1381
- </UFormField>
1382
-
1383
- <!-- Telegram -->
1384
- <template v-if="editingBridge?.platform === 'telegram'">
1385
- <UFormField
1386
- label="Bot Username"
1387
- name="botUsername"
1388
- hint="Without the @ prefix"
1389
- >
1390
- <UInput
1391
- v-model="bridgeConfigForm.botUsername"
1392
- placeholder="my_bot"
1393
- class="w-full"
1394
- />
1395
- </UFormField>
1396
- <UFormField
1397
- label="Allowed Chat IDs"
1398
- name="allowedChatIds"
1399
- hint="Comma-separated. Leave empty for all."
1400
- >
1401
- <UInput
1402
- v-model="bridgeConfigForm.allowedChatIds"
1403
- placeholder="123456, -100789"
1404
- class="w-full"
1405
- />
1406
- </UFormField>
1407
- </template>
1408
-
1409
- <!-- Discord -->
1410
- <template v-if="editingBridge?.platform === 'discord'">
1411
- <UFormField
1412
- label="Listen Mode"
1413
- name="listenMode"
1414
- >
1415
- <USelect
1416
- v-model="bridgeConfigForm.listenMode"
1417
- :items="discordListenModeOptions"
1418
- value-key="value"
1419
- class="w-full"
1420
- />
1421
- </UFormField>
1422
- <UFormField
1423
- label="Server ID"
1424
- name="guildId"
1425
- hint="Optional. Limit to one server."
1426
- >
1427
- <UInput
1428
- v-model="bridgeConfigForm.guildId"
1429
- placeholder="Discord server ID"
1430
- class="w-full"
1431
- />
1432
- </UFormField>
1433
- <UFormField
1434
- label="Channel ID"
1435
- name="channelId"
1436
- hint="Optional. Limit to one channel (for 'all' mode)."
1437
- >
1438
- <UInput
1439
- v-model="bridgeConfigForm.channelId"
1440
- placeholder="Discord channel ID"
1441
- class="w-full"
1442
- />
1443
- </UFormField>
1444
- </template>
1445
-
1446
- <!-- iMessage -->
1447
- <template v-if="editingBridge?.platform === 'imessage'">
1448
- <UFormField
1449
- label="Strategy"
1450
- name="strategy"
1451
- >
1452
- <USelect
1453
- v-model="bridgeConfigForm.strategy"
1454
- :items="imessageStrategyOptions"
1455
- value-key="value"
1456
- class="w-full"
1457
- />
1458
- </UFormField>
1459
- <div
1460
- v-if="bridgeConfigForm.strategy === 'bluebubbles' && !secretsData.some(s => s.key === 'BLUEBUBBLES_PASSWORD')"
1461
- class="flex items-start gap-2 rounded-lg bg-error/10 border border-error/20 p-3 text-sm"
1462
- >
1463
- <UIcon
1464
- name="i-lucide-alert-triangle"
1465
- class="size-4 mt-0.5 text-error shrink-0"
1466
- />
1467
- <span class="text-dimmed">
1468
- BlueBubbles requires secret <code class="text-xs bg-elevated px-1.5 py-0.5 rounded">BLUEBUBBLES_PASSWORD</code> in the Secrets tab.
1469
- </span>
1470
- </div>
1471
- <UFormField
1472
- v-if="bridgeConfigForm.strategy === 'bluebubbles'"
1473
- label="BlueBubbles URL"
1474
- name="blueBubblesUrl"
1475
- >
1476
- <UInput
1477
- v-model="bridgeConfigForm.blueBubblesUrl"
1478
- placeholder="http://192.168.1.100:1234"
1479
- class="w-full"
1480
- />
1481
- </UFormField>
1482
- <UFormField
1483
- label="Allowed Numbers"
1484
- name="allowedNumbers"
1485
- hint="Comma-separated. Leave empty for all."
1486
- >
1487
- <UInput
1488
- v-model="bridgeConfigForm.allowedNumbers"
1489
- placeholder="+15551234567, +15559876543"
1490
- class="w-full"
1491
- />
1492
- </UFormField>
1493
- </template>
1494
-
1495
- <!-- Google Suite -->
1496
- <template v-if="editingBridge?.platform === 'google'">
1497
- <UFormField
1498
- label="Enabled Services"
1499
- name="enabledServices"
1500
- >
1501
- <div class="space-y-2">
1502
- <label
1503
- v-for="svc in googleServiceOptions"
1504
- :key="svc.value"
1505
- class="flex items-center gap-2 cursor-pointer"
1506
- >
1507
- <input
1508
- type="checkbox"
1509
- :checked="bridgeConfigForm.enabledServices.includes(svc.value)"
1510
- class="rounded"
1511
- @change="
1512
- bridgeConfigForm.enabledServices.includes(svc.value)
1513
- ? bridgeConfigForm.enabledServices = bridgeConfigForm.enabledServices.filter(s => s !== svc.value)
1514
- : bridgeConfigForm.enabledServices.push(svc.value)
1515
- "
1516
- >
1517
- <span class="text-sm">{{ svc.label }}</span>
1518
- </label>
1519
- </div>
1520
- </UFormField>
1521
- <UFormField
1522
- label="Google Account"
1523
- name="account"
1524
- hint="Email used with gogcli"
1525
- >
1526
- <UInput
1527
- v-model="bridgeConfigForm.account"
1528
- placeholder="user@gmail.com"
1529
- class="w-full"
1530
- />
1531
- </UFormField>
1532
- </template>
1533
-
1534
- <!-- Email (IMAP/SMTP) -->
1535
- <template v-if="editingBridge?.platform === 'email'">
1536
- <UFormField
1537
- label="Email Address"
1538
- name="emailAddress"
1539
- >
1540
- <UInput
1541
- v-model="bridgeConfigForm.emailAddress"
1542
- placeholder="user@example.com"
1543
- class="w-full"
1544
- />
1545
- </UFormField>
1546
- <div class="grid grid-cols-2 gap-4">
1547
- <UFormField
1548
- label="IMAP Host"
1549
- name="imapHost"
1550
- >
1551
- <UInput
1552
- v-model="bridgeConfigForm.imapHost"
1553
- placeholder="imap.example.com"
1554
- class="w-full"
1555
- />
1556
- </UFormField>
1557
- <UFormField
1558
- label="IMAP Port"
1559
- name="imapPort"
1560
- >
1561
- <UInput
1562
- v-model="bridgeConfigForm.imapPort"
1563
- placeholder="993"
1564
- class="w-full"
1565
- />
1566
- </UFormField>
1567
- </div>
1568
- <div class="grid grid-cols-2 gap-4">
1569
- <UFormField
1570
- label="SMTP Host"
1571
- name="smtpHost"
1572
- >
1573
- <UInput
1574
- v-model="bridgeConfigForm.smtpHost"
1575
- placeholder="smtp.example.com"
1576
- class="w-full"
1577
- />
1578
- </UFormField>
1579
- <UFormField
1580
- label="SMTP Port"
1581
- name="smtpPort"
1582
- >
1583
- <UInput
1584
- v-model="bridgeConfigForm.smtpPort"
1585
- placeholder="587"
1586
- class="w-full"
1587
- />
1588
- </UFormField>
1589
- </div>
1590
- </template>
1591
-
1592
- <!-- Save / Cancel -->
1593
- <div class="flex justify-end gap-2 pt-4">
1594
- <UButton
1595
- variant="ghost"
1596
- @click="bridgeConfigModal = false"
1597
- >
1598
- Cancel
1599
- </UButton>
1600
- <UButton
1601
- :loading="bridgeConfigSaving"
1602
- @click="handleBridgeConfigSave"
1603
- >
1604
- Save
1605
- </UButton>
1606
- </div>
1607
- </div>
1608
- </template>
1609
- </UModal>
1610
49
  </UDashboardPanel>
1611
50
  </template>