cognova 0.1.9 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (299) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/_nuxt/{B4LjQa2N.js → 4MiwzAAt.js} +2 -2
  3. package/.output/public/_nuxt/{BZZeOWzQ.js → 5ZXA0Ckq.js} +1 -1
  4. package/.output/public/_nuxt/{B194okoV.js → 9IQmsEjr.js} +1 -1
  5. package/.output/public/_nuxt/{DQ_ZoW_9.js → 9QnH0xQM.js} +1 -1
  6. package/.output/public/_nuxt/{CF0HGIV6.js → B1X4Bzcy.js} +1 -1
  7. package/.output/public/_nuxt/{D9lowtoI.js → B3y_Qqox.js} +1 -1
  8. package/.output/public/_nuxt/{BB1R_SaV.js → B6S_ob86.js} +1 -1
  9. package/.output/public/_nuxt/{DVf1IpSh.js → BAIz-dEB.js} +1 -1
  10. package/.output/public/_nuxt/{CNFNMRY1.js → BCtfQCzC.js} +1 -1
  11. package/.output/public/_nuxt/BIIJhjQO.js +1 -0
  12. package/.output/public/_nuxt/BIckl6wA.js +1 -0
  13. package/.output/public/_nuxt/{Biy6boRV.js → BJ3o57WW.js} +1 -1
  14. package/.output/public/_nuxt/{B5rIsDOv.js → BNetzZzF.js} +1 -1
  15. package/.output/public/_nuxt/{DFXLcgxL.js → BOj6t-oo.js} +1 -1
  16. package/.output/public/_nuxt/{BDUpLPg_.js → BS0ofHJK.js} +1 -1
  17. package/.output/public/_nuxt/{DIdcflWi.js → BWhMnjID.js} +1 -1
  18. package/.output/public/_nuxt/{CExLOuIW.js → BYjadNrw.js} +1 -1
  19. package/.output/public/_nuxt/{C8ytOrRD.js → BZXMQuYP.js} +1 -1
  20. package/.output/public/_nuxt/{Ddih3UKd.js → B_3_hrpn.js} +1 -1
  21. package/.output/public/_nuxt/{CxSyRXlk.js → BaBZjmMC.js} +1 -1
  22. package/.output/public/_nuxt/BaMqDm5u.js +1 -0
  23. package/.output/public/_nuxt/BasgsT_S.js +1 -0
  24. package/.output/public/_nuxt/{Cehzuz52.js → BffWCM73.js} +1 -1
  25. package/.output/public/_nuxt/{Dh0BcDEI.js → BhzMoffi.js} +1 -1
  26. package/.output/public/_nuxt/{DmV68Eyp.js → BjjCvHLT.js} +1 -1
  27. package/.output/public/_nuxt/{nzAH3Wk0.js → BlAZO7nq.js} +1 -1
  28. package/.output/public/_nuxt/{BRPJdy3u.js → BlhFigLL.js} +1 -1
  29. package/.output/public/_nuxt/{CfTFjEuh.js → Bm9LyG4F.js} +1 -1
  30. package/.output/public/_nuxt/BoIxv-gM.js +1 -0
  31. package/.output/public/_nuxt/{BslL2E8V.js → BqKFIuRj.js} +1 -1
  32. package/.output/public/_nuxt/Br19oYkq.js +1 -0
  33. package/.output/public/_nuxt/{PtGESE-F.js → BrNqhp1a.js} +3 -3
  34. package/.output/public/_nuxt/{FvV9sv7q.js → BsEZoHd1.js} +1 -1
  35. package/.output/public/_nuxt/{BaPPQtFe.js → BxXOsXrM.js} +10 -10
  36. package/.output/public/_nuxt/BzOqrmGa.js +7 -0
  37. package/.output/public/_nuxt/{BNAsyazX.js → C0JKNMDO.js} +1 -1
  38. package/.output/public/_nuxt/C0kh_F7v.js +1 -0
  39. package/.output/public/_nuxt/{4Ew5YctE.js → C3FxIITy.js} +1 -1
  40. package/.output/public/_nuxt/{DnToOWVx.js → C4pxqdgg.js} +1 -1
  41. package/.output/public/_nuxt/{C5Ue4WbD.js → C61KgSco.js} +1 -1
  42. package/.output/public/_nuxt/{DcMvYcFq.js → C69W7k2j.js} +1 -1
  43. package/.output/public/_nuxt/{BnnFvWzv.js → C6RC3lA1.js} +1 -1
  44. package/.output/public/_nuxt/{DBENYi9d.js → C6aqGHu1.js} +1 -1
  45. package/.output/public/_nuxt/{YldWqMAh.js → CJUdYEdO.js} +1 -1
  46. package/.output/public/_nuxt/{BPAM-iC7.js → CKkC3Ptm.js} +1 -1
  47. package/.output/public/_nuxt/{CTXhFIUN.js → CNnJrDvu.js} +1 -1
  48. package/.output/public/_nuxt/{BwH2E0-a.js → CVJQGP1Q.js} +1 -1
  49. package/.output/public/_nuxt/{q0vhMpUB.js → CVgTJeSq.js} +1 -1
  50. package/.output/public/_nuxt/CWMUi89H.js +1 -0
  51. package/.output/public/_nuxt/{BU9VyOEC.js → CYP_MLH8.js} +1 -1
  52. package/.output/public/_nuxt/{76CHU1js.js → C_BdYLzz.js} +1 -1
  53. package/.output/public/_nuxt/{BQbnVFXA.js → Cdt3I3Go.js} +1 -1
  54. package/.output/public/_nuxt/Cdu2qGgq.js +1 -0
  55. package/.output/public/_nuxt/{ChMIRIq6.js → CeIVm4A3.js} +1 -1
  56. package/.output/public/_nuxt/CeIu7z4p.js +1 -0
  57. package/.output/public/_nuxt/{CZZw0T2a.js → Ci7UEZQh.js} +1 -1
  58. package/.output/public/_nuxt/{BP_jQhKs.js → CihWZmJe.js} +2 -2
  59. package/.output/public/_nuxt/{Bluqlt6l.js → CitkKxhw.js} +1 -1
  60. package/.output/public/_nuxt/{DIM73BjE.js → CmzH6R-N.js} +2 -2
  61. package/.output/public/_nuxt/{CD5hHJBM.js → Cp2MA0cm.js} +1 -1
  62. package/.output/public/_nuxt/{DflBkWfa.js → CqU2XbzO.js} +1 -1
  63. package/.output/public/_nuxt/{Ccc8OoR8.js → Cqy_L_ip.js} +1 -1
  64. package/.output/public/_nuxt/Cr8ixbr1.js +1 -0
  65. package/.output/public/_nuxt/{DdLGO0wP.js → CsJ9KhQ4.js} +1 -1
  66. package/.output/public/_nuxt/{mHNfVoS0.js → CtYFj7k1.js} +1 -1
  67. package/.output/public/_nuxt/{DIBimEIe.js → CtchsY6e.js} +1 -1
  68. package/.output/public/_nuxt/{BboG47Wz.js → Cvp7FI3T.js} +1 -1
  69. package/.output/public/_nuxt/CxuZBC8n.js +70 -0
  70. package/.output/public/_nuxt/D2689qk4.js +1 -0
  71. package/.output/public/_nuxt/{CWWmC6Y7.js → D31L7Ks6.js} +1 -1
  72. package/.output/public/_nuxt/{JxSV3wOO.js → D3RiUGdz.js} +1 -1
  73. package/.output/public/_nuxt/{C1tYtAeV.js → D3e44mCL.js} +1 -1
  74. package/.output/public/_nuxt/{BTWyDDBM.js → D6t3dcTl.js} +1 -1
  75. package/.output/public/_nuxt/{DpLjEbYb.js → DAQhmSv4.js} +1 -1
  76. package/.output/public/_nuxt/{BnwR3LQL.js → DB359q8R.js} +1 -1
  77. package/.output/public/_nuxt/{jCKYMNtT.js → DCzfkCGa.js} +1 -1
  78. package/.output/public/_nuxt/{BXWm1qrj.js → DG-T44jj.js} +1 -1
  79. package/.output/public/_nuxt/{BBygTbuF.js → DGX0tzL8.js} +1 -1
  80. package/.output/public/_nuxt/DHG66LPS.js +1 -0
  81. package/.output/public/_nuxt/{jBqB49XU.js → DHKLCQRG.js} +1 -1
  82. package/.output/public/_nuxt/{BDw-JoCx.js → DIoI0uJm.js} +1 -1
  83. package/.output/public/_nuxt/{DOQA5CEF.js → DJ5V-y_x.js} +1 -1
  84. package/.output/public/_nuxt/{BGaNWD0s.js → DJMS2og1.js} +1 -1
  85. package/.output/public/_nuxt/{UmgHYYVi.js → DJjDvbZE.js} +1 -1
  86. package/.output/public/_nuxt/{DdtX5kio.js → DK9jxJ0g.js} +1 -1
  87. package/.output/public/_nuxt/{Cc6GlQCO.js → DKZxeXDQ.js} +1 -1
  88. package/.output/public/_nuxt/{Tp-9mt9j.js → DLETdGFL.js} +1 -1
  89. package/.output/public/_nuxt/DOICd-Ld.js +1 -0
  90. package/.output/public/_nuxt/DPEcH-gi.js +65 -0
  91. package/.output/public/_nuxt/{CVNY6Bll.js → DSRrg8JT.js} +1 -1
  92. package/.output/public/_nuxt/{Sm7TJN7_.js → DTDgHTuh.js} +2 -2
  93. package/.output/public/_nuxt/{DeGKAWfY.js → D_3Rq1BS.js} +1 -1
  94. package/.output/public/_nuxt/{BvnL4Jyv.js → Daz4MeL6.js} +1 -1
  95. package/.output/public/_nuxt/{Dyd3g9po.js → Db2v8O7O.js} +1 -1
  96. package/.output/public/_nuxt/{D0FBwEzN.js → Db8-_gO7.js} +1 -1
  97. package/.output/public/_nuxt/{Cv4Qjbqk.js → DfQu3kEw.js} +1 -1
  98. package/.output/public/_nuxt/{c-RGQpsW.js → DgV-EDJ9.js} +1 -1
  99. package/.output/public/_nuxt/{CSF2cTe9.js → DnjGH3SQ.js} +1 -1
  100. package/.output/public/_nuxt/Dp2X5R2m.js +1 -0
  101. package/.output/public/_nuxt/DqB723Z0.js +1 -0
  102. package/.output/public/_nuxt/{DhRePYUI.js → DyRelyfs.js} +1 -1
  103. package/.output/public/_nuxt/{DZiAD6YN.js → Dy_Cq5LQ.js} +1 -1
  104. package/.output/public/_nuxt/{DWQMV4k2.js → DznawRFW.js} +1 -1
  105. package/.output/public/_nuxt/EuOqK1A6.js +1 -0
  106. package/.output/public/_nuxt/{BVZHIuu-.js → FNC8XZTk.js} +1 -1
  107. package/.output/public/_nuxt/{Dsk8JtTw.js → Fukkqjkf.js} +1 -1
  108. package/.output/public/_nuxt/{DNpdNXnv.js → FvlxxmNk.js} +2 -2
  109. package/.output/public/_nuxt/{zt0Txq93.js → IRSbVPIu.js} +1 -1
  110. package/.output/public/_nuxt/{T_w6A2Zo.js → JJ3634gV.js} +9 -9
  111. package/.output/public/_nuxt/{6Tt_Iaya.js → Jez9DHn7.js} +1 -1
  112. package/.output/public/_nuxt/{Cr7nQmYw.js → KKK6HVeG.js} +1 -1
  113. package/.output/public/_nuxt/{BJPw54Qz.js → Lwdv_RKd.js} +1 -1
  114. package/.output/public/_nuxt/{WqUIRM3G.js → Nb2jBtYT.js} +1 -1
  115. package/.output/public/_nuxt/Q8Ps7oN5.js +1 -0
  116. package/.output/public/_nuxt/SXTDhzp6.js +1 -0
  117. package/.output/public/_nuxt/{rfcePMfo.js → Sg2Lwc46.js} +1 -1
  118. package/.output/public/_nuxt/{gEpwc5gO.js → YuTZB7sD.js} +1 -1
  119. package/.output/public/_nuxt/{B2OKrc4G.js → _CYZi8HN.js} +1 -1
  120. package/.output/public/_nuxt/_J_7XIn-.js +1 -0
  121. package/.output/public/_nuxt/builds/latest.json +1 -1
  122. package/.output/public/_nuxt/builds/meta/a1e9100c-1a4f-4f7e-bb53-9dbe0d07effb.json +1 -0
  123. package/.output/public/_nuxt/{Bwx_Kl-4.js → cABRLVee.js} +1 -1
  124. package/.output/public/_nuxt/{DPuJYSUH.js → eko-0FUm.js} +1 -1
  125. package/.output/public/_nuxt/entry.CGxIBGAf.css +1 -0
  126. package/.output/public/_nuxt/{TQOIXkvr.js → g20UHUCv.js} +1 -1
  127. package/.output/public/_nuxt/{zOs8gUGl.js → gBC9k4Qj.js} +1 -1
  128. package/.output/public/_nuxt/{CJ3DQEW9.js → ghuJ76mD.js} +1 -1
  129. package/.output/public/_nuxt/{S6GTaxiQ.js → iBGCpHdw.js} +1 -1
  130. package/.output/public/_nuxt/{VGlRzjk3.js → inmzPrjz.js} +1 -1
  131. package/.output/public/_nuxt/{CCkumwhk.js → m5kGCDpI.js} +4 -4
  132. package/.output/public/_nuxt/{CXkbkhUa.js → nIU2F7ia.js} +3 -3
  133. package/.output/public/_nuxt/oIX-ZDN6.js +1 -0
  134. package/.output/public/_nuxt/{TsIha0iy.js → pcUI-zuY.js} +1 -1
  135. package/.output/public/_nuxt/{BN_2zhBR.js → sf57orEk.js} +1 -1
  136. package/.output/public/_nuxt/usage.vakN1lvi.css +1 -0
  137. package/.output/public/_nuxt/{DHYn0MnY.js → wCGVE8_e.js} +1 -1
  138. package/.output/public/_nuxt/{DMkXE4gH.js → xuzLdW-o.js} +1 -1
  139. package/.output/public/_nuxt/{DdoFnl6e.js → yNrp2XvX.js} +1 -1
  140. package/.output/public/_nuxt/{4hIPJQLp.js → yuf23kh9.js} +1 -1
  141. package/.output/server/chunks/build/CodeIcon-CWD5HcV7.mjs +1 -1
  142. package/.output/server/chunks/build/DropdownMenu-BBrV9nXz.mjs +1 -1
  143. package/.output/server/chunks/build/EditorToolbar-DIfb5arC.mjs +1 -1
  144. package/.output/server/chunks/build/Img-CWLmvN1t.mjs +1 -1
  145. package/.output/server/chunks/build/MDC-Dx0YPDhe.mjs +1 -1
  146. package/.output/server/chunks/build/Select-BB1oLrCD.mjs +1 -1
  147. package/.output/server/chunks/build/SelectMenu-DPssg6zD.mjs +1 -1
  148. package/.output/server/chunks/build/Table-DCwTlhCj.mjs +1 -1
  149. package/.output/server/chunks/build/Tooltip-TRyl6dje.mjs +1 -1
  150. package/.output/server/chunks/build/{_uuid_-CQEMsF1D.mjs → _uuid_-0UgdUhfY.mjs} +8 -7
  151. package/.output/server/chunks/build/{_uuid_-CQEMsF1D.mjs.map → _uuid_-0UgdUhfY.mjs.map} +1 -1
  152. package/.output/server/chunks/build/chat-CZMiB68R.mjs.map +1 -1
  153. package/.output/server/chunks/build/client.precomputed.mjs +1 -1
  154. package/.output/server/chunks/build/cookie-C_iulBi6.mjs +1 -1
  155. package/.output/server/chunks/build/{dashboard-24rCroiO.mjs → dashboard-CpMVYnDV.mjs} +13 -6
  156. package/.output/server/chunks/build/{dashboard-24rCroiO.mjs.map → dashboard-CpMVYnDV.mjs.map} +1 -1
  157. package/.output/server/chunks/build/dashboard-YEscLBQN.mjs +1109 -0
  158. package/.output/server/chunks/build/dashboard-YEscLBQN.mjs.map +1 -0
  159. package/.output/server/chunks/build/{docs-DOGZUctm.mjs → docs-Dk2JnYq3.mjs} +8 -7
  160. package/.output/server/chunks/build/{docs-DOGZUctm.mjs.map → docs-Dk2JnYq3.mjs.map} +1 -1
  161. package/.output/server/chunks/build/fetch-BB7Qzkwe.mjs +1 -1
  162. package/.output/server/chunks/build/{hooks-CuyNIRFI.mjs → hooks-DP8WoUPS.mjs} +2 -2
  163. package/.output/server/chunks/build/{hooks-CuyNIRFI.mjs.map → hooks-DP8WoUPS.mjs.map} +1 -1
  164. package/.output/server/chunks/build/{index-BZI-Mj1-.mjs → index-Ba_bPJgk.mjs} +2 -2
  165. package/.output/server/chunks/build/{index-BZI-Mj1-.mjs.map → index-Ba_bPJgk.mjs.map} +1 -1
  166. package/.output/server/chunks/build/index-CxDxc9fm.mjs +1 -1
  167. package/.output/server/chunks/build/server.mjs +26 -17
  168. package/.output/server/chunks/build/server.mjs.map +1 -1
  169. package/.output/server/chunks/build/settings-DdkKCJ00.mjs +1 -1
  170. package/.output/server/chunks/build/styles.mjs +2 -2
  171. package/.output/server/chunks/build/{tasks-DR55Wjpi.mjs → tasks-DiOi1HG_.mjs} +2 -2
  172. package/.output/server/chunks/build/{tasks-DR55Wjpi.mjs.map → tasks-DiOi1HG_.mjs.map} +1 -1
  173. package/.output/server/chunks/build/usage-H_mcd_fz.mjs +1578 -0
  174. package/.output/server/chunks/build/usage-H_mcd_fz.mjs.map +1 -0
  175. package/.output/server/chunks/build/{CodeEditorFallback-DfYly7f5.mjs → useCopyToClipboard-vi6FYyyZ.mjs} +29 -2
  176. package/.output/server/chunks/build/useCopyToClipboard-vi6FYyyZ.mjs.map +1 -0
  177. package/.output/server/chunks/build/useNotificationBus-BG5JNQf1.mjs +1 -1
  178. package/.output/server/chunks/build/{usePreferences-CUvZsOq5.mjs → usePreferences-CzC8fRzd.mjs} +7 -1
  179. package/.output/server/chunks/build/usePreferences-CzC8fRzd.mjs.map +1 -0
  180. package/.output/server/chunks/build/view-Dc8mvzCB.mjs +1 -1
  181. package/.output/server/chunks/nitro/nitro.mjs +884 -818
  182. package/.output/server/chunks/nitro/nitro.mjs.map +1 -1
  183. package/.output/server/chunks/routes/_ws/chat.mjs +18 -4
  184. package/.output/server/chunks/routes/_ws/chat.mjs.map +1 -1
  185. package/.output/server/chunks/routes/api/dashboard/overview.get.mjs +149 -0
  186. package/.output/server/chunks/routes/api/dashboard/overview.get.mjs.map +1 -0
  187. package/.output/server/chunks/routes/api/documents/_id/public.get.mjs +1 -1
  188. package/.output/server/chunks/routes/api/documents/_id/restore.post.mjs +1 -1
  189. package/.output/server/chunks/routes/api/documents/by-path.post.mjs +1 -1
  190. package/.output/server/chunks/routes/api/documents/index.delete.mjs +1 -1
  191. package/.output/server/chunks/routes/api/documents/index.put.mjs +1 -1
  192. package/.output/server/chunks/routes/api/fs/delete.post.mjs +1 -1
  193. package/.output/server/chunks/routes/api/fs/list.get.mjs +1 -1
  194. package/.output/server/chunks/routes/api/fs/mkdir.post.mjs +1 -1
  195. package/.output/server/chunks/routes/api/fs/move.post.mjs +1 -1
  196. package/.output/server/chunks/routes/api/fs/read.post.mjs +1 -1
  197. package/.output/server/chunks/routes/api/fs/rename.post.mjs +1 -1
  198. package/.output/server/chunks/routes/api/fs/write.post.mjs +1 -1
  199. package/.output/server/chunks/routes/api/health.get.mjs +1 -1
  200. package/.output/server/chunks/routes/api/home.get.mjs +1 -1
  201. package/.output/server/chunks/routes/api/hooks/index.get.mjs +1 -1
  202. package/.output/server/chunks/routes/api/hooks/index.post.mjs +1 -1
  203. package/.output/server/chunks/routes/api/hooks/stats.get.mjs +1 -1
  204. package/.output/server/chunks/routes/api/index.get3.mjs +1 -1
  205. package/.output/server/chunks/routes/api/index.get4.mjs +1 -1
  206. package/.output/server/chunks/routes/api/index.get5.mjs +1 -1
  207. package/.output/server/chunks/routes/api/index.get6.mjs +1 -1
  208. package/.output/server/chunks/routes/api/index.get7.mjs +1 -1
  209. package/.output/server/chunks/routes/api/index.get8.mjs +82 -0
  210. package/.output/server/chunks/routes/api/index.get8.mjs.map +1 -0
  211. package/.output/server/chunks/routes/api/index.post2.mjs +1 -1
  212. package/.output/server/chunks/routes/api/index.post3.mjs +1 -1
  213. package/.output/server/chunks/routes/api/index.post4.mjs +1 -1
  214. package/.output/server/chunks/routes/api/index.put.mjs +1 -1
  215. package/.output/server/chunks/routes/api/memory/_id_.delete.mjs +1 -1
  216. package/.output/server/chunks/routes/api/memory/context.get.mjs +1 -1
  217. package/.output/server/chunks/routes/api/memory/extract.post.mjs +20 -1
  218. package/.output/server/chunks/routes/api/memory/extract.post.mjs.map +1 -1
  219. package/.output/server/chunks/routes/api/memory/search.get.mjs +1 -1
  220. package/.output/server/chunks/routes/api/memory/store.post.mjs +1 -1
  221. package/.output/server/chunks/routes/api/projects/index.delete.mjs +1 -1
  222. package/.output/server/chunks/routes/api/projects/index.get.mjs +1 -1
  223. package/.output/server/chunks/routes/api/projects/index.put.mjs +1 -1
  224. package/.output/server/chunks/routes/api/secrets/_key_.delete.mjs +1 -1
  225. package/.output/server/chunks/routes/api/secrets/_key_.get.mjs +1 -1
  226. package/.output/server/chunks/routes/api/secrets/_key_.put.mjs +1 -1
  227. package/.output/server/chunks/routes/api/tasks/_id/restore.post.mjs +1 -1
  228. package/.output/server/chunks/routes/api/tasks/index.delete.mjs +1 -1
  229. package/.output/server/chunks/routes/api/tasks/index.put.mjs +1 -1
  230. package/.output/server/chunks/routes/api/tasks/tags.get.mjs +1 -1
  231. package/.output/server/chunks/routes/api/usage/stats.get.mjs +130 -0
  232. package/.output/server/chunks/routes/api/usage/stats.get.mjs.map +1 -0
  233. package/.output/server/chunks/routes/api/user/email.patch.mjs +1 -1
  234. package/.output/server/chunks/routes/notifications.mjs +1 -1
  235. package/.output/server/chunks/routes/renderer.mjs +1 -1
  236. package/.output/server/chunks/routes/terminal.mjs +1 -1
  237. package/.output/server/index.mjs +1 -1
  238. package/.output/server/package.json +1 -1
  239. package/app/components/dashboard/RecentChats.vue +93 -0
  240. package/app/components/dashboard/RecentDocs.vue +108 -0
  241. package/app/components/dashboard/StatCards.vue +82 -0
  242. package/app/components/dashboard/UpcomingTasks.vue +119 -0
  243. package/app/components/dashboard/UsageSummary.vue +94 -0
  244. package/app/components/editor/DocumentEditor.vue +5 -3
  245. package/app/components/usage/UsageCostChart.client.vue +174 -0
  246. package/app/components/usage/UsageCostChart.server.vue +22 -0
  247. package/app/components/usage/UsageRecordsTable.vue +249 -0
  248. package/app/components/usage/UsageSourceDonut.client.vue +93 -0
  249. package/app/components/usage/UsageSourceDonut.server.vue +21 -0
  250. package/app/components/usage/UsageStatsCards.vue +101 -0
  251. package/app/components/usage/UsageTopConsumers.vue +89 -0
  252. package/app/composables/useCopyToClipboard.ts +37 -0
  253. package/app/composables/usePreferences.ts +10 -0
  254. package/app/layouts/dashboard.vue +8 -1
  255. package/app/pages/chat.vue +11 -1
  256. package/app/pages/dashboard.vue +65 -64
  257. package/app/pages/usage.vue +130 -0
  258. package/app/pages/view/[uuid].vue +4 -3
  259. package/package.json +1 -1
  260. package/server/api/dashboard/overview.get.ts +133 -0
  261. package/server/api/usage/index.get.ts +69 -0
  262. package/server/api/usage/stats.get.ts +127 -0
  263. package/server/db/schema.ts +17 -0
  264. package/server/drizzle/migrations/0011_tearful_johnny_storm.sql +12 -0
  265. package/server/drizzle/migrations/meta/0011_snapshot.json +1634 -0
  266. package/server/drizzle/migrations/meta/_journal.json +7 -0
  267. package/server/routes/_ws/chat.ts +29 -2
  268. package/server/services/agent-executor.ts +13 -0
  269. package/server/services/memory-extractor.ts +25 -0
  270. package/server/utils/log-token-usage.ts +22 -0
  271. package/shared/types/index.ts +81 -0
  272. package/.output/public/_nuxt/B34UfOg4.js +0 -1
  273. package/.output/public/_nuxt/B3DmiwBX.js +0 -1
  274. package/.output/public/_nuxt/BESViHC6.js +0 -1
  275. package/.output/public/_nuxt/BIm_buQF.js +0 -1
  276. package/.output/public/_nuxt/BMh-dHBK.js +0 -1
  277. package/.output/public/_nuxt/Bkv1Ouue.js +0 -1
  278. package/.output/public/_nuxt/C4i10ReQ.js +0 -1
  279. package/.output/public/_nuxt/C5euqjeX.js +0 -1
  280. package/.output/public/_nuxt/C9hy_rtV.js +0 -1
  281. package/.output/public/_nuxt/CWJhKIyp.js +0 -1
  282. package/.output/public/_nuxt/Ck24Z-8o.js +0 -1
  283. package/.output/public/_nuxt/D1JmZKz7.js +0 -1
  284. package/.output/public/_nuxt/D6xvQUYG.js +0 -1
  285. package/.output/public/_nuxt/DGTbw5AQ.js +0 -1
  286. package/.output/public/_nuxt/Da8Xd1w6.js +0 -1
  287. package/.output/public/_nuxt/DamUHI17.js +0 -1
  288. package/.output/public/_nuxt/DoIiMqKa.js +0 -76
  289. package/.output/public/_nuxt/builds/meta/75ff59f2-0860-4bfc-a288-c670397cae5c.json +0 -1
  290. package/.output/public/_nuxt/entry.tLLAvqs1.css +0 -1
  291. package/.output/public/_nuxt/iJ4KRInq.js +0 -1
  292. package/.output/public/_nuxt/mSr5c6cB.js +0 -1
  293. package/.output/public/_nuxt/r8g_V5AH.js +0 -1
  294. package/.output/public/_nuxt/rZmMQHiC.js +0 -1
  295. package/.output/server/chunks/build/CodeEditorFallback-DfYly7f5.mjs.map +0 -1
  296. package/.output/server/chunks/build/dashboard-DtbGchTa.mjs +0 -277
  297. package/.output/server/chunks/build/dashboard-DtbGchTa.mjs.map +0 -1
  298. package/.output/server/chunks/build/usePreferences-CUvZsOq5.mjs.map +0 -1
  299. /package/.output/public/_nuxt/{language-detection.Be_IvFWy.css → useCopyToClipboard.Be_IvFWy.css} +0 -0
