@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
|
@@ -8,7 +8,6 @@ import { UsageView } from '../views/Usage/UsageView.js';
|
|
|
8
8
|
import { BackupView } from '../views/Backup/BackupView.js';
|
|
9
9
|
import { NotificationsView } from '../views/Notifications/NotificationsView.js';
|
|
10
10
|
import { DiagnosticsView } from '../views/Diagnostics/DiagnosticsView.js';
|
|
11
|
-
import { HeadroomView } from '../views/Headroom/HeadroomView.js';
|
|
12
11
|
import { EvalView } from '../views/Eval/EvalView.js';
|
|
13
12
|
|
|
14
13
|
/**
|
|
@@ -140,22 +139,6 @@ describe('DiagnosticsView (S35)', () => {
|
|
|
140
139
|
});
|
|
141
140
|
});
|
|
142
141
|
|
|
143
|
-
describe('HeadroomView (S35)', () => {
|
|
144
|
-
it('shows status + controls + stats', async () => {
|
|
145
|
-
installFetchMock([
|
|
146
|
-
{ match: (u) => u.endsWith('/api/headroom/status'), run: () => ({ status: 'not-installed' }) },
|
|
147
|
-
{ match: (u) => u.includes('/api/headroom/stats'), run: () => ({ hours: 24, requests: 142, tokensSaved: 1234, cacheHits: 12, cacheMisses: 4 }) },
|
|
148
|
-
{ match: (u) => u.endsWith('/api/headroom/install'), run: () => ({ ok: true }) },
|
|
149
|
-
{ match: () => true, run: () => ({}) },
|
|
150
|
-
]);
|
|
151
|
-
render(<Providers><HeadroomView /></Providers>);
|
|
152
|
-
expect(await screen.findByText('not-installed')).toBeTruthy();
|
|
153
|
-
expect(screen.getByText('142')).toBeTruthy();
|
|
154
|
-
expect(screen.getByText('1234')).toBeTruthy();
|
|
155
|
-
expect(screen.getByTestId('headroom-install')).toBeTruthy();
|
|
156
|
-
});
|
|
157
|
-
});
|
|
158
|
-
|
|
159
142
|
describe('EvalView (S35)', () => {
|
|
160
143
|
it('lists runs + launches a suite via POST /api/eval/run', async () => {
|
|
161
144
|
const { calls } = installFetchMock([
|
|
@@ -167,7 +167,6 @@ const DEFAULT_SECTIONS: SidebarSection[] = [
|
|
|
167
167
|
{ id: 'backup', label: 'Backups', icon: Archive },
|
|
168
168
|
{ id: 'notifications', label: 'Notifications', icon: Bell },
|
|
169
169
|
{ id: 'diagnostics', label: 'Diagnostics', icon: Terminal },
|
|
170
|
-
{ id: 'headroom', label: 'Headroom', icon: Network },
|
|
171
170
|
{ id: 'eval', label: 'Eval', icon: FlaskConical },
|
|
172
171
|
{ id: 'settings', label: 'Settings', icon: SettingsIcon },
|
|
173
172
|
],
|
|
@@ -57,9 +57,6 @@ const NotificationsView = lazy(() =>
|
|
|
57
57
|
const DiagnosticsView = lazy(() =>
|
|
58
58
|
import('./Diagnostics/DiagnosticsView.js').then((m) => ({ default: m.DiagnosticsView })),
|
|
59
59
|
);
|
|
60
|
-
const HeadroomView = lazy(() =>
|
|
61
|
-
import('./Headroom/HeadroomView.js').then((m) => ({ default: m.HeadroomView })),
|
|
62
|
-
);
|
|
63
60
|
const EvalView = lazy(() =>
|
|
64
61
|
import('./Eval/EvalView.js').then((m) => ({ default: m.EvalView })),
|
|
65
62
|
);
|
|
@@ -160,8 +157,6 @@ export function useViewForId(id: string): JSX.Element {
|
|
|
160
157
|
return <NotificationsView />;
|
|
161
158
|
case 'diagnostics':
|
|
162
159
|
return <DiagnosticsView />;
|
|
163
|
-
case 'headroom':
|
|
164
|
-
return <HeadroomView />;
|
|
165
160
|
case 'eval':
|
|
166
161
|
return <EvalView />;
|
|
167
162
|
case 'chat':
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* _update-test-helpers.mjs — shared test helpers for update route tests.
|
|
3
|
+
*
|
|
4
|
+
* Exports a pre-loaded module instance so all test files share the same
|
|
5
|
+
* module state (important for ESM module caching in Node.js).
|
|
6
|
+
*/
|
|
7
|
+
import { resolve } from 'node:path';
|
|
8
|
+
|
|
9
|
+
const REPO = resolve(import.meta.dirname, '..', '..');
|
|
10
|
+
|
|
11
|
+
export const { createUpdateRouter, resetUpdateCache, resetConcurrencyGuard } = await import(
|
|
12
|
+
`${REPO}/bizar-dash/src/server/routes/update.mjs`
|
|
13
|
+
);
|
|
@@ -16,7 +16,7 @@ import { resolve } from 'node:path';
|
|
|
16
16
|
const REPO = resolve(import.meta.dirname, '..', '..');
|
|
17
17
|
|
|
18
18
|
const { createUpdateRouter, resetUpdateCache } = await import(
|
|
19
|
-
|
|
19
|
+
'./_update-test-helpers.mjs'
|
|
20
20
|
);
|
|
21
21
|
|
|
22
22
|
let app;
|
|
@@ -64,34 +64,39 @@ describe('update.mjs — /api/updates/status', () => {
|
|
|
64
64
|
it('returns the installed package map', async () => {
|
|
65
65
|
const res = await request(app, 'GET', '/api/updates/status');
|
|
66
66
|
assert.equal(res.status, 200);
|
|
67
|
-
assert.ok(res.body.
|
|
68
|
-
assert.equal(typeof res.body.
|
|
69
|
-
for (const id of ['bizar', 'bizar-
|
|
70
|
-
assert.ok(id in res.body.
|
|
67
|
+
assert.ok(res.body.installed, 'response should have installed field');
|
|
68
|
+
assert.equal(typeof res.body.installed, 'object');
|
|
69
|
+
for (const id of ['bizar', 'bizar-sdk', 'claude-agent', 'claude-code']) {
|
|
70
|
+
assert.ok(id in res.body.installed, `installed.${id} should be a key`);
|
|
71
71
|
assert.ok(
|
|
72
|
-
res.body.
|
|
73
|
-
`
|
|
72
|
+
res.body.installed[id] === null || typeof res.body.installed[id] === 'string',
|
|
73
|
+
`installed.${id} should be string or null`,
|
|
74
74
|
);
|
|
75
75
|
}
|
|
76
76
|
});
|
|
77
77
|
|
|
78
|
-
it('does not include latest/hasUpdates fields', async () => {
|
|
78
|
+
it('does not include latest/hasUpdates/available fields', async () => {
|
|
79
79
|
const res = await request(app, 'GET', '/api/updates/status');
|
|
80
80
|
assert.equal(res.status, 200);
|
|
81
81
|
assert.equal(res.body.latest, undefined, 'status should not include latest');
|
|
82
82
|
assert.equal(res.body.hasUpdates, undefined, 'status should not include hasUpdates');
|
|
83
|
+
assert.equal(res.body.available, undefined, 'status should not include available');
|
|
83
84
|
});
|
|
84
85
|
});
|
|
85
86
|
|
|
86
87
|
describe('update.mjs — /api/updates/check', () => {
|
|
87
|
-
it('returns shape {
|
|
88
|
+
it('returns shape { installed, available, hasUpdates, channel }', async () => {
|
|
88
89
|
const res = await request(app, 'GET', '/api/updates/check');
|
|
89
90
|
assert.equal(res.status, 200);
|
|
90
|
-
assert.ok(res.body.
|
|
91
|
-
assert.ok(res.body.
|
|
91
|
+
assert.ok(res.body.installed, 'should have installed');
|
|
92
|
+
assert.ok(res.body.available, 'should have available');
|
|
93
|
+
assert.equal(typeof res.body.available, 'object');
|
|
94
|
+
assert.ok('stable' in res.body.available, 'available should have stable key');
|
|
95
|
+
assert.ok('beta' in res.body.available, 'available should have beta key');
|
|
92
96
|
assert.equal(typeof res.body.hasUpdates, 'boolean');
|
|
93
|
-
|
|
94
|
-
|
|
97
|
+
assert.equal(res.body.channel, 'stable');
|
|
98
|
+
for (const id of ['bizar', 'bizar-sdk', 'claude-agent', 'claude-code']) {
|
|
99
|
+
assert.ok(id in res.body.installed, `installed.${id} should be a key`);
|
|
95
100
|
}
|
|
96
101
|
});
|
|
97
102
|
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* update-packages.test.mjs — tests for the v10.3.0 package list and channel selection.
|
|
3
|
+
*
|
|
4
|
+
* Run with: node --test bizar-dash/tests/update-packages.test.mjs
|
|
5
|
+
*
|
|
6
|
+
* Strategy: test HTTP response shapes + response fields without needing to
|
|
7
|
+
* patch spawn. The channel selection is encoded in the POST response body
|
|
8
|
+
* (channel field) and the concurrency guard (runningSince) is checked from
|
|
9
|
+
* the 409 response. The actual npm spawn behavior is tested in
|
|
10
|
+
* update-run.test.mjs which has the proper spawn interception setup.
|
|
11
|
+
*/
|
|
12
|
+
import { describe, it, beforeEach } from 'node:test';
|
|
13
|
+
import assert from 'node:assert/strict';
|
|
14
|
+
import express from 'express';
|
|
15
|
+
import { resolve } from 'node:path';
|
|
16
|
+
|
|
17
|
+
const REPO = resolve(import.meta.dirname, '..', '..');
|
|
18
|
+
|
|
19
|
+
const { createUpdateRouter, resetUpdateCache, resetConcurrencyGuard } = await import(
|
|
20
|
+
`${REPO}/bizar-dash/src/server/routes/update.mjs`
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
// ─── HTTP helper ─────────────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
async function request(app, method, path, body) {
|
|
26
|
+
const http = await import('node:http');
|
|
27
|
+
return new Promise((resolveP, reject) => {
|
|
28
|
+
const server = app.listen(0, '127.0.0.1', () => {
|
|
29
|
+
const { port } = server.address();
|
|
30
|
+
const bodyStr = body ? JSON.stringify(body) : '';
|
|
31
|
+
const req = http.request({
|
|
32
|
+
hostname: '127.0.0.1',
|
|
33
|
+
port,
|
|
34
|
+
path,
|
|
35
|
+
method,
|
|
36
|
+
headers: bodyStr
|
|
37
|
+
? { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(bodyStr) }
|
|
38
|
+
: {},
|
|
39
|
+
}, (res) => {
|
|
40
|
+
let data = '';
|
|
41
|
+
res.on('data', (c) => { data += c; });
|
|
42
|
+
res.on('end', () => {
|
|
43
|
+
server.close();
|
|
44
|
+
try {
|
|
45
|
+
resolveP({ status: res.statusCode, body: JSON.parse(data) });
|
|
46
|
+
} catch {
|
|
47
|
+
resolveP({ status: res.statusCode, body: data });
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
req.on('error', (err) => { server.close(); reject(err); });
|
|
52
|
+
if (bodyStr) req.write(bodyStr);
|
|
53
|
+
req.end();
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ─── Tests ─────────────────────────────────────────────────────────────────
|
|
59
|
+
|
|
60
|
+
describe('update.mjs — v10.3.0 package list', () => {
|
|
61
|
+
it('returns exactly 4 packages (bizar, bizar-sdk, claude-agent, claude-code)', async () => {
|
|
62
|
+
resetUpdateCache();
|
|
63
|
+
const app = express();
|
|
64
|
+
app.use(express.json());
|
|
65
|
+
app.use('/api', createUpdateRouter({ broadcast: () => {} }));
|
|
66
|
+
|
|
67
|
+
const res = await request(app, 'GET', '/api/updates/status');
|
|
68
|
+
assert.equal(res.status, 200);
|
|
69
|
+
assert.ok(res.body.installed, 'should have installed field');
|
|
70
|
+
|
|
71
|
+
const ids = Object.keys(res.body.installed).sort();
|
|
72
|
+
assert.deepEqual(ids.sort(), ['bizar', 'bizar-sdk', 'claude-agent', 'claude-code'].sort());
|
|
73
|
+
assert.ok(!ids.includes('bizar-dash'), 'bizar-dash should not be present');
|
|
74
|
+
assert.ok(!ids.includes('bizar-plugin'), 'bizar-plugin should not be present');
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe('update.mjs — channel selection', () => {
|
|
79
|
+
let app;
|
|
80
|
+
|
|
81
|
+
beforeEach(() => {
|
|
82
|
+
resetUpdateCache();
|
|
83
|
+
resetConcurrencyGuard();
|
|
84
|
+
const localApp = express();
|
|
85
|
+
localApp.use(express.json());
|
|
86
|
+
localApp.use('/api', createUpdateRouter({ broadcast: () => {} }));
|
|
87
|
+
app = localApp;
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('POST with channel=beta returns 202 and reflects channel=beta in body', async () => {
|
|
91
|
+
const res = await request(app, 'POST', '/api/updates/apply', {
|
|
92
|
+
packages: ['bizar'],
|
|
93
|
+
channel: 'beta',
|
|
94
|
+
});
|
|
95
|
+
// Returns 202 (npm may not be on PATH in test env, so accept 500 too)
|
|
96
|
+
assert.ok([202, 500].includes(res.status), `unexpected status ${res.status}`);
|
|
97
|
+
if (res.status === 202) {
|
|
98
|
+
assert.equal(res.body.channel, 'beta', 'response should reflect channel=beta');
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('POST without channel returns 202 and reflects channel=stable in body', async () => {
|
|
103
|
+
const res = await request(app, 'POST', '/api/updates/apply', {
|
|
104
|
+
packages: ['bizar'],
|
|
105
|
+
});
|
|
106
|
+
assert.ok([202, 500].includes(res.status), `unexpected status ${res.status}`);
|
|
107
|
+
if (res.status === 202) {
|
|
108
|
+
assert.equal(res.body.channel, 'stable', 'default channel should be stable');
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe('update.mjs — concurrency guard returns runningSince', () => {
|
|
114
|
+
it('second concurrent POST returns 409 with runningSince ISO8601 timestamp', async () => {
|
|
115
|
+
resetUpdateCache();
|
|
116
|
+
resetConcurrencyGuard();
|
|
117
|
+
const localApp = express();
|
|
118
|
+
localApp.use(express.json());
|
|
119
|
+
localApp.use('/api', createUpdateRouter({ broadcast: () => {} }));
|
|
120
|
+
|
|
121
|
+
// First request may succeed or fail depending on npm availability
|
|
122
|
+
const first = await request(localApp, 'POST', '/api/updates/apply', { packages: ['bizar'] });
|
|
123
|
+
assert.ok([202, 500].includes(first.status), `first status unexpected: ${first.status}`);
|
|
124
|
+
|
|
125
|
+
// Second concurrent request should always hit the guard (regardless of npm availability)
|
|
126
|
+
const second = await request(localApp, 'POST', '/api/updates/apply', { packages: ['bizar'] });
|
|
127
|
+
assert.equal(second.status, 409, 'second request should get 409');
|
|
128
|
+
assert.equal(second.body.error, 'already_running');
|
|
129
|
+
assert.ok('runningSince' in second.body, 'response should include runningSince');
|
|
130
|
+
assert.ok(second.body.runningSince !== null, 'runningSince should not be null');
|
|
131
|
+
assert.match(
|
|
132
|
+
second.body.runningSince,
|
|
133
|
+
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/,
|
|
134
|
+
'runningSince should be ISO8601',
|
|
135
|
+
);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe('update.mjs — /check returns stable and beta in available', () => {
|
|
140
|
+
it('GET /api/updates/check includes available.stable and available.beta', async () => {
|
|
141
|
+
resetUpdateCache();
|
|
142
|
+
const app = express();
|
|
143
|
+
app.use(express.json());
|
|
144
|
+
app.use('/api', createUpdateRouter({ broadcast: () => {} }));
|
|
145
|
+
|
|
146
|
+
const res = await request(app, 'GET', '/api/updates/check');
|
|
147
|
+
assert.equal(res.status, 200);
|
|
148
|
+
assert.ok(res.body.available, 'should have available field');
|
|
149
|
+
assert.ok('stable' in res.body.available, 'available should have stable key');
|
|
150
|
+
assert.ok('beta' in res.body.available, 'available should have beta key');
|
|
151
|
+
// Both may be null if npm registry is unreachable, but keys must be present
|
|
152
|
+
assert.equal(typeof res.body.available.stable, 'string');
|
|
153
|
+
});
|
|
154
|
+
});
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* exit code. Capture the broadcast channel's emitted messages and
|
|
9
9
|
* assert they fire in the documented order.
|
|
10
10
|
*/
|
|
11
|
-
import { describe, it, beforeEach } from 'node:test';
|
|
11
|
+
import { describe, it, beforeEach, afterEach } from 'node:test';
|
|
12
12
|
import assert from 'node:assert/strict';
|
|
13
13
|
import express from 'express';
|
|
14
14
|
import { EventEmitter } from 'node:events';
|
|
@@ -17,10 +17,14 @@ import { resolve } from 'node:path';
|
|
|
17
17
|
|
|
18
18
|
const REPO = resolve(import.meta.dirname, '..', '..');
|
|
19
19
|
|
|
20
|
-
// ───
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
// ─── Dynamic import to get fresh module (bypasses ESM static-capture) ─────────
|
|
21
|
+
|
|
22
|
+
async function importUpdateModuleFresh() {
|
|
23
|
+
const url = `${REPO}/bizar-dash/src/server/routes/update.mjs?fresh=${Date.now()}-${Math.random()}`;
|
|
24
|
+
return import(url);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ─── Spawn fake ─────────────────────────────────────────────────────────────
|
|
24
28
|
|
|
25
29
|
class FakeChildProcess extends EventEmitter {
|
|
26
30
|
constructor() {
|
|
@@ -28,7 +32,6 @@ class FakeChildProcess extends EventEmitter {
|
|
|
28
32
|
this.stdout = new Readable({ read() {} });
|
|
29
33
|
this.stderr = new Readable({ read() {} });
|
|
30
34
|
}
|
|
31
|
-
// Helpers for tests
|
|
32
35
|
emitStdout(line) { this.stdout.push(line + '\n'); }
|
|
33
36
|
emitStderr(line) { this.stderr.push(line + '\n'); }
|
|
34
37
|
finish(code = 0) {
|
|
@@ -36,9 +39,7 @@ class FakeChildProcess extends EventEmitter {
|
|
|
36
39
|
this.stderr.push(null);
|
|
37
40
|
this.emit('close', code);
|
|
38
41
|
}
|
|
39
|
-
failWith(err) {
|
|
40
|
-
this.emit('error', err);
|
|
41
|
-
}
|
|
42
|
+
failWith(err) { this.emit('error', err); }
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
let fakeProc = null;
|
|
@@ -49,37 +50,9 @@ function makeFakeSpawn() {
|
|
|
49
50
|
};
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
// Install the fake by intercepting `node:child_process`. We do this by
|
|
53
|
-
// rewriting `Module._resolveFilename` for one test at a time.
|
|
54
53
|
import { createRequire } from 'node:module';
|
|
55
54
|
const require = createRequire(import.meta.url);
|
|
56
55
|
|
|
57
|
-
let originalLoad;
|
|
58
|
-
let patched = false;
|
|
59
|
-
function installFakeSpawn() {
|
|
60
|
-
if (patched) return;
|
|
61
|
-
// Override via global require cache: pre-populate the require cache
|
|
62
|
-
// for `node:child_process` with a stub module.
|
|
63
|
-
const realChild = require('node:child_process');
|
|
64
|
-
const stub = { ...realChild, spawn: makeFakeSpawn() };
|
|
65
|
-
require.cache[require.resolve('node:child_process')] = {
|
|
66
|
-
id: 'node:child_process',
|
|
67
|
-
filename: 'node:child_process',
|
|
68
|
-
loaded: true,
|
|
69
|
-
exports: stub,
|
|
70
|
-
};
|
|
71
|
-
patched = true;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Load update.mjs once after we've patched child_process. We do this
|
|
75
|
-
// dynamically so the import picks up the stubbed `spawn` from the
|
|
76
|
-
// require cache that ESM bridges through.
|
|
77
|
-
//
|
|
78
|
-
// NOTE: ESM `import` does NOT use the require cache, so the patch above
|
|
79
|
-
// only works for CommonJS callers of `child_process`. The route imports
|
|
80
|
-
// `spawn` from `node:child_process` directly via ESM. To make this test
|
|
81
|
-
// work, we patch the actual child_process module's `spawn` property at
|
|
82
|
-
// runtime instead.
|
|
83
56
|
function patchSpawnDirectly() {
|
|
84
57
|
const cp = require('node:child_process');
|
|
85
58
|
cp.__originalSpawn = cp.__originalSpawn || cp.spawn;
|
|
@@ -94,25 +67,6 @@ function unpatchSpawn() {
|
|
|
94
67
|
fakeProc = null;
|
|
95
68
|
}
|
|
96
69
|
|
|
97
|
-
// Now we need the route to use the patched spawn. update.mjs does:
|
|
98
|
-
// import { spawn, execFileSync } from 'node:child_process';
|
|
99
|
-
// In Node.js, ESM imports for built-in modules go through the loader,
|
|
100
|
-
// which uses a private internal cache separate from the CommonJS
|
|
101
|
-
// require cache. So patching require.cache alone won't redirect ESM.
|
|
102
|
-
//
|
|
103
|
-
// The reliable approach is to use a loader hook. For simplicity in
|
|
104
|
-
// this test, we instead use module-aliasing via a wrapper: we use a
|
|
105
|
-
// small middleware-only test that monkey-patches spawn on the imported
|
|
106
|
-
// module instance directly. Since the same module instance is shared
|
|
107
|
-
// across imports within a process, this works after first import.
|
|
108
|
-
|
|
109
|
-
async function importUpdateModuleFresh() {
|
|
110
|
-
// Use a query string to force re-import. ESM treats query strings as
|
|
111
|
-
// distinct specifiers, so this gives us a fresh module instance.
|
|
112
|
-
const url = `${REPO}/bizar-dash/src/server/routes/update.mjs?case=${Date.now()}-${Math.random()}`;
|
|
113
|
-
return import(url);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
70
|
// ─── App setup ─────────────────────────────────────────────────────────────
|
|
117
71
|
|
|
118
72
|
function setupApp(broadcast) {
|
|
@@ -163,14 +117,8 @@ function waitFor(predicate, { timeoutMs = 4000, intervalMs = 10 } = {}) {
|
|
|
163
117
|
return new Promise((resolveP, reject) => {
|
|
164
118
|
const start = Date.now();
|
|
165
119
|
const tick = () => {
|
|
166
|
-
if (predicate()) {
|
|
167
|
-
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
if (Date.now() - start > timeoutMs) {
|
|
171
|
-
reject(new Error(`waitFor timed out after ${timeoutMs}ms`));
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
120
|
+
if (predicate()) { resolveP(); return; }
|
|
121
|
+
if (Date.now() - start > timeoutMs) { reject(new Error(`waitFor timed out after ${timeoutMs}ms`)); return; }
|
|
174
122
|
setTimeout(tick, intervalMs);
|
|
175
123
|
};
|
|
176
124
|
tick();
|
|
@@ -186,40 +134,37 @@ describe('update.mjs — apply run broadcasts events in order', () => {
|
|
|
186
134
|
broadcast = [];
|
|
187
135
|
});
|
|
188
136
|
|
|
137
|
+
afterEach(() => {
|
|
138
|
+
unpatchSpawn();
|
|
139
|
+
});
|
|
140
|
+
|
|
189
141
|
it('emits starting → log → done → complete for a successful install', async () => {
|
|
190
142
|
const { app } = await setupApp((msg) => broadcast.push(msg));
|
|
191
143
|
const res = await request(app, 'POST', '/api/updates/apply', { packages: ['bizar'] });
|
|
192
144
|
assert.equal(res.status, 202);
|
|
193
145
|
assert.deepEqual(res.body.started, ['bizar']);
|
|
194
146
|
|
|
195
|
-
// The first event should be a `starting` progress for `bizar`
|
|
196
147
|
await waitFor(() => broadcast.length >= 1);
|
|
197
148
|
assert.equal(broadcast[0].type, 'update:progress');
|
|
198
149
|
assert.equal(broadcast[0].pkg, 'bizar');
|
|
199
150
|
assert.equal(broadcast[0].status, 'starting');
|
|
200
151
|
|
|
201
|
-
// Now drive the fake spawn to emit log lines + finish
|
|
202
152
|
assert.ok(fakeProc, 'fakeProc should exist after request');
|
|
203
153
|
fakeProc.emitStdout('npm WARN deprecated foo');
|
|
204
154
|
fakeProc.emitStdout('added 1 package in 2s');
|
|
205
155
|
fakeProc.finish(0);
|
|
206
156
|
|
|
207
|
-
// Wait for the complete event
|
|
208
157
|
await waitFor(() => broadcast.some((m) => m.type === 'update:complete'), { timeoutMs: 5000 });
|
|
209
158
|
|
|
210
|
-
// Verify ordering and types
|
|
211
159
|
const types = broadcast.map((m) => `${m.type}${m.pkg ? `:${m.pkg}` : ''}:${m.status ?? ''}`);
|
|
212
160
|
assert.ok(types.includes('update:progress:bizar:starting'), 'should have starting progress');
|
|
213
161
|
assert.ok(types.includes('update:log:bizar:'), 'should have at least one log');
|
|
214
162
|
assert.ok(types.includes('update:progress:bizar:done'), 'should have done progress');
|
|
215
163
|
assert.ok(types.at(-1) === 'update:complete:', 'last event should be update:complete');
|
|
216
164
|
|
|
217
|
-
// Done event should carry newVersion
|
|
218
165
|
const doneEvent = broadcast.find((m) => m.type === 'update:progress' && m.status === 'done');
|
|
219
166
|
assert.ok(doneEvent, 'done event should exist');
|
|
220
167
|
assert.ok('newVersion' in doneEvent, 'done event should carry newVersion field');
|
|
221
|
-
|
|
222
|
-
unpatchSpawn();
|
|
223
168
|
});
|
|
224
169
|
|
|
225
170
|
it('emits error progress + complete when npm exits non-zero', async () => {
|
|
@@ -241,26 +186,18 @@ describe('update.mjs — apply run broadcasts events in order', () => {
|
|
|
241
186
|
|
|
242
187
|
const lastEvent = broadcast.at(-1);
|
|
243
188
|
assert.equal(lastEvent.type, 'update:complete');
|
|
244
|
-
|
|
245
|
-
unpatchSpawn();
|
|
246
189
|
});
|
|
247
190
|
|
|
248
191
|
it('returns 409 when an apply is already running', async () => {
|
|
249
192
|
const { app } = await setupApp(() => {});
|
|
250
|
-
// First call — kicks off the run but the fake proc never closes,
|
|
251
|
-
// so the run is "active" for the duration of the test.
|
|
252
193
|
const first = await request(app, 'POST', '/api/updates/apply', { packages: ['bizar'] });
|
|
253
194
|
assert.equal(first.status, 202);
|
|
254
195
|
|
|
255
|
-
// Second call — should hit the concurrency guard
|
|
256
196
|
const second = await request(app, 'POST', '/api/updates/apply', { packages: ['bizar'] });
|
|
257
197
|
assert.equal(second.status, 409);
|
|
258
198
|
assert.equal(second.body.error, 'already_running');
|
|
259
199
|
|
|
260
|
-
// Cleanup: close the fake proc so the run's finally clears the guard
|
|
261
200
|
if (fakeProc) fakeProc.finish(0);
|
|
262
|
-
// Give the async run a moment to clear
|
|
263
201
|
await new Promise((r) => setTimeout(r, 50));
|
|
264
|
-
unpatchSpawn();
|
|
265
202
|
});
|
|
266
|
-
});
|
|
203
|
+
});
|
package/cli/banner.mjs
CHANGED
|
@@ -25,7 +25,7 @@ export function showBanner() {
|
|
|
25
25
|
console.log(chalk.hex('#6366f1').bold(RUNE_HELM));
|
|
26
26
|
console.log(chalk.hex('#a855f7')(' Norse Pantheon Agent System for cline'));
|
|
27
27
|
console.log();
|
|
28
|
-
console.log(chalk.dim(' 13 agents · 4 cost tiers · Obsidian vault long-term memory ·
|
|
28
|
+
console.log(chalk.dim(' 13 agents · 4 cost tiers · Obsidian vault long-term memory · Semble · Skills CLI · Mods'));
|
|
29
29
|
console.log();
|
|
30
30
|
}
|
|
31
31
|
|
package/cli/bin.mjs
CHANGED
|
@@ -90,7 +90,7 @@ if (
|
|
|
90
90
|
!process.env.BIZAR_SKIP_BUILD
|
|
91
91
|
) {
|
|
92
92
|
try {
|
|
93
|
-
const { buildSdk, buildPlugin } = await import('./provision
|
|
93
|
+
const { buildSdk, buildPlugin } = await import('./provision.mjs');
|
|
94
94
|
const sdkRes = await buildSdk();
|
|
95
95
|
if (sdkRes.ok && !sdkRes.skipped) {
|
|
96
96
|
console.error(chalk.dim(` → ${sdkRes.message}`));
|
|
@@ -124,6 +124,7 @@ function showHelp() {
|
|
|
124
124
|
|
|
125
125
|
Commands:
|
|
126
126
|
install Run the interactive installer
|
|
127
|
+
migrate Migrate legacy ~/.config/cline/ or ~/.config/bizar/ to ~/.claude/
|
|
127
128
|
audit Run security audit on agent configuration
|
|
128
129
|
init Initialize .bizar/ in current project
|
|
129
130
|
export [target] Export agents/rules to another harness
|
|
@@ -293,6 +294,12 @@ async function main() {
|
|
|
293
294
|
break;
|
|
294
295
|
}
|
|
295
296
|
|
|
297
|
+
case 'migrate': {
|
|
298
|
+
const { runMigrate } = await import('./migrate.mjs');
|
|
299
|
+
await runMigrate(cmdArgs);
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
|
|
296
303
|
case 'service': {
|
|
297
304
|
const mod = await importCommand('service');
|
|
298
305
|
if (!mod) {
|
package/cli/bootstrap.mjs
CHANGED
|
@@ -49,7 +49,7 @@ export function checkSetupStatus() {
|
|
|
49
49
|
*/
|
|
50
50
|
function printSetupBanner() {
|
|
51
51
|
// Use console.log directly to avoid chalk formatting issues in non-TTY
|
|
52
|
-
console.log('\n ⚡ First-time setup — Bizar needs to install agents, plugin,
|
|
52
|
+
console.log('\n ⚡ First-time setup — Bizar needs to install agents, plugin, Semble, Skills CLI...\n');
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
/**
|
|
@@ -49,7 +49,7 @@ const CLAUDE_BIN = process.env.CLAUDE_BIN || 'claude';
|
|
|
49
49
|
*
|
|
50
50
|
* Mirrors Claude Code's own resolver. Used so all subcommands
|
|
51
51
|
* delegate to the same `~/.claude/` directory that the rest of the
|
|
52
|
-
* Bizar harness writes to via `cli/provision
|
|
52
|
+
* Bizar harness writes to via `cli/provision.mjs`.
|
|
53
53
|
*/
|
|
54
54
|
export function resolveClaudeDir() {
|
|
55
55
|
if (process.env.CLAUDE_CONFIG_DIR && process.env.CLAUDE_CONFIG_DIR.trim()) {
|
|
@@ -74,9 +74,6 @@ function generateComposeScaffold(outDir, opts) {
|
|
|
74
74
|
'# Tailscale (optional)',
|
|
75
75
|
'# TAILSCALE_AUTHKEY=tskey-auth-xxxxxxxxxxxxxxxxxxxx',
|
|
76
76
|
'',
|
|
77
|
-
'# Headroom context compression (optional)',
|
|
78
|
-
'# HEADROOM_ENABLED=1',
|
|
79
|
-
'',
|
|
80
77
|
].join('\n');
|
|
81
78
|
writeFileSync(join(scaffoldRoot, '.env.template'), envTemplate);
|
|
82
79
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/install/banner.mjs
|
|
3
|
+
*
|
|
4
|
+
* Banner, pantheon card, and section heading for the install flow.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
|
|
9
|
+
const RUNE_HELM = `
|
|
10
|
+
██████╗ ██╗██╗ ██╗ █████╗ ██████╗
|
|
11
|
+
██╔══██╗██║██║ ██║██╔══██╗██╔══██╗
|
|
12
|
+
██████╔╝██║███████║███████║██████╔╝
|
|
13
|
+
██╔══██╗██║╚════██║██╔══██║██╔══██╗
|
|
14
|
+
██████╔╝██║ ██║██║ ██║██║ ██║
|
|
15
|
+
╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
const PANTHEON = `
|
|
19
|
+
╔══════════════════════════════════════════╗
|
|
20
|
+
║ ║
|
|
21
|
+
║ ᛟ Odin ᛗ Mimir ᚹ Heimdall ║
|
|
22
|
+
║ ᚱ Hermod ᚦ Thor ᛏ Tyr ║
|
|
23
|
+
║ ᛉ Vidarr ᚨ Forseti ᛒ Baldr ║
|
|
24
|
+
║ ║
|
|
25
|
+
╚══════════════════════════════════════════╝
|
|
26
|
+
`;
|
|
27
|
+
|
|
28
|
+
const TAGLINE = '14 agents · 6 cost tiers · dashboard · Obsidian vault long-term memory · Mods · Skills CLI';
|
|
29
|
+
|
|
30
|
+
export const palette = {
|
|
31
|
+
primary: chalk.hex('#6366f1'),
|
|
32
|
+
success: chalk.green,
|
|
33
|
+
warn: chalk.yellow,
|
|
34
|
+
error: chalk.red,
|
|
35
|
+
dim: chalk.dim,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Print the Bizar mark + tagline.
|
|
40
|
+
* @param {string} [version] - Optional version string to prepend.
|
|
41
|
+
*/
|
|
42
|
+
export function showBanner(version) {
|
|
43
|
+
if (version) {
|
|
44
|
+
console.log(chalk.bold.hex('#6366f1')(`\n ⚡ BizarHarness v${version}\n`));
|
|
45
|
+
} else {
|
|
46
|
+
console.log(chalk.bold.hex('#6366f1')(RUNE_HELM));
|
|
47
|
+
console.log(chalk.hex('#a855f7')(' Norse Pantheon Agent System for Claude Code'));
|
|
48
|
+
}
|
|
49
|
+
console.log();
|
|
50
|
+
console.log(chalk.dim(` ${TAGLINE}`));
|
|
51
|
+
console.log();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Print the Norse pantheon card. */
|
|
55
|
+
export function showPantheon() {
|
|
56
|
+
console.log(chalk.hex('#312e81')(PANTHEON));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Print a section header divider.
|
|
61
|
+
* @param {string} text - Section title.
|
|
62
|
+
*/
|
|
63
|
+
export function sectionHeading(text) {
|
|
64
|
+
console.log();
|
|
65
|
+
console.log(chalk.bold.hex('#6366f1')(` ── ${text} ──`));
|
|
66
|
+
console.log();
|
|
67
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/install/banner.test.mjs
|
|
3
|
+
*
|
|
4
|
+
* Tests for cli/install/banner.mjs
|
|
5
|
+
*/
|
|
6
|
+
import { test, describe } from 'node:test';
|
|
7
|
+
import assert from 'node:assert/strict';
|
|
8
|
+
import { showBanner, showPantheon, sectionHeading, palette } from './banner.mjs';
|
|
9
|
+
|
|
10
|
+
describe('showBanner()', () => {
|
|
11
|
+
test('does not throw', () => {
|
|
12
|
+
assert.doesNotThrow(() => showBanner());
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('does not throw with version', () => {
|
|
16
|
+
assert.doesNotThrow(() => showBanner('10.0.0'));
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('palette', () => {
|
|
21
|
+
test('has all 5 required keys', () => {
|
|
22
|
+
const required = ['primary', 'success', 'warn', 'error', 'dim'];
|
|
23
|
+
for (const key of required) {
|
|
24
|
+
assert.ok(key in palette, `missing palette key: ${key}`);
|
|
25
|
+
assert.ok(typeof palette[key] === 'function', `${key} should be a function`);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('sectionHeading()', () => {
|
|
31
|
+
let output;
|
|
32
|
+
const orig = console.log;
|
|
33
|
+
|
|
34
|
+
test('does not throw', () => {
|
|
35
|
+
assert.doesNotThrow(() => sectionHeading('Test Section'));
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
console.log(' banner.test.mjs loaded — run with: node --test cli/install/banner.test.mjs');
|