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.
- package/.output/nitro.json +1 -1
- package/.output/public/_nuxt/{B4LjQa2N.js → 4MiwzAAt.js} +2 -2
- package/.output/public/_nuxt/{BZZeOWzQ.js → 5ZXA0Ckq.js} +1 -1
- package/.output/public/_nuxt/{B194okoV.js → 9IQmsEjr.js} +1 -1
- package/.output/public/_nuxt/{DQ_ZoW_9.js → 9QnH0xQM.js} +1 -1
- package/.output/public/_nuxt/{CF0HGIV6.js → B1X4Bzcy.js} +1 -1
- package/.output/public/_nuxt/{D9lowtoI.js → B3y_Qqox.js} +1 -1
- package/.output/public/_nuxt/{BB1R_SaV.js → B6S_ob86.js} +1 -1
- package/.output/public/_nuxt/{DVf1IpSh.js → BAIz-dEB.js} +1 -1
- package/.output/public/_nuxt/{CNFNMRY1.js → BCtfQCzC.js} +1 -1
- package/.output/public/_nuxt/BIIJhjQO.js +1 -0
- package/.output/public/_nuxt/BIckl6wA.js +1 -0
- package/.output/public/_nuxt/{Biy6boRV.js → BJ3o57WW.js} +1 -1
- package/.output/public/_nuxt/{B5rIsDOv.js → BNetzZzF.js} +1 -1
- package/.output/public/_nuxt/{DFXLcgxL.js → BOj6t-oo.js} +1 -1
- package/.output/public/_nuxt/{BDUpLPg_.js → BS0ofHJK.js} +1 -1
- package/.output/public/_nuxt/{DIdcflWi.js → BWhMnjID.js} +1 -1
- package/.output/public/_nuxt/{CExLOuIW.js → BYjadNrw.js} +1 -1
- package/.output/public/_nuxt/{C8ytOrRD.js → BZXMQuYP.js} +1 -1
- package/.output/public/_nuxt/{Ddih3UKd.js → B_3_hrpn.js} +1 -1
- package/.output/public/_nuxt/{CxSyRXlk.js → BaBZjmMC.js} +1 -1
- package/.output/public/_nuxt/BaMqDm5u.js +1 -0
- package/.output/public/_nuxt/BasgsT_S.js +1 -0
- package/.output/public/_nuxt/{Cehzuz52.js → BffWCM73.js} +1 -1
- package/.output/public/_nuxt/{Dh0BcDEI.js → BhzMoffi.js} +1 -1
- package/.output/public/_nuxt/{DmV68Eyp.js → BjjCvHLT.js} +1 -1
- package/.output/public/_nuxt/{nzAH3Wk0.js → BlAZO7nq.js} +1 -1
- package/.output/public/_nuxt/{BRPJdy3u.js → BlhFigLL.js} +1 -1
- package/.output/public/_nuxt/{CfTFjEuh.js → Bm9LyG4F.js} +1 -1
- package/.output/public/_nuxt/BoIxv-gM.js +1 -0
- package/.output/public/_nuxt/{BslL2E8V.js → BqKFIuRj.js} +1 -1
- package/.output/public/_nuxt/Br19oYkq.js +1 -0
- package/.output/public/_nuxt/{PtGESE-F.js → BrNqhp1a.js} +3 -3
- package/.output/public/_nuxt/{FvV9sv7q.js → BsEZoHd1.js} +1 -1
- package/.output/public/_nuxt/{BaPPQtFe.js → BxXOsXrM.js} +10 -10
- package/.output/public/_nuxt/BzOqrmGa.js +7 -0
- package/.output/public/_nuxt/{BNAsyazX.js → C0JKNMDO.js} +1 -1
- package/.output/public/_nuxt/C0kh_F7v.js +1 -0
- package/.output/public/_nuxt/{4Ew5YctE.js → C3FxIITy.js} +1 -1
- package/.output/public/_nuxt/{DnToOWVx.js → C4pxqdgg.js} +1 -1
- package/.output/public/_nuxt/{C5Ue4WbD.js → C61KgSco.js} +1 -1
- package/.output/public/_nuxt/{DcMvYcFq.js → C69W7k2j.js} +1 -1
- package/.output/public/_nuxt/{BnnFvWzv.js → C6RC3lA1.js} +1 -1
- package/.output/public/_nuxt/{DBENYi9d.js → C6aqGHu1.js} +1 -1
- package/.output/public/_nuxt/{YldWqMAh.js → CJUdYEdO.js} +1 -1
- package/.output/public/_nuxt/{BPAM-iC7.js → CKkC3Ptm.js} +1 -1
- package/.output/public/_nuxt/{CTXhFIUN.js → CNnJrDvu.js} +1 -1
- package/.output/public/_nuxt/{BwH2E0-a.js → CVJQGP1Q.js} +1 -1
- package/.output/public/_nuxt/{q0vhMpUB.js → CVgTJeSq.js} +1 -1
- package/.output/public/_nuxt/CWMUi89H.js +1 -0
- package/.output/public/_nuxt/{BU9VyOEC.js → CYP_MLH8.js} +1 -1
- package/.output/public/_nuxt/{76CHU1js.js → C_BdYLzz.js} +1 -1
- package/.output/public/_nuxt/{BQbnVFXA.js → Cdt3I3Go.js} +1 -1
- package/.output/public/_nuxt/Cdu2qGgq.js +1 -0
- package/.output/public/_nuxt/{ChMIRIq6.js → CeIVm4A3.js} +1 -1
- package/.output/public/_nuxt/CeIu7z4p.js +1 -0
- package/.output/public/_nuxt/{CZZw0T2a.js → Ci7UEZQh.js} +1 -1
- package/.output/public/_nuxt/{BP_jQhKs.js → CihWZmJe.js} +2 -2
- package/.output/public/_nuxt/{Bluqlt6l.js → CitkKxhw.js} +1 -1
- package/.output/public/_nuxt/{DIM73BjE.js → CmzH6R-N.js} +2 -2
- package/.output/public/_nuxt/{CD5hHJBM.js → Cp2MA0cm.js} +1 -1
- package/.output/public/_nuxt/{DflBkWfa.js → CqU2XbzO.js} +1 -1
- package/.output/public/_nuxt/{Ccc8OoR8.js → Cqy_L_ip.js} +1 -1
- package/.output/public/_nuxt/Cr8ixbr1.js +1 -0
- package/.output/public/_nuxt/{DdLGO0wP.js → CsJ9KhQ4.js} +1 -1
- package/.output/public/_nuxt/{mHNfVoS0.js → CtYFj7k1.js} +1 -1
- package/.output/public/_nuxt/{DIBimEIe.js → CtchsY6e.js} +1 -1
- package/.output/public/_nuxt/{BboG47Wz.js → Cvp7FI3T.js} +1 -1
- package/.output/public/_nuxt/CxuZBC8n.js +70 -0
- package/.output/public/_nuxt/D2689qk4.js +1 -0
- package/.output/public/_nuxt/{CWWmC6Y7.js → D31L7Ks6.js} +1 -1
- package/.output/public/_nuxt/{JxSV3wOO.js → D3RiUGdz.js} +1 -1
- package/.output/public/_nuxt/{C1tYtAeV.js → D3e44mCL.js} +1 -1
- package/.output/public/_nuxt/{BTWyDDBM.js → D6t3dcTl.js} +1 -1
- package/.output/public/_nuxt/{DpLjEbYb.js → DAQhmSv4.js} +1 -1
- package/.output/public/_nuxt/{BnwR3LQL.js → DB359q8R.js} +1 -1
- package/.output/public/_nuxt/{jCKYMNtT.js → DCzfkCGa.js} +1 -1
- package/.output/public/_nuxt/{BXWm1qrj.js → DG-T44jj.js} +1 -1
- package/.output/public/_nuxt/{BBygTbuF.js → DGX0tzL8.js} +1 -1
- package/.output/public/_nuxt/DHG66LPS.js +1 -0
- package/.output/public/_nuxt/{jBqB49XU.js → DHKLCQRG.js} +1 -1
- package/.output/public/_nuxt/{BDw-JoCx.js → DIoI0uJm.js} +1 -1
- package/.output/public/_nuxt/{DOQA5CEF.js → DJ5V-y_x.js} +1 -1
- package/.output/public/_nuxt/{BGaNWD0s.js → DJMS2og1.js} +1 -1
- package/.output/public/_nuxt/{UmgHYYVi.js → DJjDvbZE.js} +1 -1
- package/.output/public/_nuxt/{DdtX5kio.js → DK9jxJ0g.js} +1 -1
- package/.output/public/_nuxt/{Cc6GlQCO.js → DKZxeXDQ.js} +1 -1
- package/.output/public/_nuxt/{Tp-9mt9j.js → DLETdGFL.js} +1 -1
- package/.output/public/_nuxt/DOICd-Ld.js +1 -0
- package/.output/public/_nuxt/DPEcH-gi.js +65 -0
- package/.output/public/_nuxt/{CVNY6Bll.js → DSRrg8JT.js} +1 -1
- package/.output/public/_nuxt/{Sm7TJN7_.js → DTDgHTuh.js} +2 -2
- package/.output/public/_nuxt/{DeGKAWfY.js → D_3Rq1BS.js} +1 -1
- package/.output/public/_nuxt/{BvnL4Jyv.js → Daz4MeL6.js} +1 -1
- package/.output/public/_nuxt/{Dyd3g9po.js → Db2v8O7O.js} +1 -1
- package/.output/public/_nuxt/{D0FBwEzN.js → Db8-_gO7.js} +1 -1
- package/.output/public/_nuxt/{Cv4Qjbqk.js → DfQu3kEw.js} +1 -1
- package/.output/public/_nuxt/{c-RGQpsW.js → DgV-EDJ9.js} +1 -1
- package/.output/public/_nuxt/{CSF2cTe9.js → DnjGH3SQ.js} +1 -1
- package/.output/public/_nuxt/Dp2X5R2m.js +1 -0
- package/.output/public/_nuxt/DqB723Z0.js +1 -0
- package/.output/public/_nuxt/{DhRePYUI.js → DyRelyfs.js} +1 -1
- package/.output/public/_nuxt/{DZiAD6YN.js → Dy_Cq5LQ.js} +1 -1
- package/.output/public/_nuxt/{DWQMV4k2.js → DznawRFW.js} +1 -1
- package/.output/public/_nuxt/EuOqK1A6.js +1 -0
- package/.output/public/_nuxt/{BVZHIuu-.js → FNC8XZTk.js} +1 -1
- package/.output/public/_nuxt/{Dsk8JtTw.js → Fukkqjkf.js} +1 -1
- package/.output/public/_nuxt/{DNpdNXnv.js → FvlxxmNk.js} +2 -2
- package/.output/public/_nuxt/{zt0Txq93.js → IRSbVPIu.js} +1 -1
- package/.output/public/_nuxt/{T_w6A2Zo.js → JJ3634gV.js} +9 -9
- package/.output/public/_nuxt/{6Tt_Iaya.js → Jez9DHn7.js} +1 -1
- package/.output/public/_nuxt/{Cr7nQmYw.js → KKK6HVeG.js} +1 -1
- package/.output/public/_nuxt/{BJPw54Qz.js → Lwdv_RKd.js} +1 -1
- package/.output/public/_nuxt/{WqUIRM3G.js → Nb2jBtYT.js} +1 -1
- package/.output/public/_nuxt/Q8Ps7oN5.js +1 -0
- package/.output/public/_nuxt/SXTDhzp6.js +1 -0
- package/.output/public/_nuxt/{rfcePMfo.js → Sg2Lwc46.js} +1 -1
- package/.output/public/_nuxt/{gEpwc5gO.js → YuTZB7sD.js} +1 -1
- package/.output/public/_nuxt/{B2OKrc4G.js → _CYZi8HN.js} +1 -1
- package/.output/public/_nuxt/_J_7XIn-.js +1 -0
- package/.output/public/_nuxt/builds/latest.json +1 -1
- package/.output/public/_nuxt/builds/meta/a1e9100c-1a4f-4f7e-bb53-9dbe0d07effb.json +1 -0
- package/.output/public/_nuxt/{Bwx_Kl-4.js → cABRLVee.js} +1 -1
- package/.output/public/_nuxt/{DPuJYSUH.js → eko-0FUm.js} +1 -1
- package/.output/public/_nuxt/entry.CGxIBGAf.css +1 -0
- package/.output/public/_nuxt/{TQOIXkvr.js → g20UHUCv.js} +1 -1
- package/.output/public/_nuxt/{zOs8gUGl.js → gBC9k4Qj.js} +1 -1
- package/.output/public/_nuxt/{CJ3DQEW9.js → ghuJ76mD.js} +1 -1
- package/.output/public/_nuxt/{S6GTaxiQ.js → iBGCpHdw.js} +1 -1
- package/.output/public/_nuxt/{VGlRzjk3.js → inmzPrjz.js} +1 -1
- package/.output/public/_nuxt/{CCkumwhk.js → m5kGCDpI.js} +4 -4
- package/.output/public/_nuxt/{CXkbkhUa.js → nIU2F7ia.js} +3 -3
- package/.output/public/_nuxt/oIX-ZDN6.js +1 -0
- package/.output/public/_nuxt/{TsIha0iy.js → pcUI-zuY.js} +1 -1
- package/.output/public/_nuxt/{BN_2zhBR.js → sf57orEk.js} +1 -1
- package/.output/public/_nuxt/usage.vakN1lvi.css +1 -0
- package/.output/public/_nuxt/{DHYn0MnY.js → wCGVE8_e.js} +1 -1
- package/.output/public/_nuxt/{DMkXE4gH.js → xuzLdW-o.js} +1 -1
- package/.output/public/_nuxt/{DdoFnl6e.js → yNrp2XvX.js} +1 -1
- package/.output/public/_nuxt/{4hIPJQLp.js → yuf23kh9.js} +1 -1
- package/.output/server/chunks/build/CodeIcon-CWD5HcV7.mjs +1 -1
- package/.output/server/chunks/build/DropdownMenu-BBrV9nXz.mjs +1 -1
- package/.output/server/chunks/build/EditorToolbar-DIfb5arC.mjs +1 -1
- package/.output/server/chunks/build/Img-CWLmvN1t.mjs +1 -1
- package/.output/server/chunks/build/MDC-Dx0YPDhe.mjs +1 -1
- package/.output/server/chunks/build/Select-BB1oLrCD.mjs +1 -1
- package/.output/server/chunks/build/SelectMenu-DPssg6zD.mjs +1 -1
- package/.output/server/chunks/build/Table-DCwTlhCj.mjs +1 -1
- package/.output/server/chunks/build/Tooltip-TRyl6dje.mjs +1 -1
- package/.output/server/chunks/build/{_uuid_-CQEMsF1D.mjs → _uuid_-0UgdUhfY.mjs} +8 -7
- package/.output/server/chunks/build/{_uuid_-CQEMsF1D.mjs.map → _uuid_-0UgdUhfY.mjs.map} +1 -1
- package/.output/server/chunks/build/chat-CZMiB68R.mjs.map +1 -1
- package/.output/server/chunks/build/client.precomputed.mjs +1 -1
- package/.output/server/chunks/build/cookie-C_iulBi6.mjs +1 -1
- package/.output/server/chunks/build/{dashboard-24rCroiO.mjs → dashboard-CpMVYnDV.mjs} +13 -6
- package/.output/server/chunks/build/{dashboard-24rCroiO.mjs.map → dashboard-CpMVYnDV.mjs.map} +1 -1
- package/.output/server/chunks/build/dashboard-YEscLBQN.mjs +1109 -0
- package/.output/server/chunks/build/dashboard-YEscLBQN.mjs.map +1 -0
- package/.output/server/chunks/build/{docs-DOGZUctm.mjs → docs-Dk2JnYq3.mjs} +8 -7
- package/.output/server/chunks/build/{docs-DOGZUctm.mjs.map → docs-Dk2JnYq3.mjs.map} +1 -1
- package/.output/server/chunks/build/fetch-BB7Qzkwe.mjs +1 -1
- package/.output/server/chunks/build/{hooks-CuyNIRFI.mjs → hooks-DP8WoUPS.mjs} +2 -2
- package/.output/server/chunks/build/{hooks-CuyNIRFI.mjs.map → hooks-DP8WoUPS.mjs.map} +1 -1
- package/.output/server/chunks/build/{index-BZI-Mj1-.mjs → index-Ba_bPJgk.mjs} +2 -2
- package/.output/server/chunks/build/{index-BZI-Mj1-.mjs.map → index-Ba_bPJgk.mjs.map} +1 -1
- package/.output/server/chunks/build/index-CxDxc9fm.mjs +1 -1
- package/.output/server/chunks/build/server.mjs +26 -17
- package/.output/server/chunks/build/server.mjs.map +1 -1
- package/.output/server/chunks/build/settings-DdkKCJ00.mjs +1 -1
- package/.output/server/chunks/build/styles.mjs +2 -2
- package/.output/server/chunks/build/{tasks-DR55Wjpi.mjs → tasks-DiOi1HG_.mjs} +2 -2
- package/.output/server/chunks/build/{tasks-DR55Wjpi.mjs.map → tasks-DiOi1HG_.mjs.map} +1 -1
- package/.output/server/chunks/build/usage-H_mcd_fz.mjs +1578 -0
- package/.output/server/chunks/build/usage-H_mcd_fz.mjs.map +1 -0
- package/.output/server/chunks/build/{CodeEditorFallback-DfYly7f5.mjs → useCopyToClipboard-vi6FYyyZ.mjs} +29 -2
- package/.output/server/chunks/build/useCopyToClipboard-vi6FYyyZ.mjs.map +1 -0
- package/.output/server/chunks/build/useNotificationBus-BG5JNQf1.mjs +1 -1
- package/.output/server/chunks/build/{usePreferences-CUvZsOq5.mjs → usePreferences-CzC8fRzd.mjs} +7 -1
- package/.output/server/chunks/build/usePreferences-CzC8fRzd.mjs.map +1 -0
- package/.output/server/chunks/build/view-Dc8mvzCB.mjs +1 -1
- package/.output/server/chunks/nitro/nitro.mjs +884 -818
- package/.output/server/chunks/nitro/nitro.mjs.map +1 -1
- package/.output/server/chunks/routes/_ws/chat.mjs +18 -4
- package/.output/server/chunks/routes/_ws/chat.mjs.map +1 -1
- package/.output/server/chunks/routes/api/dashboard/overview.get.mjs +149 -0
- package/.output/server/chunks/routes/api/dashboard/overview.get.mjs.map +1 -0
- package/.output/server/chunks/routes/api/documents/_id/public.get.mjs +1 -1
- package/.output/server/chunks/routes/api/documents/_id/restore.post.mjs +1 -1
- package/.output/server/chunks/routes/api/documents/by-path.post.mjs +1 -1
- package/.output/server/chunks/routes/api/documents/index.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/documents/index.put.mjs +1 -1
- package/.output/server/chunks/routes/api/fs/delete.post.mjs +1 -1
- package/.output/server/chunks/routes/api/fs/list.get.mjs +1 -1
- package/.output/server/chunks/routes/api/fs/mkdir.post.mjs +1 -1
- package/.output/server/chunks/routes/api/fs/move.post.mjs +1 -1
- package/.output/server/chunks/routes/api/fs/read.post.mjs +1 -1
- package/.output/server/chunks/routes/api/fs/rename.post.mjs +1 -1
- package/.output/server/chunks/routes/api/fs/write.post.mjs +1 -1
- package/.output/server/chunks/routes/api/health.get.mjs +1 -1
- package/.output/server/chunks/routes/api/home.get.mjs +1 -1
- package/.output/server/chunks/routes/api/hooks/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/hooks/index.post.mjs +1 -1
- package/.output/server/chunks/routes/api/hooks/stats.get.mjs +1 -1
- package/.output/server/chunks/routes/api/index.get3.mjs +1 -1
- package/.output/server/chunks/routes/api/index.get4.mjs +1 -1
- package/.output/server/chunks/routes/api/index.get5.mjs +1 -1
- package/.output/server/chunks/routes/api/index.get6.mjs +1 -1
- package/.output/server/chunks/routes/api/index.get7.mjs +1 -1
- package/.output/server/chunks/routes/api/index.get8.mjs +82 -0
- package/.output/server/chunks/routes/api/index.get8.mjs.map +1 -0
- package/.output/server/chunks/routes/api/index.post2.mjs +1 -1
- package/.output/server/chunks/routes/api/index.post3.mjs +1 -1
- package/.output/server/chunks/routes/api/index.post4.mjs +1 -1
- package/.output/server/chunks/routes/api/index.put.mjs +1 -1
- package/.output/server/chunks/routes/api/memory/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/memory/context.get.mjs +1 -1
- package/.output/server/chunks/routes/api/memory/extract.post.mjs +20 -1
- package/.output/server/chunks/routes/api/memory/extract.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/memory/search.get.mjs +1 -1
- package/.output/server/chunks/routes/api/memory/store.post.mjs +1 -1
- package/.output/server/chunks/routes/api/projects/index.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/projects/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/projects/index.put.mjs +1 -1
- package/.output/server/chunks/routes/api/secrets/_key_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/secrets/_key_.get.mjs +1 -1
- package/.output/server/chunks/routes/api/secrets/_key_.put.mjs +1 -1
- package/.output/server/chunks/routes/api/tasks/_id/restore.post.mjs +1 -1
- package/.output/server/chunks/routes/api/tasks/index.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/tasks/index.put.mjs +1 -1
- package/.output/server/chunks/routes/api/tasks/tags.get.mjs +1 -1
- package/.output/server/chunks/routes/api/usage/stats.get.mjs +130 -0
- package/.output/server/chunks/routes/api/usage/stats.get.mjs.map +1 -0
- package/.output/server/chunks/routes/api/user/email.patch.mjs +1 -1
- package/.output/server/chunks/routes/notifications.mjs +1 -1
- package/.output/server/chunks/routes/renderer.mjs +1 -1
- package/.output/server/chunks/routes/terminal.mjs +1 -1
- package/.output/server/index.mjs +1 -1
- package/.output/server/package.json +1 -1
- package/app/components/dashboard/RecentChats.vue +93 -0
- package/app/components/dashboard/RecentDocs.vue +108 -0
- package/app/components/dashboard/StatCards.vue +82 -0
- package/app/components/dashboard/UpcomingTasks.vue +119 -0
- package/app/components/dashboard/UsageSummary.vue +94 -0
- package/app/components/editor/DocumentEditor.vue +5 -3
- package/app/components/usage/UsageCostChart.client.vue +174 -0
- package/app/components/usage/UsageCostChart.server.vue +22 -0
- package/app/components/usage/UsageRecordsTable.vue +249 -0
- package/app/components/usage/UsageSourceDonut.client.vue +93 -0
- package/app/components/usage/UsageSourceDonut.server.vue +21 -0
- package/app/components/usage/UsageStatsCards.vue +101 -0
- package/app/components/usage/UsageTopConsumers.vue +89 -0
- package/app/composables/useCopyToClipboard.ts +37 -0
- package/app/composables/usePreferences.ts +10 -0
- package/app/layouts/dashboard.vue +8 -1
- package/app/pages/chat.vue +11 -1
- package/app/pages/dashboard.vue +65 -64
- package/app/pages/usage.vue +130 -0
- package/app/pages/view/[uuid].vue +4 -3
- package/package.json +1 -1
- package/server/api/dashboard/overview.get.ts +133 -0
- package/server/api/usage/index.get.ts +69 -0
- package/server/api/usage/stats.get.ts +127 -0
- package/server/db/schema.ts +17 -0
- package/server/drizzle/migrations/0011_tearful_johnny_storm.sql +12 -0
- package/server/drizzle/migrations/meta/0011_snapshot.json +1634 -0
- package/server/drizzle/migrations/meta/_journal.json +7 -0
- package/server/routes/_ws/chat.ts +29 -2
- package/server/services/agent-executor.ts +13 -0
- package/server/services/memory-extractor.ts +25 -0
- package/server/utils/log-token-usage.ts +22 -0
- package/shared/types/index.ts +81 -0
- package/.output/public/_nuxt/B34UfOg4.js +0 -1
- package/.output/public/_nuxt/B3DmiwBX.js +0 -1
- package/.output/public/_nuxt/BESViHC6.js +0 -1
- package/.output/public/_nuxt/BIm_buQF.js +0 -1
- package/.output/public/_nuxt/BMh-dHBK.js +0 -1
- package/.output/public/_nuxt/Bkv1Ouue.js +0 -1
- package/.output/public/_nuxt/C4i10ReQ.js +0 -1
- package/.output/public/_nuxt/C5euqjeX.js +0 -1
- package/.output/public/_nuxt/C9hy_rtV.js +0 -1
- package/.output/public/_nuxt/CWJhKIyp.js +0 -1
- package/.output/public/_nuxt/Ck24Z-8o.js +0 -1
- package/.output/public/_nuxt/D1JmZKz7.js +0 -1
- package/.output/public/_nuxt/D6xvQUYG.js +0 -1
- package/.output/public/_nuxt/DGTbw5AQ.js +0 -1
- package/.output/public/_nuxt/Da8Xd1w6.js +0 -1
- package/.output/public/_nuxt/DamUHI17.js +0 -1
- package/.output/public/_nuxt/DoIiMqKa.js +0 -76
- package/.output/public/_nuxt/builds/meta/75ff59f2-0860-4bfc-a288-c670397cae5c.json +0 -1
- package/.output/public/_nuxt/entry.tLLAvqs1.css +0 -1
- package/.output/public/_nuxt/iJ4KRInq.js +0 -1
- package/.output/public/_nuxt/mSr5c6cB.js +0 -1
- package/.output/public/_nuxt/r8g_V5AH.js +0 -1
- package/.output/public/_nuxt/rZmMQHiC.js +0 -1
- package/.output/server/chunks/build/CodeEditorFallback-DfYly7f5.mjs.map +0 -1
- package/.output/server/chunks/build/dashboard-DtbGchTa.mjs +0 -277
- package/.output/server/chunks/build/dashboard-DtbGchTa.mjs.map +0 -1
- package/.output/server/chunks/build/usePreferences-CUvZsOq5.mjs.map +0 -1
- /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
|
-
|
|
127
|
-
|
|
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
|
-
}
|
|
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>
|