@polderlabs/bizar 10.2.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 -8
- package/bizar-dash/CONTROL_SURFACES.md +0 -1
- package/bizar-dash/dist/assets/{ActivityFeed-JjmOY2AQ.js → ActivityFeed-xXiUCZLB.js} +1 -1
- package/bizar-dash/dist/assets/{ActivityFeed-JjmOY2AQ.js.map → ActivityFeed-xXiUCZLB.js.map} +1 -1
- package/bizar-dash/dist/assets/{ActivityView-BIIMvGWo.js → ActivityView-nOYhCp3h.js} +1 -1
- package/bizar-dash/dist/assets/{ActivityView-BIIMvGWo.js.map → ActivityView-nOYhCp3h.js.map} +1 -1
- package/bizar-dash/dist/assets/{AdminView-DRidJtkq.js → AdminView-Coi4V3sq.js} +1 -1
- package/bizar-dash/dist/assets/{AdminView-DRidJtkq.js.map → AdminView-Coi4V3sq.js.map} +1 -1
- package/bizar-dash/dist/assets/{AgentActivity-79l4SBMC.js → AgentActivity-nEri4lbx.js} +1 -1
- package/bizar-dash/dist/assets/{AgentActivity-79l4SBMC.js.map → AgentActivity-nEri4lbx.js.map} +1 -1
- package/bizar-dash/dist/assets/{AgentsView-0vT4uPSc.js → AgentsView-ByiIJ0ej.js} +3 -3
- package/bizar-dash/dist/assets/{AgentsView-0vT4uPSc.js.map → AgentsView-ByiIJ0ej.js.map} +1 -1
- package/bizar-dash/dist/assets/{Alert-CG5q_s75.js → Alert-Dg_Vwxh5.js} +1 -1
- package/bizar-dash/dist/assets/{Alert-CG5q_s75.js.map → Alert-Dg_Vwxh5.js.map} +1 -1
- package/bizar-dash/dist/assets/{ArtifactsView-DM8ZmpR9.js → ArtifactsView-CGclRNnz.js} +1 -1
- package/bizar-dash/dist/assets/{ArtifactsView-DM8ZmpR9.js.map → ArtifactsView-CGclRNnz.js.map} +1 -1
- package/bizar-dash/dist/assets/{AuthView-BveN94MC.js → AuthView-utVm6M3D.js} +1 -1
- package/bizar-dash/dist/assets/{AuthView-BveN94MC.js.map → AuthView-utVm6M3D.js.map} +1 -1
- package/bizar-dash/dist/assets/{Avatar-ulUPIYTd.js → Avatar-CXDZdKy4.js} +1 -1
- package/bizar-dash/dist/assets/{Avatar-ulUPIYTd.js.map → Avatar-CXDZdKy4.js.map} +1 -1
- package/bizar-dash/dist/assets/{BackgroundJobsView-BWErhqFo.js → BackgroundJobsView-j603pz6o.js} +1 -1
- package/bizar-dash/dist/assets/{BackgroundJobsView-BWErhqFo.js.map → BackgroundJobsView-j603pz6o.js.map} +1 -1
- package/bizar-dash/dist/assets/{BackupView-CnBjzG9N.js → BackupView-DgvnntTE.js} +1 -1
- package/bizar-dash/dist/assets/{BackupView-CnBjzG9N.js.map → BackupView-DgvnntTE.js.map} +1 -1
- package/bizar-dash/dist/assets/{Card-B0imhxMk.js → Card-D9019JOD.js} +1 -1
- package/bizar-dash/dist/assets/{Card-B0imhxMk.js.map → Card-D9019JOD.js.map} +1 -1
- package/bizar-dash/dist/assets/{ChatView-D6zGytzx.js → ChatView-C0cTgj5d.js} +1 -1
- package/bizar-dash/dist/assets/{ChatView-D6zGytzx.js.map → ChatView-C0cTgj5d.js.map} +1 -1
- package/bizar-dash/dist/assets/{Chip-wwZSGwLE.js → Chip-DQtalZ4y.js} +1 -1
- package/bizar-dash/dist/assets/{Chip-wwZSGwLE.js.map → Chip-DQtalZ4y.js.map} +1 -1
- package/bizar-dash/dist/assets/{ClaudeSessionsView-DK1sOLhi.js → ClaudeSessionsView-H3Bqjo6b.js} +1 -1
- package/bizar-dash/dist/assets/{ClaudeSessionsView-DK1sOLhi.js.map → ClaudeSessionsView-H3Bqjo6b.js.map} +1 -1
- package/bizar-dash/dist/assets/{ClipboardView-CZ2lYPr0.js → ClipboardView-BAguaKbx.js} +1 -1
- package/bizar-dash/dist/assets/{ClipboardView-CZ2lYPr0.js.map → ClipboardView-BAguaKbx.js.map} +1 -1
- package/bizar-dash/dist/assets/{ConfigView-D-zT9eNA.js → ConfigView-DWHJgoe8.js} +1 -1
- package/bizar-dash/dist/assets/{ConfigView-D-zT9eNA.js.map → ConfigView-DWHJgoe8.js.map} +1 -1
- package/bizar-dash/dist/assets/{DiagnosticsView-BocV68Lb.js → DiagnosticsView-oCT624hk.js} +1 -1
- package/bizar-dash/dist/assets/{DiagnosticsView-BocV68Lb.js.map → DiagnosticsView-oCT624hk.js.map} +1 -1
- package/bizar-dash/dist/assets/{DialogsView-BVjPI5x7.js → DialogsView-CqHNals_.js} +1 -1
- package/bizar-dash/dist/assets/{DialogsView-BVjPI5x7.js.map → DialogsView-CqHNals_.js.map} +1 -1
- package/bizar-dash/dist/assets/{DoctorView-84gJcZwM.js → DoctorView-6kfQq_Mx.js} +1 -1
- package/bizar-dash/dist/assets/{DoctorView-84gJcZwM.js.map → DoctorView-6kfQq_Mx.js.map} +1 -1
- package/bizar-dash/dist/assets/{EmptyState-BjgVOrcO.js → EmptyState-DLmtBX_6.js} +1 -1
- package/bizar-dash/dist/assets/{EmptyState-BjgVOrcO.js.map → EmptyState-DLmtBX_6.js.map} +1 -1
- package/bizar-dash/dist/assets/{EnvVarsView-BJ2TEzF7.js → EnvVarsView-DySOXlP1.js} +2 -2
- package/bizar-dash/dist/assets/{EnvVarsView-BJ2TEzF7.js.map → EnvVarsView-DySOXlP1.js.map} +1 -1
- package/bizar-dash/dist/assets/{ErrorState-BE3WBFqF.js → ErrorState-Bump8diE.js} +1 -1
- package/bizar-dash/dist/assets/{ErrorState-BE3WBFqF.js.map → ErrorState-Bump8diE.js.map} +1 -1
- package/bizar-dash/dist/assets/{EvalView-BJJ2P-kK.js → EvalView-FnZ5d93z.js} +1 -1
- package/bizar-dash/dist/assets/{EvalView-BJJ2P-kK.js.map → EvalView-FnZ5d93z.js.map} +1 -1
- package/bizar-dash/dist/assets/{GoalsView-pwROHApi.js → GoalsView-C4m1WnVv.js} +1 -1
- package/bizar-dash/dist/assets/{GoalsView-pwROHApi.js.map → GoalsView-C4m1WnVv.js.map} +1 -1
- package/bizar-dash/dist/assets/{Grid-qcvbhx6e.js → Grid-dEnoGGws.js} +1 -1
- package/bizar-dash/dist/assets/{Grid-qcvbhx6e.js.map → Grid-dEnoGGws.js.map} +1 -1
- package/bizar-dash/dist/assets/{HistoryView-C7SSOLJA.js → HistoryView-xdRybjZ2.js} +1 -1
- package/bizar-dash/dist/assets/{HistoryView-C7SSOLJA.js.map → HistoryView-xdRybjZ2.js.map} +1 -1
- package/bizar-dash/dist/assets/{Input-Bgzxf_Hu.js → Input-BSa3lh_-.js} +1 -1
- package/bizar-dash/dist/assets/{Input-Bgzxf_Hu.js.map → Input-BSa3lh_-.js.map} +1 -1
- package/bizar-dash/dist/assets/{LibrariesView-CbFFksox.js → LibrariesView-1zR04Eci.js} +1 -1
- package/bizar-dash/dist/assets/{LibrariesView-CbFFksox.js.map → LibrariesView-1zR04Eci.js.map} +1 -1
- package/bizar-dash/dist/assets/{LightRAGView-BqDrJbRq.js → LightRAGView-PubmA9oL.js} +1 -1
- package/bizar-dash/dist/assets/{LightRAGView-BqDrJbRq.js.map → LightRAGView-PubmA9oL.js.map} +1 -1
- package/bizar-dash/dist/assets/{ListHeader-TjPJrUfY.js → ListHeader-Ak9WJd39.js} +1 -1
- package/bizar-dash/dist/assets/{ListHeader-TjPJrUfY.js.map → ListHeader-Ak9WJd39.js.map} +1 -1
- package/bizar-dash/dist/assets/{MemoryView-BlTeHLhX.js → MemoryView-DBGpuETw.js} +1 -1
- package/bizar-dash/dist/assets/{MemoryView-BlTeHLhX.js.map → MemoryView-DBGpuETw.js.map} +1 -1
- package/bizar-dash/dist/assets/MiscView-DufqBHkX.js +1 -0
- package/bizar-dash/dist/assets/{MiscView-B8QY82Pm.js.map → MiscView-DufqBHkX.js.map} +1 -1
- package/bizar-dash/dist/assets/{ModsView-CvW1OWts.js → ModsView-DcX6eVU2.js} +2 -2
- package/bizar-dash/dist/assets/{ModsView-CvW1OWts.js.map → ModsView-DcX6eVU2.js.map} +1 -1
- package/bizar-dash/dist/assets/{NotificationsView-CsBt5U7x.js → NotificationsView-B8IhHRQC.js} +1 -1
- package/bizar-dash/dist/assets/{NotificationsView-CsBt5U7x.js.map → NotificationsView-B8IhHRQC.js.map} +1 -1
- package/bizar-dash/dist/assets/ObsidianView-DEuk65zS.js +1 -0
- package/bizar-dash/dist/assets/{ObsidianView-BCTejEj2.js.map → ObsidianView-DEuk65zS.js.map} +1 -1
- package/bizar-dash/dist/assets/{OverviewView-BsuPxwnB.js → OverviewView-CTI2spTd.js} +1 -1
- package/bizar-dash/dist/assets/{OverviewView-BsuPxwnB.js.map → OverviewView-CTI2spTd.js.map} +1 -1
- package/bizar-dash/dist/assets/{ProgressBar-DlZTETEn.js → ProgressBar-D3lkz42E.js} +1 -1
- package/bizar-dash/dist/assets/{ProgressBar-DlZTETEn.js.map → ProgressBar-D3lkz42E.js.map} +1 -1
- package/bizar-dash/dist/assets/{ProjectsView-Soxtmpet.js → ProjectsView-Du0LzE7A.js} +1 -1
- package/bizar-dash/dist/assets/{ProjectsView-Soxtmpet.js.map → ProjectsView-Du0LzE7A.js.map} +1 -1
- package/bizar-dash/dist/assets/{ProvidersView-DaCEV9pM.js → ProvidersView-5bCgXwBo.js} +1 -1
- package/bizar-dash/dist/assets/{ProvidersView-DaCEV9pM.js.map → ProvidersView-5bCgXwBo.js.map} +1 -1
- package/bizar-dash/dist/assets/{SchedulesView-DY2ByBj_.js → SchedulesView-DFHdIz5P.js} +1 -1
- package/bizar-dash/dist/assets/{SchedulesView-DY2ByBj_.js.map → SchedulesView-DFHdIz5P.js.map} +1 -1
- package/bizar-dash/dist/assets/{Select-Dbd88_n8.js → Select-C8EJj4NM.js} +1 -1
- package/bizar-dash/dist/assets/{Select-Dbd88_n8.js.map → Select-C8EJj4NM.js.map} +1 -1
- package/bizar-dash/dist/assets/{SettingsRow-CLMNd434.js → SettingsRow-BdOOmYMD.js} +1 -1
- package/bizar-dash/dist/assets/{SettingsRow-CLMNd434.js.map → SettingsRow-BdOOmYMD.js.map} +1 -1
- package/bizar-dash/dist/assets/{SettingsView-b24oFq9x.js → SettingsView-CqwQ6Knv.js} +1 -1
- package/bizar-dash/dist/assets/{SettingsView-b24oFq9x.js.map → SettingsView-CqwQ6Knv.js.map} +1 -1
- package/bizar-dash/dist/assets/{Sheet-DnDid58j.js → Sheet-Cztsvgse.js} +1 -1
- package/bizar-dash/dist/assets/{Sheet-DnDid58j.js.map → Sheet-Cztsvgse.js.map} +1 -1
- package/bizar-dash/dist/assets/{Slider-BJCvKI1I.js → Slider-BexNfF6K.js} +1 -1
- package/bizar-dash/dist/assets/{Slider-BJCvKI1I.js.map → Slider-BexNfF6K.js.map} +1 -1
- package/bizar-dash/dist/assets/{Sparkline-ktqQLdyb.js → Sparkline-CLZBtvsN.js} +1 -1
- package/bizar-dash/dist/assets/{Sparkline-ktqQLdyb.js.map → Sparkline-CLZBtvsN.js.map} +1 -1
- package/bizar-dash/dist/assets/{Switch-CwXfsDii.js → Switch-B1tiRwNK.js} +1 -1
- package/bizar-dash/dist/assets/{Switch-CwXfsDii.js.map → Switch-B1tiRwNK.js.map} +1 -1
- package/bizar-dash/dist/assets/{TasksView-BHmpzFCf.js → TasksView-CT0fPlos.js} +1 -1
- package/bizar-dash/dist/assets/{TasksView-BHmpzFCf.js.map → TasksView-CT0fPlos.js.map} +1 -1
- package/bizar-dash/dist/assets/{Textarea-BzIAwSlp.js → Textarea-D_TPpVMy.js} +1 -1
- package/bizar-dash/dist/assets/{Textarea-BzIAwSlp.js.map → Textarea-D_TPpVMy.js.map} +1 -1
- package/bizar-dash/dist/assets/{UpdateView-CSmX9NN0.js → UpdateView-tD2q2OhW.js} +1 -1
- package/bizar-dash/dist/assets/{UpdateView-CSmX9NN0.js.map → UpdateView-tD2q2OhW.js.map} +1 -1
- package/bizar-dash/dist/assets/{UsageView-COyr_Gfi.js → UsageView-B4BuAFKA.js} +1 -1
- package/bizar-dash/dist/assets/{UsageView-COyr_Gfi.js.map → UsageView-B4BuAFKA.js.map} +1 -1
- package/bizar-dash/dist/assets/{ViewHeader-BzYZ3m-e.js → ViewHeader-V_SzbUpw.js} +1 -1
- package/bizar-dash/dist/assets/{ViewHeader-BzYZ3m-e.js.map → ViewHeader-V_SzbUpw.js.map} +1 -1
- package/bizar-dash/dist/assets/{VoiceView-D33_xG5t.js → VoiceView-D3vb5OM2.js} +1 -1
- package/bizar-dash/dist/assets/{VoiceView-D33_xG5t.js.map → VoiceView-D3vb5OM2.js.map} +1 -1
- package/bizar-dash/dist/assets/{brain-CM-Vls8r.js → brain-BmfWcitQ.js} +1 -1
- package/bizar-dash/dist/assets/{brain-CM-Vls8r.js.map → brain-BmfWcitQ.js.map} +1 -1
- package/bizar-dash/dist/assets/{calendar-C0lgZk69.js → calendar-VntJUzFB.js} +1 -1
- package/bizar-dash/dist/assets/{calendar-C0lgZk69.js.map → calendar-VntJUzFB.js.map} +1 -1
- package/bizar-dash/dist/assets/{circle-Bx_hQEBC.js → circle-KrQQySeK.js} +1 -1
- package/bizar-dash/dist/assets/{circle-Bx_hQEBC.js.map → circle-KrQQySeK.js.map} +1 -1
- package/bizar-dash/dist/assets/{circle-alert-DHvsqpw5.js → circle-alert-CQ9MLygI.js} +1 -1
- package/bizar-dash/dist/assets/{circle-alert-DHvsqpw5.js.map → circle-alert-CQ9MLygI.js.map} +1 -1
- package/bizar-dash/dist/assets/{circle-check-Bx8wmXR9.js → circle-check-COtVgsWw.js} +1 -1
- package/bizar-dash/dist/assets/{circle-check-Bx8wmXR9.js.map → circle-check-COtVgsWw.js.map} +1 -1
- package/bizar-dash/dist/assets/{clock-CejGoecX.js → clock-C0GlM4qp.js} +1 -1
- package/bizar-dash/dist/assets/{clock-CejGoecX.js.map → clock-C0GlM4qp.js.map} +1 -1
- package/bizar-dash/dist/assets/{download-4cEUWue1.js → download-CW4DmtRe.js} +1 -1
- package/bizar-dash/dist/assets/{download-4cEUWue1.js.map → download-CW4DmtRe.js.map} +1 -1
- package/bizar-dash/dist/assets/{external-link-0NqUhI4i.js → external-link-CAOnh_DH.js} +1 -1
- package/bizar-dash/dist/assets/{external-link-0NqUhI4i.js.map → external-link-CAOnh_DH.js.map} +1 -1
- package/bizar-dash/dist/assets/{file-text-yEMkrSoZ.js → file-text-CS1jB_oV.js} +1 -1
- package/bizar-dash/dist/assets/{file-text-yEMkrSoZ.js.map → file-text-CS1jB_oV.js.map} +1 -1
- package/bizar-dash/dist/assets/{filter-BXt60zAS.js → filter-DyQbml3L.js} +1 -1
- package/bizar-dash/dist/assets/{filter-BXt60zAS.js.map → filter-DyQbml3L.js.map} +1 -1
- package/bizar-dash/dist/assets/{git-pull-request-BjG5lNtR.js → git-pull-request-Co6gYfba.js} +1 -1
- package/bizar-dash/dist/assets/{git-pull-request-BjG5lNtR.js.map → git-pull-request-Co6gYfba.js.map} +1 -1
- package/bizar-dash/dist/assets/{main-CokHAXWM.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-CAmulU0F.js → pause-CKYjSTGX.js} +1 -1
- package/bizar-dash/dist/assets/{pause-CAmulU0F.js.map → pause-CKYjSTGX.js.map} +1 -1
- package/bizar-dash/dist/assets/{pencil-eq5IqBmU.js → pencil-BUJ2DpWs.js} +1 -1
- package/bizar-dash/dist/assets/{pencil-eq5IqBmU.js.map → pencil-BUJ2DpWs.js.map} +1 -1
- package/bizar-dash/dist/assets/{play-BoLrrcBA.js → play-CGxZWqtW.js} +1 -1
- package/bizar-dash/dist/assets/{play-BoLrrcBA.js.map → play-CGxZWqtW.js.map} +1 -1
- package/bizar-dash/dist/assets/{plug-DBrVM9lK.js → plug-CQYGgGSy.js} +1 -1
- package/bizar-dash/dist/assets/{plug-DBrVM9lK.js.map → plug-CQYGgGSy.js.map} +1 -1
- package/bizar-dash/dist/assets/{plus-DENm0hqV.js → plus-fFxt-8z5.js} +1 -1
- package/bizar-dash/dist/assets/{plus-DENm0hqV.js.map → plus-fFxt-8z5.js.map} +1 -1
- package/bizar-dash/dist/assets/{power-DLjXF7zE.js → power-Cjgy2KsI.js} +1 -1
- package/bizar-dash/dist/assets/{power-DLjXF7zE.js.map → power-Cjgy2KsI.js.map} +1 -1
- package/bizar-dash/dist/assets/{power-off-D47cOnG_.js → power-off-B81hLRah.js} +1 -1
- package/bizar-dash/dist/assets/{power-off-D47cOnG_.js.map → power-off-B81hLRah.js.map} +1 -1
- package/bizar-dash/dist/assets/{refresh-ccw-DNrXX9gB.js → refresh-ccw-BCXx86xp.js} +1 -1
- package/bizar-dash/dist/assets/{refresh-ccw-DNrXX9gB.js.map → refresh-ccw-BCXx86xp.js.map} +1 -1
- package/bizar-dash/dist/assets/{refresh-cw-CoAHDpeS.js → refresh-cw-COTg1nZo.js} +1 -1
- package/bizar-dash/dist/assets/{refresh-cw-CoAHDpeS.js.map → refresh-cw-COTg1nZo.js.map} +1 -1
- package/bizar-dash/dist/assets/{rotate-ccw-ObX5awaG.js → rotate-ccw-BZrEVc0Z.js} +1 -1
- package/bizar-dash/dist/assets/{rotate-ccw-ObX5awaG.js.map → rotate-ccw-BZrEVc0Z.js.map} +1 -1
- package/bizar-dash/dist/assets/{rotate-cw-xpPJw9LP.js → rotate-cw-CsTtaLV2.js} +1 -1
- package/bizar-dash/dist/assets/{rotate-cw-xpPJw9LP.js.map → rotate-cw-CsTtaLV2.js.map} +1 -1
- package/bizar-dash/dist/assets/{save-D1iKiJJl.js → save-DtMTge1F.js} +1 -1
- package/bizar-dash/dist/assets/{save-D1iKiJJl.js.map → save-DtMTge1F.js.map} +1 -1
- package/bizar-dash/dist/assets/{send-BRcAhZUm.js → send-DyBhyk8t.js} +1 -1
- package/bizar-dash/dist/assets/{send-BRcAhZUm.js.map → send-DyBhyk8t.js.map} +1 -1
- package/bizar-dash/dist/assets/{shield-alert-C6vEcK-g.js → shield-alert-BPGJlRF8.js} +1 -1
- package/bizar-dash/dist/assets/{shield-alert-C6vEcK-g.js.map → shield-alert-BPGJlRF8.js.map} +1 -1
- package/bizar-dash/dist/assets/{trash-2-DZDjEnNh.js → trash-2-2NsyTU8c.js} +1 -1
- package/bizar-dash/dist/assets/{trash-2-DZDjEnNh.js.map → trash-2-2NsyTU8c.js.map} +1 -1
- package/bizar-dash/dist/assets/{trending-up-DMj-AJVf.js → trending-up-BXwP23VK.js} +1 -1
- package/bizar-dash/dist/assets/{trending-up-DMj-AJVf.js.map → trending-up-BXwP23VK.js.map} +1 -1
- package/bizar-dash/dist/assets/{triangle-alert-_2xe_tl4.js → triangle-alert-DFmQ_RXN.js} +1 -1
- package/bizar-dash/dist/assets/{triangle-alert-_2xe_tl4.js.map → triangle-alert-DFmQ_RXN.js.map} +1 -1
- package/bizar-dash/dist/assets/{upload-DdDljAeV.js → upload-Cj-HYRYV.js} +1 -1
- package/bizar-dash/dist/assets/{upload-DdDljAeV.js.map → upload-Cj-HYRYV.js.map} +1 -1
- package/bizar-dash/dist/assets/{zap-mHsgh0FP.js → zap-BaOiybcl.js} +1 -1
- package/bizar-dash/dist/assets/{zap-mHsgh0FP.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/routes/update.mjs +48 -21
- 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/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/package.json +1 -1
- package/scripts/install-curl.sh +17 -0
- package/bizar-dash/dist/assets/HeadroomView-D1UiTYbt.js +0 -11
- package/bizar-dash/dist/assets/HeadroomView-D1UiTYbt.js.map +0 -1
- package/bizar-dash/dist/assets/MiscView-B8QY82Pm.js +0 -1
- package/bizar-dash/dist/assets/ObsidianView-BCTejEj2.js +0 -1
- package/bizar-dash/dist/assets/main-CokHAXWM.js.map +0 -1
- package/bizar-dash/skills/headroom/SKILL.md +0 -94
- package/bizar-dash/src/server/headroom.mjs +0 -649
- package/bizar-dash/src/server/routes/headroom.mjs +0 -126
- 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/headroom-install.test.mjs +0 -173
- package/bizar-dash/tests/headroom-settings.test.mjs +0 -126
- package/bizar-dash/tests/headroom-status.test.mjs +0 -117
- 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/commands/headroom.mjs +0 -204
- package/cli/provision-claude.mjs +0 -1176
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* tests/headroom-settings.test.mjs
|
|
3
|
-
*
|
|
4
|
-
* Tests for DEFAULT_SETTINGS.headroom and mergeSettings() headroom merging.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { test, describe } from 'node:test';
|
|
8
|
-
import assert from 'node:assert/strict';
|
|
9
|
-
|
|
10
|
-
// We need to import the actual module — we test the _shared.mjs exports
|
|
11
|
-
|
|
12
|
-
test('DEFAULT_SETTINGS has headroom key', async () => {
|
|
13
|
-
const { DEFAULT_SETTINGS } = await import('../src/server/routes/_shared.mjs');
|
|
14
|
-
assert.ok('headroom' in DEFAULT_SETTINGS, 'headroom key exists in DEFAULT_SETTINGS');
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
test('DEFAULT_SETTINGS.headroom has all required fields', async () => {
|
|
18
|
-
const { DEFAULT_SETTINGS } = await import('../src/server/routes/_shared.mjs');
|
|
19
|
-
const h = DEFAULT_SETTINGS.headroom;
|
|
20
|
-
|
|
21
|
-
assert.strictEqual(typeof h.enabled, 'boolean', 'enabled is boolean');
|
|
22
|
-
assert.strictEqual(typeof h.autoInstall, 'boolean', 'autoInstall is boolean');
|
|
23
|
-
assert.strictEqual(typeof h.port, 'number', 'port is number');
|
|
24
|
-
assert.strictEqual(typeof h.host, 'string', 'host is string');
|
|
25
|
-
assert.strictEqual(typeof h.outputShaper, 'boolean', 'outputShaper is boolean');
|
|
26
|
-
assert.strictEqual(typeof h.telemetry, 'boolean', 'telemetry is boolean');
|
|
27
|
-
assert.strictEqual(typeof h.budget, 'number', 'budget is number');
|
|
28
|
-
assert.strictEqual(typeof h.backend, 'string', 'backend is string');
|
|
29
|
-
assert.strictEqual(typeof h.autoStart, 'boolean', 'autoStart is boolean');
|
|
30
|
-
assert.strictEqual(typeof h.autoWrap, 'boolean', 'autoWrap is boolean');
|
|
31
|
-
assert.strictEqual(typeof h.routeAllProviders, 'boolean', 'routeAllProviders is boolean');
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
test('DEFAULT_SETTINGS.headroom has correct defaults', async () => {
|
|
35
|
-
const { DEFAULT_SETTINGS } = await import('../src/server/routes/_shared.mjs');
|
|
36
|
-
const h = DEFAULT_SETTINGS.headroom;
|
|
37
|
-
|
|
38
|
-
assert.strictEqual(h.enabled, true, 'enabled defaults to true');
|
|
39
|
-
assert.strictEqual(h.autoInstall, true, 'autoInstall defaults to true');
|
|
40
|
-
assert.strictEqual(h.port, 8787, 'port defaults to 8787');
|
|
41
|
-
assert.strictEqual(h.host, '127.0.0.1', 'host defaults to 127.0.0.1');
|
|
42
|
-
assert.strictEqual(h.outputShaper, false, 'outputShaper defaults to false');
|
|
43
|
-
assert.strictEqual(h.telemetry, false, 'telemetry defaults to false');
|
|
44
|
-
assert.strictEqual(h.budget, 0, 'budget defaults to 0 (unlimited)');
|
|
45
|
-
assert.strictEqual(h.backend, 'anthropic', 'backend defaults to anthropic');
|
|
46
|
-
assert.strictEqual(h.autoStart, true, 'autoStart defaults to true');
|
|
47
|
-
assert.strictEqual(h.autoWrap, true, 'autoWrap defaults to true');
|
|
48
|
-
assert.strictEqual(h.routeAllProviders, true, 'routeAllProviders defaults to true');
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
test('mergeSettings preserves headroom sub-object', async () => {
|
|
52
|
-
const { mergeSettings, DEFAULT_SETTINGS } = await import('../src/server/routes/_shared.mjs');
|
|
53
|
-
|
|
54
|
-
const existing = {
|
|
55
|
-
theme: { mode: 'light' },
|
|
56
|
-
headroom: { port: 9999, enabled: false },
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
const merged = mergeSettings(existing);
|
|
60
|
-
|
|
61
|
-
assert.ok('headroom' in merged, 'headroom preserved in merged');
|
|
62
|
-
assert.strictEqual(merged.headroom.port, 9999, 'headroom.port is overridden');
|
|
63
|
-
assert.strictEqual(merged.headroom.enabled, false, 'headroom.enabled is overridden');
|
|
64
|
-
// Non-overridden fields should fall back to defaults
|
|
65
|
-
assert.strictEqual(merged.headroom.host, DEFAULT_SETTINGS.headroom.host, 'headroom.host falls back to default');
|
|
66
|
-
assert.strictEqual(merged.headroom.autoInstall, DEFAULT_SETTINGS.headroom.autoInstall, 'headroom.autoInstall falls back to default');
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
test('mergeSettings handles missing headroom in existing', async () => {
|
|
70
|
-
const { mergeSettings, DEFAULT_SETTINGS } = await import('../src/server/routes/_shared.mjs');
|
|
71
|
-
|
|
72
|
-
const existing = { theme: { mode: 'light' } };
|
|
73
|
-
const merged = mergeSettings(existing);
|
|
74
|
-
|
|
75
|
-
assert.ok('headroom' in merged, 'headroom present in merged');
|
|
76
|
-
assert.deepStrictEqual(merged.headroom, DEFAULT_SETTINGS.headroom, 'headroom matches default when not in existing');
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
test('mergeSettings handles null existing', async () => {
|
|
80
|
-
const { mergeSettings, DEFAULT_SETTINGS } = await import('../src/server/routes/_shared.mjs');
|
|
81
|
-
|
|
82
|
-
const merged = mergeSettings(null);
|
|
83
|
-
|
|
84
|
-
assert.ok('headroom' in merged, 'headroom present in merged');
|
|
85
|
-
assert.deepStrictEqual(merged.headroom, DEFAULT_SETTINGS.headroom, 'headroom matches default when existing is null');
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
test('mergeSettings handles undefined existing', async () => {
|
|
89
|
-
const { mergeSettings, DEFAULT_SETTINGS } = await import('../src/server/routes/_shared.mjs');
|
|
90
|
-
|
|
91
|
-
const merged = mergeSettings(undefined);
|
|
92
|
-
|
|
93
|
-
assert.ok('headroom' in merged, 'headroom present in merged');
|
|
94
|
-
assert.deepStrictEqual(merged.headroom, DEFAULT_SETTINGS.headroom, 'headroom matches default when existing is undefined');
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// ── HeadroomSettings TypeScript type validation ────────────────────────────────
|
|
98
|
-
// This is validated by `tsc --noEmit`. We just verify the JS shape here.
|
|
99
|
-
|
|
100
|
-
test('HeadroomSettings type shape matches DEFAULT_SETTINGS.headroom', async () => {
|
|
101
|
-
const { DEFAULT_SETTINGS } = await import('../src/server/routes/_shared.mjs');
|
|
102
|
-
const h = DEFAULT_SETTINGS.headroom;
|
|
103
|
-
|
|
104
|
-
// All fields must be present
|
|
105
|
-
const requiredKeys = [
|
|
106
|
-
'enabled', 'autoInstall', 'port', 'host', 'outputShaper',
|
|
107
|
-
'telemetry', 'budget', 'backend', 'autoStart', 'autoWrap', 'routeAllProviders',
|
|
108
|
-
];
|
|
109
|
-
|
|
110
|
-
for (const key of requiredKeys) {
|
|
111
|
-
assert.ok(key in h, `headroom.${key} is present`);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// All values must be the correct type
|
|
115
|
-
assert.strictEqual(typeof h.enabled, 'boolean');
|
|
116
|
-
assert.strictEqual(typeof h.autoInstall, 'boolean');
|
|
117
|
-
assert.strictEqual(typeof h.port, 'number');
|
|
118
|
-
assert.strictEqual(typeof h.host, 'string');
|
|
119
|
-
assert.strictEqual(typeof h.outputShaper, 'boolean');
|
|
120
|
-
assert.strictEqual(typeof h.telemetry, 'boolean');
|
|
121
|
-
assert.strictEqual(typeof h.budget, 'number');
|
|
122
|
-
assert.strictEqual(typeof h.backend, 'string');
|
|
123
|
-
assert.strictEqual(typeof h.autoStart, 'boolean');
|
|
124
|
-
assert.strictEqual(typeof h.autoWrap, 'boolean');
|
|
125
|
-
assert.strictEqual(typeof h.routeAllProviders, 'boolean');
|
|
126
|
-
});
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* tests/headroom-status.test.mjs
|
|
3
|
-
*
|
|
4
|
-
* Tests for getHeadroomStatus() and the /api/headroom/status endpoint.
|
|
5
|
-
* Uses real headroom binary when available, mocked when not.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { test, describe, beforeEach, afterEach, mock } from 'node:test';
|
|
9
|
-
import assert from 'node:assert/strict';
|
|
10
|
-
import { tmpdir } from 'node:os';
|
|
11
|
-
import { join } from 'node:path';
|
|
12
|
-
import { mkdirSync, rmSync, writeFileSync, existsSync, unlinkSync } from 'node:fs';
|
|
13
|
-
import http from 'node:http';
|
|
14
|
-
|
|
15
|
-
const HEADROOM_PORT_FILE = join(tmpdir(), `headroom-port-test-${Date.now()}.txt`);
|
|
16
|
-
|
|
17
|
-
// ── HTTP helpers ─────────────────────────────────────────────────────────────
|
|
18
|
-
|
|
19
|
-
const httpGet = (port, path) =>
|
|
20
|
-
new Promise((resolve, reject) => {
|
|
21
|
-
const req = http.request({ hostname: 'localhost', port, path, method: 'GET' }, (res) => {
|
|
22
|
-
let body = '';
|
|
23
|
-
res.on('data', (c) => (body += c));
|
|
24
|
-
res.on('end', () => {
|
|
25
|
-
try { resolve({ status: res.statusCode, body: JSON.parse(body || '{}') }); }
|
|
26
|
-
catch { resolve({ status: res.statusCode, body }); }
|
|
27
|
-
});
|
|
28
|
-
});
|
|
29
|
-
req.on('error', reject);
|
|
30
|
-
req.end();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
// ── Status shape validation ──────────────────────────────────────────────────
|
|
34
|
-
|
|
35
|
-
test('getHeadroomStatus returns correct shape', async () => {
|
|
36
|
-
const { getHeadroomStatus } = await import('../src/server/headroom.mjs');
|
|
37
|
-
const status = await getHeadroomStatus();
|
|
38
|
-
|
|
39
|
-
assert.ok(typeof status.installed === 'boolean', 'installed is boolean');
|
|
40
|
-
assert.ok(typeof status.version === 'string' || status.version === null, 'version is string|null');
|
|
41
|
-
assert.ok(typeof status.proxyRunning === 'boolean', 'proxyRunning is boolean');
|
|
42
|
-
assert.ok(typeof status.proxyPort === 'number' || status.proxyPort === null, 'proxyPort is number|null');
|
|
43
|
-
assert.ok(typeof status.proxyPid === 'number' || status.proxyPid === null, 'proxyPid is number|null');
|
|
44
|
-
assert.ok(typeof status.wrapped === 'boolean', 'wrapped is boolean');
|
|
45
|
-
assert.ok(typeof status.configPath === 'string' || status.configPath === null, 'configPath is string|null');
|
|
46
|
-
assert.ok(['ok', 'warn', 'fail'].includes(status.healthy), 'healthy is ok|warn|fail');
|
|
47
|
-
assert.ok(Array.isArray(status.messages), 'messages is array');
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test('getHeadroomStatus.healthy is warn when not installed', async () => {
|
|
51
|
-
const { getHeadroomStatus } = await import('../src/server/headroom.mjs');
|
|
52
|
-
const status = await getHeadroomStatus();
|
|
53
|
-
|
|
54
|
-
if (!status.installed) {
|
|
55
|
-
assert.strictEqual(status.healthy, 'warn', 'healthy should be warn when not installed');
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test('getHeadroomStatus.healthy is warn when not wrapped but installed', async () => {
|
|
60
|
-
const { getHeadroomStatus } = await import('../src/server/headroom.mjs');
|
|
61
|
-
const status = await getHeadroomStatus();
|
|
62
|
-
|
|
63
|
-
if (status.installed && !status.wrapped) {
|
|
64
|
-
assert.strictEqual(status.healthy, 'warn', 'healthy should be warn when not wrapped but installed');
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
// ── /api/headroom/status endpoint ───────────────────────────────────────────
|
|
69
|
-
|
|
70
|
-
async function buildApp() {
|
|
71
|
-
const { createHeadroomRouter } = await import('../src/server/routes/headroom.mjs');
|
|
72
|
-
const express = (await import('express')).default;
|
|
73
|
-
const app = express();
|
|
74
|
-
app.use(express.json());
|
|
75
|
-
// Mount at /api/headroom so routes like /status become /api/headroom/status
|
|
76
|
-
app.use('/api/headroom', await createHeadroomRouter());
|
|
77
|
-
return app;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
test('GET /api/headroom/status returns status object', async () => {
|
|
81
|
-
const app = await buildApp();
|
|
82
|
-
const server = app.listen(0);
|
|
83
|
-
const { port } = server.address();
|
|
84
|
-
|
|
85
|
-
try {
|
|
86
|
-
const res = await httpGet(port, '/api/headroom/status');
|
|
87
|
-
assert.strictEqual(res.status, 200, 'status should be 200');
|
|
88
|
-
assert.ok(typeof res.body.installed === 'boolean', 'installed is boolean');
|
|
89
|
-
assert.ok(['ok', 'warn', 'fail'].includes(res.body.healthy), 'healthy is ok|warn|fail');
|
|
90
|
-
assert.ok(Array.isArray(res.body.messages), 'messages is array');
|
|
91
|
-
} finally {
|
|
92
|
-
server.close();
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
test('GET /api/headroom/status includes version when installed', async () => {
|
|
97
|
-
const { getHeadroomStatus } = await import('../src/server/headroom.mjs');
|
|
98
|
-
const status = await getHeadroomStatus();
|
|
99
|
-
|
|
100
|
-
if (status.installed && status.version) {
|
|
101
|
-
assert.ok(status.version.length > 0, 'version is non-empty string');
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
test('GET /api/headroom/status includes proxyPort from cached file', async () => {
|
|
106
|
-
// Write a fake port file
|
|
107
|
-
writeFileSync(HEADROOM_PORT_FILE, '9999', 'utf8');
|
|
108
|
-
|
|
109
|
-
// Override the module's port file path temporarily by mocking the fs access
|
|
110
|
-
const { getHeadroomStatus } = await import('../src/server/headroom.mjs');
|
|
111
|
-
// Note: in a real scenario you'd override via the import.meta trick or env var
|
|
112
|
-
// Here we just verify the status structure is correct
|
|
113
|
-
const status = await getHeadroomStatus();
|
|
114
|
-
assert.ok(typeof status.proxyPort === 'number' || status.proxyPort === null, 'proxyPort is number|null');
|
|
115
|
-
|
|
116
|
-
try { unlinkSync(HEADROOM_PORT_FILE); } catch { /* ignore */ }
|
|
117
|
-
});
|
|
@@ -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
|
-
});
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* tests/mobile-settings.test.tsx
|
|
3
|
-
*
|
|
4
|
-
* v5.4 — Component tests for the mobile settings accordion view.
|
|
5
|
-
*/
|
|
6
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
7
|
-
import { render, screen } from '@testing-library/react';
|
|
8
|
-
import userEvent from '@testing-library/user-event';
|
|
9
|
-
import React from 'react';
|
|
10
|
-
import { MobileSettings } from '../src/web/mobile/MobileSettings';
|
|
11
|
-
import type { Settings, Snapshot } from '../src/web/lib/types';
|
|
12
|
-
|
|
13
|
-
// ─── Mock sub-components that make API calls ────────────────────────────────
|
|
14
|
-
|
|
15
|
-
vi.mock('../src/web/views/settings/GeneralSection', () => ({
|
|
16
|
-
GeneralSection: () => <div data-testid="general-section">GeneralSection</div>,
|
|
17
|
-
}));
|
|
18
|
-
|
|
19
|
-
vi.mock('../src/web/views/settings/EnvVarsSection', () => ({
|
|
20
|
-
EnvVarsSection: () => <div data-testid="env-vars-section">EnvVarsSection</div>,
|
|
21
|
-
}));
|
|
22
|
-
|
|
23
|
-
vi.mock('../src/web/views/settings/MemorySection', () => ({
|
|
24
|
-
MemorySection: () => <div data-testid="memory-section">MemorySection</div>,
|
|
25
|
-
}));
|
|
26
|
-
|
|
27
|
-
vi.mock('../src/web/views/settings/SystemLlmSection', () => ({
|
|
28
|
-
SystemLlmSection: () => <div data-testid="system-llm-section">SystemLlmSection</div>,
|
|
29
|
-
}));
|
|
30
|
-
|
|
31
|
-
vi.mock('../src/web/views/settings/UpdatesSection', () => ({
|
|
32
|
-
UpdatesSection: () => <div data-testid="updates-section">UpdatesSection</div>,
|
|
33
|
-
}));
|
|
34
|
-
|
|
35
|
-
vi.mock('../src/web/views/settings/HeadroomSection', () => ({
|
|
36
|
-
HeadroomSection: () => <div data-testid="headroom-section">HeadroomSection</div>,
|
|
37
|
-
}));
|
|
38
|
-
|
|
39
|
-
vi.mock('../src/web/components/TailscaleSettings', () => ({
|
|
40
|
-
TailscaleSettings: () => <div data-testid="tailscale-section">TailscaleSettings</div>,
|
|
41
|
-
}));
|
|
42
|
-
|
|
43
|
-
vi.mock('../src/web/views/settings/ActivitySection', () => ({
|
|
44
|
-
ActivitySection: () => <div data-testid="about-section">AboutSection</div>,
|
|
45
|
-
}));
|
|
46
|
-
|
|
47
|
-
// ─── Test data ─────────────────────────────────────────────────────────────
|
|
48
|
-
|
|
49
|
-
const DEFAULT_SETTINGS: Settings = {
|
|
50
|
-
theme: {
|
|
51
|
-
mode: 'dark',
|
|
52
|
-
accent: '#8b5cf6',
|
|
53
|
-
success: '#3fb950',
|
|
54
|
-
warning: '#f0883e',
|
|
55
|
-
error: '#f85149',
|
|
56
|
-
info: '#58a6ff',
|
|
57
|
-
fontFamily: 'sans-serif',
|
|
58
|
-
fontSize: 14,
|
|
59
|
-
compactMode: false,
|
|
60
|
-
animations: true,
|
|
61
|
-
},
|
|
62
|
-
ui: {
|
|
63
|
-
layout: 'topnav',
|
|
64
|
-
showHeader: true,
|
|
65
|
-
showStatusBar: true,
|
|
66
|
-
defaultTab: 'overview',
|
|
67
|
-
},
|
|
68
|
-
defaultAgent: 'odin',
|
|
69
|
-
defaultModel: '',
|
|
70
|
-
notifications: { onAgentComplete: false, onPlanApproval: false },
|
|
71
|
-
dashboard: { autoLaunchWeb: false },
|
|
72
|
-
service: { enabled: false, autostart: false },
|
|
73
|
-
about: { version: '5.4.0', homepage: 'https://github.com/DrB0rk/BizarHarness', license: 'MIT' },
|
|
74
|
-
agents: { maxParallel: 6, stuckThresholdMs: 600000, autoRestart: false },
|
|
75
|
-
personalization: { displayName: '', role: '', team: '', aboutMe: '', preferences: '' },
|
|
76
|
-
workflow: { artifactsEnabled: true, agentsDecideAutonomously: false, chatAutonomous: false },
|
|
77
|
-
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 },
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
const SNAPSHOT_WITH_PROVIDERS: Snapshot = {
|
|
81
|
-
agents: [],
|
|
82
|
-
artifacts: [],
|
|
83
|
-
projects: [],
|
|
84
|
-
activeProject: null,
|
|
85
|
-
config: { path: '', data: null, raw: '', exists: false },
|
|
86
|
-
settings: { path: '', data: DEFAULT_SETTINGS, exists: true },
|
|
87
|
-
tasks: [],
|
|
88
|
-
mods: [],
|
|
89
|
-
schedules: [],
|
|
90
|
-
providers: [
|
|
91
|
-
{ id: 'p1', name: 'OpenAI', enabled: true, kind: 'openai', models: [] },
|
|
92
|
-
{ id: 'p2', name: 'Anthropic', enabled: true, kind: 'anthropic', models: [] },
|
|
93
|
-
],
|
|
94
|
-
mcps: [],
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
// ─── Tests ───────────────────────────────────────────────────────────────────
|
|
98
|
-
|
|
99
|
-
describe('MobileSettings', () => {
|
|
100
|
-
const onSettingsChange = vi.fn();
|
|
101
|
-
const autoSave = vi.fn();
|
|
102
|
-
|
|
103
|
-
beforeEach(() => {
|
|
104
|
-
vi.clearAllMocks();
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('renders all 9 settings sections', () => {
|
|
108
|
-
render(
|
|
109
|
-
<MobileSettings
|
|
110
|
-
settings={DEFAULT_SETTINGS}
|
|
111
|
-
onSettingsChange={onSettingsChange}
|
|
112
|
-
autoSave={autoSave}
|
|
113
|
-
snapshot={SNAPSHOT_WITH_PROVIDERS}
|
|
114
|
-
/>,
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
expect(screen.getByText('General')).toBeInTheDocument();
|
|
118
|
-
expect(screen.getByText('AI Providers')).toBeInTheDocument();
|
|
119
|
-
expect(screen.getByText('Environment Variables')).toBeInTheDocument();
|
|
120
|
-
expect(screen.getByText('Memory Vault')).toBeInTheDocument();
|
|
121
|
-
expect(screen.getByText('System LLM')).toBeInTheDocument();
|
|
122
|
-
expect(screen.getByText('Updates')).toBeInTheDocument();
|
|
123
|
-
expect(screen.getByText('Headroom')).toBeInTheDocument();
|
|
124
|
-
expect(screen.getByText('Tailscale')).toBeInTheDocument();
|
|
125
|
-
expect(screen.getByText('About')).toBeInTheDocument();
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it('filters sections by search query', async () => {
|
|
129
|
-
const user = userEvent.setup();
|
|
130
|
-
render(
|
|
131
|
-
<MobileSettings
|
|
132
|
-
settings={DEFAULT_SETTINGS}
|
|
133
|
-
onSettingsChange={onSettingsChange}
|
|
134
|
-
autoSave={autoSave}
|
|
135
|
-
/>,
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
const searchInput = screen.getByRole('textbox', { name: /search settings/i });
|
|
139
|
-
await user.type(searchInput, 'provider');
|
|
140
|
-
|
|
141
|
-
// Only "AI Providers" matches
|
|
142
|
-
expect(screen.getByText('AI Providers')).toBeInTheDocument();
|
|
143
|
-
expect(screen.queryByText('General')).not.toBeInTheDocument();
|
|
144
|
-
expect(screen.queryByText('Memory Vault')).not.toBeInTheDocument();
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it('expands/collapses section on click', async () => {
|
|
148
|
-
const user = userEvent.setup();
|
|
149
|
-
render(
|
|
150
|
-
<MobileSettings
|
|
151
|
-
settings={DEFAULT_SETTINGS}
|
|
152
|
-
onSettingsChange={onSettingsChange}
|
|
153
|
-
autoSave={autoSave}
|
|
154
|
-
/>,
|
|
155
|
-
);
|
|
156
|
-
|
|
157
|
-
// Initially nothing is expanded
|
|
158
|
-
expect(screen.queryByTestId('general-section')).not.toBeInTheDocument();
|
|
159
|
-
|
|
160
|
-
// Click General — expands it
|
|
161
|
-
await user.click(screen.getByText('General'));
|
|
162
|
-
expect(screen.getByTestId('general-section')).toBeInTheDocument();
|
|
163
|
-
|
|
164
|
-
// Click again — collapses
|
|
165
|
-
await user.click(screen.getByText('General'));
|
|
166
|
-
expect(screen.queryByTestId('general-section')).not.toBeInTheDocument();
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
it('shows "2 configured" for AI Providers when snapshot has 2 providers', () => {
|
|
170
|
-
render(
|
|
171
|
-
<MobileSettings
|
|
172
|
-
settings={DEFAULT_SETTINGS}
|
|
173
|
-
onSettingsChange={onSettingsChange}
|
|
174
|
-
autoSave={autoSave}
|
|
175
|
-
snapshot={SNAPSHOT_WITH_PROVIDERS}
|
|
176
|
-
/>,
|
|
177
|
-
);
|
|
178
|
-
|
|
179
|
-
expect(screen.getByText('2 configured')).toBeInTheDocument();
|
|
180
|
-
});
|
|
181
|
-
});
|