@polderlabs/bizar 10.0.7 → 10.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -2
- package/bizar-dash/dist/assets/{ActivityFeed-Da-JCrE6.js → ActivityFeed-JjmOY2AQ.js} +1 -1
- package/bizar-dash/dist/assets/{ActivityFeed-Da-JCrE6.js.map → ActivityFeed-JjmOY2AQ.js.map} +1 -1
- package/bizar-dash/dist/assets/{ActivityView-nIRsSETb.js → ActivityView-BIIMvGWo.js} +1 -1
- package/bizar-dash/dist/assets/{ActivityView-nIRsSETb.js.map → ActivityView-BIIMvGWo.js.map} +1 -1
- package/bizar-dash/dist/assets/{AdminView-DP1NMxmG.js → AdminView-DRidJtkq.js} +1 -1
- package/bizar-dash/dist/assets/{AdminView-DP1NMxmG.js.map → AdminView-DRidJtkq.js.map} +1 -1
- package/bizar-dash/dist/assets/{AgentActivity-lSPkOr-O.js → AgentActivity-79l4SBMC.js} +1 -1
- package/bizar-dash/dist/assets/{AgentActivity-lSPkOr-O.js.map → AgentActivity-79l4SBMC.js.map} +1 -1
- package/bizar-dash/dist/assets/{AgentsView-BIWGkz7w.js → AgentsView-0vT4uPSc.js} +1 -1
- package/bizar-dash/dist/assets/{AgentsView-BIWGkz7w.js.map → AgentsView-0vT4uPSc.js.map} +1 -1
- package/bizar-dash/dist/assets/{Alert-DN0UysMg.js → Alert-CG5q_s75.js} +1 -1
- package/bizar-dash/dist/assets/{Alert-DN0UysMg.js.map → Alert-CG5q_s75.js.map} +1 -1
- package/bizar-dash/dist/assets/ArtifactsView-DM8ZmpR9.js +21 -0
- package/bizar-dash/dist/assets/ArtifactsView-DM8ZmpR9.js.map +1 -0
- package/bizar-dash/dist/assets/{AuthView-DW5cs7ma.js → AuthView-BveN94MC.js} +1 -1
- package/bizar-dash/dist/assets/{AuthView-DW5cs7ma.js.map → AuthView-BveN94MC.js.map} +1 -1
- package/bizar-dash/dist/assets/{Avatar-DZPv9pvd.js → Avatar-ulUPIYTd.js} +1 -1
- package/bizar-dash/dist/assets/{Avatar-DZPv9pvd.js.map → Avatar-ulUPIYTd.js.map} +1 -1
- package/bizar-dash/dist/assets/{BackgroundJobsView-D8O-EyMD.js → BackgroundJobsView-BWErhqFo.js} +1 -1
- package/bizar-dash/dist/assets/{BackgroundJobsView-D8O-EyMD.js.map → BackgroundJobsView-BWErhqFo.js.map} +1 -1
- package/bizar-dash/dist/assets/{BackupView-uwXkhxOV.js → BackupView-CnBjzG9N.js} +1 -1
- package/bizar-dash/dist/assets/{BackupView-uwXkhxOV.js.map → BackupView-CnBjzG9N.js.map} +1 -1
- package/bizar-dash/dist/assets/{Card-Tyboihu7.js → Card-B0imhxMk.js} +1 -1
- package/bizar-dash/dist/assets/{Card-Tyboihu7.js.map → Card-B0imhxMk.js.map} +1 -1
- package/bizar-dash/dist/assets/{ChatView-DRBHzgay.js → ChatView-D6zGytzx.js} +1 -1
- package/bizar-dash/dist/assets/{ChatView-DRBHzgay.js.map → ChatView-D6zGytzx.js.map} +1 -1
- package/bizar-dash/dist/assets/{Chip-BrXIlR9w.js → Chip-wwZSGwLE.js} +1 -1
- package/bizar-dash/dist/assets/{Chip-BrXIlR9w.js.map → Chip-wwZSGwLE.js.map} +1 -1
- package/bizar-dash/dist/assets/{ClaudeSessionsView-Ck62jaOr.js → ClaudeSessionsView-DK1sOLhi.js} +1 -1
- package/bizar-dash/dist/assets/{ClaudeSessionsView-Ck62jaOr.js.map → ClaudeSessionsView-DK1sOLhi.js.map} +1 -1
- package/bizar-dash/dist/assets/{ClipboardView-DfND6xL6.js → ClipboardView-CZ2lYPr0.js} +1 -1
- package/bizar-dash/dist/assets/{ClipboardView-DfND6xL6.js.map → ClipboardView-CZ2lYPr0.js.map} +1 -1
- package/bizar-dash/dist/assets/{ConfigView-DjJmSrOJ.js → ConfigView-D-zT9eNA.js} +1 -1
- package/bizar-dash/dist/assets/{ConfigView-DjJmSrOJ.js.map → ConfigView-D-zT9eNA.js.map} +1 -1
- package/bizar-dash/dist/assets/{DiagnosticsView-CKgItf0J.js → DiagnosticsView-BocV68Lb.js} +1 -1
- package/bizar-dash/dist/assets/{DiagnosticsView-CKgItf0J.js.map → DiagnosticsView-BocV68Lb.js.map} +1 -1
- package/bizar-dash/dist/assets/{DialogsView-DHYF8dE1.js → DialogsView-BVjPI5x7.js} +1 -1
- package/bizar-dash/dist/assets/{DialogsView-DHYF8dE1.js.map → DialogsView-BVjPI5x7.js.map} +1 -1
- package/bizar-dash/dist/assets/{DoctorView-DDLHrSKk.js → DoctorView-84gJcZwM.js} +1 -1
- package/bizar-dash/dist/assets/{DoctorView-DDLHrSKk.js.map → DoctorView-84gJcZwM.js.map} +1 -1
- package/bizar-dash/dist/assets/{EmptyState-DZ2ouKwd.js → EmptyState-BjgVOrcO.js} +1 -1
- package/bizar-dash/dist/assets/{EmptyState-DZ2ouKwd.js.map → EmptyState-BjgVOrcO.js.map} +1 -1
- package/bizar-dash/dist/assets/{EnvVarsView-D1cx83mB.js → EnvVarsView-BJ2TEzF7.js} +1 -1
- package/bizar-dash/dist/assets/{EnvVarsView-D1cx83mB.js.map → EnvVarsView-BJ2TEzF7.js.map} +1 -1
- package/bizar-dash/dist/assets/{ErrorState-DwwJ2-Jq.js → ErrorState-BE3WBFqF.js} +1 -1
- package/bizar-dash/dist/assets/{ErrorState-DwwJ2-Jq.js.map → ErrorState-BE3WBFqF.js.map} +1 -1
- package/bizar-dash/dist/assets/{EvalView-C9aYixXW.js → EvalView-BJJ2P-kK.js} +1 -1
- package/bizar-dash/dist/assets/{EvalView-C9aYixXW.js.map → EvalView-BJJ2P-kK.js.map} +1 -1
- package/bizar-dash/dist/assets/{GoalsView-DZa_mGmh.js → GoalsView-pwROHApi.js} +1 -1
- package/bizar-dash/dist/assets/{GoalsView-DZa_mGmh.js.map → GoalsView-pwROHApi.js.map} +1 -1
- package/bizar-dash/dist/assets/{Grid-CiRyhAgR.js → Grid-qcvbhx6e.js} +1 -1
- package/bizar-dash/dist/assets/{Grid-CiRyhAgR.js.map → Grid-qcvbhx6e.js.map} +1 -1
- package/bizar-dash/dist/assets/{HeadroomView-CNc-TI7u.js → HeadroomView-D1UiTYbt.js} +1 -1
- package/bizar-dash/dist/assets/{HeadroomView-CNc-TI7u.js.map → HeadroomView-D1UiTYbt.js.map} +1 -1
- package/bizar-dash/dist/assets/{HistoryView-Dv7aPxJE.js → HistoryView-C7SSOLJA.js} +1 -1
- package/bizar-dash/dist/assets/{HistoryView-Dv7aPxJE.js.map → HistoryView-C7SSOLJA.js.map} +1 -1
- package/bizar-dash/dist/assets/{Input-BbFUDQnS.js → Input-Bgzxf_Hu.js} +1 -1
- package/bizar-dash/dist/assets/{Input-BbFUDQnS.js.map → Input-Bgzxf_Hu.js.map} +1 -1
- package/bizar-dash/dist/assets/{LibrariesView-DBQ83TNa.js → LibrariesView-CbFFksox.js} +1 -1
- package/bizar-dash/dist/assets/{LibrariesView-DBQ83TNa.js.map → LibrariesView-CbFFksox.js.map} +1 -1
- package/bizar-dash/dist/assets/{LightRAGView-JYpE5u9p.js → LightRAGView-BqDrJbRq.js} +1 -1
- package/bizar-dash/dist/assets/{LightRAGView-JYpE5u9p.js.map → LightRAGView-BqDrJbRq.js.map} +1 -1
- package/bizar-dash/dist/assets/{ListHeader-BGjm11Z0.js → ListHeader-TjPJrUfY.js} +1 -1
- package/bizar-dash/dist/assets/{ListHeader-BGjm11Z0.js.map → ListHeader-TjPJrUfY.js.map} +1 -1
- package/bizar-dash/dist/assets/{MemoryView-CnOP2MvM.js → MemoryView-BlTeHLhX.js} +1 -1
- package/bizar-dash/dist/assets/{MemoryView-CnOP2MvM.js.map → MemoryView-BlTeHLhX.js.map} +1 -1
- package/bizar-dash/dist/assets/{MiscView-BBcpUbZZ.js → MiscView-B8QY82Pm.js} +1 -1
- package/bizar-dash/dist/assets/{MiscView-BBcpUbZZ.js.map → MiscView-B8QY82Pm.js.map} +1 -1
- package/bizar-dash/dist/assets/{ModsView-CU4cAmIz.js → ModsView-CvW1OWts.js} +1 -1
- package/bizar-dash/dist/assets/{ModsView-CU4cAmIz.js.map → ModsView-CvW1OWts.js.map} +1 -1
- package/bizar-dash/dist/assets/{NotificationsView-BYffvthq.js → NotificationsView-CsBt5U7x.js} +1 -1
- package/bizar-dash/dist/assets/{NotificationsView-BYffvthq.js.map → NotificationsView-CsBt5U7x.js.map} +1 -1
- package/bizar-dash/dist/assets/{ObsidianView-D3bI0row.js → ObsidianView-BCTejEj2.js} +1 -1
- package/bizar-dash/dist/assets/{ObsidianView-D3bI0row.js.map → ObsidianView-BCTejEj2.js.map} +1 -1
- package/bizar-dash/dist/assets/{OverviewView-0tq8f-Y0.js → OverviewView-BsuPxwnB.js} +1 -1
- package/bizar-dash/dist/assets/{OverviewView-0tq8f-Y0.js.map → OverviewView-BsuPxwnB.js.map} +1 -1
- package/bizar-dash/dist/assets/{ProgressBar-B-h60LKA.js → ProgressBar-DlZTETEn.js} +1 -1
- package/bizar-dash/dist/assets/{ProgressBar-B-h60LKA.js.map → ProgressBar-DlZTETEn.js.map} +1 -1
- package/bizar-dash/dist/assets/{ProjectsView-DhX5lzHS.js → ProjectsView-Soxtmpet.js} +1 -1
- package/bizar-dash/dist/assets/{ProjectsView-DhX5lzHS.js.map → ProjectsView-Soxtmpet.js.map} +1 -1
- package/bizar-dash/dist/assets/{ProvidersView-CJMX7jm9.js → ProvidersView-DaCEV9pM.js} +1 -1
- package/bizar-dash/dist/assets/{ProvidersView-CJMX7jm9.js.map → ProvidersView-DaCEV9pM.js.map} +1 -1
- package/bizar-dash/dist/assets/{SchedulesView-CcwDQ9Nm.js → SchedulesView-DY2ByBj_.js} +1 -1
- package/bizar-dash/dist/assets/{SchedulesView-CcwDQ9Nm.js.map → SchedulesView-DY2ByBj_.js.map} +1 -1
- package/bizar-dash/dist/assets/{Select-Cw7Qtz2M.js → Select-Dbd88_n8.js} +1 -1
- package/bizar-dash/dist/assets/{Select-Cw7Qtz2M.js.map → Select-Dbd88_n8.js.map} +1 -1
- package/bizar-dash/dist/assets/{SettingsRow-DgsgbPSI.js → SettingsRow-CLMNd434.js} +1 -1
- package/bizar-dash/dist/assets/{SettingsRow-DgsgbPSI.js.map → SettingsRow-CLMNd434.js.map} +1 -1
- package/bizar-dash/dist/assets/{SettingsView-DTAFdKyK.js → SettingsView-b24oFq9x.js} +1 -1
- package/bizar-dash/dist/assets/{SettingsView-DTAFdKyK.js.map → SettingsView-b24oFq9x.js.map} +1 -1
- package/bizar-dash/dist/assets/{Sheet-DH-WNJ3Y.js → Sheet-DnDid58j.js} +1 -1
- package/bizar-dash/dist/assets/{Sheet-DH-WNJ3Y.js.map → Sheet-DnDid58j.js.map} +1 -1
- package/bizar-dash/dist/assets/{Slider-DXbJuQ7y.js → Slider-BJCvKI1I.js} +1 -1
- package/bizar-dash/dist/assets/{Slider-DXbJuQ7y.js.map → Slider-BJCvKI1I.js.map} +1 -1
- package/bizar-dash/dist/assets/{Sparkline-CYtAn82j.js → Sparkline-ktqQLdyb.js} +1 -1
- package/bizar-dash/dist/assets/{Sparkline-CYtAn82j.js.map → Sparkline-ktqQLdyb.js.map} +1 -1
- package/bizar-dash/dist/assets/{Switch-BItGrPqr.js → Switch-CwXfsDii.js} +1 -1
- package/bizar-dash/dist/assets/{Switch-BItGrPqr.js.map → Switch-CwXfsDii.js.map} +1 -1
- package/bizar-dash/dist/assets/{TasksView-i5tm6CkX.js → TasksView-BHmpzFCf.js} +1 -1
- package/bizar-dash/dist/assets/{TasksView-i5tm6CkX.js.map → TasksView-BHmpzFCf.js.map} +1 -1
- package/bizar-dash/dist/assets/{Textarea-qYDf3yds.js → Textarea-BzIAwSlp.js} +1 -1
- package/bizar-dash/dist/assets/{Textarea-qYDf3yds.js.map → Textarea-BzIAwSlp.js.map} +1 -1
- package/bizar-dash/dist/assets/{UpdateView-Xln-ej26.js → UpdateView-CSmX9NN0.js} +1 -1
- package/bizar-dash/dist/assets/{UpdateView-Xln-ej26.js.map → UpdateView-CSmX9NN0.js.map} +1 -1
- package/bizar-dash/dist/assets/{UsageView-BGuZSUZ6.js → UsageView-COyr_Gfi.js} +1 -1
- package/bizar-dash/dist/assets/{UsageView-BGuZSUZ6.js.map → UsageView-COyr_Gfi.js.map} +1 -1
- package/bizar-dash/dist/assets/{ViewHeader-BqIPVKuG.js → ViewHeader-BzYZ3m-e.js} +1 -1
- package/bizar-dash/dist/assets/{ViewHeader-BqIPVKuG.js.map → ViewHeader-BzYZ3m-e.js.map} +1 -1
- package/bizar-dash/dist/assets/{VoiceView-CWUOCQs_.js → VoiceView-D33_xG5t.js} +1 -1
- package/bizar-dash/dist/assets/{VoiceView-CWUOCQs_.js.map → VoiceView-D33_xG5t.js.map} +1 -1
- package/bizar-dash/dist/assets/{brain-fsBSDl2g.js → brain-CM-Vls8r.js} +1 -1
- package/bizar-dash/dist/assets/{brain-fsBSDl2g.js.map → brain-CM-Vls8r.js.map} +1 -1
- package/bizar-dash/dist/assets/{calendar-DsHTZoCU.js → calendar-C0lgZk69.js} +1 -1
- package/bizar-dash/dist/assets/{calendar-DsHTZoCU.js.map → calendar-C0lgZk69.js.map} +1 -1
- package/bizar-dash/dist/assets/{circle-CqSxqqeJ.js → circle-Bx_hQEBC.js} +1 -1
- package/bizar-dash/dist/assets/{circle-CqSxqqeJ.js.map → circle-Bx_hQEBC.js.map} +1 -1
- package/bizar-dash/dist/assets/{circle-alert-BsLl001l.js → circle-alert-DHvsqpw5.js} +1 -1
- package/bizar-dash/dist/assets/{circle-alert-BsLl001l.js.map → circle-alert-DHvsqpw5.js.map} +1 -1
- package/bizar-dash/dist/assets/{circle-check-rVKMQmZp.js → circle-check-Bx8wmXR9.js} +1 -1
- package/bizar-dash/dist/assets/{circle-check-rVKMQmZp.js.map → circle-check-Bx8wmXR9.js.map} +1 -1
- package/bizar-dash/dist/assets/{clock-OuficfgH.js → clock-CejGoecX.js} +1 -1
- package/bizar-dash/dist/assets/{clock-OuficfgH.js.map → clock-CejGoecX.js.map} +1 -1
- package/bizar-dash/dist/assets/{download-BdHlstUY.js → download-4cEUWue1.js} +1 -1
- package/bizar-dash/dist/assets/{download-BdHlstUY.js.map → download-4cEUWue1.js.map} +1 -1
- package/bizar-dash/dist/assets/{external-link-DA3_VGm-.js → external-link-0NqUhI4i.js} +1 -1
- package/bizar-dash/dist/assets/{external-link-DA3_VGm-.js.map → external-link-0NqUhI4i.js.map} +1 -1
- package/bizar-dash/dist/assets/{file-text-B-g8H227.js → file-text-yEMkrSoZ.js} +1 -1
- package/bizar-dash/dist/assets/{file-text-B-g8H227.js.map → file-text-yEMkrSoZ.js.map} +1 -1
- package/bizar-dash/dist/assets/{filter-D9B8s5CG.js → filter-BXt60zAS.js} +1 -1
- package/bizar-dash/dist/assets/{filter-D9B8s5CG.js.map → filter-BXt60zAS.js.map} +1 -1
- package/bizar-dash/dist/assets/{git-pull-request-ByH0YuVz.js → git-pull-request-BjG5lNtR.js} +1 -1
- package/bizar-dash/dist/assets/{git-pull-request-ByH0YuVz.js.map → git-pull-request-BjG5lNtR.js.map} +1 -1
- package/bizar-dash/dist/assets/{main-CdOm1Pj9.js → main-CokHAXWM.js} +2 -2
- package/bizar-dash/dist/assets/{main-CdOm1Pj9.js.map → main-CokHAXWM.js.map} +1 -1
- package/bizar-dash/dist/assets/{pause-C9hgd8wX.js → pause-CAmulU0F.js} +1 -1
- package/bizar-dash/dist/assets/{pause-C9hgd8wX.js.map → pause-CAmulU0F.js.map} +1 -1
- package/bizar-dash/dist/assets/{pencil-BanqaMw7.js → pencil-eq5IqBmU.js} +1 -1
- package/bizar-dash/dist/assets/{pencil-BanqaMw7.js.map → pencil-eq5IqBmU.js.map} +1 -1
- package/bizar-dash/dist/assets/{play-CawUZ02_.js → play-BoLrrcBA.js} +1 -1
- package/bizar-dash/dist/assets/{play-CawUZ02_.js.map → play-BoLrrcBA.js.map} +1 -1
- package/bizar-dash/dist/assets/{plug-Cmv0-T_e.js → plug-DBrVM9lK.js} +1 -1
- package/bizar-dash/dist/assets/{plug-Cmv0-T_e.js.map → plug-DBrVM9lK.js.map} +1 -1
- package/bizar-dash/dist/assets/{plus-M6LYiKX8.js → plus-DENm0hqV.js} +1 -1
- package/bizar-dash/dist/assets/{plus-M6LYiKX8.js.map → plus-DENm0hqV.js.map} +1 -1
- package/bizar-dash/dist/assets/{power-DP5x_SRF.js → power-DLjXF7zE.js} +1 -1
- package/bizar-dash/dist/assets/{power-DP5x_SRF.js.map → power-DLjXF7zE.js.map} +1 -1
- package/bizar-dash/dist/assets/{power-off-NOJRLAIs.js → power-off-D47cOnG_.js} +1 -1
- package/bizar-dash/dist/assets/{power-off-NOJRLAIs.js.map → power-off-D47cOnG_.js.map} +1 -1
- package/bizar-dash/dist/assets/{refresh-ccw-CHMprsm5.js → refresh-ccw-DNrXX9gB.js} +1 -1
- package/bizar-dash/dist/assets/{refresh-ccw-CHMprsm5.js.map → refresh-ccw-DNrXX9gB.js.map} +1 -1
- package/bizar-dash/dist/assets/{refresh-cw-1RBqDKgJ.js → refresh-cw-CoAHDpeS.js} +1 -1
- package/bizar-dash/dist/assets/{refresh-cw-1RBqDKgJ.js.map → refresh-cw-CoAHDpeS.js.map} +1 -1
- package/bizar-dash/dist/assets/{rotate-ccw-DJRekBbi.js → rotate-ccw-ObX5awaG.js} +1 -1
- package/bizar-dash/dist/assets/{rotate-ccw-DJRekBbi.js.map → rotate-ccw-ObX5awaG.js.map} +1 -1
- package/bizar-dash/dist/assets/{rotate-cw-DHooc0Tx.js → rotate-cw-xpPJw9LP.js} +1 -1
- package/bizar-dash/dist/assets/{rotate-cw-DHooc0Tx.js.map → rotate-cw-xpPJw9LP.js.map} +1 -1
- package/bizar-dash/dist/assets/{save-DQKCKyZK.js → save-D1iKiJJl.js} +1 -1
- package/bizar-dash/dist/assets/{save-DQKCKyZK.js.map → save-D1iKiJJl.js.map} +1 -1
- package/bizar-dash/dist/assets/{send-xgy3RAi7.js → send-BRcAhZUm.js} +1 -1
- package/bizar-dash/dist/assets/{send-xgy3RAi7.js.map → send-BRcAhZUm.js.map} +1 -1
- package/bizar-dash/dist/assets/{shield-alert-CWV2F5ab.js → shield-alert-C6vEcK-g.js} +1 -1
- package/bizar-dash/dist/assets/{shield-alert-CWV2F5ab.js.map → shield-alert-C6vEcK-g.js.map} +1 -1
- package/bizar-dash/dist/assets/{trash-2-CL4khSRv.js → trash-2-DZDjEnNh.js} +1 -1
- package/bizar-dash/dist/assets/{trash-2-CL4khSRv.js.map → trash-2-DZDjEnNh.js.map} +1 -1
- package/bizar-dash/dist/assets/{trending-up-CSJJhWTS.js → trending-up-DMj-AJVf.js} +1 -1
- package/bizar-dash/dist/assets/{trending-up-CSJJhWTS.js.map → trending-up-DMj-AJVf.js.map} +1 -1
- package/bizar-dash/dist/assets/{triangle-alert-BPLb7uLt.js → triangle-alert-_2xe_tl4.js} +1 -1
- package/bizar-dash/dist/assets/{triangle-alert-BPLb7uLt.js.map → triangle-alert-_2xe_tl4.js.map} +1 -1
- package/bizar-dash/dist/assets/{upload-Cww6MvFq.js → upload-DdDljAeV.js} +1 -1
- package/bizar-dash/dist/assets/{upload-Cww6MvFq.js.map → upload-DdDljAeV.js.map} +1 -1
- package/bizar-dash/dist/assets/{zap-DmhVUzRA.js → zap-mHsgh0FP.js} +1 -1
- package/bizar-dash/dist/assets/{zap-DmhVUzRA.js.map → zap-mHsgh0FP.js.map} +1 -1
- package/bizar-dash/dist/index.html +1 -1
- package/bizar-dash/src/server/api.mjs +13 -6
- package/bizar-dash/src/server/artifacts-store.mjs +120 -14
- package/bizar-dash/src/server/claude-artifacts.mjs +281 -0
- package/bizar-dash/src/server/diagnostics-store.mjs +5 -35
- package/bizar-dash/src/server/mod-security.mjs +1 -2
- package/bizar-dash/src/server/providers-store.mjs +1 -1
- package/bizar-dash/src/server/routes/_shared.mjs +0 -17
- package/bizar-dash/src/server/routes/artifacts.mjs +42 -6
- package/bizar-dash/src/server/routes/audit.mjs +78 -0
- package/bizar-dash/src/server/routes/decisions.mjs +78 -0
- package/bizar-dash/src/server/routes/model-router.mjs +158 -0
- package/bizar-dash/src/server/routes/model-router.test.mjs +104 -0
- package/bizar-dash/src/server/routes/sessions.mjs +80 -0
- package/bizar-dash/src/server/server.mjs +1 -27
- package/bizar-dash/src/server/sprint.test.mjs +163 -0
- package/bizar-dash/src/web/v8/views/Artifacts/ArtifactsView.tsx +232 -21
- package/bizar-dash/src/web/v8/views/Artifacts/ClaudeArtifactView.tsx +179 -0
- package/bizar-dash/tests/cli-bugfixes.test.mjs +1 -1
- package/bizar-dash/tests/cli-error-visibility.test.mjs +1 -1
- package/bizar-dash/tests/cli-refactor.test.mjs +0 -1
- package/bizar-dash/tests/diagnostics-store.test.mjs +0 -1
- package/cli/bin.mjs +1 -15
- package/cli/cli-commands-validation.test.mjs +1 -1
- package/cli/commands/lightrag.mjs +1 -1
- package/cli/commands/util.mjs +1 -1
- package/cli/copy.mjs +1 -47
- package/cli/doctor.mjs +3 -3
- package/cli/install.mjs +3 -37
- package/cli/service-controller.mjs +0 -192
- package/cli/service-env.mjs +2 -9
- package/cli/utils.mjs +0 -7
- package/package.json +1 -1
- package/packages/sdk/dist/agent/cron.d.ts +44 -0
- package/packages/sdk/dist/agent/cron.js +79 -0
- package/packages/sdk/dist/agent/heartbeat.d.ts +43 -0
- package/packages/sdk/dist/agent/heartbeat.js +116 -0
- package/packages/sdk/dist/index.d.ts +3 -0
- package/packages/sdk/dist/index.js +5 -0
- package/packages/sdk/dist/learning/decisions.d.ts +46 -0
- package/packages/sdk/dist/learning/decisions.js +132 -0
- package/packages/sdk/dist/learning/index.d.ts +8 -0
- package/packages/sdk/dist/learning/index.js +8 -0
- package/packages/sdk/dist/learning/instincts.d.ts +38 -0
- package/packages/sdk/dist/learning/instincts.js +139 -0
- package/packages/sdk/dist/mcp/server.js +119 -1
- package/packages/sdk/dist/router/agent-model-registry.d.ts +89 -0
- package/packages/sdk/dist/router/agent-model-registry.js +144 -0
- package/packages/sdk/dist/router/index.d.ts +1 -0
- package/packages/sdk/dist/router/index.js +1 -0
- package/packages/sdk/package.json +3 -2
- package/scripts/audit.mjs +388 -0
- package/scripts/audit.test.mjs +149 -0
- package/scripts/check-deps.mjs +0 -24
- package/scripts/eval-gate.mjs +134 -0
- package/scripts/eval-gate.test.mjs +138 -0
- package/scripts/feature-state-machine.mjs +178 -0
- package/scripts/feature-state-machine.test.mjs +135 -0
- package/scripts/spec-dedup.test.mjs +124 -0
- package/scripts/sprint.mjs +213 -0
- package/scripts/worktree-setup.sh +79 -0
- package/bizar-dash/dist/assets/ArtifactsView-CDIhPBAx.js +0 -6
- package/bizar-dash/dist/assets/ArtifactsView-CDIhPBAx.js.map +0 -1
|
@@ -51,18 +51,54 @@ export function createArtifactsRouter({ state, broadcast, projectRoot }) {
|
|
|
51
51
|
res.json(artifact);
|
|
52
52
|
}));
|
|
53
53
|
|
|
54
|
-
// v3.21.0 — Compile the artifact
|
|
55
|
-
// glyph
|
|
56
|
-
//
|
|
57
|
-
//
|
|
54
|
+
// v3.21.0 — Compile the artifact for the renderer. Dispatches by kind:
|
|
55
|
+
// mdx → JSON glyph blocks (legacy v3-v9 renderer)
|
|
56
|
+
// claude-html → iframe-ready HTML envelope
|
|
57
|
+
// claude-svg → iframe-ready HTML envelope (SVG body)
|
|
58
|
+
// claude-react → iframe-ready HTML envelope (Babel-standalone JSX)
|
|
59
|
+
// The Claude-* envelope is ready to drop into an iframe `srcdoc`; the
|
|
60
|
+
// client previewer component (`<ClaudeArtifactView>`) picks the
|
|
61
|
+
// correct renderer based on `kind` and `html`.
|
|
58
62
|
router.get('/artifacts/:slug/render', wrap(async (req, res) => {
|
|
59
63
|
const artifact = artifactsStore.get(req.params.slug, projectRoot);
|
|
60
64
|
if (!artifact) {
|
|
61
65
|
res.status(404).json({ error: 'not_found' });
|
|
62
66
|
return;
|
|
63
67
|
}
|
|
64
|
-
const
|
|
65
|
-
|
|
68
|
+
const kind = artifact.kind || 'mdx';
|
|
69
|
+
if (kind === 'mdx') {
|
|
70
|
+
const compiled = await compileGlyphMdx(artifact.planMdx || '');
|
|
71
|
+
res.json({ slug: req.params.slug, kind, ...compiled });
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const compiled = artifactsStore.compile(req.params.slug, projectRoot);
|
|
75
|
+
if (!compiled) {
|
|
76
|
+
res.status(404).json({ error: 'not_found' });
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
res.json({
|
|
80
|
+
slug: req.params.slug,
|
|
81
|
+
kind: compiled.kind,
|
|
82
|
+
html: compiled.html,
|
|
83
|
+
warnings: compiled.warnings || [],
|
|
84
|
+
safeMode: !!compiled.safeMode,
|
|
85
|
+
compiledAt: compiled.compiledAt,
|
|
86
|
+
});
|
|
87
|
+
}));
|
|
88
|
+
|
|
89
|
+
// v10.1.0 — Full-screen viewer. Returns the compiled HTML envelope
|
|
90
|
+
// with `Content-Type: text/html` so a `<a target="_blank">` opens
|
|
91
|
+
// the artifact on its own page (no dashboard chrome). The iframe
|
|
92
|
+
// `sandbox` rules from the envelope apply — the page is just the
|
|
93
|
+
// envelope served as `text/html`.
|
|
94
|
+
router.get('/artifacts/:slug/view', wrap(async (req, res) => {
|
|
95
|
+
const compiled = artifactsStore.compile(req.params.slug, projectRoot);
|
|
96
|
+
if (!compiled) {
|
|
97
|
+
res.status(404).type('html').send('<h1>Not found</h1>');
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
res.set('Content-Security-Policy', "default-src 'self'; script-src 'unsafe-inline' 'unsafe-eval' https://unpkg.com https://cdn.jsdelivr.net https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src * data:;");
|
|
101
|
+
res.type('html').send(compiled.html);
|
|
66
102
|
}));
|
|
67
103
|
|
|
68
104
|
// v3.1.0 — Full artifact CRUD + canvas editing.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/server/routes/audit.mjs
|
|
3
|
+
*
|
|
4
|
+
* GET /api/audit — returns latest harness audit score + diff vs prior.
|
|
5
|
+
*
|
|
6
|
+
* Reads .harness/audit/latest.json (written by `node scripts/audit.mjs --write`).
|
|
7
|
+
* Returns the full audit result plus a `diff` object showing which categories
|
|
8
|
+
* changed score vs the prior run (if any).
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { wrap } from './_shared.mjs';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {object} deps
|
|
16
|
+
* @param {string} deps.projectRoot
|
|
17
|
+
* @returns {import('express').Router}
|
|
18
|
+
*/
|
|
19
|
+
export function createAuditRouter({ projectRoot }) {
|
|
20
|
+
const router = require('express').Router();
|
|
21
|
+
const LATEST = join(projectRoot, '.harness', 'audit', 'latest.json');
|
|
22
|
+
|
|
23
|
+
router.get('/audit', wrap(async (_req, res) => {
|
|
24
|
+
if (!existsSync(LATEST)) {
|
|
25
|
+
res.status(404).json({
|
|
26
|
+
error: 'not_ready',
|
|
27
|
+
message: 'No audit run yet. Run `node scripts/audit.mjs --write` first.',
|
|
28
|
+
});
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let latest;
|
|
33
|
+
try {
|
|
34
|
+
latest = JSON.parse(readFileSync(LATEST, 'utf8'));
|
|
35
|
+
} catch (err) {
|
|
36
|
+
res.status(500).json({ error: 'parse_error', message: err.message });
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Compute diff vs the run before last (if we have history)
|
|
41
|
+
/** @type {Record<string, {current: number, prior: number, delta: number} | null>} */
|
|
42
|
+
const diff = {};
|
|
43
|
+
const priorPath = join(projectRoot, '.harness', 'audit', 'previous.json');
|
|
44
|
+
|
|
45
|
+
if (existsSync(priorPath)) {
|
|
46
|
+
try {
|
|
47
|
+
const prior = JSON.parse(readFileSync(priorPath, 'utf8'));
|
|
48
|
+
const priorScores = prior.scores ?? {};
|
|
49
|
+
for (const [key, weight] of Object.entries(latest.weights ?? {})) {
|
|
50
|
+
const current = latest.scores?.[key] ?? 0;
|
|
51
|
+
const priorScore = priorScores[key] ?? 0;
|
|
52
|
+
if (current !== priorScore) {
|
|
53
|
+
diff[key] = { current, prior: priorScore, delta: current - priorScore };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Also diff total
|
|
57
|
+
const currentTotal = latest.total ?? 0;
|
|
58
|
+
const priorTotal = prior.total ?? 0;
|
|
59
|
+
if (currentTotal !== priorTotal) {
|
|
60
|
+
diff._total = { current: currentTotal, prior: priorTotal, delta: currentTotal - priorTotal };
|
|
61
|
+
}
|
|
62
|
+
} catch {
|
|
63
|
+
// prior file unreadable — no diff
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
res.json({
|
|
68
|
+
latest,
|
|
69
|
+
diff,
|
|
70
|
+
_links: {
|
|
71
|
+
run: 'node scripts/audit.mjs --write',
|
|
72
|
+
latestPath: LATEST,
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
}));
|
|
76
|
+
|
|
77
|
+
return router;
|
|
78
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/server/routes/decisions.mjs
|
|
3
|
+
*
|
|
4
|
+
* v10.1.1 — Pillar D: Decisions log API.
|
|
5
|
+
*
|
|
6
|
+
* GET /api/decisions
|
|
7
|
+
* Returns list of decisions + tamper-evidence status.
|
|
8
|
+
*
|
|
9
|
+
* Storage: <projectRoot>/.bizar/learning/decisions.jsonl
|
|
10
|
+
* Falls back to BIZAR_HOME if projectRoot is not set.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
'use strict';
|
|
14
|
+
|
|
15
|
+
import { Router } from 'express';
|
|
16
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
17
|
+
import { join } from 'node:path';
|
|
18
|
+
import { homedir } from 'node:os';
|
|
19
|
+
import { wrap } from './_shared.mjs';
|
|
20
|
+
|
|
21
|
+
/** Default root: projectRoot > cwd > $HOME */
|
|
22
|
+
function resolveRoot(projectRoot) {
|
|
23
|
+
if (projectRoot && projectRoot !== '.') return projectRoot;
|
|
24
|
+
try { return process.cwd(); } catch { return homedir(); }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {object} deps
|
|
29
|
+
* @param {string} [deps.projectRoot]
|
|
30
|
+
* @returns {import('express').Router}
|
|
31
|
+
*/
|
|
32
|
+
export function createDecisionsRouter({ projectRoot } = {}) {
|
|
33
|
+
const router = Router();
|
|
34
|
+
const root = projectRoot ?? resolveRoot(projectRoot);
|
|
35
|
+
|
|
36
|
+
// Lazy-import SDK to avoid hard coupling at boot time.
|
|
37
|
+
async function getDecisionsMod() {
|
|
38
|
+
// Try the published SDK first, then fall back to monorepo dist.
|
|
39
|
+
try {
|
|
40
|
+
return await import('@polderlabs/bizar-sdk/learning');
|
|
41
|
+
} catch {
|
|
42
|
+
return await import('../../../packages/sdk/dist/learning/index.js');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// GET /decisions — list + tamper status
|
|
47
|
+
router.get('/decisions', wrap(async (_req, res) => {
|
|
48
|
+
let mod;
|
|
49
|
+
try {
|
|
50
|
+
mod = await getDecisionsMod();
|
|
51
|
+
} catch (err) {
|
|
52
|
+
res.status(503).json({ error: 'unavailable', message: 'SDK not loaded', detail: err?.message });
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const decisions = mod.listDecisions ? mod.listDecisions({ project: root }) : [];
|
|
57
|
+
const verified = mod.verifyChain ? mod.verifyChain({ project: root }) : { ok: true };
|
|
58
|
+
|
|
59
|
+
// Apply datamark to all string fields to sanitise surrogates.
|
|
60
|
+
const sanitised = decisions.map((entry) => {
|
|
61
|
+
const out = {};
|
|
62
|
+
for (const [k, v] of Object.entries(entry)) {
|
|
63
|
+
out[k] = mod.datamark ? mod.datamark(v) : v;
|
|
64
|
+
}
|
|
65
|
+
return out;
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
res.json({
|
|
69
|
+
decisions: sanitised,
|
|
70
|
+
tamperEvidence: {
|
|
71
|
+
status: verified.ok ? 'intact' : 'broken',
|
|
72
|
+
at: verified.at ?? null,
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
}));
|
|
76
|
+
|
|
77
|
+
return router;
|
|
78
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/server/routes/model-router.mjs
|
|
3
|
+
*
|
|
4
|
+
* Surfaces the local model-router configuration and live model list.
|
|
5
|
+
*
|
|
6
|
+
* GET /api/model-router — full policy + agent→model map
|
|
7
|
+
* GET /api/model-router/models — live model list from the local router
|
|
8
|
+
* GET /api/model-router/agents — agent→model map only
|
|
9
|
+
* GET /api/model-router/resolve/:agent — concrete model + endpoint for an agent
|
|
10
|
+
*
|
|
11
|
+
* The local router URL defaults to ANTHROPIC_BASE_URL or
|
|
12
|
+
* BIZAR_MODEL_ROUTER_URL or `http://localhost:20128/v1`.
|
|
13
|
+
*/
|
|
14
|
+
import { Router } from 'express';
|
|
15
|
+
import { readFile } from 'node:fs/promises';
|
|
16
|
+
import { resolve, dirname } from 'node:path';
|
|
17
|
+
import { fileURLToPath } from 'node:url';
|
|
18
|
+
import { wrap } from './_shared.mjs';
|
|
19
|
+
|
|
20
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
// .claude/model-router.json — repo root.
|
|
22
|
+
// From src/server/routes/ up to repo root is 3 levels (routes -> server -> src -> repo).
|
|
23
|
+
const ROUTER_POLICY_PATH = resolve(__dirname, '../../../../.claude/model-router.json');
|
|
24
|
+
|
|
25
|
+
const DEFAULT_ENDPOINT = 'http://localhost:20128/v1';
|
|
26
|
+
|
|
27
|
+
function resolveEndpoint(state) {
|
|
28
|
+
const fromEnv =
|
|
29
|
+
process.env.BIZAR_MODEL_ROUTER_URL ||
|
|
30
|
+
process.env.ANTHROPIC_BASE_URL ||
|
|
31
|
+
state?.config?.modelRouterUrl;
|
|
32
|
+
return (fromEnv || DEFAULT_ENDPOINT).replace(/\/+$/, '');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function loadPolicy() {
|
|
36
|
+
try {
|
|
37
|
+
const raw = await readFile(ROUTER_POLICY_PATH, 'utf8');
|
|
38
|
+
return JSON.parse(raw);
|
|
39
|
+
} catch {
|
|
40
|
+
return {
|
|
41
|
+
version: '0.0.0',
|
|
42
|
+
endpoint: DEFAULT_ENDPOINT,
|
|
43
|
+
tiers: {},
|
|
44
|
+
agents: {},
|
|
45
|
+
policies: {},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function fetchLiveModels(endpoint) {
|
|
51
|
+
try {
|
|
52
|
+
const res = await fetch(`${endpoint}/models`, {
|
|
53
|
+
signal: AbortSignal.timeout(2000),
|
|
54
|
+
});
|
|
55
|
+
if (!res.ok) return { reachable: false, error: `HTTP ${res.status}` };
|
|
56
|
+
const body = await res.json();
|
|
57
|
+
return {
|
|
58
|
+
reachable: true,
|
|
59
|
+
endpoint,
|
|
60
|
+
models: Array.isArray(body?.data) ? body.data : [],
|
|
61
|
+
};
|
|
62
|
+
} catch (err) {
|
|
63
|
+
return {
|
|
64
|
+
reachable: false,
|
|
65
|
+
endpoint,
|
|
66
|
+
error: err instanceof Error ? err.message : String(err),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* v10.1.0 — Resolve the concrete model + endpoint for a named agent
|
|
73
|
+
* directly from the on-disk policy. No SDK import needed (keeps the
|
|
74
|
+
* dashboard route surface lightweight). Used by the front-end's
|
|
75
|
+
* "agent fleet" view, which renders one card per agent with its
|
|
76
|
+
* modelId + endpoint badge.
|
|
77
|
+
*/
|
|
78
|
+
function resolveAgentFromPolicy(policy, agent, endpoint) {
|
|
79
|
+
const agents = policy?.agents || {};
|
|
80
|
+
const entry = agents[agent];
|
|
81
|
+
if (entry && typeof entry.model === 'string') {
|
|
82
|
+
return {
|
|
83
|
+
agent,
|
|
84
|
+
modelId: entry.model,
|
|
85
|
+
tier: typeof entry.tier === 'string' ? entry.tier : 'default',
|
|
86
|
+
endpoint,
|
|
87
|
+
rationale: typeof entry.rationale === 'string' ? entry.rationale : '',
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
// Fallback: first known entry by name, otherwise the default tier.
|
|
91
|
+
const fallback = Object.values(agents).find((e) => e && typeof e.model === 'string');
|
|
92
|
+
if (fallback) {
|
|
93
|
+
return {
|
|
94
|
+
agent,
|
|
95
|
+
modelId: fallback.model,
|
|
96
|
+
tier: typeof fallback.tier === 'string' ? fallback.tier : 'default',
|
|
97
|
+
endpoint,
|
|
98
|
+
rationale: 'fallback (agent not in policy)',
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
agent,
|
|
103
|
+
modelId: 'bizar/MiniMax-M3',
|
|
104
|
+
tier: 'default',
|
|
105
|
+
endpoint,
|
|
106
|
+
rationale: 'fallback (no policy)',
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* @param {object} deps
|
|
112
|
+
* @param {object} deps.state
|
|
113
|
+
* @returns {import('express').Router}
|
|
114
|
+
*/
|
|
115
|
+
export function createModelRouterRouter({ state }) {
|
|
116
|
+
const router = Router();
|
|
117
|
+
|
|
118
|
+
router.get('/model-router', wrap(async (_req, res) => {
|
|
119
|
+
const policy = await loadPolicy();
|
|
120
|
+
const endpoint = resolveEndpoint(state);
|
|
121
|
+
const live = await fetchLiveModels(endpoint);
|
|
122
|
+
res.json({
|
|
123
|
+
endpoint,
|
|
124
|
+
policy,
|
|
125
|
+
live,
|
|
126
|
+
agents: policy.agents || {},
|
|
127
|
+
tiers: policy.tiers || {},
|
|
128
|
+
});
|
|
129
|
+
}));
|
|
130
|
+
|
|
131
|
+
router.get('/model-router/models', wrap(async (_req, res) => {
|
|
132
|
+
const endpoint = resolveEndpoint(state);
|
|
133
|
+
const live = await fetchLiveModels(endpoint);
|
|
134
|
+
res.json(live);
|
|
135
|
+
}));
|
|
136
|
+
|
|
137
|
+
router.get('/model-router/agents', wrap(async (_req, res) => {
|
|
138
|
+
const policy = await loadPolicy();
|
|
139
|
+
res.json({ agents: policy.agents || {}, tiers: policy.tiers || {} });
|
|
140
|
+
}));
|
|
141
|
+
|
|
142
|
+
// v10.1.0 — Resolve one agent to its concrete model + endpoint.
|
|
143
|
+
// The front-end's ModelRouter page calls this so it doesn't need to
|
|
144
|
+
// duplicate the registry logic client-side.
|
|
145
|
+
router.get('/model-router/resolve/:agent', wrap(async (req, res) => {
|
|
146
|
+
const agent = String(req.params.agent || '').trim();
|
|
147
|
+
if (!/^[a-z][a-z0-9_-]{0,63}$/i.test(agent)) {
|
|
148
|
+
res.status(400).json({ error: 'bad_slug', message: 'agent must match /^[a-z][a-z0-9_-]{0,63}$/' });
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const policy = await loadPolicy();
|
|
152
|
+
const endpoint = resolveEndpoint(state);
|
|
153
|
+
const resolved = resolveAgentFromPolicy(policy, agent, endpoint);
|
|
154
|
+
res.json(resolved);
|
|
155
|
+
}));
|
|
156
|
+
|
|
157
|
+
return router;
|
|
158
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smoke test for /api/model-router. Verifies:
|
|
3
|
+
* - the route resolves and returns the agent→model map
|
|
4
|
+
* - live model probe gracefully degrades when the endpoint is unreachable
|
|
5
|
+
* - the policy file is read from `.claude/model-router.json`
|
|
6
|
+
*
|
|
7
|
+
* Run with: bun test src/server/routes/model-router.test.mjs
|
|
8
|
+
* or: node --test src/server/routes/model-router.test.mjs
|
|
9
|
+
*/
|
|
10
|
+
import { test } from 'node:test';
|
|
11
|
+
import assert from 'node:assert/strict';
|
|
12
|
+
import express from 'express';
|
|
13
|
+
import { createModelRouterRouter } from './model-router.mjs';
|
|
14
|
+
|
|
15
|
+
function buildApp() {
|
|
16
|
+
const app = express();
|
|
17
|
+
// Mount under /api to match the dashboard's mount prefix.
|
|
18
|
+
app.use('/api', createModelRouterRouter({ state: {} }));
|
|
19
|
+
return app;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function getJson(app, path) {
|
|
23
|
+
const server = app.listen(0);
|
|
24
|
+
try {
|
|
25
|
+
const port = server.address().port;
|
|
26
|
+
const res = await fetch(`http://127.0.0.1:${port}${path}`);
|
|
27
|
+
return { status: res.status, body: await res.json() };
|
|
28
|
+
} finally {
|
|
29
|
+
await new Promise((r) => server.close(r));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
test('GET /api/model-router returns agent→model map from .claude/model-router.json', async () => {
|
|
34
|
+
const app = buildApp();
|
|
35
|
+
const { status, body } = await getJson(app, '/api/model-router');
|
|
36
|
+
assert.equal(status, 200);
|
|
37
|
+
assert.ok(typeof body.endpoint === 'string' && body.endpoint.length > 0, 'endpoint present');
|
|
38
|
+
assert.ok(body.policy, 'policy present');
|
|
39
|
+
assert.ok(body.agents, 'agents map present');
|
|
40
|
+
// Forseti / Baldr / Vidarr must be GPT (premium tier per the routing rules).
|
|
41
|
+
assert.match(body.agents.forseti.model, /gpt-5\.6-sol/, 'forseti uses premium GPT');
|
|
42
|
+
assert.match(body.agents.baldr.model, /gpt-5\.6-sol/, 'baldr uses premium GPT');
|
|
43
|
+
assert.match(body.agents.vidarr.model, /gpt-5\.6-sol/, 'vidarr uses premium GPT');
|
|
44
|
+
// Tyr + Odin use the high-tier terra model.
|
|
45
|
+
assert.match(body.agents.tyr.model, /gpt-5\.6-terra/, 'tyr uses high-tier GPT');
|
|
46
|
+
assert.match(body.agents.odin.model, /gpt-5\.6-terra/, 'odin uses high-tier GPT');
|
|
47
|
+
// Everyday default agents use MiniMax-M3.
|
|
48
|
+
assert.equal(body.agents.frigg.model, 'bizar/MiniMax-M3', 'frigg uses default');
|
|
49
|
+
assert.equal(body.agents.hermod.model, 'bizar/MiniMax-M3', 'hermod uses default');
|
|
50
|
+
// Vor uses the budget tier.
|
|
51
|
+
assert.match(body.agents.vor.model, /mimo|deepseek/, 'vor uses budget tier');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('GET /api/model-router/agents returns just the agents+tiers map', async () => {
|
|
55
|
+
const app = buildApp();
|
|
56
|
+
const { status, body } = await getJson(app, '/api/model-router/agents');
|
|
57
|
+
assert.equal(status, 200);
|
|
58
|
+
assert.ok(body.agents.thor, 'thor present');
|
|
59
|
+
assert.ok(body.tiers.default, 'default tier present');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('GET /api/model-router/models probe handles unreachable endpoint gracefully', async () => {
|
|
63
|
+
// Override env to a guaranteed-unreachable port.
|
|
64
|
+
const prev = process.env.BIZAR_MODEL_ROUTER_URL;
|
|
65
|
+
process.env.BIZAR_MODEL_ROUTER_URL = 'http://127.0.0.1:1';
|
|
66
|
+
try {
|
|
67
|
+
const app = buildApp();
|
|
68
|
+
const { status, body } = await getJson(app, '/api/model-router/models');
|
|
69
|
+
assert.equal(status, 200);
|
|
70
|
+
assert.equal(body.reachable, false);
|
|
71
|
+
assert.ok(typeof body.error === 'string', 'error string present');
|
|
72
|
+
} finally {
|
|
73
|
+
if (prev === undefined) delete process.env.BIZAR_MODEL_ROUTER_URL;
|
|
74
|
+
else process.env.BIZAR_MODEL_ROUTER_URL = prev;
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('GET /api/model-router/resolve/:agent returns the concrete model + endpoint', async () => {
|
|
79
|
+
const app = buildApp();
|
|
80
|
+
const { status, body } = await getJson(app, '/api/model-router/resolve/tyr');
|
|
81
|
+
assert.equal(status, 200);
|
|
82
|
+
assert.equal(body.agent, 'tyr');
|
|
83
|
+
assert.match(body.modelId, /gpt-5\.6-terra/, 'tyr uses high-tier terra');
|
|
84
|
+
assert.match(body.endpoint, /^http/, 'endpoint is a URL');
|
|
85
|
+
assert.equal(typeof body.tier, 'string');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('GET /api/model-router/resolve/:agent falls back for unknown agents', async () => {
|
|
89
|
+
const app = buildApp();
|
|
90
|
+
const { status, body } = await getJson(app, '/api/model-router/resolve/nonexistent-agent-xyz');
|
|
91
|
+
assert.equal(status, 200);
|
|
92
|
+
assert.equal(body.agent, 'nonexistent-agent-xyz');
|
|
93
|
+
assert.match(body.modelId, /[a-z0-9/_.-]+/, 'returns a model id');
|
|
94
|
+
assert.match(body.endpoint, /^http/, 'endpoint is a URL');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('GET /api/model-router/resolve/:agent rejects bad slugs', async () => {
|
|
98
|
+
const app = buildApp();
|
|
99
|
+
// Express decodes percent-encoded path segments before the handler runs.
|
|
100
|
+
// Use a slug that's syntactically illegal per the route's regex.
|
|
101
|
+
const { status, body } = await getJson(app, '/api/model-router/resolve/has spaces');
|
|
102
|
+
assert.equal(status, 400);
|
|
103
|
+
assert.equal(body.error, 'bad_slug');
|
|
104
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/server/routes/sessions.mjs
|
|
3
|
+
*
|
|
4
|
+
* Pillar A — Session heartbeat list + stop.
|
|
5
|
+
*
|
|
6
|
+
* GET /api/sessions — list live sessions from .harness/traces/heartbeat.jsonl
|
|
7
|
+
* POST /api/sessions/:id/stop — mark a session as done
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Router } from "express";
|
|
11
|
+
import { existsSync, readFileSync, appendFileSync, mkdirSync } from "node:fs";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import { wrap } from "./_shared.mjs";
|
|
14
|
+
|
|
15
|
+
const HEARTBEAT_FILE = ".harness/traces/heartbeat.jsonl";
|
|
16
|
+
|
|
17
|
+
function resolveHeartbeatPath(repoRoot) {
|
|
18
|
+
return join(repoRoot ?? process.cwd(), HEARTBEAT_FILE);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param {string} repoRoot
|
|
23
|
+
* @returns {Array<{session_id: string, started_at: string, last_heartbeat_at: string, status: string}>}
|
|
24
|
+
*/
|
|
25
|
+
function readSessions(repoRoot) {
|
|
26
|
+
const fp = resolveHeartbeatPath(repoRoot);
|
|
27
|
+
if (!existsSync(fp)) return [];
|
|
28
|
+
try {
|
|
29
|
+
const raw = readFileSync(fp, "utf-8");
|
|
30
|
+
const lines = raw.split("\n").filter(Boolean);
|
|
31
|
+
// Deduplicate by session_id, keeping the last record per session
|
|
32
|
+
const map = new Map();
|
|
33
|
+
for (const line of lines) {
|
|
34
|
+
try {
|
|
35
|
+
const rec = JSON.parse(line);
|
|
36
|
+
if (rec?.session_id) map.set(rec.session_id, rec);
|
|
37
|
+
} catch {
|
|
38
|
+
// skip malformed lines
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return Array.from(map.values());
|
|
42
|
+
} catch {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function createSessionsRouter({ projectRoot = process.cwd() } = {}) {
|
|
48
|
+
const router = Router();
|
|
49
|
+
|
|
50
|
+
// GET /api/sessions
|
|
51
|
+
router.get("/sessions", wrap(async (_req, res) => {
|
|
52
|
+
const sessions = readSessions(projectRoot);
|
|
53
|
+
res.json({ sessions });
|
|
54
|
+
}));
|
|
55
|
+
|
|
56
|
+
// POST /api/sessions/:id/stop
|
|
57
|
+
router.post("/sessions/:id/stop", wrap(async (req, res) => {
|
|
58
|
+
const id = String(req.params?.id ?? "").trim();
|
|
59
|
+
if (!id) {
|
|
60
|
+
res.status(400).json({ error: "bad_request", message: "session id is required" });
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const fp = resolveHeartbeatPath(projectRoot);
|
|
64
|
+
// Ensure parent dir exists
|
|
65
|
+
const dir = join(fp, "..");
|
|
66
|
+
if (!existsSync(dir)) {
|
|
67
|
+
mkdirSync(dir, { recursive: true });
|
|
68
|
+
}
|
|
69
|
+
const record = {
|
|
70
|
+
session_id: id,
|
|
71
|
+
started_at: new Date().toISOString(),
|
|
72
|
+
last_heartbeat_at: new Date().toISOString(),
|
|
73
|
+
status: "done",
|
|
74
|
+
};
|
|
75
|
+
appendFileSync(fp, JSON.stringify(record) + "\n", "utf-8");
|
|
76
|
+
res.json({ session_id: id, status: "done" });
|
|
77
|
+
}));
|
|
78
|
+
|
|
79
|
+
return router;
|
|
80
|
+
}
|
|
@@ -436,32 +436,6 @@ export async function createServer({
|
|
|
436
436
|
broadcast: localBroadcast,
|
|
437
437
|
});
|
|
438
438
|
|
|
439
|
-
// v5.0.0 — Headroom startup hook. Runs after api.mjs is loaded so the
|
|
440
|
-
// headroom routes are registered. Errors are caught and logged — startup
|
|
441
|
-
// must not fail if Headroom has issues.
|
|
442
|
-
//
|
|
443
|
-
// v10-S9 — opt-out via BIZAR_HEADROOM_AUTOSTART=0. The hook runs
|
|
444
|
-
// `npm install` + a child process spawn which can take 2-5s on cold
|
|
445
|
-
// boot. Real users hit this on every dashboard launch; tests hit it
|
|
446
|
-
// too. Setting the env var skips the install + child-process spawn
|
|
447
|
-
// entirely. Default behaviour is unchanged (settings.json
|
|
448
|
-
// `headroom.enabled` still controls).
|
|
449
|
-
if (/^(0|false|no|off)$/i.test((process.env.BIZAR_HEADROOM_AUTOSTART || '').trim())) {
|
|
450
|
-
console.log('[bizar-dash] headroom startup skipped (BIZAR_HEADROOM_AUTOSTART=0)');
|
|
451
|
-
} else {
|
|
452
|
-
const { headroomStartupHook } = await import('./headroom.mjs');
|
|
453
|
-
try {
|
|
454
|
-
const settings = readSettings();
|
|
455
|
-
if (settings?.data?.headroom) {
|
|
456
|
-
headroomStartupHook(settings.data.headroom).catch((err) => {
|
|
457
|
-
console.warn('[bizar-dash] headroomStartupHook error:', err?.message || err);
|
|
458
|
-
});
|
|
459
|
-
}
|
|
460
|
-
} catch (err) {
|
|
461
|
-
console.warn('[bizar-dash] headroom startup hook skipped:', err?.message || err);
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
|
|
465
439
|
// v5.5.2 — Auto-migrate legacy git.repoPath from the old vault location
|
|
466
440
|
// to the new default. Runs before the lightrag hook so git-dependent
|
|
467
441
|
// checks are already correct.
|
|
@@ -479,7 +453,7 @@ export async function createServer({
|
|
|
479
453
|
console.warn('[bizar-dash] git.repoPath migration check failed:', err?.message || err);
|
|
480
454
|
}
|
|
481
455
|
|
|
482
|
-
// v5.x — LightRAG startup hook (issue #6).
|
|
456
|
+
// v5.x — LightRAG startup hook (issue #6).
|
|
483
457
|
// reads config from .bizar/memory.json + env vars, then calls
|
|
484
458
|
// lightragStartupHook() which respects `lightrag.enabled` and the
|
|
485
459
|
// BIZAR_LIGHTRAG_AUTOSTART env override. All errors are caught — the
|