@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,583 @@
1
+ /**
2
+ * src/server/glyphs/mdx-compiler.mjs
3
+ *
4
+ * v3.21.0 — Server-side MDX compile for Glyphs (the visual artifact /
5
+ * plan / recap system). Takes an MDX source string and returns a
6
+ * JSON-serializable "compiled glyph" object that the browser can
7
+ * render with React components per block type.
8
+ *
9
+ * Why this approach instead of @mdx-js/mdx compile() + evaluate()?
10
+ * 1. evaluate() returns a React component, not JSON — can't serialize
11
+ * across HTTP or for caching.
12
+ * 2. Blocks have JSON-serializable props (no functions, no React
13
+ * elements), so we can emit a clean data structure.
14
+ * 3. The browser renders blocks from that JSON, with a per-type React
15
+ * component map on the client.
16
+ *
17
+ * Pipeline:
18
+ * 1. Validate with @mdx-js/mdx compile() so syntax errors surface
19
+ * early (unclosed tags, bad import statements, broken JSX).
20
+ * 2. Extract frontmatter (--- delimited YAML-ish).
21
+ * 3. Walk the body and find <BlockName id="..." attr1="..." attr2={...}>
22
+ * children </BlockName> patterns. Emit a flat list of typed blocks.
23
+ * 4. Parse JSX expression values via a tiny recursive-descent parser
24
+ * that supports objects, arrays, strings, numbers, booleans, null.
25
+ * Identifiers are treated as `undefined` (literals only — actual
26
+ * runtime values must come from frontmatter if needed).
27
+ * 5. Treat the markdown text OUTSIDE blocks as a fallback "RichText"
28
+ * section with id `__prologue__`, so legacy plans that have no
29
+ * blocks still render something useful.
30
+ *
31
+ * The block vocabulary (matches agent-native.com /visual-plan and the
32
+ * glyphs-research.md spec):
33
+ * <RichText id="...">markdown</RichText>
34
+ * <Callout id="..." tone="info|warn|success|danger">markdown</Callout>
35
+ * <Checklist id="..." items={[{id, label, checked}]} />
36
+ * <Table id="..." columns={["a", "b"]} rows={[["x", "y"]]} />
37
+ * <CodeTabs id="..." tabs={[{id, label, language, code, caption?}]} />
38
+ * <Decision id="..." title question options={[{id, label, detail, recommended?}]} />
39
+ * <OpenQuestions id="..." questions={[{id, label, kind, options?}]} />
40
+ * <FileTree id="..." title entries={[{path, change, note?}]} />
41
+ * <Diff id="..." filename language mode="split|unified" before after />
42
+ * <Stat id="..." label value trend? />
43
+ * <Workflow id="..." steps={[{id, label, type}]} connections={[{from, to, label?}]} />
44
+ * <Mockup id="..." title x y w h html />
45
+ * <CommentPin id="..." x y commentId? />
46
+ * <Diagram id="..." title dataHtml dataCss />
47
+ *
48
+ * Returned shape:
49
+ * {
50
+ * frontmatter: { title?, status?, author?, tags?, ... },
51
+ * blocks: [{ id, type, data, childrenMarkdown? }],
52
+ * errors: [{ line, message }],
53
+ * compiledAt: ISO timestamp,
54
+ * }
55
+ */
56
+
57
+ import { compile } from '@mdx-js/mdx';
58
+
59
+ const BLOCK_TYPES = new Set([
60
+ 'RichText',
61
+ 'Callout',
62
+ 'Checklist',
63
+ 'Table',
64
+ 'CodeTabs',
65
+ 'Decision',
66
+ 'OpenQuestions',
67
+ 'FileTree',
68
+ 'Diff',
69
+ 'Stat',
70
+ 'Workflow',
71
+ 'Mockup',
72
+ 'CommentPin',
73
+ 'Diagram',
74
+ ]);
75
+
76
+ const PROLOGUE_ID = '__prologue__';
77
+
78
+ /**
79
+ * Parse a YAML-ish frontmatter block. Scalar values only; numbers and
80
+ * booleans are coerced, anything else stays as a string.
81
+ *
82
+ * @param {string} raw
83
+ * @returns {{ frontmatter: Record<string, unknown>, body: string }}
84
+ */
85
+ function parseFrontmatter(raw) {
86
+ if (!raw || !raw.startsWith('---')) {
87
+ return { frontmatter: {}, body: raw || '' };
88
+ }
89
+ // First "---" closes after a line that begins with "---"
90
+ const lines = raw.split(/\r?\n/);
91
+ if (lines[0].trim() !== '---') {
92
+ return { frontmatter: {}, body: raw };
93
+ }
94
+ let endIdx = -1;
95
+ for (let i = 1; i < lines.length; i += 1) {
96
+ if (lines[i].trim() === '---') {
97
+ endIdx = i;
98
+ break;
99
+ }
100
+ }
101
+ if (endIdx === -1) {
102
+ return { frontmatter: {}, body: raw };
103
+ }
104
+ const fmBlock = lines.slice(1, endIdx).join('\n');
105
+ const body = lines.slice(endIdx + 1).join('\n').replace(/^\s+/, '');
106
+ const frontmatter = {};
107
+ for (const line of fmBlock.split(/\r?\n/)) {
108
+ const m = /^([A-Za-z0-9_-]+)\s*:\s*(.*)$/.exec(line);
109
+ if (!m) continue;
110
+ const key = m[1];
111
+ let val = m[2].trim();
112
+ if (
113
+ (val.startsWith('"') && val.endsWith('"')) ||
114
+ (val.startsWith("'") && val.endsWith("'"))
115
+ ) {
116
+ val = val.slice(1, -1);
117
+ } else if (val === 'true') {
118
+ val = true;
119
+ } else if (val === 'false') {
120
+ val = false;
121
+ } else if (val !== '' && !Number.isNaN(Number(val))) {
122
+ val = Number(val);
123
+ } else if (val === 'null') {
124
+ val = null;
125
+ }
126
+ frontmatter[key] = val;
127
+ }
128
+ return { frontmatter, body };
129
+ }
130
+
131
+ /**
132
+ * Recursive-descent parser for JSX expression values. Supports the
133
+ * subset that the glyph block vocabulary uses:
134
+ * - Objects: { key: value, key2: value2 }
135
+ * - Arrays: [value, value2]
136
+ * - Strings: "value" or 'value'
137
+ * - Numbers: 123 or 1.5 or -1.5
138
+ * - Booleans: true / false
139
+ * - null
140
+ * - Identifiers are treated as `undefined` (we don't import runtime
141
+ * values; if you need one, put it in frontmatter and reference it).
142
+ *
143
+ * @param {string} src
144
+ * @returns {{ value: unknown, rest: string }}
145
+ */
146
+ function parseJsxValue(src) {
147
+ let i = 0;
148
+ const len = src.length;
149
+ const skipWs = () => {
150
+ while (i < len && /\s/.test(src[i])) i += 1;
151
+ };
152
+
153
+ const readString = (quote) => {
154
+ let out = '';
155
+ i += 1; // opening quote
156
+ while (i < len) {
157
+ const ch = src[i];
158
+ if (ch === '\\' && i + 1 < len) {
159
+ // Standard JS escape sequences. We support the common ones;
160
+ // unknown escapes pass through the character verbatim.
161
+ const next = src[i + 1];
162
+ const esc =
163
+ next === 'n' ? '\n' :
164
+ next === 't' ? '\t' :
165
+ next === 'r' ? '\r' :
166
+ next === 'b' ? '\b' :
167
+ next === 'f' ? '\f' :
168
+ next === 'v' ? '\v' :
169
+ next === '0' ? '\0' :
170
+ next;
171
+ out += esc;
172
+ i += 2;
173
+ continue;
174
+ }
175
+ if (ch === quote) {
176
+ i += 1;
177
+ return out;
178
+ }
179
+ out += ch;
180
+ i += 1;
181
+ }
182
+ throw new Error(`unterminated string starting at offset ${i}`);
183
+ };
184
+
185
+ const parseValue = () => {
186
+ skipWs();
187
+ if (i >= len) throw new Error('unexpected end of expression');
188
+ const ch = src[i];
189
+ if (ch === '{' || ch === '[') {
190
+ const closer = ch === '{' ? '}' : ']';
191
+ const open = ch;
192
+ i += 1;
193
+ const arr = ch === '[';
194
+ const items = arr ? [] : {};
195
+ skipWs();
196
+ if (src[i] === closer) {
197
+ i += 1;
198
+ return arr ? items : items;
199
+ }
200
+ while (i < len) {
201
+ skipWs();
202
+ if (arr) {
203
+ items.push(parseValue());
204
+ } else {
205
+ // key (bareword or quoted string)
206
+ let key;
207
+ if (src[i] === '"' || src[i] === "'") {
208
+ key = readString(src[i]);
209
+ } else {
210
+ const start = i;
211
+ while (i < len && /[A-Za-z0-9_-]/.test(src[i])) i += 1;
212
+ key = src.slice(start, i);
213
+ if (!key) throw new Error(`expected object key at offset ${start}`);
214
+ }
215
+ skipWs();
216
+ if (src[i] !== ':') throw new Error(`expected ':' at offset ${i}`);
217
+ i += 1;
218
+ items[key] = parseValue();
219
+ }
220
+ skipWs();
221
+ if (src[i] === ',') {
222
+ i += 1;
223
+ continue;
224
+ }
225
+ if (src[i] === closer) {
226
+ i += 1;
227
+ return items;
228
+ }
229
+ throw new Error(`expected ',' or '${closer}' at offset ${i}`);
230
+ }
231
+ throw new Error(`unterminated ${open === '{' ? 'object' : 'array'}`);
232
+ }
233
+ if (ch === '"' || ch === "'") return readString(ch);
234
+ if (ch === 't' && src.slice(i, i + 4) === 'true') {
235
+ i += 4;
236
+ return true;
237
+ }
238
+ if (ch === 'f' && src.slice(i, i + 5) === 'false') {
239
+ i += 5;
240
+ return false;
241
+ }
242
+ if (ch === 'n' && src.slice(i, i + 4) === 'null') {
243
+ i += 4;
244
+ return null;
245
+ }
246
+ if (ch === '-' || (ch >= '0' && ch <= '9')) {
247
+ const start = i;
248
+ if (ch === '-') i += 1;
249
+ while (i < len && /[0-9.]/.test(src[i])) i += 1;
250
+ const numStr = src.slice(start, i);
251
+ const num = Number(numStr);
252
+ if (Number.isNaN(num)) throw new Error(`bad number '${numStr}' at offset ${start}`);
253
+ return num;
254
+ }
255
+ // bareword identifier — treat as undefined marker
256
+ const start = i;
257
+ while (i < len && /[A-Za-z0-9_$.]/.test(src[i])) i += 1;
258
+ const ident = src.slice(start, i);
259
+ return { __ident: ident };
260
+ };
261
+
262
+ const value = parseValue();
263
+ skipWs();
264
+ return { value, rest: src.slice(i) };
265
+ }
266
+
267
+ /**
268
+ * Parse attributes on a JSX element opening tag. Returns an object
269
+ * keyed by attribute name, plus an array of validation errors.
270
+ *
271
+ * Handles:
272
+ * attr="value" → string
273
+ * attr='value' → string
274
+ * attr={jsExpression} → parsed JSON-ish value
275
+ * attr → boolean true
276
+ *
277
+ * @param {string} inside
278
+ * @returns {{ attrs: Record<string, unknown>, errors: Array<{ message: string }> }}
279
+ */
280
+ function parseAttrs(inside) {
281
+ const attrs = {};
282
+ const errors = [];
283
+ let i = 0;
284
+ const len = inside.length;
285
+ const skipWs = () => {
286
+ while (i < len && /\s/.test(inside[i])) i += 1;
287
+ };
288
+ while (i < len) {
289
+ skipWs();
290
+ if (i >= len) break;
291
+ // Read attribute name
292
+ const nameStart = i;
293
+ while (i < len && /[A-Za-z0-9_:-]/.test(inside[i])) i += 1;
294
+ const name = inside.slice(nameStart, i);
295
+ if (!name) break;
296
+ skipWs();
297
+ if (inside[i] === '=') {
298
+ i += 1;
299
+ skipWs();
300
+ if (inside[i] === '"' || inside[i] === "'") {
301
+ const q = inside[i];
302
+ i += 1;
303
+ const valStart = i;
304
+ while (i < len && inside[i] !== q) i += 1;
305
+ attrs[name] = inside.slice(valStart, i);
306
+ if (i < len) i += 1; // closing quote
307
+ } else if (inside[i] === '{') {
308
+ // Try parse; on failure, capture the raw source for debug.
309
+ // We need to find the matching closing brace (best-effort depth walk
310
+ // over the whole value) and pass the slice WITHOUT the trailing `}`,
311
+ // so parseJsxValue doesn't see an unterminated string at end of input.
312
+ let depth = 1;
313
+ let j = i + 1;
314
+ while (j < len && depth > 0) {
315
+ if (inside[j] === '{') depth += 1;
316
+ else if (inside[j] === '}') depth -= 1;
317
+ if (depth > 0) j += 1;
318
+ }
319
+ const valueSrc = inside.slice(i + 1, j);
320
+ try {
321
+ const { value } = parseJsxValue(valueSrc);
322
+ attrs[name] = value && typeof value === 'object' && value.__ident
323
+ ? undefined
324
+ : value;
325
+ } catch (err) {
326
+ errors.push({
327
+ message: `failed to parse attr "${name}": ${err.message}`,
328
+ });
329
+ }
330
+ // Skip past the matching closing brace
331
+ i = j + 1;
332
+ } else {
333
+ // Bare value (rare)
334
+ const valStart = i;
335
+ while (i < len && !/\s/.test(inside[i])) i += 1;
336
+ attrs[name] = inside.slice(valStart, i);
337
+ }
338
+ } else {
339
+ attrs[name] = true;
340
+ }
341
+ }
342
+ return { attrs, errors };
343
+ }
344
+
345
+ /**
346
+ * Walk the MDX body string and extract blocks. We look for `<BlockName`
347
+ * tags whose name is in BLOCK_TYPES, parse attributes, and capture
348
+ * children (markdown text or self-closing form).
349
+ *
350
+ * Returns:
351
+ * { blocks: [...], errors: [...] }
352
+ *
353
+ * The line numbers reported are 1-indexed against the body (after
354
+ * frontmatter stripping); the caller is responsible for offset if it
355
+ * wants file-line numbers.
356
+ *
357
+ * @param {string} body
358
+ * @returns {{ blocks: Array, errors: Array }}
359
+ */
360
+ function extractBlocks(body) {
361
+ const blocks = [];
362
+ const errors = [];
363
+ // Track trailing free-form markdown BEFORE the first block as the
364
+ // "__prologue__" RichText — this lets legacy plans without blocks
365
+ // still render their content.
366
+ let cursor = 0;
367
+ let prologueCaptured = false;
368
+ let blockCounter = 0;
369
+
370
+ const lineOf = (offset) => {
371
+ let line = 1;
372
+ for (let k = 0; k < offset && k < body.length; k += 1) {
373
+ if (body[k] === '\n') line += 1;
374
+ }
375
+ return line;
376
+ };
377
+
378
+ // Find the next opening tag for a known block. We use a custom walker
379
+ // instead of a regex because MDX attribute values can contain `>` (in
380
+ // strings or arrow functions) — a regex would stop too early and
381
+ // truncate the attribute list. The walker respects:
382
+ // - string literals (single + double quotes, with escape sequences)
383
+ // - JSX expression braces `{...}` (depth-balanced)
384
+ // - HTML comments `<!-- ... -->`
385
+ const findNextOpenTag = (startFrom) => {
386
+ let i = startFrom;
387
+ while (i < body.length) {
388
+ const ch = body[i];
389
+ if (ch !== '<') { i += 1; continue; }
390
+ // Skip comments `<!-- ... -->`
391
+ if (body.slice(i, i + 4) === '<!--') {
392
+ const end = body.indexOf('-->', i + 4);
393
+ if (end === -1) return null;
394
+ i = end + 3;
395
+ continue;
396
+ }
397
+ // Must be `<Word` (capital letter followed by alpha) — a known block opening tag.
398
+ if (!/[A-Z]/.test(body[i + 1] || '')) { i += 1; continue; }
399
+ let j = i + 1;
400
+ while (j < body.length && /[A-Za-z0-9]/.test(body[j])) j += 1;
401
+ const name = body.slice(i + 1, j);
402
+ if (!BLOCK_TYPES.has(name)) { i = j; continue; }
403
+ // Walk past the rest of the opening tag, respecting strings + braces.
404
+ while (j < body.length && body[j] !== '>') {
405
+ const c = body[j];
406
+ if (c === '"' || c === "'") {
407
+ // Skip string literal
408
+ j += 1;
409
+ while (j < body.length && body[j] !== c) {
410
+ if (body[j] === '\\') j += 2;
411
+ else j += 1;
412
+ }
413
+ j += 1;
414
+ continue;
415
+ }
416
+ j += 1;
417
+ }
418
+ if (j >= body.length) return null;
419
+ const selfClose = body[j - 1] === '/' ? '/' : '';
420
+ const inside = body.slice(i + 1 + name.length, j - (selfClose ? 1 : 0));
421
+ return { name, inside, selfClose, start: i, end: j + 1 };
422
+ }
423
+ return null;
424
+ };
425
+
426
+ while (cursor < body.length) {
427
+ const found = findNextOpenTag(cursor);
428
+ if (!found) break;
429
+ const { name, inside, selfClose, start, end } = found;
430
+ const { attrs, errors: attrErrors } = parseAttrs(inside);
431
+ for (const e of attrErrors) errors.push({ line: lineOf(start), ...e });
432
+ blockCounter += 1;
433
+ const id = typeof attrs.id === 'string' && attrs.id
434
+ ? attrs.id
435
+ : `${name.toLowerCase()}_${blockCounter}`;
436
+ // Capture anything between this tag and its closer as children
437
+ let childrenMarkdown = '';
438
+ let closeOffset = -1;
439
+ if (selfClose === '/') {
440
+ closeOffset = end;
441
+ cursor = closeOffset;
442
+ } else {
443
+ const closeStr = `</${name}>`;
444
+ const ci = body.indexOf(closeStr, end);
445
+ if (ci === -1) {
446
+ errors.push({
447
+ line: lineOf(start),
448
+ message: `<${name}> opened at line ${lineOf(start)} has no closing tag`,
449
+ });
450
+ closeOffset = end;
451
+ cursor = closeOffset;
452
+ } else {
453
+ closeOffset = ci;
454
+ childrenMarkdown = body.slice(end, ci);
455
+ cursor = ci + closeStr.length;
456
+ }
457
+ }
458
+ // Capture free-form markdown BEFORE this block as the prologue.
459
+ if (!prologueCaptured) {
460
+ const pre = body.slice(cursor, start).trim();
461
+ if (pre) {
462
+ blocks.push({
463
+ id: PROLOGUE_ID,
464
+ type: 'RichText',
465
+ data: {},
466
+ childrenMarkdown: pre,
467
+ });
468
+ }
469
+ prologueCaptured = true;
470
+ }
471
+ const data = { ...attrs };
472
+ delete data.id;
473
+ blocks.push({
474
+ id,
475
+ type: name,
476
+ data,
477
+ childrenMarkdown: childrenMarkdown.trim(),
478
+ });
479
+ }
480
+
481
+ // Trailing free-form markdown after the last block
482
+ if (!prologueCaptured) {
483
+ const rest = body.slice(cursor).trim();
484
+ if (rest) {
485
+ blocks.push({
486
+ id: PROLOGUE_ID,
487
+ type: 'RichText',
488
+ data: {},
489
+ childrenMarkdown: rest,
490
+ });
491
+ }
492
+ } else {
493
+ const rest = body.slice(cursor).trim();
494
+ if (rest) {
495
+ blocks.push({
496
+ id: `${PROLOGUE_ID}_tail`,
497
+ type: 'RichText',
498
+ data: {},
499
+ childrenMarkdown: rest,
500
+ });
501
+ }
502
+ }
503
+
504
+ return { blocks, errors };
505
+ }
506
+
507
+ /**
508
+ * Compile MDX source into a JSON-serializable glyph object.
509
+ *
510
+ * Validates the source with `@mdx-js/mdx` so MDX syntax errors surface
511
+ * as structured errors, then walks the body to extract blocks using
512
+ * the recursive parser above.
513
+ *
514
+ * @param {string} source Raw MDX text (frontmatter optional).
515
+ * @param {object} [options]
516
+ * @param {boolean} [options.skipValidate] Skip the @mdx-js/mdx pass (faster, for trusted input).
517
+ * @returns {Promise<{
518
+ * frontmatter: Record<string, unknown>,
519
+ * blocks: Array<{ id: string, type: string, data: object, childrenMarkdown?: string }>,
520
+ * errors: Array<{ line?: number, message: string }>,
521
+ * compiledAt: string,
522
+ * }>}
523
+ */
524
+ export async function compileGlyphMdx(source, options = {}) {
525
+ const { skipValidate = false } = options;
526
+ const errors = [];
527
+ const { frontmatter, body } = parseFrontmatter(source || '');
528
+
529
+ if (!skipValidate) {
530
+ try {
531
+ await compile(source || '', { jsx: false, development: false });
532
+ } catch (err) {
533
+ errors.push({
534
+ line: err.line ?? null,
535
+ message: err.message || String(err),
536
+ });
537
+ }
538
+ }
539
+
540
+ const { blocks, errors: extractErrors } = extractBlocks(body);
541
+ for (const e of extractErrors) errors.push(e);
542
+
543
+ return {
544
+ frontmatter,
545
+ blocks,
546
+ errors,
547
+ compiledAt: new Date().toISOString(),
548
+ };
549
+ }
550
+
551
+ /**
552
+ * Synchronous variant of `compileGlyphMdx`. Skips MDX validation (so
553
+ * it doesn't need `compile()` from @mdx-js/mdx, which is async-only).
554
+ * Use this only for trusted input where you want zero-overhead
555
+ * extraction.
556
+ *
557
+ * @param {string} source
558
+ * @returns {{
559
+ * frontmatter: Record<string, unknown>,
560
+ * blocks: Array<{ id: string, type: string, data: object, childrenMarkdown?: string }>,
561
+ * errors: Array<{ line?: number, message: string }>,
562
+ * compiledAt: string,
563
+ * }}
564
+ */
565
+ export function compileGlyphMdxSync(source) {
566
+ const { frontmatter, body } = parseFrontmatter(source || '');
567
+ const { blocks, errors } = extractBlocks(body);
568
+ return {
569
+ frontmatter,
570
+ blocks,
571
+ errors,
572
+ compiledAt: new Date().toISOString(),
573
+ };
574
+ }
575
+
576
+ export const _internal = {
577
+ BLOCK_TYPES,
578
+ PROLOGUE_ID,
579
+ parseFrontmatter,
580
+ parseJsxValue,
581
+ parseAttrs,
582
+ extractBlocks,
583
+ };