@polderlabs/bizar 10.1.0 → 10.3.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/README.md +31 -10
- package/bizar-dash/CONTROL_SURFACES.md +0 -1
- package/bizar-dash/dist/assets/{ActivityFeed-Da-JCrE6.js → ActivityFeed-xXiUCZLB.js} +1 -1
- package/bizar-dash/dist/assets/{ActivityFeed-Da-JCrE6.js.map → ActivityFeed-xXiUCZLB.js.map} +1 -1
- package/bizar-dash/dist/assets/{ActivityView-nIRsSETb.js → ActivityView-nOYhCp3h.js} +1 -1
- package/bizar-dash/dist/assets/{ActivityView-nIRsSETb.js.map → ActivityView-nOYhCp3h.js.map} +1 -1
- package/bizar-dash/dist/assets/{AdminView-DP1NMxmG.js → AdminView-Coi4V3sq.js} +1 -1
- package/bizar-dash/dist/assets/{AdminView-DP1NMxmG.js.map → AdminView-Coi4V3sq.js.map} +1 -1
- package/bizar-dash/dist/assets/{AgentActivity-lSPkOr-O.js → AgentActivity-nEri4lbx.js} +1 -1
- package/bizar-dash/dist/assets/{AgentActivity-lSPkOr-O.js.map → AgentActivity-nEri4lbx.js.map} +1 -1
- package/bizar-dash/dist/assets/{AgentsView-BIWGkz7w.js → AgentsView-ByiIJ0ej.js} +3 -3
- package/bizar-dash/dist/assets/{AgentsView-BIWGkz7w.js.map → AgentsView-ByiIJ0ej.js.map} +1 -1
- package/bizar-dash/dist/assets/{Alert-DN0UysMg.js → Alert-Dg_Vwxh5.js} +1 -1
- package/bizar-dash/dist/assets/{Alert-DN0UysMg.js.map → Alert-Dg_Vwxh5.js.map} +1 -1
- package/bizar-dash/dist/assets/ArtifactsView-CGclRNnz.js +21 -0
- package/bizar-dash/dist/assets/ArtifactsView-CGclRNnz.js.map +1 -0
- package/bizar-dash/dist/assets/{AuthView-DW5cs7ma.js → AuthView-utVm6M3D.js} +1 -1
- package/bizar-dash/dist/assets/{AuthView-DW5cs7ma.js.map → AuthView-utVm6M3D.js.map} +1 -1
- package/bizar-dash/dist/assets/{Avatar-DZPv9pvd.js → Avatar-CXDZdKy4.js} +1 -1
- package/bizar-dash/dist/assets/{Avatar-DZPv9pvd.js.map → Avatar-CXDZdKy4.js.map} +1 -1
- package/bizar-dash/dist/assets/{BackgroundJobsView-D8O-EyMD.js → BackgroundJobsView-j603pz6o.js} +1 -1
- package/bizar-dash/dist/assets/{BackgroundJobsView-D8O-EyMD.js.map → BackgroundJobsView-j603pz6o.js.map} +1 -1
- package/bizar-dash/dist/assets/{BackupView-uwXkhxOV.js → BackupView-DgvnntTE.js} +1 -1
- package/bizar-dash/dist/assets/{BackupView-uwXkhxOV.js.map → BackupView-DgvnntTE.js.map} +1 -1
- package/bizar-dash/dist/assets/{Card-Tyboihu7.js → Card-D9019JOD.js} +1 -1
- package/bizar-dash/dist/assets/{Card-Tyboihu7.js.map → Card-D9019JOD.js.map} +1 -1
- package/bizar-dash/dist/assets/{ChatView-DRBHzgay.js → ChatView-C0cTgj5d.js} +1 -1
- package/bizar-dash/dist/assets/{ChatView-DRBHzgay.js.map → ChatView-C0cTgj5d.js.map} +1 -1
- package/bizar-dash/dist/assets/{Chip-BrXIlR9w.js → Chip-DQtalZ4y.js} +1 -1
- package/bizar-dash/dist/assets/{Chip-BrXIlR9w.js.map → Chip-DQtalZ4y.js.map} +1 -1
- package/bizar-dash/dist/assets/{ClaudeSessionsView-Ck62jaOr.js → ClaudeSessionsView-H3Bqjo6b.js} +1 -1
- package/bizar-dash/dist/assets/{ClaudeSessionsView-Ck62jaOr.js.map → ClaudeSessionsView-H3Bqjo6b.js.map} +1 -1
- package/bizar-dash/dist/assets/{ClipboardView-DfND6xL6.js → ClipboardView-BAguaKbx.js} +1 -1
- package/bizar-dash/dist/assets/{ClipboardView-DfND6xL6.js.map → ClipboardView-BAguaKbx.js.map} +1 -1
- package/bizar-dash/dist/assets/{ConfigView-DjJmSrOJ.js → ConfigView-DWHJgoe8.js} +1 -1
- package/bizar-dash/dist/assets/{ConfigView-DjJmSrOJ.js.map → ConfigView-DWHJgoe8.js.map} +1 -1
- package/bizar-dash/dist/assets/{DiagnosticsView-CKgItf0J.js → DiagnosticsView-oCT624hk.js} +1 -1
- package/bizar-dash/dist/assets/{DiagnosticsView-CKgItf0J.js.map → DiagnosticsView-oCT624hk.js.map} +1 -1
- package/bizar-dash/dist/assets/{DialogsView-DHYF8dE1.js → DialogsView-CqHNals_.js} +1 -1
- package/bizar-dash/dist/assets/{DialogsView-DHYF8dE1.js.map → DialogsView-CqHNals_.js.map} +1 -1
- package/bizar-dash/dist/assets/{DoctorView-DDLHrSKk.js → DoctorView-6kfQq_Mx.js} +1 -1
- package/bizar-dash/dist/assets/{DoctorView-DDLHrSKk.js.map → DoctorView-6kfQq_Mx.js.map} +1 -1
- package/bizar-dash/dist/assets/{EmptyState-DZ2ouKwd.js → EmptyState-DLmtBX_6.js} +1 -1
- package/bizar-dash/dist/assets/{EmptyState-DZ2ouKwd.js.map → EmptyState-DLmtBX_6.js.map} +1 -1
- package/bizar-dash/dist/assets/{EnvVarsView-D1cx83mB.js → EnvVarsView-DySOXlP1.js} +2 -2
- package/bizar-dash/dist/assets/{EnvVarsView-D1cx83mB.js.map → EnvVarsView-DySOXlP1.js.map} +1 -1
- package/bizar-dash/dist/assets/{ErrorState-DwwJ2-Jq.js → ErrorState-Bump8diE.js} +1 -1
- package/bizar-dash/dist/assets/{ErrorState-DwwJ2-Jq.js.map → ErrorState-Bump8diE.js.map} +1 -1
- package/bizar-dash/dist/assets/{EvalView-C9aYixXW.js → EvalView-FnZ5d93z.js} +1 -1
- package/bizar-dash/dist/assets/{EvalView-C9aYixXW.js.map → EvalView-FnZ5d93z.js.map} +1 -1
- package/bizar-dash/dist/assets/{GoalsView-DZa_mGmh.js → GoalsView-C4m1WnVv.js} +1 -1
- package/bizar-dash/dist/assets/{GoalsView-DZa_mGmh.js.map → GoalsView-C4m1WnVv.js.map} +1 -1
- package/bizar-dash/dist/assets/{Grid-CiRyhAgR.js → Grid-dEnoGGws.js} +1 -1
- package/bizar-dash/dist/assets/{Grid-CiRyhAgR.js.map → Grid-dEnoGGws.js.map} +1 -1
- package/bizar-dash/dist/assets/{HistoryView-Dv7aPxJE.js → HistoryView-xdRybjZ2.js} +1 -1
- package/bizar-dash/dist/assets/{HistoryView-Dv7aPxJE.js.map → HistoryView-xdRybjZ2.js.map} +1 -1
- package/bizar-dash/dist/assets/{Input-BbFUDQnS.js → Input-BSa3lh_-.js} +1 -1
- package/bizar-dash/dist/assets/{Input-BbFUDQnS.js.map → Input-BSa3lh_-.js.map} +1 -1
- package/bizar-dash/dist/assets/{LibrariesView-DBQ83TNa.js → LibrariesView-1zR04Eci.js} +1 -1
- package/bizar-dash/dist/assets/{LibrariesView-DBQ83TNa.js.map → LibrariesView-1zR04Eci.js.map} +1 -1
- package/bizar-dash/dist/assets/{LightRAGView-JYpE5u9p.js → LightRAGView-PubmA9oL.js} +1 -1
- package/bizar-dash/dist/assets/{LightRAGView-JYpE5u9p.js.map → LightRAGView-PubmA9oL.js.map} +1 -1
- package/bizar-dash/dist/assets/{ListHeader-BGjm11Z0.js → ListHeader-Ak9WJd39.js} +1 -1
- package/bizar-dash/dist/assets/{ListHeader-BGjm11Z0.js.map → ListHeader-Ak9WJd39.js.map} +1 -1
- package/bizar-dash/dist/assets/{MemoryView-CnOP2MvM.js → MemoryView-DBGpuETw.js} +1 -1
- package/bizar-dash/dist/assets/{MemoryView-CnOP2MvM.js.map → MemoryView-DBGpuETw.js.map} +1 -1
- package/bizar-dash/dist/assets/MiscView-DufqBHkX.js +1 -0
- package/bizar-dash/dist/assets/{MiscView-BBcpUbZZ.js.map → MiscView-DufqBHkX.js.map} +1 -1
- package/bizar-dash/dist/assets/{ModsView-CU4cAmIz.js → ModsView-DcX6eVU2.js} +2 -2
- package/bizar-dash/dist/assets/{ModsView-CU4cAmIz.js.map → ModsView-DcX6eVU2.js.map} +1 -1
- package/bizar-dash/dist/assets/{NotificationsView-BYffvthq.js → NotificationsView-B8IhHRQC.js} +1 -1
- package/bizar-dash/dist/assets/{NotificationsView-BYffvthq.js.map → NotificationsView-B8IhHRQC.js.map} +1 -1
- package/bizar-dash/dist/assets/ObsidianView-DEuk65zS.js +1 -0
- package/bizar-dash/dist/assets/{ObsidianView-D3bI0row.js.map → ObsidianView-DEuk65zS.js.map} +1 -1
- package/bizar-dash/dist/assets/{OverviewView-0tq8f-Y0.js → OverviewView-CTI2spTd.js} +1 -1
- package/bizar-dash/dist/assets/{OverviewView-0tq8f-Y0.js.map → OverviewView-CTI2spTd.js.map} +1 -1
- package/bizar-dash/dist/assets/{ProgressBar-B-h60LKA.js → ProgressBar-D3lkz42E.js} +1 -1
- package/bizar-dash/dist/assets/{ProgressBar-B-h60LKA.js.map → ProgressBar-D3lkz42E.js.map} +1 -1
- package/bizar-dash/dist/assets/{ProjectsView-DhX5lzHS.js → ProjectsView-Du0LzE7A.js} +1 -1
- package/bizar-dash/dist/assets/{ProjectsView-DhX5lzHS.js.map → ProjectsView-Du0LzE7A.js.map} +1 -1
- package/bizar-dash/dist/assets/{ProvidersView-CJMX7jm9.js → ProvidersView-5bCgXwBo.js} +1 -1
- package/bizar-dash/dist/assets/{ProvidersView-CJMX7jm9.js.map → ProvidersView-5bCgXwBo.js.map} +1 -1
- package/bizar-dash/dist/assets/{SchedulesView-CcwDQ9Nm.js → SchedulesView-DFHdIz5P.js} +1 -1
- package/bizar-dash/dist/assets/{SchedulesView-CcwDQ9Nm.js.map → SchedulesView-DFHdIz5P.js.map} +1 -1
- package/bizar-dash/dist/assets/{Select-Cw7Qtz2M.js → Select-C8EJj4NM.js} +1 -1
- package/bizar-dash/dist/assets/{Select-Cw7Qtz2M.js.map → Select-C8EJj4NM.js.map} +1 -1
- package/bizar-dash/dist/assets/{SettingsRow-DgsgbPSI.js → SettingsRow-BdOOmYMD.js} +1 -1
- package/bizar-dash/dist/assets/{SettingsRow-DgsgbPSI.js.map → SettingsRow-BdOOmYMD.js.map} +1 -1
- package/bizar-dash/dist/assets/{SettingsView-DTAFdKyK.js → SettingsView-CqwQ6Knv.js} +1 -1
- package/bizar-dash/dist/assets/{SettingsView-DTAFdKyK.js.map → SettingsView-CqwQ6Knv.js.map} +1 -1
- package/bizar-dash/dist/assets/{Sheet-DH-WNJ3Y.js → Sheet-Cztsvgse.js} +1 -1
- package/bizar-dash/dist/assets/{Sheet-DH-WNJ3Y.js.map → Sheet-Cztsvgse.js.map} +1 -1
- package/bizar-dash/dist/assets/{Slider-DXbJuQ7y.js → Slider-BexNfF6K.js} +1 -1
- package/bizar-dash/dist/assets/{Slider-DXbJuQ7y.js.map → Slider-BexNfF6K.js.map} +1 -1
- package/bizar-dash/dist/assets/{Sparkline-CYtAn82j.js → Sparkline-CLZBtvsN.js} +1 -1
- package/bizar-dash/dist/assets/{Sparkline-CYtAn82j.js.map → Sparkline-CLZBtvsN.js.map} +1 -1
- package/bizar-dash/dist/assets/{Switch-BItGrPqr.js → Switch-B1tiRwNK.js} +1 -1
- package/bizar-dash/dist/assets/{Switch-BItGrPqr.js.map → Switch-B1tiRwNK.js.map} +1 -1
- package/bizar-dash/dist/assets/{TasksView-i5tm6CkX.js → TasksView-CT0fPlos.js} +1 -1
- package/bizar-dash/dist/assets/{TasksView-i5tm6CkX.js.map → TasksView-CT0fPlos.js.map} +1 -1
- package/bizar-dash/dist/assets/{Textarea-qYDf3yds.js → Textarea-D_TPpVMy.js} +1 -1
- package/bizar-dash/dist/assets/{Textarea-qYDf3yds.js.map → Textarea-D_TPpVMy.js.map} +1 -1
- package/bizar-dash/dist/assets/{UpdateView-Xln-ej26.js → UpdateView-tD2q2OhW.js} +1 -1
- package/bizar-dash/dist/assets/{UpdateView-Xln-ej26.js.map → UpdateView-tD2q2OhW.js.map} +1 -1
- package/bizar-dash/dist/assets/{UsageView-BGuZSUZ6.js → UsageView-B4BuAFKA.js} +1 -1
- package/bizar-dash/dist/assets/{UsageView-BGuZSUZ6.js.map → UsageView-B4BuAFKA.js.map} +1 -1
- package/bizar-dash/dist/assets/{ViewHeader-BqIPVKuG.js → ViewHeader-V_SzbUpw.js} +1 -1
- package/bizar-dash/dist/assets/{ViewHeader-BqIPVKuG.js.map → ViewHeader-V_SzbUpw.js.map} +1 -1
- package/bizar-dash/dist/assets/{VoiceView-CWUOCQs_.js → VoiceView-D3vb5OM2.js} +1 -1
- package/bizar-dash/dist/assets/{VoiceView-CWUOCQs_.js.map → VoiceView-D3vb5OM2.js.map} +1 -1
- package/bizar-dash/dist/assets/{brain-fsBSDl2g.js → brain-BmfWcitQ.js} +1 -1
- package/bizar-dash/dist/assets/{brain-fsBSDl2g.js.map → brain-BmfWcitQ.js.map} +1 -1
- package/bizar-dash/dist/assets/{calendar-DsHTZoCU.js → calendar-VntJUzFB.js} +1 -1
- package/bizar-dash/dist/assets/{calendar-DsHTZoCU.js.map → calendar-VntJUzFB.js.map} +1 -1
- package/bizar-dash/dist/assets/{circle-CqSxqqeJ.js → circle-KrQQySeK.js} +1 -1
- package/bizar-dash/dist/assets/{circle-CqSxqqeJ.js.map → circle-KrQQySeK.js.map} +1 -1
- package/bizar-dash/dist/assets/{circle-alert-BsLl001l.js → circle-alert-CQ9MLygI.js} +1 -1
- package/bizar-dash/dist/assets/{circle-alert-BsLl001l.js.map → circle-alert-CQ9MLygI.js.map} +1 -1
- package/bizar-dash/dist/assets/{circle-check-rVKMQmZp.js → circle-check-COtVgsWw.js} +1 -1
- package/bizar-dash/dist/assets/{circle-check-rVKMQmZp.js.map → circle-check-COtVgsWw.js.map} +1 -1
- package/bizar-dash/dist/assets/{clock-OuficfgH.js → clock-C0GlM4qp.js} +1 -1
- package/bizar-dash/dist/assets/{clock-OuficfgH.js.map → clock-C0GlM4qp.js.map} +1 -1
- package/bizar-dash/dist/assets/{download-BdHlstUY.js → download-CW4DmtRe.js} +1 -1
- package/bizar-dash/dist/assets/{download-BdHlstUY.js.map → download-CW4DmtRe.js.map} +1 -1
- package/bizar-dash/dist/assets/{external-link-DA3_VGm-.js → external-link-CAOnh_DH.js} +1 -1
- package/bizar-dash/dist/assets/{external-link-DA3_VGm-.js.map → external-link-CAOnh_DH.js.map} +1 -1
- package/bizar-dash/dist/assets/{file-text-B-g8H227.js → file-text-CS1jB_oV.js} +1 -1
- package/bizar-dash/dist/assets/{file-text-B-g8H227.js.map → file-text-CS1jB_oV.js.map} +1 -1
- package/bizar-dash/dist/assets/{filter-D9B8s5CG.js → filter-DyQbml3L.js} +1 -1
- package/bizar-dash/dist/assets/{filter-D9B8s5CG.js.map → filter-DyQbml3L.js.map} +1 -1
- package/bizar-dash/dist/assets/{git-pull-request-ByH0YuVz.js → git-pull-request-Co6gYfba.js} +1 -1
- package/bizar-dash/dist/assets/{git-pull-request-ByH0YuVz.js.map → git-pull-request-Co6gYfba.js.map} +1 -1
- package/bizar-dash/dist/assets/{main-CdOm1Pj9.js → main-CyAIirBB.js} +28 -33
- package/bizar-dash/dist/assets/main-CyAIirBB.js.map +1 -0
- package/bizar-dash/dist/assets/network-CDn43Gsj.js +6 -0
- package/bizar-dash/dist/assets/network-CDn43Gsj.js.map +1 -0
- package/bizar-dash/dist/assets/{pause-C9hgd8wX.js → pause-CKYjSTGX.js} +1 -1
- package/bizar-dash/dist/assets/{pause-C9hgd8wX.js.map → pause-CKYjSTGX.js.map} +1 -1
- package/bizar-dash/dist/assets/{pencil-BanqaMw7.js → pencil-BUJ2DpWs.js} +1 -1
- package/bizar-dash/dist/assets/{pencil-BanqaMw7.js.map → pencil-BUJ2DpWs.js.map} +1 -1
- package/bizar-dash/dist/assets/{play-CawUZ02_.js → play-CGxZWqtW.js} +1 -1
- package/bizar-dash/dist/assets/{play-CawUZ02_.js.map → play-CGxZWqtW.js.map} +1 -1
- package/bizar-dash/dist/assets/{plug-Cmv0-T_e.js → plug-CQYGgGSy.js} +1 -1
- package/bizar-dash/dist/assets/{plug-Cmv0-T_e.js.map → plug-CQYGgGSy.js.map} +1 -1
- package/bizar-dash/dist/assets/{plus-M6LYiKX8.js → plus-fFxt-8z5.js} +1 -1
- package/bizar-dash/dist/assets/{plus-M6LYiKX8.js.map → plus-fFxt-8z5.js.map} +1 -1
- package/bizar-dash/dist/assets/{power-DP5x_SRF.js → power-Cjgy2KsI.js} +1 -1
- package/bizar-dash/dist/assets/{power-DP5x_SRF.js.map → power-Cjgy2KsI.js.map} +1 -1
- package/bizar-dash/dist/assets/{power-off-NOJRLAIs.js → power-off-B81hLRah.js} +1 -1
- package/bizar-dash/dist/assets/{power-off-NOJRLAIs.js.map → power-off-B81hLRah.js.map} +1 -1
- package/bizar-dash/dist/assets/{refresh-ccw-CHMprsm5.js → refresh-ccw-BCXx86xp.js} +1 -1
- package/bizar-dash/dist/assets/{refresh-ccw-CHMprsm5.js.map → refresh-ccw-BCXx86xp.js.map} +1 -1
- package/bizar-dash/dist/assets/{refresh-cw-1RBqDKgJ.js → refresh-cw-COTg1nZo.js} +1 -1
- package/bizar-dash/dist/assets/{refresh-cw-1RBqDKgJ.js.map → refresh-cw-COTg1nZo.js.map} +1 -1
- package/bizar-dash/dist/assets/{rotate-ccw-DJRekBbi.js → rotate-ccw-BZrEVc0Z.js} +1 -1
- package/bizar-dash/dist/assets/{rotate-ccw-DJRekBbi.js.map → rotate-ccw-BZrEVc0Z.js.map} +1 -1
- package/bizar-dash/dist/assets/{rotate-cw-DHooc0Tx.js → rotate-cw-CsTtaLV2.js} +1 -1
- package/bizar-dash/dist/assets/{rotate-cw-DHooc0Tx.js.map → rotate-cw-CsTtaLV2.js.map} +1 -1
- package/bizar-dash/dist/assets/{save-DQKCKyZK.js → save-DtMTge1F.js} +1 -1
- package/bizar-dash/dist/assets/{save-DQKCKyZK.js.map → save-DtMTge1F.js.map} +1 -1
- package/bizar-dash/dist/assets/{send-xgy3RAi7.js → send-DyBhyk8t.js} +1 -1
- package/bizar-dash/dist/assets/{send-xgy3RAi7.js.map → send-DyBhyk8t.js.map} +1 -1
- package/bizar-dash/dist/assets/{shield-alert-CWV2F5ab.js → shield-alert-BPGJlRF8.js} +1 -1
- package/bizar-dash/dist/assets/{shield-alert-CWV2F5ab.js.map → shield-alert-BPGJlRF8.js.map} +1 -1
- package/bizar-dash/dist/assets/{trash-2-CL4khSRv.js → trash-2-2NsyTU8c.js} +1 -1
- package/bizar-dash/dist/assets/{trash-2-CL4khSRv.js.map → trash-2-2NsyTU8c.js.map} +1 -1
- package/bizar-dash/dist/assets/{trending-up-CSJJhWTS.js → trending-up-BXwP23VK.js} +1 -1
- package/bizar-dash/dist/assets/{trending-up-CSJJhWTS.js.map → trending-up-BXwP23VK.js.map} +1 -1
- package/bizar-dash/dist/assets/{triangle-alert-BPLb7uLt.js → triangle-alert-DFmQ_RXN.js} +1 -1
- package/bizar-dash/dist/assets/{triangle-alert-BPLb7uLt.js.map → triangle-alert-DFmQ_RXN.js.map} +1 -1
- package/bizar-dash/dist/assets/{upload-Cww6MvFq.js → upload-Cj-HYRYV.js} +1 -1
- package/bizar-dash/dist/assets/{upload-Cww6MvFq.js.map → upload-Cj-HYRYV.js.map} +1 -1
- package/bizar-dash/dist/assets/{zap-DmhVUzRA.js → zap-BaOiybcl.js} +1 -1
- package/bizar-dash/dist/assets/{zap-DmhVUzRA.js.map → zap-BaOiybcl.js.map} +1 -1
- package/bizar-dash/dist/index.html +1 -1
- package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/bizar-dash/src/server/api.mjs +11 -0
- package/bizar-dash/src/server/artifacts-store.mjs +120 -14
- package/bizar-dash/src/server/claude-artifacts.mjs +281 -0
- package/bizar-dash/src/server/routes/artifacts.mjs +42 -6
- package/bizar-dash/src/server/routes/audit.mjs +78 -0
- package/bizar-dash/src/server/routes/decisions.mjs +78 -0
- package/bizar-dash/src/server/routes/model-router.mjs +55 -0
- package/bizar-dash/src/server/routes/model-router.test.mjs +28 -0
- package/bizar-dash/src/server/routes/sessions.mjs +80 -0
- package/bizar-dash/src/server/routes/update.mjs +48 -21
- package/bizar-dash/src/server/sprint.test.mjs +163 -0
- package/bizar-dash/src/web/v8/App.tsx +0 -4
- package/bizar-dash/src/web/v8/__tests__/new-views.test.tsx +0 -17
- package/bizar-dash/src/web/v8/shell/Sidebar.tsx +0 -1
- package/bizar-dash/src/web/v8/views/Artifacts/ArtifactsView.tsx +232 -21
- package/bizar-dash/src/web/v8/views/Artifacts/ClaudeArtifactView.tsx +179 -0
- package/bizar-dash/src/web/v8/views/Router.tsx +0 -5
- package/bizar-dash/tests/_update-test-helpers.mjs +13 -0
- package/bizar-dash/tests/update-check.test.mjs +18 -13
- package/bizar-dash/tests/update-packages.test.mjs +154 -0
- package/bizar-dash/tests/update-run.test.mjs +17 -80
- package/cli/banner.mjs +1 -1
- package/cli/bin.mjs +8 -1
- package/cli/bootstrap.mjs +1 -1
- package/cli/commands/claude-cmd.mjs +1 -1
- package/cli/commands/deploy/docker.mjs +0 -3
- package/cli/install/banner.mjs +67 -0
- package/cli/install/banner.test.mjs +39 -0
- package/cli/install/index.mjs +32 -0
- package/cli/install/index.test.mjs +61 -0
- package/cli/install/paths.mjs +80 -0
- package/cli/install/paths.test.mjs +77 -0
- package/cli/install/plugin.mjs +161 -0
- package/cli/install/postinstall.mjs +111 -0
- package/cli/install.mjs +13 -600
- package/cli/migrate.mjs +157 -0
- package/cli/migrate.test.mjs +85 -0
- package/cli/provision.mjs +615 -1918
- package/cli/provision.test.mjs +1 -1
- package/cli/service-controller.test.mjs +0 -1
- package/cli/service-env.mjs +0 -1
- package/cli/utils.mjs +0 -1
- package/install.sh +54 -466
- package/package.json +1 -1
- package/packages/sdk/dist/agent/cron.d.ts +44 -0
- package/packages/sdk/dist/agent/cron.js +79 -0
- package/packages/sdk/dist/agent/heartbeat.d.ts +43 -0
- package/packages/sdk/dist/agent/heartbeat.js +116 -0
- package/packages/sdk/dist/index.d.ts +3 -0
- package/packages/sdk/dist/index.js +5 -0
- package/packages/sdk/dist/learning/decisions.d.ts +46 -0
- package/packages/sdk/dist/learning/decisions.js +132 -0
- package/packages/sdk/dist/learning/index.d.ts +8 -0
- package/packages/sdk/dist/learning/index.js +8 -0
- package/packages/sdk/dist/learning/instincts.d.ts +38 -0
- package/packages/sdk/dist/learning/instincts.js +139 -0
- package/packages/sdk/dist/mcp/server.js +119 -1
- package/packages/sdk/dist/router/agent-model-registry.d.ts +89 -0
- package/packages/sdk/dist/router/agent-model-registry.js +144 -0
- package/packages/sdk/dist/router/index.d.ts +1 -0
- package/packages/sdk/dist/router/index.js +1 -0
- package/packages/sdk/package.json +3 -2
- package/scripts/audit.mjs +388 -0
- package/scripts/audit.test.mjs +149 -0
- package/scripts/eval-gate.mjs +134 -0
- package/scripts/eval-gate.test.mjs +138 -0
- package/scripts/feature-state-machine.mjs +178 -0
- package/scripts/feature-state-machine.test.mjs +135 -0
- package/scripts/install-curl.sh +17 -0
- package/scripts/spec-dedup.test.mjs +124 -0
- package/scripts/sprint.mjs +213 -0
- package/scripts/worktree-setup.sh +79 -0
- package/bizar-dash/dist/assets/ArtifactsView-CDIhPBAx.js +0 -6
- package/bizar-dash/dist/assets/ArtifactsView-CDIhPBAx.js.map +0 -1
- package/bizar-dash/dist/assets/HeadroomView-CNc-TI7u.js +0 -11
- package/bizar-dash/dist/assets/HeadroomView-CNc-TI7u.js.map +0 -1
- package/bizar-dash/dist/assets/MiscView-BBcpUbZZ.js +0 -1
- package/bizar-dash/dist/assets/ObsidianView-D3bI0row.js +0 -1
- package/bizar-dash/dist/assets/main-CdOm1Pj9.js.map +0 -1
- package/bizar-dash/src/web/v8/views/Headroom/HeadroomView.tsx +0 -201
- package/bizar-dash/tests/graphify-mod-spawn.node.test.mjs +0 -90
- package/bizar-dash/tests/v7-archive/mobile-overview.test.tsx +0 -214
- package/bizar-dash/tests/v7-archive/mobile-settings.test.tsx +0 -181
- package/bizar-dash/tests/v7-archive/navigation/CommandPalette.test.tsx +0 -105
- package/bizar-dash/tests/v7-archive/settings-nav.test.tsx +0 -128
- package/bizar-dash/tests/v7-archive/views/Overview.test.tsx +0 -413
- package/cli/provision-claude.mjs +0 -1176
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react';
|
|
2
|
-
import { Network, Play, Square, Plug, Unplug, RotateCw, RefreshCw } from 'lucide-react';
|
|
3
|
-
import { Stack } from '../../ui/primitives/Stack.js';
|
|
4
|
-
import { Inline } from '../../ui/primitives/Inline.js';
|
|
5
|
-
import { Grid } from '../../ui/primitives/Grid.js';
|
|
6
|
-
import { ViewHeader } from '../../ui/data/ViewHeader.js';
|
|
7
|
-
import { Card, CardBody } from '../../ui/data/Card.js';
|
|
8
|
-
import { Badge } from '../../ui/data/Badge.js';
|
|
9
|
-
import { Button } from '../../ui/controls/Button.js';
|
|
10
|
-
import { Skeleton } from '../../ui/feedback/Skeleton.js';
|
|
11
|
-
import { useFetch } from '../../data/useFetch.js';
|
|
12
|
-
import { fetchJson, FetchError } from '../../data/fetcher.js';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* HeadroomView — Sprint S35. Headroom proxy install + lifecycle.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
type Status = 'installed' | 'not-installed' | 'running' | 'stopped';
|
|
19
|
-
|
|
20
|
-
interface HeadroomStatus {
|
|
21
|
-
status: Status;
|
|
22
|
-
version?: string;
|
|
23
|
-
port?: number;
|
|
24
|
-
message?: string;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
interface HeadroomStats {
|
|
28
|
-
hours?: number;
|
|
29
|
-
requests?: number;
|
|
30
|
-
tokensSaved?: number;
|
|
31
|
-
cacheHits?: number;
|
|
32
|
-
cacheMisses?: number;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function HeadroomView(): JSX.Element {
|
|
36
|
-
const status = useFetch<HeadroomStatus>('/api/headroom/status');
|
|
37
|
-
const stats = useFetch<HeadroomStats>('/api/headroom/stats?hours=24');
|
|
38
|
-
const [busy, setBusy] = useState<string | null>(null);
|
|
39
|
-
const [error, setError] = useState<string | null>(null);
|
|
40
|
-
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
const id = setInterval(() => { void status.refetch(); }, 15_000);
|
|
43
|
-
return () => clearInterval(id);
|
|
44
|
-
}, [status]);
|
|
45
|
-
|
|
46
|
-
const install = async (): Promise<void> => {
|
|
47
|
-
setBusy('install');
|
|
48
|
-
setError(null);
|
|
49
|
-
try {
|
|
50
|
-
await fetchJson('/api/headroom/install', { method: 'POST', body: { force: false } });
|
|
51
|
-
void status.refetch();
|
|
52
|
-
} catch (err) {
|
|
53
|
-
setError(err instanceof FetchError ? err.message : (err as Error).message);
|
|
54
|
-
} finally {
|
|
55
|
-
setBusy(null);
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
const wrap = async (): Promise<void> => {
|
|
60
|
-
setBusy('wrap');
|
|
61
|
-
setError(null);
|
|
62
|
-
try {
|
|
63
|
-
await fetchJson('/api/headroom/wrap', { method: 'POST', body: { port: 8787 } });
|
|
64
|
-
void status.refetch();
|
|
65
|
-
} catch (err) {
|
|
66
|
-
setError(err instanceof FetchError ? err.message : (err as Error).message);
|
|
67
|
-
} finally {
|
|
68
|
-
setBusy(null);
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
const unwrap = async (): Promise<void> => {
|
|
73
|
-
setBusy('unwrap');
|
|
74
|
-
setError(null);
|
|
75
|
-
try {
|
|
76
|
-
await fetchJson('/api/headroom/unwrap', { method: 'POST' });
|
|
77
|
-
void status.refetch();
|
|
78
|
-
} catch (err) {
|
|
79
|
-
setError(err instanceof FetchError ? err.message : (err as Error).message);
|
|
80
|
-
} finally {
|
|
81
|
-
setBusy(null);
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
const start = async (): Promise<void> => {
|
|
86
|
-
setBusy('start');
|
|
87
|
-
setError(null);
|
|
88
|
-
try {
|
|
89
|
-
await fetchJson('/api/headroom/start', { method: 'POST' });
|
|
90
|
-
void status.refetch();
|
|
91
|
-
} catch (err) {
|
|
92
|
-
setError(err instanceof FetchError ? err.message : (err as Error).message);
|
|
93
|
-
} finally {
|
|
94
|
-
setBusy(null);
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
const stop = async (): Promise<void> => {
|
|
99
|
-
setBusy('stop');
|
|
100
|
-
setError(null);
|
|
101
|
-
try {
|
|
102
|
-
await fetchJson('/api/headroom/stop', { method: 'POST' });
|
|
103
|
-
void status.refetch();
|
|
104
|
-
} catch (err) {
|
|
105
|
-
setError(err instanceof FetchError ? err.message : (err as Error).message);
|
|
106
|
-
} finally {
|
|
107
|
-
setBusy(null);
|
|
108
|
-
}
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
const s = status.data;
|
|
112
|
-
const isInstalled = s && s.status !== 'not-installed';
|
|
113
|
-
const isRunning = s?.status === 'running';
|
|
114
|
-
|
|
115
|
-
return (
|
|
116
|
-
<Stack gap={5} data-testid="headroom-view">
|
|
117
|
-
<ViewHeader
|
|
118
|
-
title="Headroom"
|
|
119
|
-
description="Local proxy that reduces token cost via caching + batching. Install, wrap Cline, start/stop."
|
|
120
|
-
actions={
|
|
121
|
-
<Inline gap={2} align="center">
|
|
122
|
-
{error !== null && <span role="alert" style={{ color: 'var(--danger)', fontSize: 'var(--fs-12)' }}>{error}</span>}
|
|
123
|
-
<Button variant="ghost" onClick={() => { void status.refetch(); }} data-testid="headroom-refresh">
|
|
124
|
-
<RefreshCw size={14} aria-hidden /> Refresh
|
|
125
|
-
</Button>
|
|
126
|
-
</Inline>
|
|
127
|
-
}
|
|
128
|
-
/>
|
|
129
|
-
|
|
130
|
-
<Card variant="elevated">
|
|
131
|
-
<CardBody>
|
|
132
|
-
{status.loading && !s ? (
|
|
133
|
-
<Skeleton style={{ height: 60 }} />
|
|
134
|
-
) : (
|
|
135
|
-
<Inline align="center" justify="between" gap={3}>
|
|
136
|
-
<Stack gap={1}>
|
|
137
|
-
<Inline align="center" gap={2}>
|
|
138
|
-
<Network size={16} aria-hidden />
|
|
139
|
-
<strong>{s?.status ?? 'unknown'}</strong>
|
|
140
|
-
{s?.version && <Badge tone="neutral">v{s.version}</Badge>}
|
|
141
|
-
{s?.port && <Badge tone="info">port {s.port}</Badge>}
|
|
142
|
-
</Inline>
|
|
143
|
-
{s?.message && <span style={{ fontSize: 'var(--fs-12)', color: 'var(--fg-muted)' }}>{s.message}</span>}
|
|
144
|
-
</Stack>
|
|
145
|
-
<Inline gap={1} wrap={true}>
|
|
146
|
-
{!isInstalled && (
|
|
147
|
-
<Button variant="primary" onClick={() => void install()} disabled={busy === 'install'} data-testid="headroom-install">
|
|
148
|
-
<Plug size={12} aria-hidden /> {busy === 'install' ? 'Installing…' : 'Install'}
|
|
149
|
-
</Button>
|
|
150
|
-
)}
|
|
151
|
-
{isInstalled && (
|
|
152
|
-
<>
|
|
153
|
-
<Button variant={isRunning ? 'secondary' : 'primary'} onClick={() => void (isRunning ? stop() : start())} disabled={busy === 'start' || busy === 'stop'} data-testid="headroom-toggle-running">
|
|
154
|
-
{isRunning ? <Square size={12} aria-hidden /> : <Play size={12} aria-hidden />}
|
|
155
|
-
{isRunning ? (busy === 'stop' ? 'Stopping…' : 'Stop') : (busy === 'start' ? 'Starting…' : 'Start')}
|
|
156
|
-
</Button>
|
|
157
|
-
<Button variant="ghost" onClick={() => void wrap()} disabled={busy === 'wrap'} data-testid="headroom-wrap">
|
|
158
|
-
<RotateCw size={12} aria-hidden /> {busy === 'wrap' ? 'Wrapping…' : 'Wrap Cline'}
|
|
159
|
-
</Button>
|
|
160
|
-
<Button variant="ghost" onClick={() => void unwrap()} disabled={busy === 'unwrap'} data-testid="headroom-unwrap">
|
|
161
|
-
<Unplug size={12} aria-hidden /> {busy === 'unwrap' ? 'Unwrapping…' : 'Unwrap'}
|
|
162
|
-
</Button>
|
|
163
|
-
</>
|
|
164
|
-
)}
|
|
165
|
-
</Inline>
|
|
166
|
-
</Inline>
|
|
167
|
-
)}
|
|
168
|
-
</CardBody>
|
|
169
|
-
</Card>
|
|
170
|
-
|
|
171
|
-
<Grid cols={3}>
|
|
172
|
-
<Card variant="default">
|
|
173
|
-
<CardBody>
|
|
174
|
-
<Stack gap={1}>
|
|
175
|
-
<span style={{ fontSize: 'var(--fs-12)', color: 'var(--fg-muted)' }}>Requests (24h)</span>
|
|
176
|
-
<strong style={{ fontFamily: 'var(--font-mono)', fontSize: 'var(--fs-18)' }}>{stats.data?.requests ?? 0}</strong>
|
|
177
|
-
</Stack>
|
|
178
|
-
</CardBody>
|
|
179
|
-
</Card>
|
|
180
|
-
<Card variant="default">
|
|
181
|
-
<CardBody>
|
|
182
|
-
<Stack gap={1}>
|
|
183
|
-
<span style={{ fontSize: 'var(--fs-12)', color: 'var(--fg-muted)' }}>Tokens saved</span>
|
|
184
|
-
<strong style={{ fontFamily: 'var(--font-mono)', fontSize: 'var(--fs-18)' }}>{stats.data?.tokensSaved ?? 0}</strong>
|
|
185
|
-
</Stack>
|
|
186
|
-
</CardBody>
|
|
187
|
-
</Card>
|
|
188
|
-
<Card variant="default">
|
|
189
|
-
<CardBody>
|
|
190
|
-
<Stack gap={1}>
|
|
191
|
-
<span style={{ fontSize: 'var(--fs-12)', color: 'var(--fg-muted)' }}>Cache hits / misses</span>
|
|
192
|
-
<strong style={{ fontFamily: 'var(--font-mono)', fontSize: 'var(--fs-18)' }}>
|
|
193
|
-
{stats.data?.cacheHits ?? 0} / {stats.data?.cacheMisses ?? 0}
|
|
194
|
-
</strong>
|
|
195
|
-
</Stack>
|
|
196
|
-
</CardBody>
|
|
197
|
-
</Card>
|
|
198
|
-
</Grid>
|
|
199
|
-
</Stack>
|
|
200
|
-
);
|
|
201
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* graphify-mod-spawn.node.test.mjs — regression test for the SIGPIPE crash.
|
|
3
|
-
*
|
|
4
|
-
* The graphify mod's route.mjs used to spawn the `graphify` CLI with
|
|
5
|
-
* `spawnSync` + `stdio: 'inherit'`. That worked in a terminal, but the
|
|
6
|
-
* dashboard is a background daemon with stdin=/dev/null and
|
|
7
|
-
* stdout/stderr redirected to a log file. With `stdio: 'inherit'`, the
|
|
8
|
-
* child inherits those descriptors; if anything closes or rotates them
|
|
9
|
-
* mid-build, the kernel delivers SIGPIPE to the dashboard — silently
|
|
10
|
-
* killing it. The user reported "building the graph crashed the dash".
|
|
11
|
-
*
|
|
12
|
-
* This test verifies the source file no longer contains the dangerous
|
|
13
|
-
* pattern. We parse the file as text (the route.mjs is loaded by the
|
|
14
|
-
* dashboard's vm sandbox, not directly importable here).
|
|
15
|
-
*
|
|
16
|
-
* Run with: node --test tests/graphify-mod-spawn.node.test.mjs
|
|
17
|
-
*/
|
|
18
|
-
import { describe, it } from 'node:test';
|
|
19
|
-
import assert from 'node:assert/strict';
|
|
20
|
-
import { readFileSync } from 'node:fs';
|
|
21
|
-
import { join, resolve } from 'node:path';
|
|
22
|
-
|
|
23
|
-
// Test lives at bizar-dash/tests/, source is at the repo root.
|
|
24
|
-
const REPO = resolve(import.meta.dirname, '..', '..');
|
|
25
|
-
const SOURCE = join(REPO, 'mods-examples/graphify/route.mjs');
|
|
26
|
-
|
|
27
|
-
describe('graphify mod — SIGPIPE-safe spawn', () => {
|
|
28
|
-
it('source file exists at the expected path', () => {
|
|
29
|
-
const text = readFileSync(SOURCE, 'utf8');
|
|
30
|
-
assert.ok(text.length > 100, 'source file should be substantial');
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('does NOT import spawnSync (causes blocking + stdio inheritance)', () => {
|
|
34
|
-
const text = readFileSync(SOURCE, 'utf8');
|
|
35
|
-
assert.ok(
|
|
36
|
-
!/\bspawnSync\b/.test(text),
|
|
37
|
-
'route.mjs must not import or call spawnSync — use async spawn instead',
|
|
38
|
-
);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('does NOT use stdio: \'inherit\' (the SIGPIPE crash vector)', () => {
|
|
42
|
-
const text = readFileSync(SOURCE, 'utf8');
|
|
43
|
-
// Strip block comments and line comments before checking — we don't
|
|
44
|
-
// want the bug description itself to trip the test.
|
|
45
|
-
const stripped = text
|
|
46
|
-
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
47
|
-
.replace(/^\s*\/\/.*$/gm, '')
|
|
48
|
-
.replace(/`stdio:\s*'inherit'`/g, '');
|
|
49
|
-
// Look for the literal string in any quoting style.
|
|
50
|
-
const patterns = [
|
|
51
|
-
/stdio:\s*['"]inherit['"]/,
|
|
52
|
-
/stdio:\s*\[\s*['"]inherit['"]/,
|
|
53
|
-
];
|
|
54
|
-
for (const pat of patterns) {
|
|
55
|
-
assert.ok(
|
|
56
|
-
!pat.test(stripped),
|
|
57
|
-
`route.mjs must not use stdio: 'inherit' — it propagates SIGPIPE to the dashboard`,
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('isolates the spawned child stdio with [\'ignore\', \'pipe\', \'pipe\']', () => {
|
|
63
|
-
const text = readFileSync(SOURCE, 'utf8');
|
|
64
|
-
assert.ok(
|
|
65
|
-
/stdio:\s*\[\s*['"]ignore['"]\s*,\s*['"]pipe['"]\s*,\s*['"]pipe['"]\s*\]/.test(text),
|
|
66
|
-
'spawn options should set stdio to [ignore, pipe, pipe] for SIGPIPE isolation',
|
|
67
|
-
);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('runs graphify as an async spawn (not blocking)', () => {
|
|
71
|
-
const text = readFileSync(SOURCE, 'utf8');
|
|
72
|
-
// runGraphify should be declared `async function` and return a Promise.
|
|
73
|
-
assert.ok(
|
|
74
|
-
/async\s+function\s+runGraphify\s*\(/.test(text),
|
|
75
|
-
'runGraphify should be declared async so the dashboard event loop is not blocked',
|
|
76
|
-
);
|
|
77
|
-
assert.ok(
|
|
78
|
-
/return\s+new\s+Promise\s*\(/.test(text),
|
|
79
|
-
'runGraphify should return a Promise (async spawn lifecycle)',
|
|
80
|
-
);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('forwards child stdout/stderr to the broadcast callback (no streaming lost)', () => {
|
|
84
|
-
const text = readFileSync(SOURCE, 'utf8');
|
|
85
|
-
assert.ok(
|
|
86
|
-
/broadcast\(\s*\{\s*type:\s*['"]graphify:build:log['"]/.test(text),
|
|
87
|
-
'runGraphify should call broadcast with graphify:build:log events so the dashboard UI keeps streaming',
|
|
88
|
-
);
|
|
89
|
-
});
|
|
90
|
-
});
|
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* tests/mobile-overview.test.tsx
|
|
3
|
-
*
|
|
4
|
-
* v5.4 — Component tests for the mobile-optimized overview (hero + stats +
|
|
5
|
-
* recent activity + quick actions).
|
|
6
|
-
*/
|
|
7
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
8
|
-
import { render, screen } from '@testing-library/react';
|
|
9
|
-
import userEvent from '@testing-library/user-event';
|
|
10
|
-
import React from 'react';
|
|
11
|
-
import { MobileOverview } from '../src/web/mobile/MobileOverview';
|
|
12
|
-
import type { Snapshot } from '../src/web/lib/types';
|
|
13
|
-
|
|
14
|
-
// ─── Mocks ──────────────────────────────────────────────────────────────────
|
|
15
|
-
|
|
16
|
-
vi.mock('../src/web/lib/api', () => ({
|
|
17
|
-
api: {
|
|
18
|
-
post: vi.fn().mockResolvedValue({ ok: true }),
|
|
19
|
-
get: vi.fn().mockResolvedValue({}),
|
|
20
|
-
},
|
|
21
|
-
}));
|
|
22
|
-
|
|
23
|
-
// Stub lucide icons — keep them tiny testids so we can assert presence cheaply.
|
|
24
|
-
vi.mock('lucide-react', async (importOriginal) => {
|
|
25
|
-
const actual = await importOriginal() as Record<string, unknown>;
|
|
26
|
-
const make = (name: string) => {
|
|
27
|
-
const Cmp = (props: { size?: number }) => <svg data-testid={`icon-${name}`} data-size={props.size} />;
|
|
28
|
-
Cmp.displayName = name;
|
|
29
|
-
return Cmp;
|
|
30
|
-
};
|
|
31
|
-
return {
|
|
32
|
-
...actual,
|
|
33
|
-
CheckSquare: make('CheckSquare'),
|
|
34
|
-
Clock: make('Clock'),
|
|
35
|
-
Bot: make('Bot'),
|
|
36
|
-
Coins: make('Coins'),
|
|
37
|
-
Plus: make('Plus'),
|
|
38
|
-
MessageSquare: make('MessageSquare'),
|
|
39
|
-
Stethoscope: make('Stethoscope'),
|
|
40
|
-
Send: make('Send'),
|
|
41
|
-
};
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
// ─── Test data ──────────────────────────────────────────────────────────────
|
|
45
|
-
|
|
46
|
-
const BASE_SNAPSHOT: Snapshot = {
|
|
47
|
-
overview: {
|
|
48
|
-
counts: { agents: 0, plans: 0, projects: 0, sessions: 0 },
|
|
49
|
-
recentActivity: [],
|
|
50
|
-
versions: { node: '', platform: '', projectRoot: '', bizarRoot: '' },
|
|
51
|
-
generatedAt: '',
|
|
52
|
-
},
|
|
53
|
-
agents: [],
|
|
54
|
-
artifacts: [],
|
|
55
|
-
projects: [],
|
|
56
|
-
activeProject: null,
|
|
57
|
-
config: { path: '', data: null, raw: '', exists: false },
|
|
58
|
-
settings: {
|
|
59
|
-
path: '',
|
|
60
|
-
data: {
|
|
61
|
-
theme: { mode: 'dark', accent: '#8b5cf6', success: '#3fb950', warning: '#f0883e', error: '#f85149', info: '#58a6ff', fontFamily: 'sans-serif', fontSize: 14, compactMode: false, animations: true },
|
|
62
|
-
ui: { layout: 'topnav', showHeader: true, showStatusBar: true, defaultTab: 'overview' },
|
|
63
|
-
defaultAgent: 'odin',
|
|
64
|
-
defaultModel: '',
|
|
65
|
-
notifications: { onAgentComplete: false, onPlanApproval: false },
|
|
66
|
-
dashboard: { autoLaunchWeb: false },
|
|
67
|
-
service: { enabled: false, autostart: false },
|
|
68
|
-
about: { version: '5.4.0', homepage: '', license: 'MIT' },
|
|
69
|
-
agents: { maxParallel: 6, stuckThresholdMs: 600000, autoRestart: false },
|
|
70
|
-
personalization: { displayName: '', role: '', team: '', aboutMe: '', preferences: '' },
|
|
71
|
-
workflow: { artifactsEnabled: true, agentsDecideAutonomously: false, chatAutonomous: false },
|
|
72
|
-
headroom: { enabled: false, autoInstall: true, port: 8787, host: '127.0.0.1', outputShaper: false, telemetry: false, budget: 0, backend: 'anthropic', autoStart: true, autoWrap: true, routeAllProviders: true },
|
|
73
|
-
},
|
|
74
|
-
exists: true,
|
|
75
|
-
},
|
|
76
|
-
tasks: [],
|
|
77
|
-
mods: [],
|
|
78
|
-
schedules: [],
|
|
79
|
-
providers: [],
|
|
80
|
-
mcps: [],
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
// ─── Tests ──────────────────────────────────────────────────────────────────
|
|
84
|
-
|
|
85
|
-
describe('MobileOverview', () => {
|
|
86
|
-
const refreshSnapshot = vi.fn().mockResolvedValue(undefined);
|
|
87
|
-
|
|
88
|
-
beforeEach(() => {
|
|
89
|
-
vi.clearAllMocks();
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it('renders hero card with input', () => {
|
|
93
|
-
render(<MobileOverview snapshot={BASE_SNAPSHOT} refreshSnapshot={refreshSnapshot} />);
|
|
94
|
-
|
|
95
|
-
expect(screen.getByRole('heading', { name: /what do you want to do/i })).toBeInTheDocument();
|
|
96
|
-
const textarea = screen.getByPlaceholderText(/implement user authentication/i);
|
|
97
|
-
expect(textarea).toBeInTheDocument();
|
|
98
|
-
expect(textarea.tagName).toBe('TEXTAREA');
|
|
99
|
-
expect(screen.getByRole('button', { name: /submit/i })).toBeInTheDocument();
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('disables submit button when textarea is empty', () => {
|
|
103
|
-
render(<MobileOverview snapshot={BASE_SNAPSHOT} refreshSnapshot={refreshSnapshot} />);
|
|
104
|
-
|
|
105
|
-
const submit = screen.getByRole('button', { name: /submit/i });
|
|
106
|
-
expect(submit).toBeDisabled();
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it('enables submit button when textarea has text', async () => {
|
|
110
|
-
const user = userEvent.setup();
|
|
111
|
-
render(<MobileOverview snapshot={BASE_SNAPSHOT} refreshSnapshot={refreshSnapshot} />);
|
|
112
|
-
|
|
113
|
-
await user.type(screen.getByPlaceholderText(/implement user authentication/i), 'fix the bug');
|
|
114
|
-
expect(screen.getByRole('button', { name: /submit/i })).not.toBeDisabled();
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it('renders 4 stat cards', () => {
|
|
118
|
-
render(<MobileOverview snapshot={BASE_SNAPSHOT} refreshSnapshot={refreshSnapshot} />);
|
|
119
|
-
|
|
120
|
-
expect(screen.getByText('Tasks')).toBeInTheDocument();
|
|
121
|
-
expect(screen.getByText('Schedules')).toBeInTheDocument();
|
|
122
|
-
expect(screen.getByText('Active Agents')).toBeInTheDocument();
|
|
123
|
-
expect(screen.getByText('API Tokens')).toBeInTheDocument();
|
|
124
|
-
|
|
125
|
-
// Each stat label sits on a card with the matching modifier class.
|
|
126
|
-
expect(document.querySelectorAll('.mobile-stat-card.is-blue')).toHaveLength(1);
|
|
127
|
-
expect(document.querySelectorAll('.mobile-stat-card.is-purple')).toHaveLength(1);
|
|
128
|
-
expect(document.querySelectorAll('.mobile-stat-card.is-green')).toHaveLength(1);
|
|
129
|
-
expect(document.querySelectorAll('.mobile-stat-card.is-yellow')).toHaveLength(1);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('reflects task count and active task subtext from snapshot', () => {
|
|
133
|
-
const snap: Snapshot = {
|
|
134
|
-
...BASE_SNAPSHOT,
|
|
135
|
-
tasks: [
|
|
136
|
-
{ ...BASE_SNAPSHOT.tasks[0], id: 't1', title: 'a', description: '', status: 'doing', tags: [], priority: 'normal', createdAt: '', updatedAt: '' },
|
|
137
|
-
{ ...BASE_SNAPSHOT.tasks[0], id: 't2', title: 'b', description: '', status: 'done', tags: [], priority: 'normal', createdAt: '', updatedAt: '' },
|
|
138
|
-
{ ...BASE_SNAPSHOT.tasks[0], id: 't3', title: 'c', description: '', status: 'queued', tags: [], priority: 'normal', createdAt: '', updatedAt: '' },
|
|
139
|
-
] as Snapshot['tasks'],
|
|
140
|
-
schedules: [
|
|
141
|
-
{ id: 's1', name: 'nightly', cron: '0 0 * * *', enabled: true, lastRun: null, nextRun: null, target: 'agent', agentName: 'odin', createdAt: '', updatedAt: '' },
|
|
142
|
-
] as Snapshot['schedules'],
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
render(<MobileOverview snapshot={snap} refreshSnapshot={refreshSnapshot} />);
|
|
146
|
-
|
|
147
|
-
// Tasks stat — first mobile-stat-value inside the blue card.
|
|
148
|
-
const blueCard = document.querySelector('.mobile-stat-card.is-blue')!;
|
|
149
|
-
expect(blueCard.querySelector('.mobile-stat-value')?.textContent).toBe('3');
|
|
150
|
-
expect(blueCard.querySelector('.mobile-stat-subtext')?.textContent).toBe('1 active');
|
|
151
|
-
|
|
152
|
-
// Schedules stat.
|
|
153
|
-
const purpleCard = document.querySelector('.mobile-stat-card.is-purple')!;
|
|
154
|
-
expect(purpleCard.querySelector('.mobile-stat-value')?.textContent).toBe('1');
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it('shows recent activity when snapshot has items', () => {
|
|
158
|
-
const snap: Snapshot = {
|
|
159
|
-
...BASE_SNAPSHOT,
|
|
160
|
-
overview: {
|
|
161
|
-
...BASE_SNAPSHOT.overview,
|
|
162
|
-
recentActivity: [
|
|
163
|
-
{ ts: '2026-07-05T12:34:00.000Z', kind: 'task', title: 'hello' },
|
|
164
|
-
{ ts: '2026-07-05T12:35:00.000Z', kind: 'agent', name: 'odin' },
|
|
165
|
-
],
|
|
166
|
-
},
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
render(<MobileOverview snapshot={snap} refreshSnapshot={refreshSnapshot} />);
|
|
170
|
-
|
|
171
|
-
expect(screen.getByRole('heading', { name: /recent activity/i })).toBeInTheDocument();
|
|
172
|
-
const items = document.querySelectorAll('.mobile-activity-item');
|
|
173
|
-
expect(items).toHaveLength(2);
|
|
174
|
-
expect(screen.queryByText(/no recent activity/i)).not.toBeInTheDocument();
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it('shows empty state when there is no recent activity', () => {
|
|
178
|
-
render(<MobileOverview snapshot={BASE_SNAPSHOT} refreshSnapshot={refreshSnapshot} />);
|
|
179
|
-
|
|
180
|
-
expect(screen.getByText(/no recent activity/i)).toBeInTheDocument();
|
|
181
|
-
expect(document.querySelector('.mobile-activity-list')).toBeNull();
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it('renders quick action buttons', () => {
|
|
185
|
-
render(<MobileOverview snapshot={BASE_SNAPSHOT} refreshSnapshot={refreshSnapshot} />);
|
|
186
|
-
|
|
187
|
-
expect(screen.getByRole('button', { name: /new task/i })).toBeInTheDocument();
|
|
188
|
-
expect(screen.getByRole('button', { name: /new chat/i })).toBeInTheDocument();
|
|
189
|
-
expect(screen.getByRole('button', { name: /run doctor/i })).toBeInTheDocument();
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
it('clamps recent activity to 8 items', () => {
|
|
193
|
-
const many: { ts: string; kind: string; title: string }[] = [];
|
|
194
|
-
for (let i = 0; i < 12; i++) {
|
|
195
|
-
many.push({ ts: `2026-07-05T12:${String(i).padStart(2, '0')}:00.000Z`, kind: 'task', title: `t${i}` });
|
|
196
|
-
}
|
|
197
|
-
const snap: Snapshot = {
|
|
198
|
-
...BASE_SNAPSHOT,
|
|
199
|
-
overview: { ...BASE_SNAPSHOT.overview, recentActivity: many },
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
render(<MobileOverview snapshot={snap} refreshSnapshot={refreshSnapshot} />);
|
|
203
|
-
|
|
204
|
-
expect(document.querySelectorAll('.mobile-activity-item')).toHaveLength(8);
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
it('handles null snapshot without crashing', () => {
|
|
208
|
-
render(<MobileOverview snapshot={null} refreshSnapshot={refreshSnapshot} />);
|
|
209
|
-
|
|
210
|
-
expect(screen.getByRole('heading', { name: /what do you want to do/i })).toBeInTheDocument();
|
|
211
|
-
expect(screen.getByText(/no recent activity/i)).toBeInTheDocument();
|
|
212
|
-
expect(screen.getByText('Tasks')).toBeInTheDocument();
|
|
213
|
-
});
|
|
214
|
-
});
|