@polderlabs/bizar 10.2.0 → 10.4.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.
Files changed (235) hide show
  1. package/README.md +31 -8
  2. package/bizar-dash/CONTROL_SURFACES.md +0 -1
  3. package/bizar-dash/dist/assets/{ActivityFeed-JjmOY2AQ.js → ActivityFeed-DtRtg-JN.js} +1 -1
  4. package/bizar-dash/dist/assets/{ActivityFeed-JjmOY2AQ.js.map → ActivityFeed-DtRtg-JN.js.map} +1 -1
  5. package/bizar-dash/dist/assets/{ActivityView-BIIMvGWo.js → ActivityView-CoTXDyU_.js} +1 -1
  6. package/bizar-dash/dist/assets/{ActivityView-BIIMvGWo.js.map → ActivityView-CoTXDyU_.js.map} +1 -1
  7. package/bizar-dash/dist/assets/{AdminView-DRidJtkq.js → AdminView-DNc5Ll7L.js} +1 -1
  8. package/bizar-dash/dist/assets/{AdminView-DRidJtkq.js.map → AdminView-DNc5Ll7L.js.map} +1 -1
  9. package/bizar-dash/dist/assets/{AgentActivity-79l4SBMC.js → AgentActivity-CrX7LmQ6.js} +1 -1
  10. package/bizar-dash/dist/assets/{AgentActivity-79l4SBMC.js.map → AgentActivity-CrX7LmQ6.js.map} +1 -1
  11. package/bizar-dash/dist/assets/{AgentsView-0vT4uPSc.js → AgentsView-Du-G_skK.js} +3 -3
  12. package/bizar-dash/dist/assets/{AgentsView-0vT4uPSc.js.map → AgentsView-Du-G_skK.js.map} +1 -1
  13. package/bizar-dash/dist/assets/{Alert-CG5q_s75.js → Alert-B6DjfRFI.js} +1 -1
  14. package/bizar-dash/dist/assets/{Alert-CG5q_s75.js.map → Alert-B6DjfRFI.js.map} +1 -1
  15. package/bizar-dash/dist/assets/{ArtifactsView-DM8ZmpR9.js → ArtifactsView-znhN8GTR.js} +1 -1
  16. package/bizar-dash/dist/assets/{ArtifactsView-DM8ZmpR9.js.map → ArtifactsView-znhN8GTR.js.map} +1 -1
  17. package/bizar-dash/dist/assets/{AuthView-BveN94MC.js → AuthView-Rk8eKUPr.js} +1 -1
  18. package/bizar-dash/dist/assets/{AuthView-BveN94MC.js.map → AuthView-Rk8eKUPr.js.map} +1 -1
  19. package/bizar-dash/dist/assets/{Avatar-ulUPIYTd.js → Avatar-B41upSD1.js} +1 -1
  20. package/bizar-dash/dist/assets/{Avatar-ulUPIYTd.js.map → Avatar-B41upSD1.js.map} +1 -1
  21. package/bizar-dash/dist/assets/{BackgroundJobsView-BWErhqFo.js → BackgroundJobsView-BLWuN9qz.js} +1 -1
  22. package/bizar-dash/dist/assets/{BackgroundJobsView-BWErhqFo.js.map → BackgroundJobsView-BLWuN9qz.js.map} +1 -1
  23. package/bizar-dash/dist/assets/{BackupView-CnBjzG9N.js → BackupView-CqbtcnuQ.js} +1 -1
  24. package/bizar-dash/dist/assets/{BackupView-CnBjzG9N.js.map → BackupView-CqbtcnuQ.js.map} +1 -1
  25. package/bizar-dash/dist/assets/{Card-B0imhxMk.js → Card-rF59wHiO.js} +1 -1
  26. package/bizar-dash/dist/assets/{Card-B0imhxMk.js.map → Card-rF59wHiO.js.map} +1 -1
  27. package/bizar-dash/dist/assets/{ChatView-D6zGytzx.js → ChatView-CUriVEgU.js} +1 -1
  28. package/bizar-dash/dist/assets/{ChatView-D6zGytzx.js.map → ChatView-CUriVEgU.js.map} +1 -1
  29. package/bizar-dash/dist/assets/{Chip-wwZSGwLE.js → Chip-Dc2KmeWl.js} +1 -1
  30. package/bizar-dash/dist/assets/{Chip-wwZSGwLE.js.map → Chip-Dc2KmeWl.js.map} +1 -1
  31. package/bizar-dash/dist/assets/{ClaudeSessionsView-DK1sOLhi.js → ClaudeSessionsView-CiaHQdDZ.js} +1 -1
  32. package/bizar-dash/dist/assets/{ClaudeSessionsView-DK1sOLhi.js.map → ClaudeSessionsView-CiaHQdDZ.js.map} +1 -1
  33. package/bizar-dash/dist/assets/{ClipboardView-CZ2lYPr0.js → ClipboardView-DdP6aVso.js} +1 -1
  34. package/bizar-dash/dist/assets/{ClipboardView-CZ2lYPr0.js.map → ClipboardView-DdP6aVso.js.map} +1 -1
  35. package/bizar-dash/dist/assets/{ConfigView-D-zT9eNA.js → ConfigView-B2ysnTZU.js} +1 -1
  36. package/bizar-dash/dist/assets/{ConfigView-D-zT9eNA.js.map → ConfigView-B2ysnTZU.js.map} +1 -1
  37. package/bizar-dash/dist/assets/{DiagnosticsView-BocV68Lb.js → DiagnosticsView-88OZ5k0f.js} +1 -1
  38. package/bizar-dash/dist/assets/{DiagnosticsView-BocV68Lb.js.map → DiagnosticsView-88OZ5k0f.js.map} +1 -1
  39. package/bizar-dash/dist/assets/{DialogsView-BVjPI5x7.js → DialogsView-BcqDDCZQ.js} +1 -1
  40. package/bizar-dash/dist/assets/{DialogsView-BVjPI5x7.js.map → DialogsView-BcqDDCZQ.js.map} +1 -1
  41. package/bizar-dash/dist/assets/{DoctorView-84gJcZwM.js → DoctorView-BQwqvMAh.js} +1 -1
  42. package/bizar-dash/dist/assets/{DoctorView-84gJcZwM.js.map → DoctorView-BQwqvMAh.js.map} +1 -1
  43. package/bizar-dash/dist/assets/{EmptyState-BjgVOrcO.js → EmptyState-D65J5FQN.js} +1 -1
  44. package/bizar-dash/dist/assets/{EmptyState-BjgVOrcO.js.map → EmptyState-D65J5FQN.js.map} +1 -1
  45. package/bizar-dash/dist/assets/{EnvVarsView-BJ2TEzF7.js → EnvVarsView-DX-3oGH-.js} +2 -2
  46. package/bizar-dash/dist/assets/{EnvVarsView-BJ2TEzF7.js.map → EnvVarsView-DX-3oGH-.js.map} +1 -1
  47. package/bizar-dash/dist/assets/{ErrorState-BE3WBFqF.js → ErrorState-DULZnttJ.js} +1 -1
  48. package/bizar-dash/dist/assets/{ErrorState-BE3WBFqF.js.map → ErrorState-DULZnttJ.js.map} +1 -1
  49. package/bizar-dash/dist/assets/{EvalView-BJJ2P-kK.js → EvalView-CpdFu_ID.js} +1 -1
  50. package/bizar-dash/dist/assets/{EvalView-BJJ2P-kK.js.map → EvalView-CpdFu_ID.js.map} +1 -1
  51. package/bizar-dash/dist/assets/{GoalsView-pwROHApi.js → GoalsView-Br8oPPfb.js} +1 -1
  52. package/bizar-dash/dist/assets/{GoalsView-pwROHApi.js.map → GoalsView-Br8oPPfb.js.map} +1 -1
  53. package/bizar-dash/dist/assets/{Grid-qcvbhx6e.js → Grid-BSXhsCPC.js} +1 -1
  54. package/bizar-dash/dist/assets/{Grid-qcvbhx6e.js.map → Grid-BSXhsCPC.js.map} +1 -1
  55. package/bizar-dash/dist/assets/{HistoryView-C7SSOLJA.js → HistoryView-D0J3aoZF.js} +1 -1
  56. package/bizar-dash/dist/assets/{HistoryView-C7SSOLJA.js.map → HistoryView-D0J3aoZF.js.map} +1 -1
  57. package/bizar-dash/dist/assets/{Input-Bgzxf_Hu.js → Input-CZmRw5QX.js} +1 -1
  58. package/bizar-dash/dist/assets/{Input-Bgzxf_Hu.js.map → Input-CZmRw5QX.js.map} +1 -1
  59. package/bizar-dash/dist/assets/{LibrariesView-CbFFksox.js → LibrariesView-B1YX9wzp.js} +1 -1
  60. package/bizar-dash/dist/assets/{LibrariesView-CbFFksox.js.map → LibrariesView-B1YX9wzp.js.map} +1 -1
  61. package/bizar-dash/dist/assets/{LightRAGView-BqDrJbRq.js → LightRAGView-CykR0t9y.js} +1 -1
  62. package/bizar-dash/dist/assets/{LightRAGView-BqDrJbRq.js.map → LightRAGView-CykR0t9y.js.map} +1 -1
  63. package/bizar-dash/dist/assets/{ListHeader-TjPJrUfY.js → ListHeader-Dpun-r36.js} +1 -1
  64. package/bizar-dash/dist/assets/{ListHeader-TjPJrUfY.js.map → ListHeader-Dpun-r36.js.map} +1 -1
  65. package/bizar-dash/dist/assets/{MemoryView-BlTeHLhX.js → MemoryView-BbER5ZKt.js} +1 -1
  66. package/bizar-dash/dist/assets/{MemoryView-BlTeHLhX.js.map → MemoryView-BbER5ZKt.js.map} +1 -1
  67. package/bizar-dash/dist/assets/MiscView-DLNEGdML.js +1 -0
  68. package/bizar-dash/dist/assets/{MiscView-B8QY82Pm.js.map → MiscView-DLNEGdML.js.map} +1 -1
  69. package/bizar-dash/dist/assets/{ModsView-CvW1OWts.js → ModsView-P7gDeNHM.js} +2 -2
  70. package/bizar-dash/dist/assets/{ModsView-CvW1OWts.js.map → ModsView-P7gDeNHM.js.map} +1 -1
  71. package/bizar-dash/dist/assets/{NotificationsView-CsBt5U7x.js → NotificationsView-DAm2O0yf.js} +1 -1
  72. package/bizar-dash/dist/assets/{NotificationsView-CsBt5U7x.js.map → NotificationsView-DAm2O0yf.js.map} +1 -1
  73. package/bizar-dash/dist/assets/ObsidianView-Ci80gnwS.js +1 -0
  74. package/bizar-dash/dist/assets/{ObsidianView-BCTejEj2.js.map → ObsidianView-Ci80gnwS.js.map} +1 -1
  75. package/bizar-dash/dist/assets/{OverviewView-BsuPxwnB.js → OverviewView-rriw1GjA.js} +1 -1
  76. package/bizar-dash/dist/assets/{OverviewView-BsuPxwnB.js.map → OverviewView-rriw1GjA.js.map} +1 -1
  77. package/bizar-dash/dist/assets/{ProgressBar-DlZTETEn.js → ProgressBar-BahmeJhP.js} +1 -1
  78. package/bizar-dash/dist/assets/{ProgressBar-DlZTETEn.js.map → ProgressBar-BahmeJhP.js.map} +1 -1
  79. package/bizar-dash/dist/assets/{ProjectsView-Soxtmpet.js → ProjectsView-ChzM12bs.js} +1 -1
  80. package/bizar-dash/dist/assets/{ProjectsView-Soxtmpet.js.map → ProjectsView-ChzM12bs.js.map} +1 -1
  81. package/bizar-dash/dist/assets/{ProvidersView-DaCEV9pM.js → ProvidersView-BftGn8Cr.js} +1 -1
  82. package/bizar-dash/dist/assets/{ProvidersView-DaCEV9pM.js.map → ProvidersView-BftGn8Cr.js.map} +1 -1
  83. package/bizar-dash/dist/assets/{SchedulesView-DY2ByBj_.js → SchedulesView-CtNxtzN6.js} +1 -1
  84. package/bizar-dash/dist/assets/{SchedulesView-DY2ByBj_.js.map → SchedulesView-CtNxtzN6.js.map} +1 -1
  85. package/bizar-dash/dist/assets/{Select-Dbd88_n8.js → Select-0VcfTDeL.js} +1 -1
  86. package/bizar-dash/dist/assets/{Select-Dbd88_n8.js.map → Select-0VcfTDeL.js.map} +1 -1
  87. package/bizar-dash/dist/assets/{SettingsRow-CLMNd434.js → SettingsRow-BLic3DQl.js} +1 -1
  88. package/bizar-dash/dist/assets/{SettingsRow-CLMNd434.js.map → SettingsRow-BLic3DQl.js.map} +1 -1
  89. package/bizar-dash/dist/assets/{SettingsView-b24oFq9x.js → SettingsView-BjS3i4nr.js} +1 -1
  90. package/bizar-dash/dist/assets/{SettingsView-b24oFq9x.js.map → SettingsView-BjS3i4nr.js.map} +1 -1
  91. package/bizar-dash/dist/assets/{Sheet-DnDid58j.js → Sheet-BOProMs2.js} +1 -1
  92. package/bizar-dash/dist/assets/{Sheet-DnDid58j.js.map → Sheet-BOProMs2.js.map} +1 -1
  93. package/bizar-dash/dist/assets/{Slider-BJCvKI1I.js → Slider-B0eLEEUt.js} +1 -1
  94. package/bizar-dash/dist/assets/{Slider-BJCvKI1I.js.map → Slider-B0eLEEUt.js.map} +1 -1
  95. package/bizar-dash/dist/assets/{Sparkline-ktqQLdyb.js → Sparkline-SSrdgyW0.js} +1 -1
  96. package/bizar-dash/dist/assets/{Sparkline-ktqQLdyb.js.map → Sparkline-SSrdgyW0.js.map} +1 -1
  97. package/bizar-dash/dist/assets/{Switch-CwXfsDii.js → Switch-B3EQzb_q.js} +1 -1
  98. package/bizar-dash/dist/assets/{Switch-CwXfsDii.js.map → Switch-B3EQzb_q.js.map} +1 -1
  99. package/bizar-dash/dist/assets/{TasksView-BHmpzFCf.js → TasksView-SWjF_ZUQ.js} +1 -1
  100. package/bizar-dash/dist/assets/{TasksView-BHmpzFCf.js.map → TasksView-SWjF_ZUQ.js.map} +1 -1
  101. package/bizar-dash/dist/assets/{Textarea-BzIAwSlp.js → Textarea-QuofpMlq.js} +1 -1
  102. package/bizar-dash/dist/assets/{Textarea-BzIAwSlp.js.map → Textarea-QuofpMlq.js.map} +1 -1
  103. package/bizar-dash/dist/assets/{UpdateView-CSmX9NN0.js → UpdateView-CEHW2nd6.js} +1 -1
  104. package/bizar-dash/dist/assets/{UpdateView-CSmX9NN0.js.map → UpdateView-CEHW2nd6.js.map} +1 -1
  105. package/bizar-dash/dist/assets/{UsageView-COyr_Gfi.js → UsageView-BqOw6FBg.js} +1 -1
  106. package/bizar-dash/dist/assets/{UsageView-COyr_Gfi.js.map → UsageView-BqOw6FBg.js.map} +1 -1
  107. package/bizar-dash/dist/assets/{ViewHeader-BzYZ3m-e.js → ViewHeader-B6P-C4GD.js} +1 -1
  108. package/bizar-dash/dist/assets/{ViewHeader-BzYZ3m-e.js.map → ViewHeader-B6P-C4GD.js.map} +1 -1
  109. package/bizar-dash/dist/assets/{VoiceView-D33_xG5t.js → VoiceView-CwrPwWgi.js} +1 -1
  110. package/bizar-dash/dist/assets/{VoiceView-D33_xG5t.js.map → VoiceView-CwrPwWgi.js.map} +1 -1
  111. package/bizar-dash/dist/assets/{brain-CM-Vls8r.js → brain-CwZt9Kta.js} +1 -1
  112. package/bizar-dash/dist/assets/{brain-CM-Vls8r.js.map → brain-CwZt9Kta.js.map} +1 -1
  113. package/bizar-dash/dist/assets/{calendar-C0lgZk69.js → calendar-C5oK0_nM.js} +1 -1
  114. package/bizar-dash/dist/assets/{calendar-C0lgZk69.js.map → calendar-C5oK0_nM.js.map} +1 -1
  115. package/bizar-dash/dist/assets/{circle-Bx_hQEBC.js → circle-D1Lc_P2a.js} +1 -1
  116. package/bizar-dash/dist/assets/{circle-Bx_hQEBC.js.map → circle-D1Lc_P2a.js.map} +1 -1
  117. package/bizar-dash/dist/assets/{circle-alert-DHvsqpw5.js → circle-alert-HdHe7m8K.js} +1 -1
  118. package/bizar-dash/dist/assets/{circle-alert-DHvsqpw5.js.map → circle-alert-HdHe7m8K.js.map} +1 -1
  119. package/bizar-dash/dist/assets/{circle-check-Bx8wmXR9.js → circle-check-Cs-xoV7R.js} +1 -1
  120. package/bizar-dash/dist/assets/{circle-check-Bx8wmXR9.js.map → circle-check-Cs-xoV7R.js.map} +1 -1
  121. package/bizar-dash/dist/assets/{clock-CejGoecX.js → clock-DEIuPV60.js} +1 -1
  122. package/bizar-dash/dist/assets/{clock-CejGoecX.js.map → clock-DEIuPV60.js.map} +1 -1
  123. package/bizar-dash/dist/assets/{download-4cEUWue1.js → download-Clkq90Ct.js} +1 -1
  124. package/bizar-dash/dist/assets/{download-4cEUWue1.js.map → download-Clkq90Ct.js.map} +1 -1
  125. package/bizar-dash/dist/assets/{external-link-0NqUhI4i.js → external-link-D5tq2o_I.js} +1 -1
  126. package/bizar-dash/dist/assets/{external-link-0NqUhI4i.js.map → external-link-D5tq2o_I.js.map} +1 -1
  127. package/bizar-dash/dist/assets/{file-text-yEMkrSoZ.js → file-text-CQ12t7Oc.js} +1 -1
  128. package/bizar-dash/dist/assets/{file-text-yEMkrSoZ.js.map → file-text-CQ12t7Oc.js.map} +1 -1
  129. package/bizar-dash/dist/assets/{filter-BXt60zAS.js → filter-BcbE37wP.js} +1 -1
  130. package/bizar-dash/dist/assets/{filter-BXt60zAS.js.map → filter-BcbE37wP.js.map} +1 -1
  131. package/bizar-dash/dist/assets/{git-pull-request-BjG5lNtR.js → git-pull-request-D1qNDRia.js} +1 -1
  132. package/bizar-dash/dist/assets/{git-pull-request-BjG5lNtR.js.map → git-pull-request-D1qNDRia.js.map} +1 -1
  133. package/bizar-dash/dist/assets/{main-CokHAXWM.js → main-Cxeu4LhG.js} +28 -33
  134. package/bizar-dash/dist/assets/main-Cxeu4LhG.js.map +1 -0
  135. package/bizar-dash/dist/assets/network-DJdvVaHx.js +6 -0
  136. package/bizar-dash/dist/assets/network-DJdvVaHx.js.map +1 -0
  137. package/bizar-dash/dist/assets/{pause-CAmulU0F.js → pause-DUn9L5PB.js} +1 -1
  138. package/bizar-dash/dist/assets/{pause-CAmulU0F.js.map → pause-DUn9L5PB.js.map} +1 -1
  139. package/bizar-dash/dist/assets/{pencil-eq5IqBmU.js → pencil-DVrBlYV3.js} +1 -1
  140. package/bizar-dash/dist/assets/{pencil-eq5IqBmU.js.map → pencil-DVrBlYV3.js.map} +1 -1
  141. package/bizar-dash/dist/assets/{play-BoLrrcBA.js → play-CZjsr3XM.js} +1 -1
  142. package/bizar-dash/dist/assets/{play-BoLrrcBA.js.map → play-CZjsr3XM.js.map} +1 -1
  143. package/bizar-dash/dist/assets/{plug-DBrVM9lK.js → plug-BKuyIe5W.js} +1 -1
  144. package/bizar-dash/dist/assets/{plug-DBrVM9lK.js.map → plug-BKuyIe5W.js.map} +1 -1
  145. package/bizar-dash/dist/assets/{plus-DENm0hqV.js → plus-77h62zZm.js} +1 -1
  146. package/bizar-dash/dist/assets/{plus-DENm0hqV.js.map → plus-77h62zZm.js.map} +1 -1
  147. package/bizar-dash/dist/assets/{power-DLjXF7zE.js → power-DLmx-gCP.js} +1 -1
  148. package/bizar-dash/dist/assets/{power-DLjXF7zE.js.map → power-DLmx-gCP.js.map} +1 -1
  149. package/bizar-dash/dist/assets/{power-off-D47cOnG_.js → power-off-BFKzHXic.js} +1 -1
  150. package/bizar-dash/dist/assets/{power-off-D47cOnG_.js.map → power-off-BFKzHXic.js.map} +1 -1
  151. package/bizar-dash/dist/assets/{refresh-ccw-DNrXX9gB.js → refresh-ccw-DenGBOcq.js} +1 -1
  152. package/bizar-dash/dist/assets/{refresh-ccw-DNrXX9gB.js.map → refresh-ccw-DenGBOcq.js.map} +1 -1
  153. package/bizar-dash/dist/assets/{refresh-cw-CoAHDpeS.js → refresh-cw-ENBVR8hW.js} +1 -1
  154. package/bizar-dash/dist/assets/{refresh-cw-CoAHDpeS.js.map → refresh-cw-ENBVR8hW.js.map} +1 -1
  155. package/bizar-dash/dist/assets/{rotate-ccw-ObX5awaG.js → rotate-ccw-MxryaY7q.js} +1 -1
  156. package/bizar-dash/dist/assets/{rotate-ccw-ObX5awaG.js.map → rotate-ccw-MxryaY7q.js.map} +1 -1
  157. package/bizar-dash/dist/assets/{rotate-cw-xpPJw9LP.js → rotate-cw-CDOlRtUR.js} +1 -1
  158. package/bizar-dash/dist/assets/{rotate-cw-xpPJw9LP.js.map → rotate-cw-CDOlRtUR.js.map} +1 -1
  159. package/bizar-dash/dist/assets/{save-D1iKiJJl.js → save-Db1b-1Uq.js} +1 -1
  160. package/bizar-dash/dist/assets/{save-D1iKiJJl.js.map → save-Db1b-1Uq.js.map} +1 -1
  161. package/bizar-dash/dist/assets/{send-BRcAhZUm.js → send-yz6HFZM5.js} +1 -1
  162. package/bizar-dash/dist/assets/{send-BRcAhZUm.js.map → send-yz6HFZM5.js.map} +1 -1
  163. package/bizar-dash/dist/assets/{shield-alert-C6vEcK-g.js → shield-alert-DY6RAaC8.js} +1 -1
  164. package/bizar-dash/dist/assets/{shield-alert-C6vEcK-g.js.map → shield-alert-DY6RAaC8.js.map} +1 -1
  165. package/bizar-dash/dist/assets/{trash-2-DZDjEnNh.js → trash-2-Cv7U38jw.js} +1 -1
  166. package/bizar-dash/dist/assets/{trash-2-DZDjEnNh.js.map → trash-2-Cv7U38jw.js.map} +1 -1
  167. package/bizar-dash/dist/assets/{trending-up-DMj-AJVf.js → trending-up-CRPNagyX.js} +1 -1
  168. package/bizar-dash/dist/assets/{trending-up-DMj-AJVf.js.map → trending-up-CRPNagyX.js.map} +1 -1
  169. package/bizar-dash/dist/assets/{triangle-alert-_2xe_tl4.js → triangle-alert-tUTXbcEP.js} +1 -1
  170. package/bizar-dash/dist/assets/{triangle-alert-_2xe_tl4.js.map → triangle-alert-tUTXbcEP.js.map} +1 -1
  171. package/bizar-dash/dist/assets/{upload-DdDljAeV.js → upload-Cq7uTsrT.js} +1 -1
  172. package/bizar-dash/dist/assets/{upload-DdDljAeV.js.map → upload-Cq7uTsrT.js.map} +1 -1
  173. package/bizar-dash/dist/assets/{zap-mHsgh0FP.js → zap-5lAQBAPX.js} +1 -1
  174. package/bizar-dash/dist/assets/{zap-mHsgh0FP.js.map → zap-5lAQBAPX.js.map} +1 -1
  175. package/bizar-dash/dist/index.html +1 -1
  176. package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  177. package/bizar-dash/src/server/api.mjs +5 -1
  178. package/bizar-dash/src/server/routes/update.mjs +48 -21
  179. package/bizar-dash/src/web/v8/App.tsx +0 -4
  180. package/bizar-dash/src/web/v8/__tests__/new-views.test.tsx +0 -17
  181. package/bizar-dash/src/web/v8/shell/Sidebar.tsx +3 -4
  182. package/bizar-dash/src/web/v8/views/Router.tsx +0 -5
  183. package/bizar-dash/tests/_update-test-helpers.mjs +13 -0
  184. package/bizar-dash/tests/update-check.test.mjs +18 -13
  185. package/bizar-dash/tests/update-packages.test.mjs +154 -0
  186. package/bizar-dash/tests/update-run.test.mjs +17 -80
  187. package/cli/banner.mjs +1 -1
  188. package/cli/bin.mjs +8 -1
  189. package/cli/bootstrap.mjs +1 -1
  190. package/cli/commands/claude-cmd.mjs +1 -1
  191. package/cli/commands/deploy/docker.mjs +0 -3
  192. package/cli/doctor.mjs +1 -1
  193. package/cli/install/banner.mjs +67 -0
  194. package/cli/install/banner.test.mjs +39 -0
  195. package/cli/install/index.mjs +32 -0
  196. package/cli/install/index.test.mjs +61 -0
  197. package/cli/install/paths.mjs +80 -0
  198. package/cli/install/paths.test.mjs +77 -0
  199. package/cli/install/plugin.mjs +161 -0
  200. package/cli/install/postinstall.mjs +111 -0
  201. package/cli/install.mjs +13 -600
  202. package/cli/migrate.mjs +157 -0
  203. package/cli/migrate.test.mjs +85 -0
  204. package/cli/provision.mjs +615 -1918
  205. package/cli/provision.test.mjs +1 -1
  206. package/cli/service-controller.test.mjs +0 -1
  207. package/cli/service-env.mjs +0 -1
  208. package/cli/utils.mjs +0 -1
  209. package/install.sh +54 -466
  210. package/package.json +1 -1
  211. package/packages/sdk/dist/mcp/server.js +41 -409
  212. package/packages/sdk/dist/router/q-learning-router.d.ts +3 -5
  213. package/packages/sdk/dist/router/q-learning-router.js +9 -5
  214. package/packages/sdk/package.json +1 -1
  215. package/scripts/install-curl.sh +17 -0
  216. package/bizar-dash/dist/assets/HeadroomView-D1UiTYbt.js +0 -11
  217. package/bizar-dash/dist/assets/HeadroomView-D1UiTYbt.js.map +0 -1
  218. package/bizar-dash/dist/assets/MiscView-B8QY82Pm.js +0 -1
  219. package/bizar-dash/dist/assets/ObsidianView-BCTejEj2.js +0 -1
  220. package/bizar-dash/dist/assets/main-CokHAXWM.js.map +0 -1
  221. package/bizar-dash/skills/headroom/SKILL.md +0 -94
  222. package/bizar-dash/src/server/headroom.mjs +0 -649
  223. package/bizar-dash/src/server/routes/headroom.mjs +0 -126
  224. package/bizar-dash/src/web/v8/views/Headroom/HeadroomView.tsx +0 -201
  225. package/bizar-dash/tests/graphify-mod-spawn.node.test.mjs +0 -90
  226. package/bizar-dash/tests/headroom-install.test.mjs +0 -173
  227. package/bizar-dash/tests/headroom-settings.test.mjs +0 -126
  228. package/bizar-dash/tests/headroom-status.test.mjs +0 -117
  229. package/bizar-dash/tests/v7-archive/mobile-overview.test.tsx +0 -214
  230. package/bizar-dash/tests/v7-archive/mobile-settings.test.tsx +0 -181
  231. package/bizar-dash/tests/v7-archive/navigation/CommandPalette.test.tsx +0 -105
  232. package/bizar-dash/tests/v7-archive/settings-nav.test.tsx +0 -128
  233. package/bizar-dash/tests/v7-archive/views/Overview.test.tsx +0 -413
  234. package/cli/commands/headroom.mjs +0 -204
  235. package/cli/provision-claude.mjs +0 -1176
