@polderlabs/bizar 3.23.1 → 4.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.
Files changed (361) hide show
  1. package/README.md +83 -16
  2. package/bizar-dash/.bizar/activity.log +3 -0
  3. package/bizar-dash/.bizar/graph/.graphify_analysis.json +1540 -0
  4. package/bizar-dash/.bizar/graph/.graphify_labels.json +1 -0
  5. package/bizar-dash/.bizar/graph/GRAPH_REPORT.md +404 -0
  6. package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/0661b816358db84dfdb7f10443db8a61152fd20b3fee950caf8fbf32920a1790.json +1 -0
  7. package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/16da06d774142a6e5d74829ad982531286a6193ef798de8f593db029e123ec32.json +1 -0
  8. package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/27e5697c01254c69c64c5a33896669073b40afb5ba88375102ae77fa5404136d.json +1 -0
  9. package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/532997898027254aa4c0932243dc71d2f55888181232b65974961934997dc85b.json +1 -0
  10. package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/84e3451a64c7f99f0cd6a88b0748d7426780486f53ab4935b7476b28c08f8293.json +1 -0
  11. package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/88e5ea5f3e78fa69353fe9e6a8d1a05391d54605e25600cb125531eb5a6432ff.json +1 -0
  12. package/bizar-dash/.bizar/graph/cache/stat-index.json +1 -0
  13. package/bizar-dash/.bizar/graph/graph.html +307 -0
  14. package/bizar-dash/.bizar/graph/graph.json +40267 -0
  15. package/bizar-dash/.bizar/graph/manifest.json +762 -0
  16. package/bizar-dash/.graphifyignore +40 -0
  17. package/bizar-dash/.obsidian/README.md +14 -0
  18. package/bizar-dash/.obsidian/daily/2026-06-25.md +9 -0
  19. package/bizar-dash/.obsidian/vault.json +7 -0
  20. package/bizar-dash/.od-skills/web-prototype-e1429c1737/SKILL.md +103 -0
  21. package/bizar-dash/.od-skills/web-prototype-e1429c1737/assets/template.html +338 -0
  22. package/bizar-dash/.od-skills/web-prototype-e1429c1737/example.html +81 -0
  23. package/bizar-dash/.od-skills/web-prototype-e1429c1737/open-design.json +172 -0
  24. package/bizar-dash/.od-skills/web-prototype-e1429c1737/references/checklist.md +44 -0
  25. package/bizar-dash/.od-skills/web-prototype-e1429c1737/references/layouts.md +247 -0
  26. package/bizar-dash/CHANGELOG.md +474 -0
  27. package/bizar-dash/DESIGN.md +136 -0
  28. package/bizar-dash/dist/assets/main-B3RgW6FS.js +331 -0
  29. package/bizar-dash/dist/assets/main-B3RgW6FS.js.map +1 -0
  30. package/bizar-dash/dist/assets/main-DlJ8wgLR.css +1 -0
  31. package/bizar-dash/dist/assets/mobile-CsZQAswA.css +1 -0
  32. package/bizar-dash/dist/assets/mobile-DbxIw8BG.js +2 -0
  33. package/bizar-dash/dist/assets/mobile-DbxIw8BG.js.map +1 -0
  34. package/bizar-dash/dist/assets/mobile-Dvq2d53Y.js +354 -0
  35. package/bizar-dash/dist/assets/mobile-Dvq2d53Y.js.map +1 -0
  36. package/bizar-dash/dist/index.html +40 -0
  37. package/bizar-dash/dist/mobile.html +22 -0
  38. package/bizar-dash/plans/debug-plan/comments.json +1 -0
  39. package/bizar-dash/plans/debug-plan/meta.json +10 -0
  40. package/bizar-dash/plans/debug-plan/plan.json +174 -0
  41. package/bizar-dash/scripts/smoke-bg-retry.mjs +246 -0
  42. package/bizar-dash/src/cli/dashboard-ports.mjs +488 -0
  43. package/bizar-dash/src/cli.mjs +522 -0
  44. package/bizar-dash/src/server/activity-log.mjs +174 -0
  45. package/bizar-dash/src/server/agents-store.mjs +465 -0
  46. package/bizar-dash/src/server/api.mjs +138 -0
  47. package/bizar-dash/src/server/artifact-template.html +810 -0
  48. package/bizar-dash/src/server/artifacts-store.mjs +733 -0
  49. package/bizar-dash/src/server/auth.mjs +381 -0
  50. package/bizar-dash/src/server/background-store.mjs +447 -0
  51. package/bizar-dash/src/server/bg-poller.mjs +305 -0
  52. package/bizar-dash/src/server/bg-retry.mjs +574 -0
  53. package/bizar-dash/src/server/browser.mjs +40 -0
  54. package/bizar-dash/src/server/diagnostics-store.mjs +148 -0
  55. package/bizar-dash/src/server/dialog-poller.mjs +105 -0
  56. package/bizar-dash/src/server/dialog-store.mjs +177 -0
  57. package/bizar-dash/src/server/glyphs/mdx-compiler.mjs +583 -0
  58. package/bizar-dash/src/server/lib/path-safe.mjs +283 -0
  59. package/bizar-dash/src/server/memory-git.mjs +329 -0
  60. package/bizar-dash/src/server/memory-lightrag.mjs +767 -0
  61. package/bizar-dash/src/server/memory-schema.mjs +145 -0
  62. package/bizar-dash/src/server/memory-secrets.mjs +84 -0
  63. package/bizar-dash/src/server/memory-store.mjs +471 -0
  64. package/bizar-dash/src/server/mod-security.mjs +398 -0
  65. package/bizar-dash/src/server/mods-loader.mjs +1067 -0
  66. package/bizar-dash/src/server/notifications-store.mjs +243 -0
  67. package/bizar-dash/src/server/obsidian-store.mjs +318 -0
  68. package/bizar-dash/src/server/opencode-runner.mjs +279 -0
  69. package/bizar-dash/src/server/pair-store.mjs +138 -0
  70. package/bizar-dash/src/server/projects-store.mjs +364 -0
  71. package/bizar-dash/src/server/providers-store.mjs +789 -0
  72. package/bizar-dash/src/server/routes/_shared.mjs +415 -0
  73. package/bizar-dash/src/server/routes/activity.mjs +110 -0
  74. package/bizar-dash/src/server/routes/agents.mjs +134 -0
  75. package/bizar-dash/src/server/routes/artifacts.mjs +279 -0
  76. package/bizar-dash/src/server/routes/auth.mjs +69 -0
  77. package/bizar-dash/src/server/routes/background.mjs +165 -0
  78. package/bizar-dash/src/server/routes/chat.mjs +485 -0
  79. package/bizar-dash/src/server/routes/config.mjs +129 -0
  80. package/bizar-dash/src/server/routes/diagnostics.mjs +50 -0
  81. package/bizar-dash/src/server/routes/dialogs.mjs +86 -0
  82. package/bizar-dash/src/server/routes/fs.mjs +429 -0
  83. package/bizar-dash/src/server/routes/history.mjs +78 -0
  84. package/bizar-dash/src/server/routes/memory.mjs +658 -0
  85. package/bizar-dash/src/server/routes/misc.mjs +89 -0
  86. package/bizar-dash/src/server/routes/mods.mjs +359 -0
  87. package/bizar-dash/src/server/routes/notifications.mjs +54 -0
  88. package/bizar-dash/src/server/routes/obsidian.mjs +239 -0
  89. package/bizar-dash/src/server/routes/opencode-sessions.mjs +55 -0
  90. package/bizar-dash/src/server/routes/overview.mjs +91 -0
  91. package/bizar-dash/src/server/routes/pair.mjs +67 -0
  92. package/bizar-dash/src/server/routes/projects.mjs +175 -0
  93. package/bizar-dash/src/server/routes/providers.mjs +47 -0
  94. package/bizar-dash/src/server/routes/schedules.mjs +104 -0
  95. package/bizar-dash/src/server/routes/settings.mjs +51 -0
  96. package/bizar-dash/src/server/routes/skills.mjs +76 -0
  97. package/bizar-dash/src/server/routes/tasks.mjs +468 -0
  98. package/bizar-dash/src/server/routes/themes.mjs +51 -0
  99. package/bizar-dash/src/server/routes-v2/auth.mjs +59 -0
  100. package/bizar-dash/src/server/routes-v2/events.mjs +107 -0
  101. package/bizar-dash/src/server/routes-v2/health.mjs +21 -0
  102. package/bizar-dash/src/server/routes-v2/index.mjs +90 -0
  103. package/bizar-dash/src/server/routes-v2/sessions.mjs +111 -0
  104. package/bizar-dash/src/server/schedules-runner.mjs +369 -0
  105. package/bizar-dash/src/server/schedules-store.mjs +268 -0
  106. package/bizar-dash/src/server/search-store.mjs +220 -0
  107. package/bizar-dash/src/server/serve-info.mjs +684 -0
  108. package/bizar-dash/src/server/server.mjs +796 -0
  109. package/bizar-dash/src/server/settings-store.mjs +45 -0
  110. package/bizar-dash/src/server/skills-store.mjs +300 -0
  111. package/bizar-dash/src/server/state.mjs +437 -0
  112. package/bizar-dash/src/server/tailscale-store.mjs +113 -0
  113. package/bizar-dash/src/server/task-delegator.mjs +824 -0
  114. package/bizar-dash/src/server/tasks-store.mjs +508 -0
  115. package/bizar-dash/src/server/tui.mjs +923 -0
  116. package/bizar-dash/src/server/update-store.mjs +168 -0
  117. package/bizar-dash/src/server/v2-auth-file.mjs +99 -0
  118. package/bizar-dash/src/server/v2-event-bus.mjs +128 -0
  119. package/bizar-dash/src/server/watcher.mjs +81 -0
  120. package/bizar-dash/src/server/yaml.mjs +238 -0
  121. package/bizar-dash/src/web/App.tsx +718 -0
  122. package/bizar-dash/src/web/MobileApp.tsx +408 -0
  123. package/bizar-dash/src/web/components/ArtifactCreateDialog.tsx +95 -0
  124. package/bizar-dash/src/web/components/ArtifactListDialog.tsx +55 -0
  125. package/bizar-dash/src/web/components/ArtifactViewer.tsx +143 -0
  126. package/bizar-dash/src/web/components/AuditDialog.tsx +79 -0
  127. package/bizar-dash/src/web/components/BgStatusBadge.tsx +32 -0
  128. package/bizar-dash/src/web/components/Button.tsx +56 -0
  129. package/bizar-dash/src/web/components/CanvasContextMenu.tsx +68 -0
  130. package/bizar-dash/src/web/components/Card.tsx +40 -0
  131. package/bizar-dash/src/web/components/CollapsibleSection.tsx +72 -0
  132. package/bizar-dash/src/web/components/CommandDialog.tsx +66 -0
  133. package/bizar-dash/src/web/components/EmptyState.tsx +30 -0
  134. package/bizar-dash/src/web/components/FileBrowser.tsx +633 -0
  135. package/bizar-dash/src/web/components/HelpDialog.tsx +55 -0
  136. package/bizar-dash/src/web/components/KillConfirmDialog.tsx +88 -0
  137. package/bizar-dash/src/web/components/Modal.tsx +215 -0
  138. package/bizar-dash/src/web/components/Notifications.tsx +241 -0
  139. package/bizar-dash/src/web/components/SearchModal.tsx +224 -0
  140. package/bizar-dash/src/web/components/Sidebar.tsx +66 -0
  141. package/bizar-dash/src/web/components/Spinner.tsx +19 -0
  142. package/bizar-dash/src/web/components/StatusBadge.tsx +25 -0
  143. package/bizar-dash/src/web/components/Tag.tsx +28 -0
  144. package/bizar-dash/src/web/components/Toast.tsx +150 -0
  145. package/bizar-dash/src/web/components/Topbar.tsx +368 -0
  146. package/bizar-dash/src/web/components/VisualPlanDialog.tsx +58 -0
  147. package/bizar-dash/src/web/components/chat/AgentChip.tsx +68 -0
  148. package/bizar-dash/src/web/components/chat/ChatBubble.tsx +60 -0
  149. package/bizar-dash/src/web/components/chat/ChatThread.tsx +88 -0
  150. package/bizar-dash/src/web/components/chat/ChatTopBar.tsx +50 -0
  151. package/bizar-dash/src/web/components/chat/Composer.tsx +164 -0
  152. package/bizar-dash/src/web/components/chat/ConfirmModal.tsx +54 -0
  153. package/bizar-dash/src/web/components/chat/EmptyState.tsx +21 -0
  154. package/bizar-dash/src/web/components/chat/FirstRunGreeting.tsx +18 -0
  155. package/bizar-dash/src/web/components/chat/FloatingComposer.tsx +32 -0
  156. package/bizar-dash/src/web/components/chat/InfoPanel.tsx +119 -0
  157. package/bizar-dash/src/web/components/chat/LoadingSkeleton.tsx +19 -0
  158. package/bizar-dash/src/web/components/chat/MessageBubble.tsx +60 -0
  159. package/bizar-dash/src/web/components/chat/SessionList.tsx +153 -0
  160. package/bizar-dash/src/web/components/chat/StreamingIndicator.tsx +9 -0
  161. package/bizar-dash/src/web/components/chat/SuggestionCards.tsx +55 -0
  162. package/bizar-dash/src/web/components/chat/WelcomeScreen.tsx +36 -0
  163. package/bizar-dash/src/web/components/chat/index.ts +20 -0
  164. package/bizar-dash/src/web/components/chat/useAutoGrowTextarea.ts +18 -0
  165. package/bizar-dash/src/web/components/chat/useChat.ts +219 -0
  166. package/bizar-dash/src/web/components/chat/useSlashCommands.ts +47 -0
  167. package/bizar-dash/src/web/index.html +37 -0
  168. package/bizar-dash/src/web/lib/api.ts +193 -0
  169. package/bizar-dash/src/web/lib/markdown.tsx +59 -0
  170. package/bizar-dash/src/web/lib/types.ts +668 -0
  171. package/bizar-dash/src/web/lib/utils.ts +114 -0
  172. package/bizar-dash/src/web/lib/ws.ts +176 -0
  173. package/bizar-dash/src/web/main.tsx +34 -0
  174. package/bizar-dash/src/web/mobile/MobileApp.tsx +278 -0
  175. package/bizar-dash/src/web/mobile/MobileBottomNav.tsx +38 -0
  176. package/bizar-dash/src/web/mobile/MobileTopbar.tsx +115 -0
  177. package/bizar-dash/src/web/mobile/components/MobileBottomSheet.tsx +182 -0
  178. package/bizar-dash/src/web/mobile/components/MobileListItem.tsx +64 -0
  179. package/bizar-dash/src/web/mobile/components/MobileModal.tsx +123 -0
  180. package/bizar-dash/src/web/mobile/views/MobileActivity.tsx +273 -0
  181. package/bizar-dash/src/web/mobile/views/MobileAgents.tsx +374 -0
  182. package/bizar-dash/src/web/mobile/views/MobileArtifactCanvas.tsx +439 -0
  183. package/bizar-dash/src/web/mobile/views/MobileArtifacts.tsx +143 -0
  184. package/bizar-dash/src/web/mobile/views/MobileChat.tsx +180 -0
  185. package/bizar-dash/src/web/mobile/views/MobileConfig.tsx +175 -0
  186. package/bizar-dash/src/web/mobile/views/MobileHistory.tsx +191 -0
  187. package/bizar-dash/src/web/mobile/views/MobileMods.tsx +149 -0
  188. package/bizar-dash/src/web/mobile/views/MobileMore.tsx +121 -0
  189. package/bizar-dash/src/web/mobile/views/MobileNotifications.tsx +137 -0
  190. package/bizar-dash/src/web/mobile/views/MobilePlans.tsx +26 -0
  191. package/bizar-dash/src/web/mobile/views/MobileSchedules.tsx +685 -0
  192. package/bizar-dash/src/web/mobile/views/MobileSearchModal.tsx +172 -0
  193. package/bizar-dash/src/web/mobile/views/MobileSettings.tsx +388 -0
  194. package/bizar-dash/src/web/mobile/views/MobileSkills.tsx +153 -0
  195. package/bizar-dash/src/web/mobile/views/MobileTasks.tsx +564 -0
  196. package/bizar-dash/src/web/mobile.html +20 -0
  197. package/bizar-dash/src/web/mobile.tsx +13 -0
  198. package/bizar-dash/src/web/styles/chat.css +198 -0
  199. package/bizar-dash/src/web/styles/glyphs.css +960 -0
  200. package/bizar-dash/src/web/styles/main.css +8855 -0
  201. package/bizar-dash/src/web/styles/mobile-chat.css +5 -0
  202. package/bizar-dash/src/web/styles/mobile.css +1960 -0
  203. package/bizar-dash/src/web/views/Activity.tsx +1203 -0
  204. package/bizar-dash/src/web/views/Agents.tsx +701 -0
  205. package/bizar-dash/src/web/views/Artifacts.tsx +1480 -0
  206. package/bizar-dash/src/web/views/BackgroundAgents.tsx +450 -0
  207. package/bizar-dash/src/web/views/Chat.tsx +190 -0
  208. package/bizar-dash/src/web/views/Config.tsx +2063 -0
  209. package/bizar-dash/src/web/views/History.tsx +335 -0
  210. package/bizar-dash/src/web/views/ModView.tsx +207 -0
  211. package/bizar-dash/src/web/views/Mods.tsx +693 -0
  212. package/bizar-dash/src/web/views/Overview.tsx +812 -0
  213. package/bizar-dash/src/web/views/Providers.tsx +302 -0
  214. package/bizar-dash/src/web/views/Schedules.tsx +802 -0
  215. package/bizar-dash/src/web/views/Settings.tsx +1787 -0
  216. package/bizar-dash/src/web/views/Skills.tsx +431 -0
  217. package/bizar-dash/src/web/views/Tasks.tsx +1523 -0
  218. package/bizar-dash/src/web/views/glyphs/GlyphRenderer.tsx +613 -0
  219. package/bizar-dash/src/web/views/glyphs/components.tsx +1098 -0
  220. package/bizar-dash/templates/mod/FORMAT.md +76 -0
  221. package/bizar-dash/templates/mod/hello-mod/README.md +19 -0
  222. package/bizar-dash/templates/mod/hello-mod/agents/greeter.md +8 -0
  223. package/bizar-dash/templates/mod/hello-mod/commands/hello.md +6 -0
  224. package/bizar-dash/templates/mod/hello-mod/mod.json +20 -0
  225. package/bizar-dash/templates/mod/hello-mod/routes/ping.mjs +9 -0
  226. package/bizar-dash/templates/mod/hello-mod/views/HelloView.tsx +10 -0
  227. package/bizar-dash/tests/dashboard-ports.test.mjs +138 -0
  228. package/bizar-dash/tests/graphify-mod-spawn.node.test.mjs +90 -0
  229. package/bizar-dash/tests/memory-cli.test.mjs +542 -0
  230. package/bizar-dash/tests/memory-config.test.mjs +422 -0
  231. package/bizar-dash/tests/memory-git.test.mjs +246 -0
  232. package/bizar-dash/tests/memory-lightrag.test.mjs +153 -0
  233. package/bizar-dash/tests/memory-protocol-drift.test.mjs +45 -0
  234. package/bizar-dash/tests/memory-schema.test.mjs +198 -0
  235. package/bizar-dash/tests/memory-secrets.test.mjs +121 -0
  236. package/bizar-dash/tests/memory-store.test.mjs +390 -0
  237. package/bizar-dash/tests/memory-sync.test.mjs +141 -0
  238. package/bizar-dash/tests/mod-instructions.node.test.mjs +188 -0
  239. package/bizar-dash/tests/mod-security.test.mjs +273 -0
  240. package/bizar-dash/tests/mod-upgrade.node.test.mjs +357 -0
  241. package/bizar-dash/tests/no-agent-browser.node.test.mjs +98 -0
  242. package/bizar-dash/tests/obsidian-back-compat.test.mjs +199 -0
  243. package/bizar-dash/tests/opencode-runner.test.mjs +172 -0
  244. package/bizar-dash/tests/path-safe.test.mjs +108 -0
  245. package/bizar-dash/tests/providers-store-backup-keys.node.test.mjs +333 -0
  246. package/bizar-dash/tests/smoke-v2.mjs +212 -0
  247. package/bizar-dash/tests/strip-thinking.test.mjs +175 -0
  248. package/bizar-dash/tests/submit-feedback.test.mjs +353 -0
  249. package/bizar-dash/tests/tmux-wrap.test.mjs +145 -0
  250. package/bizar-dash/tests/yaml.test.mjs +128 -0
  251. package/bizar-dash/tsconfig.json +23 -0
  252. package/bizar-dash/vite.config.ts +27 -0
  253. package/cli/atomic.mjs +73 -0
  254. package/cli/banner.mjs +1 -1
  255. package/cli/bin.mjs +44 -47
  256. package/cli/bootstrap.mjs +1 -1
  257. package/cli/copy.mjs +25 -16
  258. package/cli/dev-link.mjs +17 -1
  259. package/cli/doctor.mjs +4 -4
  260. package/cli/doctor.test.mjs +2 -2
  261. package/cli/init.mjs +135 -1
  262. package/cli/install.mjs +31 -39
  263. package/cli/memory-constants.mjs +59 -0
  264. package/cli/memory.mjs +1436 -0
  265. package/cli/service.mjs +5 -6
  266. package/cli/utils.mjs +6 -3
  267. package/config/AGENTS.md +7 -7
  268. package/config/agents/_shared/AGENT_BASELINE.md +59 -61
  269. package/config/opencode.json +13 -38
  270. package/config/skills/memory-protocol/SKILL.md +105 -0
  271. package/config/skills/obsidian/SKILL.md +270 -65
  272. package/install.sh +59 -19
  273. package/package.json +49 -14
  274. package/packages/sdk/LICENSE +21 -0
  275. package/packages/sdk/README.md +80 -0
  276. package/packages/sdk/src/client.ts +183 -0
  277. package/packages/sdk/src/errors.ts +120 -0
  278. package/packages/sdk/src/events.ts +153 -0
  279. package/packages/sdk/src/index.ts +63 -0
  280. package/packages/sdk/src/types.ts +176 -0
  281. package/packages/sdk/src/version.ts +4 -0
  282. package/packages/sdk/tests/client.test.ts +217 -0
  283. package/packages/sdk/tests/errors.test.ts +108 -0
  284. package/packages/sdk/tests/events.test.ts +139 -0
  285. package/packages/sdk/tests/fixtures/fetch-mock.ts +152 -0
  286. package/packages/sdk/tests/fixtures/sse-mock.ts +30 -0
  287. package/packages/sdk/tsconfig.json +25 -0
  288. package/packages/sdk/vitest.config.ts +9 -0
  289. package/plugins/bizar/LICENSE +21 -0
  290. package/plugins/bizar/README.md +448 -0
  291. package/plugins/bizar/dist/index.js +29901 -0
  292. package/plugins/bizar/index.ts +1491 -0
  293. package/plugins/bizar/scripts/check-forbidden-imports.sh +33 -0
  294. package/plugins/bizar/src/background-state.ts +551 -0
  295. package/plugins/bizar/src/background.ts +1250 -0
  296. package/plugins/bizar/src/commands-impl.ts +464 -0
  297. package/plugins/bizar/src/commands.ts +1168 -0
  298. package/plugins/bizar/src/dashboard-client.ts +233 -0
  299. package/plugins/bizar/src/event-stream.ts +606 -0
  300. package/plugins/bizar/src/fingerprint.ts +120 -0
  301. package/plugins/bizar/src/handoff.ts +79 -0
  302. package/plugins/bizar/src/http-client.ts +467 -0
  303. package/plugins/bizar/src/key-rotation.ts +218 -0
  304. package/plugins/bizar/src/logger.ts +144 -0
  305. package/plugins/bizar/src/loop.ts +176 -0
  306. package/plugins/bizar/src/opencode-runner.ts +390 -0
  307. package/plugins/bizar/src/options.ts +421 -0
  308. package/plugins/bizar/src/plan-fs.ts +323 -0
  309. package/plugins/bizar/src/reasoning-clean.ts +454 -0
  310. package/plugins/bizar/src/report.ts +178 -0
  311. package/plugins/bizar/src/research-prompt.ts +35 -0
  312. package/plugins/bizar/src/serve-info.ts +228 -0
  313. package/plugins/bizar/src/serve.ts +496 -0
  314. package/plugins/bizar/src/settings.ts +349 -0
  315. package/plugins/bizar/src/state.ts +298 -0
  316. package/plugins/bizar/src/tools/bg-collect.ts +104 -0
  317. package/plugins/bizar/src/tools/bg-get-comments.ts +239 -0
  318. package/plugins/bizar/src/tools/bg-kill.ts +87 -0
  319. package/plugins/bizar/src/tools/bg-spawn.ts +401 -0
  320. package/plugins/bizar/src/tools/bg-status.ts +99 -0
  321. package/plugins/bizar/src/tools/open-kb.ts +191 -0
  322. package/plugins/bizar/src/tools/plan-action.ts +767 -0
  323. package/plugins/bizar/src/tools/read-glyph-feedback.ts +170 -0
  324. package/plugins/bizar/src/tools/wait-for-feedback.ts +402 -0
  325. package/plugins/bizar/tests/attach-handler-bug.test.ts +169 -0
  326. package/plugins/bizar/tests/background-state.test.ts +277 -0
  327. package/plugins/bizar/tests/background.test.ts +402 -0
  328. package/plugins/bizar/tests/block.test.ts +195 -0
  329. package/plugins/bizar/tests/canonical-key-order.test.ts +75 -0
  330. package/plugins/bizar/tests/commands-impl.test.ts +442 -0
  331. package/plugins/bizar/tests/commands.test.ts +584 -0
  332. package/plugins/bizar/tests/config.test.ts +122 -0
  333. package/plugins/bizar/tests/dashboard-client.test.ts +159 -0
  334. package/plugins/bizar/tests/dispose.test.ts +336 -0
  335. package/plugins/bizar/tests/event-stream.test.ts +409 -0
  336. package/plugins/bizar/tests/event.test.ts +262 -0
  337. package/plugins/bizar/tests/fingerprint.test.ts +162 -0
  338. package/plugins/bizar/tests/http-client.test.ts +404 -0
  339. package/plugins/bizar/tests/init-helpers.test.ts +203 -0
  340. package/plugins/bizar/tests/integration/slash-command.test.ts +348 -0
  341. package/plugins/bizar/tests/integration/tool-routing.test.ts +314 -0
  342. package/plugins/bizar/tests/key-rotation.test.ts +396 -0
  343. package/plugins/bizar/tests/loop.test.ts +397 -0
  344. package/plugins/bizar/tests/options.test.ts +276 -0
  345. package/plugins/bizar/tests/reasoning-clean.test.ts +422 -0
  346. package/plugins/bizar/tests/serve.test.ts +339 -0
  347. package/plugins/bizar/tests/settings.test.ts +351 -0
  348. package/plugins/bizar/tests/stall-think.test.ts +750 -0
  349. package/plugins/bizar/tests/state.test.ts +276 -0
  350. package/plugins/bizar/tests/tools/bg-collect.test.ts +337 -0
  351. package/plugins/bizar/tests/tools/bg-get-comments.test.ts +485 -0
  352. package/plugins/bizar/tests/tools/bg-kill.test.ts +235 -0
  353. package/plugins/bizar/tests/tools/bg-spawn-delegation.test.ts +147 -0
  354. package/plugins/bizar/tests/tools/bg-spawn.test.ts +311 -0
  355. package/plugins/bizar/tests/tools/bg-status.test.ts +217 -0
  356. package/plugins/bizar/tests/tools/opencode-runner.test.ts +115 -0
  357. package/plugins/bizar/tests/tools/plan-action.test.ts +599 -0
  358. package/plugins/bizar/tests/tools/read-glyph-feedback.test.ts +253 -0
  359. package/plugins/bizar/tests/tools/wait-for-feedback.test.ts +390 -0
  360. package/plugins/bizar/tests/update-deadlock.test.ts +145 -0
  361. package/plugins/bizar/tsconfig.json +29 -0
