@polderlabs/bizar 10.1.0 → 10.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -10
- package/bizar-dash/CONTROL_SURFACES.md +0 -1
- package/bizar-dash/dist/assets/{ActivityFeed-Da-JCrE6.js → ActivityFeed-xXiUCZLB.js} +1 -1
- package/bizar-dash/dist/assets/{ActivityFeed-Da-JCrE6.js.map → ActivityFeed-xXiUCZLB.js.map} +1 -1
- package/bizar-dash/dist/assets/{ActivityView-nIRsSETb.js → ActivityView-nOYhCp3h.js} +1 -1
- package/bizar-dash/dist/assets/{ActivityView-nIRsSETb.js.map → ActivityView-nOYhCp3h.js.map} +1 -1
- package/bizar-dash/dist/assets/{AdminView-DP1NMxmG.js → AdminView-Coi4V3sq.js} +1 -1
- package/bizar-dash/dist/assets/{AdminView-DP1NMxmG.js.map → AdminView-Coi4V3sq.js.map} +1 -1
- package/bizar-dash/dist/assets/{AgentActivity-lSPkOr-O.js → AgentActivity-nEri4lbx.js} +1 -1
- package/bizar-dash/dist/assets/{AgentActivity-lSPkOr-O.js.map → AgentActivity-nEri4lbx.js.map} +1 -1
- package/bizar-dash/dist/assets/{AgentsView-BIWGkz7w.js → AgentsView-ByiIJ0ej.js} +3 -3
- package/bizar-dash/dist/assets/{AgentsView-BIWGkz7w.js.map → AgentsView-ByiIJ0ej.js.map} +1 -1
- package/bizar-dash/dist/assets/{Alert-DN0UysMg.js → Alert-Dg_Vwxh5.js} +1 -1
- package/bizar-dash/dist/assets/{Alert-DN0UysMg.js.map → Alert-Dg_Vwxh5.js.map} +1 -1
- package/bizar-dash/dist/assets/ArtifactsView-CGclRNnz.js +21 -0
- package/bizar-dash/dist/assets/ArtifactsView-CGclRNnz.js.map +1 -0
- package/bizar-dash/dist/assets/{AuthView-DW5cs7ma.js → AuthView-utVm6M3D.js} +1 -1
- package/bizar-dash/dist/assets/{AuthView-DW5cs7ma.js.map → AuthView-utVm6M3D.js.map} +1 -1
- package/bizar-dash/dist/assets/{Avatar-DZPv9pvd.js → Avatar-CXDZdKy4.js} +1 -1
- package/bizar-dash/dist/assets/{Avatar-DZPv9pvd.js.map → Avatar-CXDZdKy4.js.map} +1 -1
- package/bizar-dash/dist/assets/{BackgroundJobsView-D8O-EyMD.js → BackgroundJobsView-j603pz6o.js} +1 -1
- package/bizar-dash/dist/assets/{BackgroundJobsView-D8O-EyMD.js.map → BackgroundJobsView-j603pz6o.js.map} +1 -1
- package/bizar-dash/dist/assets/{BackupView-uwXkhxOV.js → BackupView-DgvnntTE.js} +1 -1
- package/bizar-dash/dist/assets/{BackupView-uwXkhxOV.js.map → BackupView-DgvnntTE.js.map} +1 -1
- package/bizar-dash/dist/assets/{Card-Tyboihu7.js → Card-D9019JOD.js} +1 -1
- package/bizar-dash/dist/assets/{Card-Tyboihu7.js.map → Card-D9019JOD.js.map} +1 -1
- package/bizar-dash/dist/assets/{ChatView-DRBHzgay.js → ChatView-C0cTgj5d.js} +1 -1
- package/bizar-dash/dist/assets/{ChatView-DRBHzgay.js.map → ChatView-C0cTgj5d.js.map} +1 -1
- package/bizar-dash/dist/assets/{Chip-BrXIlR9w.js → Chip-DQtalZ4y.js} +1 -1
- package/bizar-dash/dist/assets/{Chip-BrXIlR9w.js.map → Chip-DQtalZ4y.js.map} +1 -1
- package/bizar-dash/dist/assets/{ClaudeSessionsView-Ck62jaOr.js → ClaudeSessionsView-H3Bqjo6b.js} +1 -1
- package/bizar-dash/dist/assets/{ClaudeSessionsView-Ck62jaOr.js.map → ClaudeSessionsView-H3Bqjo6b.js.map} +1 -1
- package/bizar-dash/dist/assets/{ClipboardView-DfND6xL6.js → ClipboardView-BAguaKbx.js} +1 -1
- package/bizar-dash/dist/assets/{ClipboardView-DfND6xL6.js.map → ClipboardView-BAguaKbx.js.map} +1 -1
- package/bizar-dash/dist/assets/{ConfigView-DjJmSrOJ.js → ConfigView-DWHJgoe8.js} +1 -1
- package/bizar-dash/dist/assets/{ConfigView-DjJmSrOJ.js.map → ConfigView-DWHJgoe8.js.map} +1 -1
- package/bizar-dash/dist/assets/{DiagnosticsView-CKgItf0J.js → DiagnosticsView-oCT624hk.js} +1 -1
- package/bizar-dash/dist/assets/{DiagnosticsView-CKgItf0J.js.map → DiagnosticsView-oCT624hk.js.map} +1 -1
- package/bizar-dash/dist/assets/{DialogsView-DHYF8dE1.js → DialogsView-CqHNals_.js} +1 -1
- package/bizar-dash/dist/assets/{DialogsView-DHYF8dE1.js.map → DialogsView-CqHNals_.js.map} +1 -1
- package/bizar-dash/dist/assets/{DoctorView-DDLHrSKk.js → DoctorView-6kfQq_Mx.js} +1 -1
- package/bizar-dash/dist/assets/{DoctorView-DDLHrSKk.js.map → DoctorView-6kfQq_Mx.js.map} +1 -1
- package/bizar-dash/dist/assets/{EmptyState-DZ2ouKwd.js → EmptyState-DLmtBX_6.js} +1 -1
- package/bizar-dash/dist/assets/{EmptyState-DZ2ouKwd.js.map → EmptyState-DLmtBX_6.js.map} +1 -1
- package/bizar-dash/dist/assets/{EnvVarsView-D1cx83mB.js → EnvVarsView-DySOXlP1.js} +2 -2
- package/bizar-dash/dist/assets/{EnvVarsView-D1cx83mB.js.map → EnvVarsView-DySOXlP1.js.map} +1 -1
- package/bizar-dash/dist/assets/{ErrorState-DwwJ2-Jq.js → ErrorState-Bump8diE.js} +1 -1
- package/bizar-dash/dist/assets/{ErrorState-DwwJ2-Jq.js.map → ErrorState-Bump8diE.js.map} +1 -1
- package/bizar-dash/dist/assets/{EvalView-C9aYixXW.js → EvalView-FnZ5d93z.js} +1 -1
- package/bizar-dash/dist/assets/{EvalView-C9aYixXW.js.map → EvalView-FnZ5d93z.js.map} +1 -1
- package/bizar-dash/dist/assets/{GoalsView-DZa_mGmh.js → GoalsView-C4m1WnVv.js} +1 -1
- package/bizar-dash/dist/assets/{GoalsView-DZa_mGmh.js.map → GoalsView-C4m1WnVv.js.map} +1 -1
- package/bizar-dash/dist/assets/{Grid-CiRyhAgR.js → Grid-dEnoGGws.js} +1 -1
- package/bizar-dash/dist/assets/{Grid-CiRyhAgR.js.map → Grid-dEnoGGws.js.map} +1 -1
- package/bizar-dash/dist/assets/{HistoryView-Dv7aPxJE.js → HistoryView-xdRybjZ2.js} +1 -1
- package/bizar-dash/dist/assets/{HistoryView-Dv7aPxJE.js.map → HistoryView-xdRybjZ2.js.map} +1 -1
- package/bizar-dash/dist/assets/{Input-BbFUDQnS.js → Input-BSa3lh_-.js} +1 -1
- package/bizar-dash/dist/assets/{Input-BbFUDQnS.js.map → Input-BSa3lh_-.js.map} +1 -1
- package/bizar-dash/dist/assets/{LibrariesView-DBQ83TNa.js → LibrariesView-1zR04Eci.js} +1 -1
- package/bizar-dash/dist/assets/{LibrariesView-DBQ83TNa.js.map → LibrariesView-1zR04Eci.js.map} +1 -1
- package/bizar-dash/dist/assets/{LightRAGView-JYpE5u9p.js → LightRAGView-PubmA9oL.js} +1 -1
- package/bizar-dash/dist/assets/{LightRAGView-JYpE5u9p.js.map → LightRAGView-PubmA9oL.js.map} +1 -1
- package/bizar-dash/dist/assets/{ListHeader-BGjm11Z0.js → ListHeader-Ak9WJd39.js} +1 -1
- package/bizar-dash/dist/assets/{ListHeader-BGjm11Z0.js.map → ListHeader-Ak9WJd39.js.map} +1 -1
- package/bizar-dash/dist/assets/{MemoryView-CnOP2MvM.js → MemoryView-DBGpuETw.js} +1 -1
- package/bizar-dash/dist/assets/{MemoryView-CnOP2MvM.js.map → MemoryView-DBGpuETw.js.map} +1 -1
- package/bizar-dash/dist/assets/MiscView-DufqBHkX.js +1 -0
- package/bizar-dash/dist/assets/{MiscView-BBcpUbZZ.js.map → MiscView-DufqBHkX.js.map} +1 -1
- package/bizar-dash/dist/assets/{ModsView-CU4cAmIz.js → ModsView-DcX6eVU2.js} +2 -2
- package/bizar-dash/dist/assets/{ModsView-CU4cAmIz.js.map → ModsView-DcX6eVU2.js.map} +1 -1
- package/bizar-dash/dist/assets/{NotificationsView-BYffvthq.js → NotificationsView-B8IhHRQC.js} +1 -1
- package/bizar-dash/dist/assets/{NotificationsView-BYffvthq.js.map → NotificationsView-B8IhHRQC.js.map} +1 -1
- package/bizar-dash/dist/assets/ObsidianView-DEuk65zS.js +1 -0
- package/bizar-dash/dist/assets/{ObsidianView-D3bI0row.js.map → ObsidianView-DEuk65zS.js.map} +1 -1
- package/bizar-dash/dist/assets/{OverviewView-0tq8f-Y0.js → OverviewView-CTI2spTd.js} +1 -1
- package/bizar-dash/dist/assets/{OverviewView-0tq8f-Y0.js.map → OverviewView-CTI2spTd.js.map} +1 -1
- package/bizar-dash/dist/assets/{ProgressBar-B-h60LKA.js → ProgressBar-D3lkz42E.js} +1 -1
- package/bizar-dash/dist/assets/{ProgressBar-B-h60LKA.js.map → ProgressBar-D3lkz42E.js.map} +1 -1
- package/bizar-dash/dist/assets/{ProjectsView-DhX5lzHS.js → ProjectsView-Du0LzE7A.js} +1 -1
- package/bizar-dash/dist/assets/{ProjectsView-DhX5lzHS.js.map → ProjectsView-Du0LzE7A.js.map} +1 -1
- package/bizar-dash/dist/assets/{ProvidersView-CJMX7jm9.js → ProvidersView-5bCgXwBo.js} +1 -1
- package/bizar-dash/dist/assets/{ProvidersView-CJMX7jm9.js.map → ProvidersView-5bCgXwBo.js.map} +1 -1
- package/bizar-dash/dist/assets/{SchedulesView-CcwDQ9Nm.js → SchedulesView-DFHdIz5P.js} +1 -1
- package/bizar-dash/dist/assets/{SchedulesView-CcwDQ9Nm.js.map → SchedulesView-DFHdIz5P.js.map} +1 -1
- package/bizar-dash/dist/assets/{Select-Cw7Qtz2M.js → Select-C8EJj4NM.js} +1 -1
- package/bizar-dash/dist/assets/{Select-Cw7Qtz2M.js.map → Select-C8EJj4NM.js.map} +1 -1
- package/bizar-dash/dist/assets/{SettingsRow-DgsgbPSI.js → SettingsRow-BdOOmYMD.js} +1 -1
- package/bizar-dash/dist/assets/{SettingsRow-DgsgbPSI.js.map → SettingsRow-BdOOmYMD.js.map} +1 -1
- package/bizar-dash/dist/assets/{SettingsView-DTAFdKyK.js → SettingsView-CqwQ6Knv.js} +1 -1
- package/bizar-dash/dist/assets/{SettingsView-DTAFdKyK.js.map → SettingsView-CqwQ6Knv.js.map} +1 -1
- package/bizar-dash/dist/assets/{Sheet-DH-WNJ3Y.js → Sheet-Cztsvgse.js} +1 -1
- package/bizar-dash/dist/assets/{Sheet-DH-WNJ3Y.js.map → Sheet-Cztsvgse.js.map} +1 -1
- package/bizar-dash/dist/assets/{Slider-DXbJuQ7y.js → Slider-BexNfF6K.js} +1 -1
- package/bizar-dash/dist/assets/{Slider-DXbJuQ7y.js.map → Slider-BexNfF6K.js.map} +1 -1
- package/bizar-dash/dist/assets/{Sparkline-CYtAn82j.js → Sparkline-CLZBtvsN.js} +1 -1
- package/bizar-dash/dist/assets/{Sparkline-CYtAn82j.js.map → Sparkline-CLZBtvsN.js.map} +1 -1
- package/bizar-dash/dist/assets/{Switch-BItGrPqr.js → Switch-B1tiRwNK.js} +1 -1
- package/bizar-dash/dist/assets/{Switch-BItGrPqr.js.map → Switch-B1tiRwNK.js.map} +1 -1
- package/bizar-dash/dist/assets/{TasksView-i5tm6CkX.js → TasksView-CT0fPlos.js} +1 -1
- package/bizar-dash/dist/assets/{TasksView-i5tm6CkX.js.map → TasksView-CT0fPlos.js.map} +1 -1
- package/bizar-dash/dist/assets/{Textarea-qYDf3yds.js → Textarea-D_TPpVMy.js} +1 -1
- package/bizar-dash/dist/assets/{Textarea-qYDf3yds.js.map → Textarea-D_TPpVMy.js.map} +1 -1
- package/bizar-dash/dist/assets/{UpdateView-Xln-ej26.js → UpdateView-tD2q2OhW.js} +1 -1
- package/bizar-dash/dist/assets/{UpdateView-Xln-ej26.js.map → UpdateView-tD2q2OhW.js.map} +1 -1
- package/bizar-dash/dist/assets/{UsageView-BGuZSUZ6.js → UsageView-B4BuAFKA.js} +1 -1
- package/bizar-dash/dist/assets/{UsageView-BGuZSUZ6.js.map → UsageView-B4BuAFKA.js.map} +1 -1
- package/bizar-dash/dist/assets/{ViewHeader-BqIPVKuG.js → ViewHeader-V_SzbUpw.js} +1 -1
- package/bizar-dash/dist/assets/{ViewHeader-BqIPVKuG.js.map → ViewHeader-V_SzbUpw.js.map} +1 -1
- package/bizar-dash/dist/assets/{VoiceView-CWUOCQs_.js → VoiceView-D3vb5OM2.js} +1 -1
- package/bizar-dash/dist/assets/{VoiceView-CWUOCQs_.js.map → VoiceView-D3vb5OM2.js.map} +1 -1
- package/bizar-dash/dist/assets/{brain-fsBSDl2g.js → brain-BmfWcitQ.js} +1 -1
- package/bizar-dash/dist/assets/{brain-fsBSDl2g.js.map → brain-BmfWcitQ.js.map} +1 -1
- package/bizar-dash/dist/assets/{calendar-DsHTZoCU.js → calendar-VntJUzFB.js} +1 -1
- package/bizar-dash/dist/assets/{calendar-DsHTZoCU.js.map → calendar-VntJUzFB.js.map} +1 -1
- package/bizar-dash/dist/assets/{circle-CqSxqqeJ.js → circle-KrQQySeK.js} +1 -1
- package/bizar-dash/dist/assets/{circle-CqSxqqeJ.js.map → circle-KrQQySeK.js.map} +1 -1
- package/bizar-dash/dist/assets/{circle-alert-BsLl001l.js → circle-alert-CQ9MLygI.js} +1 -1
- package/bizar-dash/dist/assets/{circle-alert-BsLl001l.js.map → circle-alert-CQ9MLygI.js.map} +1 -1
- package/bizar-dash/dist/assets/{circle-check-rVKMQmZp.js → circle-check-COtVgsWw.js} +1 -1
- package/bizar-dash/dist/assets/{circle-check-rVKMQmZp.js.map → circle-check-COtVgsWw.js.map} +1 -1
- package/bizar-dash/dist/assets/{clock-OuficfgH.js → clock-C0GlM4qp.js} +1 -1
- package/bizar-dash/dist/assets/{clock-OuficfgH.js.map → clock-C0GlM4qp.js.map} +1 -1
- package/bizar-dash/dist/assets/{download-BdHlstUY.js → download-CW4DmtRe.js} +1 -1
- package/bizar-dash/dist/assets/{download-BdHlstUY.js.map → download-CW4DmtRe.js.map} +1 -1
- package/bizar-dash/dist/assets/{external-link-DA3_VGm-.js → external-link-CAOnh_DH.js} +1 -1
- package/bizar-dash/dist/assets/{external-link-DA3_VGm-.js.map → external-link-CAOnh_DH.js.map} +1 -1
- package/bizar-dash/dist/assets/{file-text-B-g8H227.js → file-text-CS1jB_oV.js} +1 -1
- package/bizar-dash/dist/assets/{file-text-B-g8H227.js.map → file-text-CS1jB_oV.js.map} +1 -1
- package/bizar-dash/dist/assets/{filter-D9B8s5CG.js → filter-DyQbml3L.js} +1 -1
- package/bizar-dash/dist/assets/{filter-D9B8s5CG.js.map → filter-DyQbml3L.js.map} +1 -1
- package/bizar-dash/dist/assets/{git-pull-request-ByH0YuVz.js → git-pull-request-Co6gYfba.js} +1 -1
- package/bizar-dash/dist/assets/{git-pull-request-ByH0YuVz.js.map → git-pull-request-Co6gYfba.js.map} +1 -1
- package/bizar-dash/dist/assets/{main-CdOm1Pj9.js → main-CyAIirBB.js} +28 -33
- package/bizar-dash/dist/assets/main-CyAIirBB.js.map +1 -0
- package/bizar-dash/dist/assets/network-CDn43Gsj.js +6 -0
- package/bizar-dash/dist/assets/network-CDn43Gsj.js.map +1 -0
- package/bizar-dash/dist/assets/{pause-C9hgd8wX.js → pause-CKYjSTGX.js} +1 -1
- package/bizar-dash/dist/assets/{pause-C9hgd8wX.js.map → pause-CKYjSTGX.js.map} +1 -1
- package/bizar-dash/dist/assets/{pencil-BanqaMw7.js → pencil-BUJ2DpWs.js} +1 -1
- package/bizar-dash/dist/assets/{pencil-BanqaMw7.js.map → pencil-BUJ2DpWs.js.map} +1 -1
- package/bizar-dash/dist/assets/{play-CawUZ02_.js → play-CGxZWqtW.js} +1 -1
- package/bizar-dash/dist/assets/{play-CawUZ02_.js.map → play-CGxZWqtW.js.map} +1 -1
- package/bizar-dash/dist/assets/{plug-Cmv0-T_e.js → plug-CQYGgGSy.js} +1 -1
- package/bizar-dash/dist/assets/{plug-Cmv0-T_e.js.map → plug-CQYGgGSy.js.map} +1 -1
- package/bizar-dash/dist/assets/{plus-M6LYiKX8.js → plus-fFxt-8z5.js} +1 -1
- package/bizar-dash/dist/assets/{plus-M6LYiKX8.js.map → plus-fFxt-8z5.js.map} +1 -1
- package/bizar-dash/dist/assets/{power-DP5x_SRF.js → power-Cjgy2KsI.js} +1 -1
- package/bizar-dash/dist/assets/{power-DP5x_SRF.js.map → power-Cjgy2KsI.js.map} +1 -1
- package/bizar-dash/dist/assets/{power-off-NOJRLAIs.js → power-off-B81hLRah.js} +1 -1
- package/bizar-dash/dist/assets/{power-off-NOJRLAIs.js.map → power-off-B81hLRah.js.map} +1 -1
- package/bizar-dash/dist/assets/{refresh-ccw-CHMprsm5.js → refresh-ccw-BCXx86xp.js} +1 -1
- package/bizar-dash/dist/assets/{refresh-ccw-CHMprsm5.js.map → refresh-ccw-BCXx86xp.js.map} +1 -1
- package/bizar-dash/dist/assets/{refresh-cw-1RBqDKgJ.js → refresh-cw-COTg1nZo.js} +1 -1
- package/bizar-dash/dist/assets/{refresh-cw-1RBqDKgJ.js.map → refresh-cw-COTg1nZo.js.map} +1 -1
- package/bizar-dash/dist/assets/{rotate-ccw-DJRekBbi.js → rotate-ccw-BZrEVc0Z.js} +1 -1
- package/bizar-dash/dist/assets/{rotate-ccw-DJRekBbi.js.map → rotate-ccw-BZrEVc0Z.js.map} +1 -1
- package/bizar-dash/dist/assets/{rotate-cw-DHooc0Tx.js → rotate-cw-CsTtaLV2.js} +1 -1
- package/bizar-dash/dist/assets/{rotate-cw-DHooc0Tx.js.map → rotate-cw-CsTtaLV2.js.map} +1 -1
- package/bizar-dash/dist/assets/{save-DQKCKyZK.js → save-DtMTge1F.js} +1 -1
- package/bizar-dash/dist/assets/{save-DQKCKyZK.js.map → save-DtMTge1F.js.map} +1 -1
- package/bizar-dash/dist/assets/{send-xgy3RAi7.js → send-DyBhyk8t.js} +1 -1
- package/bizar-dash/dist/assets/{send-xgy3RAi7.js.map → send-DyBhyk8t.js.map} +1 -1
- package/bizar-dash/dist/assets/{shield-alert-CWV2F5ab.js → shield-alert-BPGJlRF8.js} +1 -1
- package/bizar-dash/dist/assets/{shield-alert-CWV2F5ab.js.map → shield-alert-BPGJlRF8.js.map} +1 -1
- package/bizar-dash/dist/assets/{trash-2-CL4khSRv.js → trash-2-2NsyTU8c.js} +1 -1
- package/bizar-dash/dist/assets/{trash-2-CL4khSRv.js.map → trash-2-2NsyTU8c.js.map} +1 -1
- package/bizar-dash/dist/assets/{trending-up-CSJJhWTS.js → trending-up-BXwP23VK.js} +1 -1
- package/bizar-dash/dist/assets/{trending-up-CSJJhWTS.js.map → trending-up-BXwP23VK.js.map} +1 -1
- package/bizar-dash/dist/assets/{triangle-alert-BPLb7uLt.js → triangle-alert-DFmQ_RXN.js} +1 -1
- package/bizar-dash/dist/assets/{triangle-alert-BPLb7uLt.js.map → triangle-alert-DFmQ_RXN.js.map} +1 -1
- package/bizar-dash/dist/assets/{upload-Cww6MvFq.js → upload-Cj-HYRYV.js} +1 -1
- package/bizar-dash/dist/assets/{upload-Cww6MvFq.js.map → upload-Cj-HYRYV.js.map} +1 -1
- package/bizar-dash/dist/assets/{zap-DmhVUzRA.js → zap-BaOiybcl.js} +1 -1
- package/bizar-dash/dist/assets/{zap-DmhVUzRA.js.map → zap-BaOiybcl.js.map} +1 -1
- package/bizar-dash/dist/index.html +1 -1
- package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/bizar-dash/src/server/api.mjs +11 -0
- package/bizar-dash/src/server/artifacts-store.mjs +120 -14
- package/bizar-dash/src/server/claude-artifacts.mjs +281 -0
- package/bizar-dash/src/server/routes/artifacts.mjs +42 -6
- package/bizar-dash/src/server/routes/audit.mjs +78 -0
- package/bizar-dash/src/server/routes/decisions.mjs +78 -0
- package/bizar-dash/src/server/routes/model-router.mjs +55 -0
- package/bizar-dash/src/server/routes/model-router.test.mjs +28 -0
- package/bizar-dash/src/server/routes/sessions.mjs +80 -0
- package/bizar-dash/src/server/routes/update.mjs +48 -21
- package/bizar-dash/src/server/sprint.test.mjs +163 -0
- package/bizar-dash/src/web/v8/App.tsx +0 -4
- package/bizar-dash/src/web/v8/__tests__/new-views.test.tsx +0 -17
- package/bizar-dash/src/web/v8/shell/Sidebar.tsx +0 -1
- package/bizar-dash/src/web/v8/views/Artifacts/ArtifactsView.tsx +232 -21
- package/bizar-dash/src/web/v8/views/Artifacts/ClaudeArtifactView.tsx +179 -0
- package/bizar-dash/src/web/v8/views/Router.tsx +0 -5
- package/bizar-dash/tests/_update-test-helpers.mjs +13 -0
- package/bizar-dash/tests/update-check.test.mjs +18 -13
- package/bizar-dash/tests/update-packages.test.mjs +154 -0
- package/bizar-dash/tests/update-run.test.mjs +17 -80
- package/cli/banner.mjs +1 -1
- package/cli/bin.mjs +8 -1
- package/cli/bootstrap.mjs +1 -1
- package/cli/commands/claude-cmd.mjs +1 -1
- package/cli/commands/deploy/docker.mjs +0 -3
- package/cli/install/banner.mjs +67 -0
- package/cli/install/banner.test.mjs +39 -0
- package/cli/install/index.mjs +32 -0
- package/cli/install/index.test.mjs +61 -0
- package/cli/install/paths.mjs +80 -0
- package/cli/install/paths.test.mjs +77 -0
- package/cli/install/plugin.mjs +161 -0
- package/cli/install/postinstall.mjs +111 -0
- package/cli/install.mjs +13 -600
- package/cli/migrate.mjs +157 -0
- package/cli/migrate.test.mjs +85 -0
- package/cli/provision.mjs +615 -1918
- package/cli/provision.test.mjs +1 -1
- package/cli/service-controller.test.mjs +0 -1
- package/cli/service-env.mjs +0 -1
- package/cli/utils.mjs +0 -1
- package/install.sh +54 -466
- package/package.json +1 -1
- package/packages/sdk/dist/agent/cron.d.ts +44 -0
- package/packages/sdk/dist/agent/cron.js +79 -0
- package/packages/sdk/dist/agent/heartbeat.d.ts +43 -0
- package/packages/sdk/dist/agent/heartbeat.js +116 -0
- package/packages/sdk/dist/index.d.ts +3 -0
- package/packages/sdk/dist/index.js +5 -0
- package/packages/sdk/dist/learning/decisions.d.ts +46 -0
- package/packages/sdk/dist/learning/decisions.js +132 -0
- package/packages/sdk/dist/learning/index.d.ts +8 -0
- package/packages/sdk/dist/learning/index.js +8 -0
- package/packages/sdk/dist/learning/instincts.d.ts +38 -0
- package/packages/sdk/dist/learning/instincts.js +139 -0
- package/packages/sdk/dist/mcp/server.js +119 -1
- package/packages/sdk/dist/router/agent-model-registry.d.ts +89 -0
- package/packages/sdk/dist/router/agent-model-registry.js +144 -0
- package/packages/sdk/dist/router/index.d.ts +1 -0
- package/packages/sdk/dist/router/index.js +1 -0
- package/packages/sdk/package.json +3 -2
- package/scripts/audit.mjs +388 -0
- package/scripts/audit.test.mjs +149 -0
- package/scripts/eval-gate.mjs +134 -0
- package/scripts/eval-gate.test.mjs +138 -0
- package/scripts/feature-state-machine.mjs +178 -0
- package/scripts/feature-state-machine.test.mjs +135 -0
- package/scripts/install-curl.sh +17 -0
- package/scripts/spec-dedup.test.mjs +124 -0
- package/scripts/sprint.mjs +213 -0
- package/scripts/worktree-setup.sh +79 -0
- package/bizar-dash/dist/assets/ArtifactsView-CDIhPBAx.js +0 -6
- package/bizar-dash/dist/assets/ArtifactsView-CDIhPBAx.js.map +0 -1
- package/bizar-dash/dist/assets/HeadroomView-CNc-TI7u.js +0 -11
- package/bizar-dash/dist/assets/HeadroomView-CNc-TI7u.js.map +0 -1
- package/bizar-dash/dist/assets/MiscView-BBcpUbZZ.js +0 -1
- package/bizar-dash/dist/assets/ObsidianView-D3bI0row.js +0 -1
- package/bizar-dash/dist/assets/main-CdOm1Pj9.js.map +0 -1
- package/bizar-dash/src/web/v8/views/Headroom/HeadroomView.tsx +0 -201
- package/bizar-dash/tests/graphify-mod-spawn.node.test.mjs +0 -90
- package/bizar-dash/tests/v7-archive/mobile-overview.test.tsx +0 -214
- package/bizar-dash/tests/v7-archive/mobile-settings.test.tsx +0 -181
- package/bizar-dash/tests/v7-archive/navigation/CommandPalette.test.tsx +0 -105
- package/bizar-dash/tests/v7-archive/settings-nav.test.tsx +0 -128
- package/bizar-dash/tests/v7-archive/views/Overview.test.tsx +0 -413
- package/cli/provision-claude.mjs +0 -1176
|
@@ -27,10 +27,22 @@ import {
|
|
|
27
27
|
import { join, dirname } from 'node:path';
|
|
28
28
|
import { homedir } from 'node:os';
|
|
29
29
|
import { randomBytes } from 'node:crypto';
|
|
30
|
+
import {
|
|
31
|
+
compileClaudeArtifact,
|
|
32
|
+
detectKind as detectArtifactKind,
|
|
33
|
+
} from './claude-artifacts.mjs';
|
|
30
34
|
|
|
31
35
|
const HOME = homedir();
|
|
32
36
|
const GLOBAL_PLANS_DIR = join(HOME, '.config', 'cline', 'artifacts');
|
|
33
37
|
|
|
38
|
+
const VALID_KINDS = new Set(['mdx', 'claude-html', 'claude-svg', 'claude-react']);
|
|
39
|
+
const ARTIFACT_FILE_BY_KIND = {
|
|
40
|
+
mdx: 'artifact.mdx',
|
|
41
|
+
'claude-html': 'artifact.html',
|
|
42
|
+
'claude-svg': 'artifact.svg',
|
|
43
|
+
'claude-react': 'artifact.jsx',
|
|
44
|
+
};
|
|
45
|
+
|
|
34
46
|
// Atomic JSON write: serialize to a sibling temp file, then rename into
|
|
35
47
|
// place. `rename` is atomic on POSIX (same filesystem), so a crash
|
|
36
48
|
// between write and rename never leaves a half-written / corrupt file.
|
|
@@ -77,7 +89,7 @@ function emptyCanvas(title = 'Untitled plan') {
|
|
|
77
89
|
};
|
|
78
90
|
}
|
|
79
91
|
|
|
80
|
-
function defaultMeta(slug, title) {
|
|
92
|
+
function defaultMeta(slug, title, kind = 'mdx') {
|
|
81
93
|
const now = new Date().toISOString();
|
|
82
94
|
return {
|
|
83
95
|
slug,
|
|
@@ -86,11 +98,31 @@ function defaultMeta(slug, title) {
|
|
|
86
98
|
tags: [],
|
|
87
99
|
description: '',
|
|
88
100
|
author: process.env.USER || 'drb0rk',
|
|
101
|
+
kind: VALID_KINDS.has(kind) ? kind : 'mdx',
|
|
89
102
|
created: now,
|
|
90
103
|
lastEdited: now,
|
|
91
104
|
};
|
|
92
105
|
}
|
|
93
106
|
|
|
107
|
+
function readArtifactBody(dir, kind) {
|
|
108
|
+
// The artifact body file depends on its kind. We always prefer the
|
|
109
|
+
// kind-specific file (artifact.html / artifact.svg / artifact.jsx / artifact.mdx)
|
|
110
|
+
// but accept legacy `plan.mdx` for the mdx case so old plans still load.
|
|
111
|
+
const primary = join(dir, ARTIFACT_FILE_BY_KIND[kind] || 'artifact.mdx');
|
|
112
|
+
if (existsSync(primary)) return { source: safeReadText(primary), file: primary };
|
|
113
|
+
if (kind === 'mdx') {
|
|
114
|
+
const legacy = join(dir, 'plan.mdx');
|
|
115
|
+
if (existsSync(legacy)) return { source: safeReadText(legacy), file: legacy };
|
|
116
|
+
}
|
|
117
|
+
return { source: '', file: primary };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function writeArtifactBody(dir, kind, source) {
|
|
121
|
+
const file = join(dir, ARTIFACT_FILE_BY_KIND[kind] || 'artifact.mdx');
|
|
122
|
+
writeFileSync(file, String(source || ''), 'utf8');
|
|
123
|
+
return file;
|
|
124
|
+
}
|
|
125
|
+
|
|
94
126
|
export const artifactsStore = {
|
|
95
127
|
GLOBAL_PLANS_DIR,
|
|
96
128
|
|
|
@@ -151,6 +183,7 @@ export const artifactsStore = {
|
|
|
151
183
|
tags: meta?.tags || [],
|
|
152
184
|
description: meta?.description || '',
|
|
153
185
|
author: meta?.author || null,
|
|
186
|
+
kind: meta?.kind || 'mdx',
|
|
154
187
|
source: dir === GLOBAL_PLANS_DIR ? 'global' : 'worktree',
|
|
155
188
|
elementCount,
|
|
156
189
|
commentCount,
|
|
@@ -172,22 +205,59 @@ export const artifactsStore = {
|
|
|
172
205
|
if (!dir) return null;
|
|
173
206
|
const meta = safeReadJSON(join(dir, 'meta.json'), null);
|
|
174
207
|
const canvas = safeReadJSON(join(dir, 'plan.json'), null);
|
|
175
|
-
|
|
176
|
-
//
|
|
177
|
-
// legacy
|
|
178
|
-
|
|
179
|
-
// though `artifact.mdx` was present and correct.
|
|
180
|
-
const planMdx = safeReadText(join(dir, 'artifact.mdx'))
|
|
181
|
-
|| safeReadText(join(dir, 'plan.mdx'));
|
|
208
|
+
const kind = meta?.kind || 'mdx';
|
|
209
|
+
// Read the body file for the resolved kind. For mdx we still honour
|
|
210
|
+
// the legacy `plan.mdx` fallback (v3.20.15 contract).
|
|
211
|
+
const { source, file } = readArtifactBody(dir, kind);
|
|
182
212
|
return {
|
|
183
213
|
slug,
|
|
184
214
|
dir,
|
|
185
|
-
meta: meta || defaultMeta(slug, slug),
|
|
215
|
+
meta: meta || defaultMeta(slug, slug, kind),
|
|
186
216
|
canvas: canvas || emptyCanvas(meta?.title || slug),
|
|
187
|
-
|
|
217
|
+
kind,
|
|
218
|
+
source,
|
|
219
|
+
bodyFile: file,
|
|
220
|
+
// `planMdx` retained for legacy callers; for non-mdx kinds it is the
|
|
221
|
+
// empty string so existing code paths degrade gracefully.
|
|
222
|
+
planMdx: kind === 'mdx' ? source : '',
|
|
188
223
|
};
|
|
189
224
|
},
|
|
190
225
|
|
|
226
|
+
/**
|
|
227
|
+
* v10.1.0 — Compile a stored Claude-*-kind artifact into a render-ready
|
|
228
|
+
* HTML envelope. Returns `{ html, warnings, safeMode, kind, compiledAt }`
|
|
229
|
+
* suitable for an iframe `srcdoc`. Falls back to returning the raw
|
|
230
|
+
* `<pre>`-wrapped mdx body when the artifact is still `kind: 'mdx'`
|
|
231
|
+
* (legacy glyphs). Returns null if the slug does not exist.
|
|
232
|
+
*/
|
|
233
|
+
compile(slug, projectRoot) {
|
|
234
|
+
const plan = this.get(slug, projectRoot);
|
|
235
|
+
if (!plan) return null;
|
|
236
|
+
const kind = plan.kind || 'mdx';
|
|
237
|
+
if (kind === 'mdx') {
|
|
238
|
+
// Legacy MDX glyphs: render as a `<pre>`-wrapped plain text panel so
|
|
239
|
+
// the dashboard's previewer can keep working without the old block
|
|
240
|
+
// pipeline.
|
|
241
|
+
const escaped = String(plan.source || plan.planMdx || '')
|
|
242
|
+
.replace(/&/g, '&')
|
|
243
|
+
.replace(/</g, '<')
|
|
244
|
+
.replace(/>/g, '>');
|
|
245
|
+
const html = [
|
|
246
|
+
'<!doctype html><html><body style="font-family:ui-monospace,monospace;padding:16px;background:#0b0e14;color:#dde2e8;white-space:pre-wrap;">',
|
|
247
|
+
escaped,
|
|
248
|
+
'</body></html>',
|
|
249
|
+
].join('');
|
|
250
|
+
return {
|
|
251
|
+
kind: 'mdx',
|
|
252
|
+
html,
|
|
253
|
+
warnings: [],
|
|
254
|
+
safeMode: true,
|
|
255
|
+
compiledAt: new Date().toISOString(),
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
return compileClaudeArtifact(plan.source || '', { kind });
|
|
259
|
+
},
|
|
260
|
+
|
|
191
261
|
/** Create a new plan. Returns the full plan record. */
|
|
192
262
|
create(slug, body, projectRoot) {
|
|
193
263
|
if (!VALID_SLUG.test(slug || '')) {
|
|
@@ -202,19 +272,35 @@ export const artifactsStore = {
|
|
|
202
272
|
throw err;
|
|
203
273
|
}
|
|
204
274
|
const title = (body && body.title) || slug;
|
|
275
|
+
const kindRaw = (body && body.kind) || 'mdx';
|
|
276
|
+
const kind = VALID_KINDS.has(kindRaw) ? kindRaw : 'mdx';
|
|
277
|
+
const source = (body && typeof body.source === 'string') ? body.source
|
|
278
|
+
: (kind === 'mdx' && body && typeof body.planMdx === 'string' ? body.planMdx : '');
|
|
205
279
|
const meta = {
|
|
206
|
-
...defaultMeta(slug, title),
|
|
280
|
+
...defaultMeta(slug, title, kind),
|
|
207
281
|
...(body || {}),
|
|
208
282
|
slug,
|
|
283
|
+
kind,
|
|
209
284
|
created: new Date().toISOString(),
|
|
210
285
|
lastEdited: new Date().toISOString(),
|
|
211
286
|
};
|
|
212
287
|
const canvas = emptyCanvas(title);
|
|
213
288
|
this._writePlan(dir, meta, canvas);
|
|
214
|
-
|
|
289
|
+
// Persist the artifact body in its kind-specific file so the next
|
|
290
|
+
// `get()` round-trips it without losing bytes.
|
|
291
|
+
if (source) writeArtifactBody(dir, kind, source);
|
|
292
|
+
return {
|
|
293
|
+
slug,
|
|
294
|
+
dir,
|
|
295
|
+
meta,
|
|
296
|
+
canvas,
|
|
297
|
+
kind,
|
|
298
|
+
source,
|
|
299
|
+
planMdx: kind === 'mdx' ? source : '',
|
|
300
|
+
};
|
|
215
301
|
},
|
|
216
302
|
|
|
217
|
-
/** Update plan metadata (
|
|
303
|
+
/** Update plan metadata + (optionally) the artifact body. */
|
|
218
304
|
updateMeta(slug, body, projectRoot) {
|
|
219
305
|
if (!VALID_SLUG.test(slug || '')) return null;
|
|
220
306
|
const dir = this.resolveDir(slug, projectRoot);
|
|
@@ -228,10 +314,30 @@ export const artifactsStore = {
|
|
|
228
314
|
if (valid.includes(body.status)) meta.status = body.status;
|
|
229
315
|
}
|
|
230
316
|
if (typeof body?.author === 'string') meta.author = body.author;
|
|
317
|
+
if (typeof body?.kind === 'string' && VALID_KINDS.has(body.kind)) meta.kind = body.kind;
|
|
231
318
|
meta.lastEdited = new Date().toISOString();
|
|
232
319
|
const canvas = safeReadJSON(join(dir, 'plan.json'), emptyCanvas(meta.title));
|
|
233
320
|
this._writePlan(dir, meta, canvas);
|
|
234
|
-
|
|
321
|
+
// Source/body update is the central new contract for v10.1.0: the
|
|
322
|
+
// editor can PATCH the kind and the source in one call and we keep
|
|
323
|
+
// both in lock-step.
|
|
324
|
+
let source = '';
|
|
325
|
+
if (typeof body?.source === 'string') {
|
|
326
|
+
const next = meta.kind || 'mdx';
|
|
327
|
+
source = body.source;
|
|
328
|
+
writeArtifactBody(dir, next, source);
|
|
329
|
+
} else {
|
|
330
|
+
source = safeReadText(join(dir, ARTIFACT_FILE_BY_KIND[meta.kind || 'mdx'] || 'artifact.mdx'));
|
|
331
|
+
}
|
|
332
|
+
return {
|
|
333
|
+
slug,
|
|
334
|
+
dir,
|
|
335
|
+
meta,
|
|
336
|
+
canvas,
|
|
337
|
+
kind: meta.kind || 'mdx',
|
|
338
|
+
source,
|
|
339
|
+
planMdx: (meta.kind || 'mdx') === 'mdx' ? source : '',
|
|
340
|
+
};
|
|
235
341
|
},
|
|
236
342
|
|
|
237
343
|
/** Delete a plan directory. */
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/server/claude-artifacts.mjs
|
|
3
|
+
*
|
|
4
|
+
* v10.1.0 — Claude Artifacts compile + sandbox wrapper.
|
|
5
|
+
*
|
|
6
|
+
* Replaces the v3–v9 MDX "glyphs" renderer with the Claude.ai-style
|
|
7
|
+
* interactive artifact surface. Three supported kinds:
|
|
8
|
+
*
|
|
9
|
+
* claude-html — static HTML + inline CSS/JS (sandboxed iframe)
|
|
10
|
+
* claude-svg — standalone <svg> document
|
|
11
|
+
* claude-react — JSX/TSX source bundled to a UMD module that the
|
|
12
|
+
* iframe loads; the wrapper injects React + ReactDOM
|
|
13
|
+
* from a CDN pinned to v18.x
|
|
14
|
+
*
|
|
15
|
+
* The compile output is a self-contained `html` string ready to drop
|
|
16
|
+
* into an iframe `srcdoc`. Validation is conservative: it scans for
|
|
17
|
+
* dangerous patterns (script src to non-allowlisted hosts, event
|
|
18
|
+
* handlers that leak `top`/`parent`, etc.) and refuses to compile
|
|
19
|
+
* anything that looks like an exfiltration attempt.
|
|
20
|
+
*
|
|
21
|
+
* This module does NOT execute user code on the server. The iframe
|
|
22
|
+
* is the only execution surface. The server only:
|
|
23
|
+
* 1. Parses the source to confirm it is well-formed.
|
|
24
|
+
* 2. Wraps it in a sandbox-friendly HTML envelope.
|
|
25
|
+
* 3. Sanitises obvious exfiltration patterns.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
// ─── Allowlist ───────────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
// Hosts we are willing to load scripts/styles from inside the artifact.
|
|
31
|
+
// Pinned to reduce surprise breakage.
|
|
32
|
+
const ALLOWED_SCRIPT_HOSTS = new Set([
|
|
33
|
+
'unpkg.com',
|
|
34
|
+
'cdn.jsdelivr.net',
|
|
35
|
+
'cdnjs.cloudflare.com',
|
|
36
|
+
'esm.sh',
|
|
37
|
+
'cdn.tailwindcss.com',
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
const ALLOWED_STYLE_HOSTS = new Set([
|
|
41
|
+
'unpkg.com',
|
|
42
|
+
'cdn.jsdelivr.net',
|
|
43
|
+
'cdnjs.cloudflare.com',
|
|
44
|
+
'fonts.googleapis.com',
|
|
45
|
+
'fonts.gstatic.com',
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
// DANGEROUS: these patterns inside a Claude artifact mean exfiltration or
|
|
49
|
+
// privilege escalation. They get flagged in `errors[]` and the artifact
|
|
50
|
+
// is rendered in `safe-mode` (scripts stripped, event handlers removed).
|
|
51
|
+
const EXFIL_PATTERNS = [
|
|
52
|
+
// top.* / parent.* access — breaks out of the sandbox
|
|
53
|
+
/\b(top|parent)\s*\.\s*[a-zA-Z_]/i,
|
|
54
|
+
// window.opener hijack
|
|
55
|
+
/\bwindow\s*\.\s*opener\b/i,
|
|
56
|
+
// fetch / XHR with literal URL → host we don't trust
|
|
57
|
+
/\bfetch\s*\(\s*['"`]https?:\/\/(?!(?:localhost|127\.0\.0\.1|unpkg\.com|cdn\.jsdelivr\.net|cdnjs\.cloudflare\.com|esm\.sh))/i,
|
|
58
|
+
// document.cookie access
|
|
59
|
+
/\bdocument\s*\.\s*cookie\b/i,
|
|
60
|
+
// localStorage / sessionStorage exfil
|
|
61
|
+
/\b(localStorage|sessionStorage)\s*\.\s*(setItem|getItem)\b/i,
|
|
62
|
+
// <script src="http://evil"> — bare remote script tag
|
|
63
|
+
/<script[^>]+src\s*=\s*["']https?:\/\/(?!(?:localhost|127\.0\.0\.1|unpkg\.com|cdn\.jsdelivr\.net|cdnjs\.cloudflare\.com|esm\.sh))/i,
|
|
64
|
+
// inline event handler that runs JS (onclick=, onerror=, ...)
|
|
65
|
+
/\son\w+\s*=\s*["'][^"']*\beval\s*\(/i,
|
|
66
|
+
// javascript: URLs
|
|
67
|
+
/\bhref\s*=\s*["']javascript:/i,
|
|
68
|
+
// imports from a path the bundler doesn't know
|
|
69
|
+
/\bimport\s+.*from\s+["'](?!react|react-dom|react-dom\/client)/i,
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
// ─── Validation ──────────────────────────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Scan a Claude artifact source for exfiltration / escape patterns.
|
|
76
|
+
* Returns the list of tripped patterns (empty = clean).
|
|
77
|
+
*
|
|
78
|
+
* @param {string} src
|
|
79
|
+
* @returns {Array<{ pattern: string, message: string }>}
|
|
80
|
+
*/
|
|
81
|
+
export function scanForExfil(src) {
|
|
82
|
+
const hits = [];
|
|
83
|
+
for (const re of EXFIL_PATTERNS) {
|
|
84
|
+
const m = re.exec(src);
|
|
85
|
+
if (m) {
|
|
86
|
+
hits.push({
|
|
87
|
+
pattern: re.source.slice(0, 80),
|
|
88
|
+
message: `potential exfiltration / sandbox escape: ${m[0].slice(0, 80)}`,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return hits;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Detect the artifact kind from the source content. Falls back to
|
|
97
|
+
* 'claude-html' when the user did not supply one.
|
|
98
|
+
*
|
|
99
|
+
* Heuristics:
|
|
100
|
+
* - bare `<svg ...>` at the root → claude-svg
|
|
101
|
+
* - contains `import React` or `export default` → claude-react
|
|
102
|
+
* - everything else → claude-html
|
|
103
|
+
*
|
|
104
|
+
* @param {string} src
|
|
105
|
+
* @param {string} [declared]
|
|
106
|
+
* @returns {'claude-html' | 'claude-svg' | 'claude-react'}
|
|
107
|
+
*/
|
|
108
|
+
export function detectKind(src, declared) {
|
|
109
|
+
if (declared === 'claude-html' || declared === 'claude-svg' || declared === 'claude-react') {
|
|
110
|
+
return declared;
|
|
111
|
+
}
|
|
112
|
+
const trimmed = (src || '').trim();
|
|
113
|
+
if (/^<svg[\s>]/i.test(trimmed)) return 'claude-svg';
|
|
114
|
+
if (/\bimport\s+React\b|\bexport\s+default\b/.test(trimmed)) return 'claude-react';
|
|
115
|
+
return 'claude-html';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ─── Sandboxed envelope ──────────────────────────────────────────────────────
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Build the iframe-ready HTML envelope. The user source is injected
|
|
122
|
+
* inside the `<body>` so the iframe owns all rendering.
|
|
123
|
+
*
|
|
124
|
+
* The sandbox attribute restricts the iframe to:
|
|
125
|
+
* - same-origin scripts (so React can hydrate)
|
|
126
|
+
* - no top navigation
|
|
127
|
+
* - no popups
|
|
128
|
+
* - no form submission
|
|
129
|
+
* - no same-origin storage (cookie/storage stays in the iframe only)
|
|
130
|
+
*
|
|
131
|
+
* @param {string} body — the user-authored artifact body
|
|
132
|
+
* @param {object} [opts]
|
|
133
|
+
* @param {'claude-html'|'claude-svg'|'claude-react'} [opts.kind]
|
|
134
|
+
* @param {boolean} [opts.safeMode] — when true, scripts are stripped
|
|
135
|
+
* @returns {string} full HTML document string for iframe srcdoc
|
|
136
|
+
*/
|
|
137
|
+
export function buildEnvelope(body, { kind = 'claude-html', safeMode = false } = {}) {
|
|
138
|
+
const safeBody = safeMode ? stripScripts(body) : body;
|
|
139
|
+
const head = buildHead(kind, safeMode);
|
|
140
|
+
return [
|
|
141
|
+
'<!doctype html>',
|
|
142
|
+
'<html lang="en">',
|
|
143
|
+
'<head>',
|
|
144
|
+
'<meta charset="utf-8">',
|
|
145
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1">',
|
|
146
|
+
head,
|
|
147
|
+
'</head>',
|
|
148
|
+
'<body>',
|
|
149
|
+
safeBody,
|
|
150
|
+
'</body>',
|
|
151
|
+
'</html>',
|
|
152
|
+
].join('\n');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function buildHead(kind, safeMode) {
|
|
156
|
+
const scriptBlock = safeMode
|
|
157
|
+
? ''
|
|
158
|
+
: kind === 'claude-react'
|
|
159
|
+
? [
|
|
160
|
+
'<script crossorigin src="https://unpkg.com/react@18.3.1/umd/react.production.min.js"></script>',
|
|
161
|
+
'<script crossorigin src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.production.min.js"></script>',
|
|
162
|
+
'<script src="https://unpkg.com/@babel/standalone@7.24.7/babel.min.js"></script>',
|
|
163
|
+
].join('\n')
|
|
164
|
+
: '';
|
|
165
|
+
const style = [
|
|
166
|
+
'html,body{margin:0;padding:0;font-family:system-ui,-apple-system,sans-serif;color:#111;background:#fff;}',
|
|
167
|
+
'a{color:#0b66c3;}',
|
|
168
|
+
'img,svg{max-width:100%;height:auto;}',
|
|
169
|
+
].join('');
|
|
170
|
+
return [
|
|
171
|
+
`<style>${style}</style>`,
|
|
172
|
+
scriptBlock,
|
|
173
|
+
].filter(Boolean).join('\n');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Strip `<script>...</script>` blocks and inline event handlers from
|
|
178
|
+
* an HTML source. Used when the artifact fails the exfil scan.
|
|
179
|
+
*
|
|
180
|
+
* @param {string} html
|
|
181
|
+
* @returns {string}
|
|
182
|
+
*/
|
|
183
|
+
export function stripScripts(html) {
|
|
184
|
+
return String(html || '')
|
|
185
|
+
.replace(/<script\b[\s\S]*?<\/script>/gi, '')
|
|
186
|
+
.replace(/\son\w+\s*=\s*("[^"]*"|'[^']*'|[^\s>]*)/gi, '');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ─── Public compile API ──────────────────────────────────────────────────────
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Compile a Claude artifact source into a render-ready envelope.
|
|
193
|
+
* Returns the envelope + a list of warnings (the caller decides
|
|
194
|
+
* whether to render in safe-mode vs full).
|
|
195
|
+
*
|
|
196
|
+
* @param {string} source raw artifact source
|
|
197
|
+
* @param {object} [opts]
|
|
198
|
+
* @param {'claude-html'|'claude-svg'|'claude-react'} [opts.kind]
|
|
199
|
+
* @returns {{
|
|
200
|
+
* kind: 'claude-html'|'claude-svg'|'claude-react',
|
|
201
|
+
* html: string,
|
|
202
|
+
* warnings: Array<{ message: string }>,
|
|
203
|
+
* compiledAt: string,
|
|
204
|
+
* }}
|
|
205
|
+
*/
|
|
206
|
+
export function compileClaudeArtifact(source, opts = {}) {
|
|
207
|
+
const src = String(source || '');
|
|
208
|
+
const kind = detectKind(src, opts.kind);
|
|
209
|
+
const warnings = scanForExfil(src);
|
|
210
|
+
const safeMode = warnings.length > 0;
|
|
211
|
+
const html = buildEnvelope(src, { kind, safeMode });
|
|
212
|
+
return {
|
|
213
|
+
kind,
|
|
214
|
+
html,
|
|
215
|
+
warnings,
|
|
216
|
+
safeMode,
|
|
217
|
+
compiledAt: new Date().toISOString(),
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// ─── Legacy MDX migration helper ─────────────────────────────────────────────
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Convert an existing `plan.mdx` / `artifact.mdx` block-list output into
|
|
225
|
+
* a Claude-React artifact. Used by the migration when an operator
|
|
226
|
+
* runs `bizar artifacts migrate-glyphs`. Best-effort — if the MDX has
|
|
227
|
+
* unsupported block types, returns an empty artifact + a warning list.
|
|
228
|
+
*
|
|
229
|
+
* @param {{ blocks: Array, frontmatter: Record<string, unknown> }} compiled
|
|
230
|
+
* @returns {{ source: string, warnings: string[] }}
|
|
231
|
+
*/
|
|
232
|
+
export function mdxBlocksToReact(compiled) {
|
|
233
|
+
const warnings = [];
|
|
234
|
+
const lines = [];
|
|
235
|
+
lines.push('// Auto-generated from legacy MDX. Edit freely.');
|
|
236
|
+
lines.push('const { useState } = React;');
|
|
237
|
+
lines.push('');
|
|
238
|
+
const blocks = Array.isArray(compiled?.blocks) ? compiled.blocks : [];
|
|
239
|
+
for (const block of blocks) {
|
|
240
|
+
if (block.type === 'RichText') {
|
|
241
|
+
const md = String(block.childrenMarkdown || '').replace(/`/g, '\\`').replace(/\$/g, '\\$');
|
|
242
|
+
lines.push(`function Block_${block.id}() {`);
|
|
243
|
+
lines.push(' return <pre style={{ whiteSpace: "pre-wrap", fontFamily: "inherit" }}>{`' + md + '`}</pre>;');
|
|
244
|
+
lines.push('}');
|
|
245
|
+
} else if (block.type === 'Callout') {
|
|
246
|
+
const tone = String(block.data?.tone || 'info');
|
|
247
|
+
lines.push(`function Block_${block.id}() {`);
|
|
248
|
+
lines.push(` return <div style={{ padding: 12, borderRadius: 8, background: "var(--callout-${tone}-bg, #eef)" }}>${escapeText(block.childrenMarkdown || '')}</div>;`);
|
|
249
|
+
lines.push('}');
|
|
250
|
+
} else if (block.type === 'Stat') {
|
|
251
|
+
const label = String(block.data?.label || '');
|
|
252
|
+
const value = String(block.data?.value || '');
|
|
253
|
+
lines.push(`function Block_${block.id}() {`);
|
|
254
|
+
lines.push(` return <div style={{ display: "inline-block", padding: 16 }}><div style={{ fontSize: 12, opacity: 0.7 }}>${escapeText(label)}</div><div style={{ fontSize: 28, fontWeight: 600 }}>${escapeText(value)}</div></div>;`);
|
|
255
|
+
lines.push('}');
|
|
256
|
+
} else {
|
|
257
|
+
warnings.push(`unsupported block type "${block.type}" (id=${block.id}) — skipped in migration`);
|
|
258
|
+
}
|
|
259
|
+
lines.push('');
|
|
260
|
+
}
|
|
261
|
+
lines.push('export default function App() {');
|
|
262
|
+
lines.push(' return (');
|
|
263
|
+
lines.push(' <div style={{ padding: 16 }}>');
|
|
264
|
+
for (const block of blocks) {
|
|
265
|
+
if (['RichText', 'Callout', 'Stat'].includes(block.type)) {
|
|
266
|
+
lines.push(` <Block_${block.id} />`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
lines.push(' </div>');
|
|
270
|
+
lines.push(' );');
|
|
271
|
+
lines.push('}');
|
|
272
|
+
return { source: lines.join('\n'), warnings };
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function escapeText(s) {
|
|
276
|
+
return String(s || '')
|
|
277
|
+
.replace(/&/g, '&')
|
|
278
|
+
.replace(/</g, '<')
|
|
279
|
+
.replace(/>/g, '>')
|
|
280
|
+
.replace(/"/g, '"');
|
|
281
|
+
}
|
|
@@ -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
|
+
}
|