@@ -0,0 +1,119 @@
1
+ <script setup lang="ts">
2
+ import type { DashboardOverview } from '~~/shared/types'
3
+
4
+ defineProps<{
5
+ tasks: DashboardOverview['tasks']['upcoming']
6
+ loading?: boolean
7
+ }>()
8
+
9
+ const priorityLabels = { 1: 'Low', 2: 'Med', 3: 'High' } as const
10
+ const priorityColors = { 1: 'neutral', 2: 'warning', 3: 'error' } as const
11
+ const statusColors = { todo: 'neutral', in_progress: 'primary', blocked: 'error' } as const
12
+
13
+ function formatDueDate(iso: string | null): string {
14
+ if (!iso) return ''
15
+ const date = new Date(iso)
16
+ const now = new Date()
17
+ const diff = date.getTime() - now.getTime()
18
+ const days = Math.ceil(diff / (1000 * 60 * 60 * 24))
19
+ if (days < 0) return `${Math.abs(days)}d overdue`
20
+ if (days === 0) return 'Today'
21
+ if (days === 1) return 'Tomorrow'
22
+ return `${days}d`
23
+ }
24
+ </script>
25
+
26
+ <template>
27
+ <UCard>
28
+ <template #header>
29
+ <div class="flex items-center justify-between">
30
+ <div class="flex items-center gap-2">
31
+ <UIcon
32
+ name="i-lucide-check-square"
33
+ class="size-4 text-primary"
34
+ />
35
+ <span class="text-sm font-medium">Upcoming Tasks</span>
36
+ </div>
37
+ <UButton
38
+ to="/tasks"
39
+ variant="ghost"
40
+ size="xs"
41
+ trailing-icon="i-lucide-arrow-right"
42
+ >
43
+ View all
44
+ </UButton>
45
+ </div>
46
+ </template>
47
+
48
+ <div
49
+ v-if="loading"
50
+ class="space-y-3"
51
+ >
52
+ <div
53
+ v-for="i in 3"
54
+ :key="i"
55
+ class="flex items-center gap-3"
56
+ >
57
+ <USkeleton class="h-5 w-5 rounded" />
58
+ <USkeleton class="h-4 flex-1" />
59
+ </div>
60
+ </div>
61
+
62
+ <div
63
+ v-else-if="tasks.length === 0"
64
+ class="text-center py-4 text-muted text-sm"
65
+ >
66
+ No upcoming tasks
67
+ </div>
68
+
69
+ <div
70
+ v-else
71
+ class="space-y-2"
72
+ >
73
+ <NuxtLink
74
+ v-for="task in tasks"
75
+ :key="task.id"
76
+ :to="{ path: '/tasks', query: { selected: task.id } }"
77
+ class="flex items-center gap-3 p-2 -mx-2 rounded-md hover:bg-elevated transition-colors"
78
+ >
79
+ <UBadge
80
+ :color="statusColors[task.status as keyof typeof statusColors] || 'neutral'"
81
+ variant="subtle"
82
+ size="xs"
83
+ >
84
+ {{ task.status === 'in_progress' ? 'WIP' : task.status }}
85
+ </UBadge>
86
+
87
+ <span class="text-sm truncate flex-1">{{ task.title }}</span>
88
+
89
+ <UBadge
90
+ v-if="task.projectName"
91
+ variant="subtle"
92
+ color="neutral"
93
+ size="xs"
94
+ >
95
+ <span
96
+ class="inline-block size-2 rounded-full mr-1"
97
+ :style="{ background: task.projectColor || 'var(--ui-primary)' }"
98
+ />
99
+ {{ task.projectName }}
100
+ </UBadge>
101
+
102
+ <UBadge
103
+ :color="priorityColors[task.priority as keyof typeof priorityColors] || 'neutral'"
104
+ variant="soft"
105
+ size="xs"
106
+ >
107
+ {{ priorityLabels[task.priority as keyof typeof priorityLabels] || 'Med' }}
108
+ </UBadge>
109
+
110
+ <span
111
+ v-if="task.dueDate"
112
+ class="text-xs text-muted whitespace-nowrap"
113
+ >
114
+ {{ formatDueDate(task.dueDate) }}
115
+ </span>
116
+ </NuxtLink>
117
+ </div>
118
+ </UCard>
119
+ </template>
@@ -0,0 +1,94 @@
1
+ <script setup lang="ts">
2
+ import type { DashboardOverview } from '~~/shared/types'
3
+
4
+ defineProps<{
5
+ usage: DashboardOverview['usage']
6
+ loading?: boolean
7
+ }>()
8
+
9
+ function formatCurrency(value: number): string {
10
+ if (value < 0.01 && value > 0) return '<$0.01'
11
+ return `$${value.toFixed(2)}`
12
+ }
13
+
14
+ function formatTokens(value: number): string {
15
+ if (value >= 1_000_000) return `${(value / 1_000_000).toFixed(1)}M`
16
+ if (value >= 1_000) return `${(value / 1_000).toFixed(1)}K`
17
+ return String(value)
18
+ }
19
+ </script>
20
+
21
+ <template>
22
+ <UCard>
23
+ <template #header>
24
+ <div class="flex items-center justify-between">
25
+ <div class="flex items-center gap-2">
26
+ <UIcon
27
+ name="i-lucide-bar-chart-3"
28
+ class="size-4 text-primary"
29
+ />
30
+ <span class="text-sm font-medium">Token Usage (7d)</span>
31
+ </div>
32
+ <UButton
33
+ to="/usage"
34
+ variant="ghost"
35
+ size="xs"
36
+ trailing-icon="i-lucide-arrow-right"
37
+ >
38
+ Details
39
+ </UButton>
40
+ </div>
41
+ </template>
42
+
43
+ <div
44
+ v-if="loading"
45
+ class="grid grid-cols-2 gap-4"
46
+ >
47
+ <div
48
+ v-for="i in 4"
49
+ :key="i"
50
+ >
51
+ <USkeleton class="h-3 w-12 mb-1" />
52
+ <USkeleton class="h-6 w-16" />
53
+ </div>
54
+ </div>
55
+
56
+ <div
57
+ v-else
58
+ class="grid grid-cols-2 gap-4"
59
+ >
60
+ <div>
61
+ <p class="text-xs text-muted mb-0.5">
62
+ Total Cost
63
+ </p>
64
+ <p class="text-lg font-semibold">
65
+ {{ formatCurrency(usage.totalCost7d) }}
66
+ </p>
67
+ </div>
68
+ <div>
69
+ <p class="text-xs text-muted mb-0.5">
70
+ API Calls
71
+ </p>
72
+ <p class="text-lg font-semibold">
73
+ {{ usage.totalCalls7d }}
74
+ </p>
75
+ </div>
76
+ <div>
77
+ <p class="text-xs text-muted mb-0.5">
78
+ Input Tokens
79
+ </p>
80
+ <p class="text-lg font-semibold">
81
+ {{ formatTokens(usage.totalInputTokens7d) }}
82
+ </p>
83
+ </div>
84
+ <div>
85
+ <p class="text-xs text-muted mb-0.5">
86
+ Output Tokens
87
+ </p>
88
+ <p class="text-lg font-semibold">
89
+ {{ formatTokens(usage.totalOutputTokens7d) }}
90
+ </p>
91
+ </div>
92
+ </div>
93
+ </UCard>
94
+ </template>
@@ -122,15 +122,17 @@ function handleShareClick() {
122
122
  }