@@ -0,0 +1,335 @@
1
+ // src/views/History.tsx — v3.4.0: cross-project history view.
2
+ // Shows per-project timelines (task events, plan events, project lifecycle)
3
+ // aggregated from the activity log.
4
+
5
+ import { useEffect, useState } from 'react';
6
+ import {
7
+ History as HistoryIcon,
8
+ RefreshCw,
9
+ Folder,
10
+ CheckSquare,
11
+ Filter,
12
+ ChevronDown,
13
+ ChevronRight,
14
+ Download,
15
+ FileText,
16
+ } from 'lucide-react';
17
+ import { Card, CardTitle, CardMeta } from '../components/Card';
18
+ import { Button } from '../components/Button';
19
+ import { Spinner } from '../components/Spinner';
20
+ import { useToast } from '../components/Toast';
21
+ import { api } from '../lib/api';
22
+ import { cn, formatRelative } from '../lib/utils';
23
+ import type { Settings, Snapshot } from '../lib/types';
24
+
25
+ type Props = {
26
+ snapshot: Snapshot;
27
+ settings: Settings;
28
+ activeTab: string;
29
+ setActiveTab: (id: string) => void;
30
+ refreshSnapshot: () => Promise<void>;
31
+ };
32
+
33
+ type HistoryEvent = {
34
+ ts: string;
35
+ kind: string;
36
+ nodeId?: string;
37
+ text?: string;
38
+ author?: string;
39
+ taskId?: string;
40
+ [k: string]: unknown;
41
+ };
42
+
43
+ type ProjectHistory = {
44
+ id: string;
45
+ name: string;
46
+ path: string;
47
+ status: string;
48
+ lastAccessed?: string | null;
49
+ tasks: { total: number; done: number; doing: number; blocked: number; queued: number };
50
+ plans: number;
51
+ };
52
+
53
+ type HistoryResponse = {
54
+ events: HistoryEvent[];
55
+ projects: ProjectHistory[];
56
+ stats: { file: string; size: number; lines: number; lastTs: string | null; counts: Record<string, number> };
57
+ generatedAt: string;
58
+ };
59
+
60
+ const TIME_RANGES = [
61
+ { id: '1h', label: 'Last hour', ms: 60 * 60 * 1000 },
62
+ { id: '1d', label: 'Last day', ms: 24 * 60 * 60 * 1000 },
63
+ { id: '7d', label: 'Last 7 days', ms: 7 * 24 * 60 * 60 * 1000 },
64
+ { id: '30d', label: 'Last 30 days', ms: 30 * 24 * 60 * 60 * 1000 },
65
+ { id: 'all', label: 'All time', ms: 0 },
66
+ ];
67
+
68
+ export function History({ snapshot }: Props) {
69
+ const toast = useToast();
70
+ const [data, setData] = useState<HistoryResponse | null>(null);
71
+ const [loading, setLoading] = useState(true);
72
+ const [rangeId, setRangeId] = useState<string>('1d');
73
+ const [expanded, setExpanded] = useState<Set<string>>(new Set());
74
+ const [tick, setTick] = useState(0);
75
+
76
+ const load = async (since?: string) => {
77
+ try {
78
+ const q = since ? `?since=${encodeURIComponent(since)}&limit=1000` : '?limit=1000';
79
+ const r = await api.get<HistoryResponse>(`/history${q}`);
80
+ setData(r);
81
+ setExpanded(
82
+ new Set(
83
+ r.projects
84
+ .filter((project) => r.events.some((ev) => (ev as { projectId?: string }).projectId === project.id))
85
+ .map((project) => project.id),
86
+ ),
87
+ );
88
+ } catch (err) {
89
+ toast.error(`History load failed: ${(err as Error).message}`);
90
+ } finally {
91
+ setLoading(false);
92
+ }
93
+ };
94
+
95
+ useEffect(() => {
96
+ setLoading(true);
97
+ const range = TIME_RANGES.find((r) => r.id === rangeId) || TIME_RANGES[1];
98
+ if (range.ms > 0) {
99
+ const since = new Date(Date.now() - range.ms).toISOString();
100
+ load(since);
101
+ } else {
102
+ load();
103
+ }
104
+ // eslint-disable-next-line react-hooks/exhaustive-deps
105
+ }, [rangeId]);
106
+
107
+ useEffect(() => {
108
+ const id = setInterval(() => setTick((t) => t + 1), 30_000);
109
+ return () => clearInterval(id);
110
+ }, []);
111
+
112
+ // Group events by project — use projectId where present, otherwise 'global'
113
+ const eventsByProject = new Map<string, HistoryEvent[]>();
114
+ for (const ev of data?.events || []) {
115
+ const pid = (ev as { projectId?: string }).projectId || 'global';
116
+ if (!eventsByProject.has(pid)) eventsByProject.set(pid, []);
117
+ eventsByProject.get(pid)!.push(ev);
118
+ }
119
+
120
+ const toggleProject = (id: string) => {
121
+ setExpanded((cur) => {
122
+ const next = new Set(cur);
123
+ if (next.has(id)) next.delete(id);
124
+ else next.add(id);
125
+ return next;
126
+ });
127
+ };
128
+
129
+ const onExportJson = () => {
130
+ if (!data) return;
131
+ const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
132
+ const url = URL.createObjectURL(blob);
133
+ const a = document.createElement('a');
134
+ a.href = url;
135
+ a.download = `bizar-history-${new Date().toISOString().replace(/[:.]/g, '-')}.json`;
136
+ a.click();
137
+ URL.revokeObjectURL(url);
138
+ toast.success('History exported.');
139
+ };
140
+
141
+ return (
142
+ <div className="view view-history" data-tick={tick}>
143
+ <header className="view-header">
144
+ <div className="view-header-text">
145
+ <h2 className="view-title">
146
+ <HistoryIcon size={18} /> History
147
+ </h2>
148
+ <p className="view-subtitle">
149
+ Cross-project history of events, tasks, and plans.
150
+ {data?.stats?.lastTs && (
151
+ <> · last event {formatRelative(data.stats.lastTs)}</>
152
+ )}
153
+ </p>
154
+ </div>
155
+ <div className="view-actions">
156
+ <div className="tasks-toolbar-group">
157
+ <Filter size={14} style={{ color: 'var(--text-dim)' }} />
158
+ <select
159
+ className="select select-sm"
160
+ value={rangeId}
161
+ onChange={(e) => setRangeId(e.target.value)}
162
+ title="Time range"
163
+ >
164
+ {TIME_RANGES.map((r) => (
165
+ <option key={r.id} value={r.id}>{r.label}</option>
166
+ ))}
167
+ </select>
168
+ </div>
169
+ <Button
170
+ variant="ghost"
171
+ size="sm"
172
+ onClick={() => {
173
+ setLoading(true);
174
+ const range = TIME_RANGES.find((r) => r.id === rangeId) || TIME_RANGES[1];
175
+ if (range.ms > 0) {
176
+ load(new Date(Date.now() - range.ms).toISOString());
177
+ } else {
178
+ load();
179
+ }
180
+ }}
181
+ >
182
+ <RefreshCw size={14} /> Refresh
183
+ </Button>
184
+ <Button variant="secondary" size="sm" onClick={onExportJson}>
185
+ <Download size={14} /> Export
186
+ </Button>
187
+ </div>
188
+ </header>
189
+
190
+ {loading && (
191
+ <div className="view-loading">
192
+ <Spinner size="lg" />
193
+ <p>Loading history…</p>
194
+ </div>
195
+ )}
196
+
197
+ {!loading && data && (
198
+ <div className="history-list">
199
+ {data.projects.length === 0 && (
200
+ <Card>
201
+ <CardTitle><Folder size={14} /> No projects</CardTitle>
202
+ <CardMeta>Register a project in Overview to start tracking history.</CardMeta>
203
+ </Card>
204
+ )}
205
+
206
+ {data.projects.map((p) => {
207
+ const events = eventsByProject.get(p.id) || [];
208
+ const isOpen = expanded.has(p.id);
209
+ return (
210
+ <Card key={p.id} className="history-project">
211
+ <div className="history-project-head">
212
+ <div style={{ display: 'flex', flexDirection: 'column', gap: 4, minWidth: 0, flex: 1 }}>
213
+ <div className="history-project-name">
214
+ <Folder size={16} /> {p.name}
215
+ </div>
216
+ <div className="history-project-meta">{p.path}</div>
217
+ </div>
218
+ <div className="history-project-stats">
219
+ <span className="history-project-stat">
220
+ <span className="history-project-stat-num">{p.tasks.done}</span>
221
+ <span className="muted">/ {p.tasks.total} done</span>
222
+ </span>
223
+ {p.tasks.doing > 0 && (
224
+ <span className="history-project-stat">
225
+ <span className="history-project-stat-num">{p.tasks.doing}</span>
226
+ <span className="muted">doing</span>
227
+ </span>
228
+ )}
229
+ {p.tasks.blocked > 0 && (
230
+ <span className="history-project-stat">
231
+ <span className="history-project-stat-num">{p.tasks.blocked}</span>
232
+ <span className="muted">blocked</span>
233
+ </span>
234
+ )}
235
+ <span className="history-project-stat">
236
+ <FileText size={11} /> <span className="history-project-stat-num">{p.plans}</span>
237
+ <span className="muted"> plan{p.plans === 1 ? '' : 's'}</span>
238
+ </span>
239
+ {p.lastAccessed && (
240
+ <span className="history-project-stat">
241
+ <span className="muted">last opened {formatRelative(p.lastAccessed)}</span>
242
+ </span>
243
+ )}
244
+ </div>
245
+ <button
246
+ type="button"
247
+ className="icon-btn"
248
+ onClick={() => toggleProject(p.id)}
249
+ aria-label={isOpen ? 'Collapse' : 'Expand'}
250
+ >
251
+ {isOpen ? <ChevronDown size={14} /> : <ChevronRight size={14} />}
252
+ </button>
253
+ </div>
254
+
255
+ {isOpen && (
256
+ <div className="history-timeline-mini">
257
+ {events.length === 0 ? (
258
+ <div className="muted" style={{ padding: '12px 8px', fontSize: 12 }}>
259
+ No events in this time range.
260
+ </div>
261
+ ) : (
262
+ events.slice(-100).reverse().map((ev, i) => (
263
+ <div key={i} className="history-timeline-row">
264
+ <span className="history-timeline-ts">
265
+ {new Date(ev.ts).toLocaleTimeString()}
266
+ </span>
267
+ <span className="history-timeline-kind">{ev.kind || 'event'}</span>
268
+ <span className="history-timeline-msg" title={String(ev.text || ev.title || '')}>
269
+ {ev.author ? `@${ev.author} ` : ''}
270
+ {String(ev.text ?? ev.title ?? ev.name ?? '')}
271
+ </span>
272
+ </div>
273
+ ))
274
+ )}
275
+ </div>
276
+ )}
277
+ </Card>
278
+ );
279
+ })}
280
+
281
+ {/* Global / unassigned events */}
282
+ {eventsByProject.has('global') && (
283
+ <Card className="history-project">
284
+ <div className="history-project-head">
285
+ <div style={{ display: 'flex', flexDirection: 'column', gap: 4, minWidth: 0, flex: 1 }}>
286
+ <div className="history-project-name">
287
+ <HistoryIcon size={16} /> Global events
288
+ </div>
289
+ <div className="history-project-meta">
290
+ Activity not tied to a specific project
291
+ </div>
292
+ </div>
293
+ </div>
294
+ <div className="history-timeline-mini">
295
+ {(eventsByProject.get('global') || []).slice(-100).reverse().map((ev, i) => (
296
+ <div key={i} className="history-timeline-row">
297
+ <span className="history-timeline-ts">
298
+ {new Date(ev.ts).toLocaleTimeString()}
299
+ </span>
300
+ <span className="history-timeline-kind">{ev.kind || 'event'}</span>
301
+ <span className="history-timeline-msg" title={String(ev.text || ev.title || '')}>
302
+ {ev.author ? `@${ev.author} ` : ''}
303
+ {String(ev.text ?? ev.title ?? ev.name ?? '')}
304
+ </span>
305
+ </div>
306
+ ))}
307
+ </div>
308
+ </Card>
309
+ )}
310
+
311
+ {data.stats?.counts && Object.keys(data.stats.counts).length > 0 && (
312
+ <Card>
313
+ <CardTitle>Event counts by kind</CardTitle>
314
+ <CardMeta>{data.stats.lines} total lines in log</CardMeta>
315
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, marginTop: 8 }}>
316
+ {Object.entries(data.stats.counts)
317
+ .sort((a, b) => b[1] - a[1])
318
+ .slice(0, 30)
319
+ .map(([kind, count]) => (
320
+ <span
321
+ key={kind}
322
+ className={cn('tag', 'tag-neutral')}
323
+ style={{ fontFamily: 'var(--font-mono)' }}
324
+ >
325
+ {kind}: {count}
326
+ </span>
327
+ ))}
328
+ </div>
329
+ </Card>
330
+ )}
331
+ </div>
332
+ )}
333
+ </div>
334
+ );
335
+ }
@@ -0,0 +1,207 @@
1
+ // src/views/ModView.tsx — render an installed mod's view (iframe or TSX tab).
2
+ //
3
+ // Mounted as a top-level tab in App.tsx when a mod contributes a view via
4
+ // `entry.view` or `web/index.html`. Mods become first-class nav items
5
+ // alongside Overview, Chat, Mods, etc.
6
+
7
+ import { Suspense, lazy, useEffect, useState } from 'react';
8
+ import { Card, CardTitle, CardMeta } from '../components/Card';
9
+ import { EmptyState } from '../components/EmptyState';
10
+ import { Button } from '../components/Button';
11
+ import { Spinner } from '../components/Spinner';
12
+ import { ExternalLink, Globe, LayoutTemplate, RefreshCw, AlertTriangle } from 'lucide-react';
13
+ import { api } from '../lib/api';
14
+ import { useToast } from '../components/Toast';
15
+
16
+ export type ModView = {
17
+ id: string;
18
+ modId: string;
19
+ kind: 'iframe' | 'tab';
20
+ label: string;
21
+ description?: string;
22
+ path?: string;
23
+ icon?: string;
24
+ component?: string | null;
25
+ };
26
+
27
+ type Props = {
28
+ /** Mod view id, e.g. 'graphify:web'. */
29
+ viewId: string;
30
+ /** Force a refresh of the iframe key — bumped when the user clicks Reload. */
31
+ reloadKey?: number;
32
+ /** Currently active tab id — kept on the Props surface so the parent
33
+ * (App.tsx) doesn't have to know which props the mod view consumes. */
34
+ activeTab?: string;
35
+ setActiveTab: (id: string) => void;
36
+ };
37
+
38
+ export function ModView({ viewId, reloadKey, setActiveTab }: Props) {
39
+ const toast = useToast();
40
+ const [view, setView] = useState<ModView | null>(null);
41
+ const [loading, setLoading] = useState(true);
42
+ const [iframeReload, setIframeReload] = useState(0);
43
+
44
+ useEffect(() => {
45
+ let cancelled = false;
46
+ (async () => {
47
+ try {
48
+ const r = await api.get<{ views: ModView[] }>('/mods/views');
49
+ if (cancelled) return;
50
+ const found = (r.views || []).find((v) => v.id === viewId);
51
+ setView(found || null);
52
+ } catch (err) {
53
+ toast.error(`Failed to load mod view: ${(err as Error).message}`);
54
+ } finally {
55
+ if (!cancelled) setLoading(false);
56
+ }
57
+ })();
58
+ return () => { cancelled = true; };
59
+ }, [viewId, toast]);
60
+
61
+ if (loading) {
62
+ return <EmptyState title="Loading mod view…" />;
63
+ }
64
+
65
+ if (!view) {
66
+ return (
67
+ <EmptyState
68
+ title="Mod view not found"
69
+ message={`No view with id "${viewId}" is currently installed.`}
70
+ action={<Button onClick={() => setActiveTab('mods')} variant="primary" size="sm">
71
+ Open Mods
72
+ </Button>}
73
+ />
74
+ );
75
+ }
76
+
77
+ // Iframe view: embed the mod's web/index.html in the dashboard.
78
+ if (view.kind === 'iframe') {
79
+ const src = `/api/mods/${view.modId}/web/index.html?t=${iframeReload + (reloadKey || 0)}`;
80
+ return (
81
+ <div className="mod-view-iframe-pane">
82
+ <div className="mod-view-iframe-header">
83
+ <span className="mod-view-iframe-label">
84
+ <Globe size={14} />
85
+ {view.label}
86
+ <span className="muted" style={{ fontSize: 11, fontWeight: 400 }}>by {view.modId}</span>
87
+ </span>
88
+ <div className="mod-view-iframe-actions">
89
+ <Button
90
+ variant="ghost"
91
+ size="sm"
92
+ onClick={() => setIframeReload((n) => n + 1)}
93
+ title="Reload this view"
94
+ >
95
+ <RefreshCw size={11} /> Reload
96
+ </Button>
97
+ <a
98
+ href={src}
99
+ target="_blank"
100
+ rel="noopener noreferrer"
101
+ className="icon-btn"
102
+ title="Open in new tab"
103
+ >
104
+ <ExternalLink size={12} />
105
+ </a>
106
+ </div>
107
+ </div>
108
+ <iframe
109
+ key={iframeReload + (reloadKey || 0)}
110
+ src={src}
111
+ className="mod-view-iframe"
112
+ title={view.label}
113
+ />
114
+ </div>
115
+ );
116
+ }
117
+
118
+ // v3.20.5 — Tab view (React component shipped with the mod). The
119
+ // component is dynamically imported from /api/mods/<id>/views/<file>.
120
+ // Mods ship a pre-built ES module (default export = React component).
121
+ // If the registry declared a component path, we lazy-load it.
122
+ if (view.component) {
123
+ // Capture the narrowed values into locals so the lazy() closure
124
+ // doesn't have to re-check nullability through `view`.
125
+ const componentPath: string = view.component;
126
+ const modId: string = view.modId;
127
+ const ModComponent = lazy(() =>
128
+ import(
129
+ /* @vite-ignore */
130
+ `/api/mods/${encodeURIComponent(modId)}/views/${encodeURIComponent(componentPath)}`
131
+ ).then((m) => ({ default: m.default ?? (() => null) })),
132
+ );
133
+ return (
134
+ <div className="mod-view-tab-pane">
135
+ <div className="mod-view-iframe-header">
136
+ <span className="mod-view-iframe-label">
137
+ <LayoutTemplate size={14} />
138
+ {view.label}
139
+ <span className="muted" style={{ fontSize: 11, fontWeight: 400 }}>by {view.modId}</span>
140
+ </span>
141
+ </div>
142
+ <div className="mod-view-tab-body">
143
+ <ErrorBoundary modId={view.modId} component={view.component}>
144
+ <Suspense fallback={<div style={{ padding: 24 }}><Spinner size="md" /> <span className="muted">Loading mod view…</span></div>}>
145
+ <ModComponent />
146
+ </Suspense>
147
+ </ErrorBoundary>
148
+ </div>
149
+ </div>
150
+ );
151
+ }
152
+
153
+ // Tab kind with no component path — show a placeholder.
154
+ return (
155
+ <Card>
156
+ <CardTitle>
157
+ <LayoutTemplate size={14} /> {view.label}
158
+ </CardTitle>
159
+ <CardMeta>by {view.modId}</CardMeta>
160
+ {view.description && <p className="muted" style={{ marginTop: 8 }}>{view.description}</p>}
161
+ <p className="muted" style={{ fontSize: 12, marginTop: 12 }}>
162
+ This mod declared a tab view but did not provide a component path in views/registry.json.
163
+ Add a <code>component</code> field (e.g. <code>"MyView.js"</code>) and ship the file under <code>views/</code>.
164
+ </p>
165
+ <div style={{ marginTop: 12 }}>
166
+ <Button variant="secondary" size="sm" onClick={() => setActiveTab('mods')}>
167
+ Open Mods page
168
+ </Button>
169
+ </div>
170
+ </Card>
171
+ );
172
+ }
173
+
174
+ /**
175
+ * Minimal error boundary for dynamically imported mod views. Catches
176
+ * a failure to load the module (404, syntax error in the mod's code,
177
+ * React.render-time throw) and renders a clean fallback instead of
178
+ * crashing the dashboard shell.
179
+ */
180
+ import { Component as ReactComponent, type ReactNode } from 'react';
181
+
182
+ class ErrorBoundary extends ReactComponent<
183
+ { children: ReactNode; modId: string; component: string },
184
+ { error: Error | null }
185
+ > {
186
+ state = { error: null as Error | null };
187
+ static getDerivedStateFromError(error: Error) {
188
+ return { error };
189
+ }
190
+ override componentDidCatch() {
191
+ /* swallow — error surfaced via state */
192
+ }
193
+ override render() {
194
+ if (this.state.error) {
195
+ return (
196
+ <div className="mod-view-error">
197
+ <AlertTriangle size={16} />
198
+ <strong>Failed to render mod view</strong>
199
+ <div className="muted" style={{ fontSize: 12, marginTop: 6 }}>
200
+ {this.props.modId} → {this.props.component}: {this.state.error.message}
201
+ </div>
202
+ </div>
203
+ );
204
+ }
205
+ return this.props.children;
206
+ }
207
+ }