@@ -1,126 +0,0 @@
1
- /**
2
- * src/server/routes/headroom.mjs
3
- *
4
- * v1.0.0 — REST endpoints for Headroom management.
5
- *
6
- * Mounted at /api/headroom/* by api.mjs.
7
- */
8
- import { Router } from 'express';
9
- import {
10
- getHeadroomStatus,
11
- getHeadroomStats,
12
- installHeadroom,
13
- wrapCline,
14
- unwrapCline,
15
- startProxy,
16
- stopProxy,
17
- getClineConfig,
18
- headroomStartupHook,
19
- } from '../headroom.mjs';
20
- import { wrap } from './_shared.mjs';
21
-
22
- export function createHeadroomRouter() {
23
- const router = Router();
24
-
25
- // GET /api/headroom/status
26
- router.get('/status', wrap(async (_req, res) => {
27
- const status = await getHeadroomStatus();
28
- res.json(status);
29
- }));
30
-
31
- // GET /api/headroom/stats?hours=24
32
- router.get('/stats', wrap(async (req, res) => {
33
- const hours = Math.max(1, Math.min(parseInt(req.query.hours, 10) || 24, 720));
34
- const stats = await getHeadroomStats({ hours });
35
- res.json(stats);
36
- }));
37
-
38
- // POST /api/headroom/install
39
- router.post('/install', wrap(async (req, res) => {
40
- const { force = false } = req.body || {};
41
- const result = await installHeadroom({ force: Boolean(force) });
42
- res.json(result);
43
- }));
44
-
45
- // POST /api/headroom/wrap
46
- router.post('/wrap', wrap(async (req, res) => {
47
- const { port } = req.body || {};
48
- const result = await wrapCline({
49
- port: port ? Math.max(1, Math.min(parseInt(port, 10), 65535)) : 8787,
50
- });
51
- res.json(result);
52
- }));
53
-
54
- // POST /api/headroom/unwrap
55
- router.post('/unwrap', wrap(async (_req, res) => {
56
- const result = await unwrapCline();
57
- res.json(result);
58
- }));
59
-
60
- // POST /api/headroom/proxy/start
61
- router.post('/proxy/start', wrap(async (req, res) => {
62
- const { port, host } = req.body || {};
63
- const result = await startProxy({
64
- port: port ? Math.max(1, Math.min(parseInt(port, 10), 65535)) : 8787,
65
- host: typeof host === 'string' && host ? host : '127.0.0.1',
66
- });
67
- res.json(result);
68
- }));
69
-
70
- // POST /api/headroom/proxy/stop
71
- router.post('/proxy/stop', wrap(async (_req, res) => {
72
- const result = await stopProxy();
73
- res.json(result);
74
- }));
75
-
76
- // GET /api/headroom/cline-config
77
- router.get('/cline-config', wrap(async (_req, res) => {
78
- const config = await getClineConfig();
79
- res.json(config);
80
- }));
81
-
82
- // POST /api/headroom/auto-route — configure all providers to route through Headroom
83
- router.post('/auto-route', wrap(async (_req, res) => {
84
- const { loadConfig, saveConfig } = await import('../providers-store.mjs');
85
- const cfg = loadConfig();
86
- const headroomPort = 8787;
87
- const proxyUrl = `http://127.0.0.1:${headroomPort}/v1`;
88
-
89
- // For each provider, prepend the headroom proxy URL to baseURL
90
- const providers = cfg.provider || {};
91
- let changed = 0;
92
- for (const [id, p] of Object.entries(providers)) {
93
- if (!p || typeof p !== 'object') continue;
94
- const baseURL = p.baseURL || p.options?.baseURL || '';
95
- // Only update if not already routed
96
- if (baseURL && !baseURL.includes('127.0.0.1:8787') && !baseURL.includes('localhost:8787')) {
97
- const newBaseURL = `${proxyUrl}/${baseURL.replace(/^https?:\/\//, '')}`;
98
- if (p.options) {
99
- providers[id] = { ...p, options: { ...p.options, baseURL: newBaseURL } };
100
- } else {
101
- providers[id] = { ...p, baseURL: newBaseURL };
102
- }
103
- changed++;
104
- }
105
- }
106
- cfg.provider = providers;
107
- saveConfig(cfg);
108
-
109
- res.json({ ok: true, changed, message: `Updated ${changed} provider(s) to route through Headroom proxy.` });
110
- }));
111
-
112
- // POST /api/headroom/startup-hook — trigger the startup hook programmatically
113
- router.post('/startup-hook', wrap(async (_req, res) => {
114
- const { readSettings } = await import('./_shared.mjs');
115
- const settings = readSettings();
116
- const headroomSettings = settings?.data?.headroom;
117
- if (!headroomSettings) {
118
- res.status(400).json({ error: 'bad_request', message: 'Headroom settings not found in settings.json' });
119
- return;
120
- }
121
- const result = await headroomStartupHook(headroomSettings);
122
- res.json(result);
123
- }));
124
-
125
- return router;
126
- }
@@ -1,201 +0,0 @@
1
- import { useEffect, useState } from 'react';
2
- import { Network, Play, Square, Plug, Unplug, RotateCw, RefreshCw } from 'lucide-react';
3
- import { Stack } from '../../ui/primitives/Stack.js';
4
- import { Inline } from '../../ui/primitives/Inline.js';
5
- import { Grid } from '../../ui/primitives/Grid.js';
6
- import { ViewHeader } from '../../ui/data/ViewHeader.js';
7
- import { Card, CardBody } from '../../ui/data/Card.js';
8
- import { Badge } from '../../ui/data/Badge.js';
9
- import { Button } from '../../ui/controls/Button.js';
10
- import { Skeleton } from '../../ui/feedback/Skeleton.js';
11
- import { useFetch } from '../../data/useFetch.js';
12
- import { fetchJson, FetchError } from '../../data/fetcher.js';
13
-
14
- /**
15
- * HeadroomView — Sprint S35. Headroom proxy install + lifecycle.
16
- */
17
-
18
- type Status = 'installed' | 'not-installed' | 'running' | 'stopped';
19
-
20
- interface HeadroomStatus {
21
- status: Status;
22
- version?: string;
23
- port?: number;
24
- message?: string;
25
- }
26
-
27
- interface HeadroomStats {
28
- hours?: number;
29
- requests?: number;
30
- tokensSaved?: number;
31
- cacheHits?: number;
32
- cacheMisses?: number;
33
- }
34
-
35
- export function HeadroomView(): JSX.Element {
36
- const status = useFetch<HeadroomStatus>('/api/headroom/status');
37
- const stats = useFetch<HeadroomStats>('/api/headroom/stats?hours=24');
38
- const [busy, setBusy] = useState<string | null>(null);
39
- const [error, setError] = useState<string | null>(null);
40
-
41
- useEffect(() => {
42
- const id = setInterval(() => { void status.refetch(); }, 15_000);
43
- return () => clearInterval(id);
44
- }, [status]);
45
-
46
- const install = async (): Promise<void> => {
47
- setBusy('install');
48
- setError(null);
49
- try {
50
- await fetchJson('/api/headroom/install', { method: 'POST', body: { force: false } });
51
- void status.refetch();
52
- } catch (err) {
53
- setError(err instanceof FetchError ? err.message : (err as Error).message);
54
- } finally {
55
- setBusy(null);
56
- }
57
- };
58
-
59
- const wrap = async (): Promise<void> => {
60
- setBusy('wrap');
61
- setError(null);
62
- try {
63
- await fetchJson('/api/headroom/wrap', { method: 'POST', body: { port: 8787 } });
64
- void status.refetch();
65
- } catch (err) {
66
- setError(err instanceof FetchError ? err.message : (err as Error).message);
67
- } finally {
68
- setBusy(null);
69
- }
70
- };
71
-
72
- const unwrap = async (): Promise<void> => {
73
- setBusy('unwrap');
74
- setError(null);
75
- try {
76
- await fetchJson('/api/headroom/unwrap', { method: 'POST' });
77
- void status.refetch();
78
- } catch (err) {
79
- setError(err instanceof FetchError ? err.message : (err as Error).message);
80
- } finally {
81
- setBusy(null);
82
- }
83
- };
84
-
85
- const start = async (): Promise<void> => {
86
- setBusy('start');
87
- setError(null);
88
- try {
89
- await fetchJson('/api/headroom/start', { method: 'POST' });
90
- void status.refetch();
91
- } catch (err) {
92
- setError(err instanceof FetchError ? err.message : (err as Error).message);
93
- } finally {
94
- setBusy(null);
95
- }
96
- };
97
-
98
- const stop = async (): Promise<void> => {
99
- setBusy('stop');
100
- setError(null);
101
- try {
102
- await fetchJson('/api/headroom/stop', { method: 'POST' });
103
- void status.refetch();
104
- } catch (err) {
105
- setError(err instanceof FetchError ? err.message : (err as Error).message);
106
- } finally {
107
- setBusy(null);
108
- }
109
- };
110
-
111
- const s = status.data;
112
- const isInstalled = s && s.status !== 'not-installed';
113
- const isRunning = s?.status === 'running';
114
-
115
- return (
116
- <Stack gap={5} data-testid="headroom-view">
117
- <ViewHeader
118
- title="Headroom"
119
- description="Local proxy that reduces token cost via caching + batching. Install, wrap Cline, start/stop."
120
- actions={
121
- <Inline gap={2} align="center">
122
- {error !== null && <span role="alert" style={{ color: 'var(--danger)', fontSize: 'var(--fs-12)' }}>{error}</span>}
123
- <Button variant="ghost" onClick={() => { void status.refetch(); }} data-testid="headroom-refresh">
124
- <RefreshCw size={14} aria-hidden /> Refresh
125
- </Button>
126
- </Inline>
127
- }
128
- />
129
-
130
- <Card variant="elevated">
131
- <CardBody>
132
- {status.loading && !s ? (
133
- <Skeleton style={{ height: 60 }} />
134
- ) : (
135
- <Inline align="center" justify="between" gap={3}>
136
- <Stack gap={1}>
137
- <Inline align="center" gap={2}>
138
- <Network size={16} aria-hidden />
139
- <strong>{s?.status ?? 'unknown'}</strong>
140
- {s?.version && <Badge tone="neutral">v{s.version}</Badge>}
141
- {s?.port && <Badge tone="info">port {s.port}</Badge>}
142
- </Inline>
143
- {s?.message && <span style={{ fontSize: 'var(--fs-12)', color: 'var(--fg-muted)' }}>{s.message}</span>}
144
- </Stack>
145
- <Inline gap={1} wrap={true}>
146
- {!isInstalled && (
147
- <Button variant="primary" onClick={() => void install()} disabled={busy === 'install'} data-testid="headroom-install">
148
- <Plug size={12} aria-hidden /> {busy === 'install' ? 'Installing…' : 'Install'}
149
- </Button>
150
- )}
151
- {isInstalled && (
152
- <>
153
- <Button variant={isRunning ? 'secondary' : 'primary'} onClick={() => void (isRunning ? stop() : start())} disabled={busy === 'start' || busy === 'stop'} data-testid="headroom-toggle-running">
154
- {isRunning ? <Square size={12} aria-hidden /> : <Play size={12} aria-hidden />}
155
- {isRunning ? (busy === 'stop' ? 'Stopping…' : 'Stop') : (busy === 'start' ? 'Starting…' : 'Start')}
156
- </Button>
157
- <Button variant="ghost" onClick={() => void wrap()} disabled={busy === 'wrap'} data-testid="headroom-wrap">
158
- <RotateCw size={12} aria-hidden /> {busy === 'wrap' ? 'Wrapping…' : 'Wrap Cline'}
159
- </Button>
160
- <Button variant="ghost" onClick={() => void unwrap()} disabled={busy === 'unwrap'} data-testid="headroom-unwrap">
161
- <Unplug size={12} aria-hidden /> {busy === 'unwrap' ? 'Unwrapping…' : 'Unwrap'}
162
- </Button>
163
- </>
164
- )}
165
- </Inline>
166
- </Inline>
167
- )}
168
- </CardBody>
169
- </Card>
170
-
171
- <Grid cols={3}>
172
- <Card variant="default">
173
- <CardBody>
174
- <Stack gap={1}>
175
- <span style={{ fontSize: 'var(--fs-12)', color: 'var(--fg-muted)' }}>Requests (24h)</span>
176
- <strong style={{ fontFamily: 'var(--font-mono)', fontSize: 'var(--fs-18)' }}>{stats.data?.requests ?? 0}</strong>
177
- </Stack>
178
- </CardBody>
179
- </Card>
180
- <Card variant="default">
181
- <CardBody>
182
- <Stack gap={1}>
183
- <span style={{ fontSize: 'var(--fs-12)', color: 'var(--fg-muted)' }}>Tokens saved</span>
184
- <strong style={{ fontFamily: 'var(--font-mono)', fontSize: 'var(--fs-18)' }}>{stats.data?.tokensSaved ?? 0}</strong>
185
- </Stack>
186
- </CardBody>
187
- </Card>
188
- <Card variant="default">
189
- <CardBody>
190
- <Stack gap={1}>
191
- <span style={{ fontSize: 'var(--fs-12)', color: 'var(--fg-muted)' }}>Cache hits / misses</span>
192
- <strong style={{ fontFamily: 'var(--font-mono)', fontSize: 'var(--fs-18)' }}>
193
- {stats.data?.cacheHits ?? 0} / {stats.data?.cacheMisses ?? 0}
194
- </strong>
195
- </Stack>
196
- </CardBody>
197
- </Card>
198
- </Grid>
199
- </Stack>
200
- );
201
- }
@@ -1,90 +0,0 @@
1
- /**
2
- * graphify-mod-spawn.node.test.mjs — regression test for the SIGPIPE crash.
3
- *
4
- * The graphify mod's route.mjs used to spawn the `graphify` CLI with
5
- * `spawnSync` + `stdio: 'inherit'`. That worked in a terminal, but the
6
- * dashboard is a background daemon with stdin=/dev/null and
7
- * stdout/stderr redirected to a log file. With `stdio: 'inherit'`, the
8
- * child inherits those descriptors; if anything closes or rotates them
9
- * mid-build, the kernel delivers SIGPIPE to the dashboard — silently
10
- * killing it. The user reported "building the graph crashed the dash".
11
- *
12
- * This test verifies the source file no longer contains the dangerous
13
- * pattern. We parse the file as text (the route.mjs is loaded by the
14
- * dashboard's vm sandbox, not directly importable here).
15
- *
16
- * Run with: node --test tests/graphify-mod-spawn.node.test.mjs
17
- */
18
- import { describe, it } from 'node:test';
19
- import assert from 'node:assert/strict';
20
- import { readFileSync } from 'node:fs';
21
- import { join, resolve } from 'node:path';
22
-
23
- // Test lives at bizar-dash/tests/, source is at the repo root.
24
- const REPO = resolve(import.meta.dirname, '..', '..');
25
- const SOURCE = join(REPO, 'mods-examples/graphify/route.mjs');
26
-
27
- describe('graphify mod — SIGPIPE-safe spawn', () => {
28
- it('source file exists at the expected path', () => {
29
- const text = readFileSync(SOURCE, 'utf8');
30
- assert.ok(text.length > 100, 'source file should be substantial');
31
- });
32
-
33
- it('does NOT import spawnSync (causes blocking + stdio inheritance)', () => {
34
- const text = readFileSync(SOURCE, 'utf8');
35
- assert.ok(
36
- !/\bspawnSync\b/.test(text),
37
- 'route.mjs must not import or call spawnSync — use async spawn instead',
38
- );
39
- });
40
-
41
- it('does NOT use stdio: \'inherit\' (the SIGPIPE crash vector)', () => {
42
- const text = readFileSync(SOURCE, 'utf8');
43
- // Strip block comments and line comments before checking — we don't
44
- // want the bug description itself to trip the test.
45
- const stripped = text
46
- .replace(/\/\*[\s\S]*?\*\//g, '')
47
- .replace(/^\s*\/\/.*$/gm, '')
48
- .replace(/`stdio:\s*'inherit'`/g, '');
49
- // Look for the literal string in any quoting style.
50
- const patterns = [
51
- /stdio:\s*['"]inherit['"]/,
52
- /stdio:\s*\[\s*['"]inherit['"]/,
53
- ];
54
- for (const pat of patterns) {
55
- assert.ok(
56
- !pat.test(stripped),
57
- `route.mjs must not use stdio: 'inherit' — it propagates SIGPIPE to the dashboard`,
58
- );
59
- }
60
- });
61
-
62
- it('isolates the spawned child stdio with [\'ignore\', \'pipe\', \'pipe\']', () => {
63
- const text = readFileSync(SOURCE, 'utf8');
64
- assert.ok(
65
- /stdio:\s*\[\s*['"]ignore['"]\s*,\s*['"]pipe['"]\s*,\s*['"]pipe['"]\s*\]/.test(text),
66
- 'spawn options should set stdio to [ignore, pipe, pipe] for SIGPIPE isolation',
67
- );
68
- });
69
-
70
- it('runs graphify as an async spawn (not blocking)', () => {
71
- const text = readFileSync(SOURCE, 'utf8');
72
- // runGraphify should be declared `async function` and return a Promise.
73
- assert.ok(
74
- /async\s+function\s+runGraphify\s*\(/.test(text),
75
- 'runGraphify should be declared async so the dashboard event loop is not blocked',
76
- );
77
- assert.ok(
78
- /return\s+new\s+Promise\s*\(/.test(text),
79
- 'runGraphify should return a Promise (async spawn lifecycle)',
80
- );
81
- });
82
-
83
- it('forwards child stdout/stderr to the broadcast callback (no streaming lost)', () => {
84
- const text = readFileSync(SOURCE, 'utf8');
85
- assert.ok(
86
- /broadcast\(\s*\{\s*type:\s*['"]graphify:build:log['"]/.test(text),
87
- 'runGraphify should call broadcast with graphify:build:log events so the dashboard UI keeps streaming',
88
- );
89
- });
90
- });
@@ -1,173 +0,0 @@
1
- /**
2
- * tests/headroom-install.test.mjs
3
- *
4
- * Tests for installHeadroom(), wrapCline(), unwrapCline(), startProxy(), stopProxy().
5
- * Uses real commands when available, mocks when not.
6
- */
7
-
8
- import { test, describe, beforeEach, afterEach } from 'node:test';
9
- import assert from 'node:assert/strict';
10
- import { spawn } from 'node:child_process';
11
- import { tmpdir } from 'node:os';
12
- import { join } from 'node:path';
13
- import { mkdirSync, rmSync, writeFileSync, existsSync, unlinkSync, readFileSync } from 'node:fs';
14
- import http from 'node:http';
15
-
16
- // ── Run command helper ──────────────────────────────────────────────────────
17
-
18
- function runCmd(cmd, args, opts = {}) {
19
- return new Promise((resolve) => {
20
- const child = spawn(cmd, args, { timeout: 5000, ...opts });
21
- let stdout = '';
22
- let stderr = '';
23
- child.stdout?.on('data', (d) => { stdout += d.toString(); });
24
- child.stderr?.on('data', (d) => { stderr += d.toString(); });
25
- child.on('close', (code) => resolve({ stdout: stdout.trim(), stderr: stderr.trim(), exitCode: code ?? 0 }));
26
- child.on('error', (err) => resolve({ stdout: '', stderr: err.message, exitCode: 1 }));
27
- });
28
- }
29
-
30
- // ── Install tests ────────────────────────────────────────────────────────────
31
-
32
- test('installHeadroom returns correct shape', async () => {
33
- const { installHeadroom } = await import('../src/server/headroom.mjs');
34
- const result = await installHeadroom({ force: false });
35
-
36
- assert.ok(typeof result.ok === 'boolean', 'ok is boolean');
37
- assert.ok(typeof result.installed === 'boolean', 'installed is boolean');
38
- assert.ok(typeof result.version === 'string' || result.version === null, 'version is string|null');
39
- assert.ok(typeof result.method === 'string', 'method is string');
40
- });
41
-
42
- test('installHeadroom returns already-installed when headroom is on PATH', async () => {
43
- const { installed } = await runCmd('headroom', ['--version']);
44
- if (installed !== 0) {
45
- // headroom not installed — skip
46
- return;
47
- }
48
-
49
- const { installHeadroom } = await import('../src/server/headroom.mjs');
50
- const result = await installHeadroom({ force: false });
51
-
52
- assert.strictEqual(result.installed, true, 'should report already installed');
53
- assert.strictEqual(result.method, 'already-installed', 'method should be already-installed');
54
- });
55
-
56
- test('installHeadroom.force reinstalls even when already installed', async () => {
57
- const { installHeadroom } = await import('../src/server/headroom.mjs');
58
- const result = await installHeadroom({ force: true });
59
-
60
- // Returns the shape regardless of outcome
61
- assert.ok(typeof result.ok === 'boolean', 'ok is boolean');
62
- assert.ok(typeof result.installed === 'boolean', 'installed is boolean');
63
- assert.ok(typeof result.method === 'string', 'method is string');
64
- });
65
-
66
- // ── Wrap / Unwrap tests ─────────────────────────────────────────────────────
67
-
68
- test('wrapCline returns correct shape', async () => {
69
- const { wrapCline } = await import('../src/server/headroom.mjs');
70
- const result = await wrapCline({ port: 8787 });
71
-
72
- assert.ok(typeof result.ok === 'boolean', 'ok is boolean');
73
- assert.ok(typeof result.port === 'number', 'port is number');
74
- assert.ok(typeof result.log === 'string', 'log is string');
75
- assert.strictEqual(result.port, 8787, 'port is 8787');
76
- });
77
-
78
- test('unwrapCline returns correct shape', async () => {
79
- const { unwrapCline } = await import('../src/server/headroom.mjs');
80
- const result = await unwrapCline();
81
-
82
- assert.ok(typeof result.ok === 'boolean', 'ok is boolean');
83
- assert.ok(typeof result.log === 'string', 'log is string');
84
- });
85
-
86
- // ── Proxy tests ─────────────────────────────────────────────────────────────
87
-
88
- test('startProxy returns correct shape', async () => {
89
- const { startProxy } = await import('../src/server/headroom.mjs');
90
- const result = await startProxy({ port: 18787, host: '127.0.0.1' });
91
-
92
- assert.ok(typeof result.ok === 'boolean', 'ok is boolean');
93
- assert.ok(typeof result.pid === 'number' || result.pid === null, 'pid is number|null');
94
- assert.ok(typeof result.port === 'number', 'port is number');
95
- assert.ok(typeof result.logPath === 'string', 'logPath is string');
96
- });
97
-
98
- test('stopProxy returns correct shape', async () => {
99
- const { stopProxy } = await import('../src/server/headroom.mjs');
100
- const result = await stopProxy();
101
-
102
- assert.ok(typeof result.ok === 'boolean', 'ok is boolean');
103
- assert.ok(Array.isArray(result.killed), 'killed is array');
104
- });
105
-
106
- // ── getClineConfig tests ──────────────────────────────────────────────────
107
-
108
- test('getClineConfig returns correct shape', async () => {
109
- const { getClineConfig } = await import('../src/server/headroom.mjs');
110
- const result = await getClineConfig();
111
-
112
- assert.ok(typeof result.configPath === 'string', 'configPath is string');
113
- assert.ok(typeof result.hasHeadroomProvider === 'boolean', 'hasHeadroomProvider is boolean');
114
- assert.ok(result.configPath.includes('cline.json'), 'configPath points to cline.json');
115
- });
116
-
117
- // ── withHeadroomProxy tests ──────────────────────────────────────────────────
118
-
119
- test('withHeadroomProxy returns URL unchanged when disabled', async () => {
120
- const { withHeadroomProxy } = await import('../src/server/headroom.mjs');
121
- const url = 'https://api.anthropic.com/v1/messages';
122
- // When headroom is not in settings (default), returns URL unchanged
123
- const result = withHeadroomProxy(url, { port: 8787 });
124
- // Result depends on settings — just verify it's a string
125
- assert.ok(typeof result === 'string', 'result is string');
126
- assert.ok(result.length > 0, 'result is non-empty');
127
- });
128
-
129
- // ── /api/headroom/install endpoint ──────────────────────────────────────────
130
-
131
- async function buildApp() {
132
- const { createHeadroomRouter } = await import('../src/server/routes/headroom.mjs');
133
- const express = (await import('express')).default;
134
- const app = express();
135
- app.use(express.json());
136
- // Mount at /api/headroom so routes like /status become /api/headroom/status
137
- app.use('/api/headroom', await createHeadroomRouter());
138
- return app;
139
- }
140
-
141
- test('POST /api/headroom/install returns result', async () => {
142
- const app = await buildApp();
143
- const server = app.listen(0);
144
- const { port } = server.address();
145
-
146
- const httpPost = () =>
147
- new Promise((resolve, reject) => {
148
- const data = JSON.stringify({ force: false });
149
- const req = http.request(
150
- { hostname: 'localhost', port, path: '/api/headroom/install', method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) } },
151
- (res) => {
152
- let body = '';
153
- res.on('data', (c) => (body += c));
154
- res.on('end', () => {
155
- try { resolve({ status: res.statusCode, body: JSON.parse(body || '{}') }); }
156
- catch { resolve({ status: res.statusCode, body }); }
157
- });
158
- },
159
- );
160
- req.on('error', reject);
161
- req.write(data);
162
- req.end();
163
- });
164
-
165
- try {
166
- const res = await httpPost();
167
- assert.strictEqual(res.status, 200, 'status should be 200');
168
- assert.ok(typeof res.body.ok === 'boolean', 'ok is boolean');
169
- assert.ok(typeof res.body.installed === 'boolean', 'installed is boolean');
170
- } finally {
171
- server.close();
172
- }
173
- });