123
123
  }
124
124
 
125
+ const { copy } = useCopyToClipboard()
126
+
125
127
  async function copyShareLink() {
126
- try {
127
- await navigator.clipboard.writeText(shareUrl.value)
128
+ const ok = await copy(shareUrl.value)
129
+ if (ok) {
128
130
  linkCopied.value = true
129
131
  toast.add({ title: 'Link copied!', icon: 'i-lucide-check', color: 'success' })
130
132
  setTimeout(() => {
131
133
  linkCopied.value = false
132
134
  }, 2000)
133
- } catch {
135
+ } else {
134
136
  toast.add({ title: 'Failed to copy link', icon: 'i-lucide-x', color: 'error' })
135
137
  }
136
138
  }
@@ -0,0 +1,174 @@
1
+ <script setup lang="ts">
2
+ import { format } from 'date-fns'
3
+ import { useElementSize } from '@vueuse/core'
4
+ import { VisXYContainer, VisLine, VisArea, VisAxis, VisCrosshair, VisTooltip } from '@unovis/vue'
5
+ import type { DailyUsageData } from '~~/shared/types'
6
+
7
+ const props = defineProps<{
8
+ data: DailyUsageData[]
9
+ title?: string
10
+ granularity: 'daily' | 'hourly'
11
+ }>()
12
+
13
+ const emit = defineEmits<{
14
+ 'update:granularity': [value: 'daily' | 'hourly']
15
+ }>()
16
+
17
+ const cardRef = useTemplateRef<HTMLElement | null>('cardRef')
18
+ const { width } = useElementSize(cardRef)
19
+
20
+ const x = (_: DailyUsageData, i: number) => i
21
+
22
+ const yTotal = (d: DailyUsageData) => d.totalCost
23
+
24
+ // Parse "2026-02-18" as local date (not UTC) to avoid off-by-one in western timezones
25
+ function parseLocalDate(dateStr: string): Date {
26
+ const [y, m, d] = dateStr.slice(0, 10).split('-').map(Number) as [number, number, number]
27
+ return new Date(y, m - 1, d)
28
+ }
29
+
30
+ const xTicks = (i: number) => {
31
+ const item = props.data[i]
32
+ if (!item) return ''
33
+ if (props.granularity === 'hourly') {
34
+ const hour = Number(item.date.slice(11, 13))
35
+ const ampm = hour >= 12 ? 'pm' : 'am'
36
+ const h12 = hour % 12 || 12
37
+ if (i === 0 || item.date.slice(0, 10) !== props.data[i - 1]?.date.slice(0, 10))
38
+ return `${format(parseLocalDate(item.date), 'MMM d')} ${h12}${ampm}`
39
+ return `${h12}${ampm}`
40
+ }
41
+ return format(parseLocalDate(item.date), 'd MMM')
42
+ }
43
+
44
+ const template = (d: DailyUsageData) => {
45
+ let label: string
46
+ if (props.granularity === 'hourly') {
47
+ const hour = Number(d.date.slice(11, 13))
48
+ const ampm = hour >= 12 ? 'pm' : 'am'
49
+ const h12 = hour % 12 || 12
50
+ label = `${format(parseLocalDate(d.date), 'MMM d, yyyy')} ${h12}:00${ampm}`
51
+ } else {
52
+ label = format(parseLocalDate(d.date), 'MMM d, yyyy')
53
+ }
54
+ return `<div class="p-2 text-sm">
55
+ <div class="font-medium">${label}</div>
56
+ <div style="color: var(--ui-primary)">Chat: $${d.chat.toFixed(4)}</div>
57
+ <div style="color: var(--ui-warning)">Agents: $${d.agent.toFixed(4)}</div>
58
+ <div style="color: var(--ui-info)">Memory: $${d.memory.toFixed(4)}</div>
59
+ <div class="text-muted mt-1">Total: $${d.totalCost.toFixed(4)}</div>
60
+ <div class="text-muted">${d.calls} calls</div>
61
+ </div>`
62
+ }
63
+ </script>
64
+
65
+ <template>
66
+ <UCard
67
+ ref="cardRef"
68
+ :ui="{ root: 'overflow-visible', body: '!px-0 !pt-0 !pb-3' }"
69
+ >
70
+ <template
71
+ v-if="title"
72
+ #header
73
+ >
74
+ <div class="flex items-center justify-between">
75
+ <div class="flex items-center gap-3">
76
+ <p class="text-sm font-medium">
77
+ {{ title }}
78
+ </p>
79
+ <UFieldGroup>
80
+ <UButton
81
+ :color="granularity === 'daily' ? 'primary' : 'neutral'"
82
+ :variant="granularity === 'daily' ? 'solid' : 'ghost'"
83
+ size="xs"
84
+ @click="emit('update:granularity', 'daily')"
85
+ >
86
+ Daily
87
+ </UButton>
88
+ <UButton
89
+ :color="granularity === 'hourly' ? 'primary' : 'neutral'"
90
+ :variant="granularity === 'hourly' ? 'solid' : 'ghost'"
91
+ size="xs"
92
+ @click="emit('update:granularity', 'hourly')"
93
+ >
94
+ Hourly
95
+ </UButton>
96
+ </UFieldGroup>
97
+ </div>
98
+ <div class="flex items-center gap-3 text-xs text-muted">
99
+ <span class="flex items-center gap-1">
100
+ <span
101
+ class="inline-block size-2.5 rounded-full"
102
+ style="background: var(--ui-primary)"
103
+ />
104
+ Chat
105
+ </span>
106
+ <span class="flex items-center gap-1">
107
+ <span
108
+ class="inline-block size-2.5 rounded-full"
109
+ style="background: var(--ui-warning)"
110
+ />
111
+ Agents
112
+ </span>
113
+ <span class="flex items-center gap-1">
114
+ <span
115
+ class="inline-block size-2.5 rounded-full"
116
+ style="background: var(--ui-info)"
117
+ />
118
+ Memory
119
+ </span>
120
+ </div>
121
+ </div>
122
+ </template>
123
+
124
+ <div
125
+ v-if="data.length === 0"
126
+ class="flex items-center justify-center h-64 text-muted"
127
+ >
128
+ No usage data
129
+ </div>
130
+
131
+ <VisXYContainer
132
+ v-else
133
+ :data="data"
134
+ :padding="{ top: 10, left: 10, right: 10, bottom: 10 }"
135
+ class="h-64"
136
+ :width="width"
137
+ >
138
+ <VisArea
139
+ :x="x"
140
+ :y="yTotal"
141
+ color="var(--ui-primary)"
142
+ :opacity="0.1"
143
+ />
144
+ <VisLine
145
+ :x="x"
146
+ :y="yTotal"
147
+ color="var(--ui-primary)"
148
+ />
149
+ <VisAxis
150
+ type="x"
151
+ :x="x"
152
+ :tick-format="xTicks"
153
+ />
154
+ <VisCrosshair
155
+ color="var(--ui-primary)"
156
+ :template="template"
157
+ />
158
+ <VisTooltip />
159
+ </VisXYContainer>
160
+ </UCard>
161
+ </template>
162
+
163
+ <style scoped>
164
+ .unovis-xy-container {
165
+ --vis-crosshair-line-stroke-color: var(--ui-primary);
166
+ --vis-crosshair-circle-stroke-color: var(--ui-bg);
167
+ --vis-axis-grid-color: var(--ui-border);
168
+ --vis-axis-tick-color: var(--ui-border);
169
+ --vis-axis-tick-label-color: var(--ui-text-dimmed);
170
+ --vis-tooltip-background-color: var(--ui-bg);
171
+ --vis-tooltip-border-color: var(--ui-border);
172
+ --vis-tooltip-text-color: var(--ui-text-highlighted);
173
+ }
174
+ </style>
@@ -0,0 +1,22 @@
1
+ <script setup lang="ts">
2
+ defineProps<{
3
+ data?: unknown[]
4
+ title?: string
5
+ granularity?: string
6
+ }>()
7
+ </script>
8
+
9
+ <template>
10
+ <UCard :ui="{ body: '!p-0' }">
11
+ <template
12
+ v-if="title"
13
+ #header
14
+ >
15
+ <p class="text-sm font-medium">
16
+ {{ title }}
17
+ </p>
18
+ </template>
19
+
20
+ <div class="h-64 bg-muted/5 rounded animate-pulse" />
21
+ </UCard>
22
+ </template>
@@ -0,0 +1,249 @@
1
+ <script setup lang="ts">
2
+ import { formatDistanceToNow } from 'date-fns'
3
+ import { useDebounceFn } from '@vueuse/core'
4
+ import type { TokenUsageSource, TokenUsageRecord, StatsPeriod } from '~~/shared/types'
5
+
6
+ const props = defineProps<{
7
+ period: StatsPeriod
8
+ }>()
9
+
10
+ const sourceLabels: Record<TokenUsageSource, string> = {
11
+ chat: 'Chat',
12
+ agent: 'Agent',
13
+ memory_extraction: 'Memory'
14
+ }
15
+
16
+ const sourceColors = {
17
+ chat: 'primary',
18
+ agent: 'warning',
19
+ memory_extraction: 'info'
20
+ } as const
21
+
22
+ // Filter state
23
+ const ALL_VALUE = '__all__'
24
+ const sourceFilter = ref(ALL_VALUE)
25
+ const searchQuery = ref('')
26
+ const page = ref(1)
27
+ const limit = 20
28
+
29
+ // Data state
30
+ const records = ref<TokenUsageRecord[]>([])
31
+ const total = ref(0)
32
+ const totalPages = ref(0)
33
+ const loading = ref(false)
34
+
35
+ const sourceOptions = [
36
+ { value: ALL_VALUE, label: 'All Sources' },
37
+ { value: 'chat', label: 'Chat' },
38
+ { value: 'agent', label: 'Agents' },
39
+ { value: 'memory_extraction', label: 'Memory' }
40
+ ]
41
+
42
+ const columns = [
43
+ { accessorKey: 'source', header: 'Source' },
44
+ { accessorKey: 'sourceName', header: 'Name' },
45
+ { accessorKey: 'inputTokens', header: 'Input' },
46
+ { accessorKey: 'outputTokens', header: 'Output' },
47
+ { accessorKey: 'costUsd', header: 'Cost' },
48
+ { accessorKey: 'durationMs', header: 'Duration' },
49
+ { accessorKey: 'createdAt', header: 'Time' }
50
+ ]
51
+
52
+ async function fetchRecords() {
53
+ loading.value = true
54
+ try {
55
+ const query: Record<string, string | number> = {
56
+ period: props.period,
57
+ page: page.value,
58
+ limit
59
+ }
60
+ if (sourceFilter.value !== ALL_VALUE)
61
+ query.source = sourceFilter.value
62
+ if (searchQuery.value.trim())
63
+ query.search = searchQuery.value.trim()
64
+
65
+ const res = await $fetch<{
66
+ data: TokenUsageRecord[]
67
+ pagination: { page: number, total: number, totalPages: number }
68
+ }>('/api/usage', { query })
69
+
70
+ records.value = res.data
71
+ total.value = res.pagination.total
72
+ totalPages.value = res.pagination.totalPages
73
+ } catch {
74
+ // Silently fail
75
+ } finally {
76
+ loading.value = false
77
+ }
78
+ }
79
+
80
+ // Debounced search
81
+ const debouncedFetch = useDebounceFn(() => {
82
+ page.value = 1
83
+ fetchRecords()
84
+ }, 300)
85
+
86
+ watch(sourceFilter, () => {
87
+ page.value = 1
88
+ fetchRecords()
89
+ })
90
+
91
+ watch(searchQuery, debouncedFetch)
92
+
93
+ watch(page, fetchRecords)
94
+
95
+ watch(() => props.period, () => {
96
+ page.value = 1
97
+ fetchRecords()
98
+ })
99
+
100
+ function formatCost(value: number): string {
101
+ if (value < 0.0001 && value > 0) return '<$0.0001'
102
+ return `$${value.toFixed(4)}`
103
+ }
104
+
105
+ function formatTokenCount(value: number): string {
106
+ if (value >= 1_000_000) return `${(value / 1_000_000).toFixed(1)}M`
107
+ if (value >= 1_000) return `${(value / 1_000).toFixed(1)}K`
108
+ return String(value)
109
+ }
110
+
111
+ function formatDuration(ms?: number | null): string {
112
+ if (!ms) return '-'
113
+ if (ms < 1000) return `${ms}ms`
114
+ if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`
115
+ return `${(ms / 60000).toFixed(1)}m`
116
+ }
117
+
118
+ function formatTime(date: Date | string): string {
119
+ return formatDistanceToNow(new Date(date), { addSuffix: true })
120
+ }
121
+
122
+ onMounted(fetchRecords)
123
+ </script>
124
+
125
+ <template>
126
+ <UCard>
127
+ <template #header>
128
+ <div class="flex items-center justify-between flex-wrap gap-3">
129
+ <div class="flex items-center gap-2">
130
+ <p class="text-sm font-medium">
131
+ Usage Records
132
+ </p>
133
+ <UBadge
134
+ v-if="total > 0"
135
+ color="neutral"
136
+ variant="subtle"
137
+ >
138
+ {{ total }}
139
+ </UBadge>
140
+ </div>
141
+
142
+ <div class="flex items-center gap-2">
143
+ <USelect
144
+ v-model="sourceFilter"
145
+ :items="sourceOptions"
146
+ value-key="value"
147
+ class="w-36"
148
+ size="sm"
149
+ />
150
+ <UInput
151
+ v-model="searchQuery"
152
+ placeholder="Search name..."
153
+ icon="i-lucide-search"
154
+ class="w-48"
155
+ size="sm"
156
+ />
157
+ </div>
158
+ </div>
159
+ </template>
160
+
161
+ <div v-if="loading && records.length === 0">
162
+ <USkeleton
163
+ v-for="i in 5"
164
+ :key="i"
165
+ class="h-10 w-full mb-2"
166
+ />
167
+ </div>
168
+
169
+ <div
170
+ v-else-if="records.length === 0"
171
+ class="flex flex-col items-center justify-center h-32 text-muted"
172
+ >
173
+ <p class="text-sm">
174
+ No records found
175
+ </p>
176
+ </div>
177
+
178
+ <UTable
179
+ v-else
180
+ :data="records"
181
+ :columns="columns"
182
+ :loading="loading"
183
+ >
184
+ <template #source-cell="{ row }">
185
+ <UBadge
186
+ :color="sourceColors[row.original.source as TokenUsageSource]"
187
+ variant="subtle"
188
+ size="sm"
189
+ >
190
+ {{ sourceLabels[row.original.source as TokenUsageSource] || row.original.source }}
191
+ </UBadge>
192
+ </template>
193
+
194
+ <template #sourceName-cell="{ row }">
195
+ <span
196
+ class="text-sm truncate max-w-48 inline-block"
197
+ :title="row.original.sourceName"
198
+ >
199
+ {{ row.original.sourceName || '-' }}
200
+ </span>
201
+ </template>
202
+
203
+ <template #inputTokens-cell="{ row }">
204
+ <span class="text-sm text-muted">
205
+ {{ formatTokenCount(row.original.inputTokens) }}
206
+ </span>
207
+ </template>
208
+
209
+ <template #outputTokens-cell="{ row }">
210
+ <span class="text-sm text-muted">
211
+ {{ formatTokenCount(row.original.outputTokens) }}
212
+ </span>
213
+ </template>
214
+
215
+ <template #costUsd-cell="{ row }">
216
+ <span class="text-sm font-medium">
217
+ {{ formatCost(row.original.costUsd) }}
218
+ </span>
219
+ </template>
220
+
221
+ <template #durationMs-cell="{ row }">
222
+ <span class="text-sm text-muted">
223
+ {{ formatDuration(row.original.durationMs) }}
224
+ </span>
225
+ </template>
226
+
227
+ <template #createdAt-cell="{ row }">
228
+ <span class="text-sm text-muted">
229
+ {{ formatTime(row.original.createdAt) }}
230
+ </span>
231
+ </template>
232
+ </UTable>
233
+
234
+ <template
235
+ v-if="totalPages > 1"
236
+ #footer
237
+ >
238
+ <div class="flex justify-center">
239
+ <UPagination
240
+ v-model:page="page"
241
+ :total="total"
242
+ :items-per-page="limit"
243
+ :sibling-count="1"
244
+ show-edges
245
+ />
246
+ </div>
247
+ </template>
248
+ </UCard>
249
+ </template>