@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.
- package/README.md +83 -16
- package/bizar-dash/.bizar/activity.log +3 -0
- package/bizar-dash/.bizar/graph/.graphify_analysis.json +1540 -0
- package/bizar-dash/.bizar/graph/.graphify_labels.json +1 -0
- package/bizar-dash/.bizar/graph/GRAPH_REPORT.md +404 -0
- package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/0661b816358db84dfdb7f10443db8a61152fd20b3fee950caf8fbf32920a1790.json +1 -0
- package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/16da06d774142a6e5d74829ad982531286a6193ef798de8f593db029e123ec32.json +1 -0
- package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/27e5697c01254c69c64c5a33896669073b40afb5ba88375102ae77fa5404136d.json +1 -0
- package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/532997898027254aa4c0932243dc71d2f55888181232b65974961934997dc85b.json +1 -0
- package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/84e3451a64c7f99f0cd6a88b0748d7426780486f53ab4935b7476b28c08f8293.json +1 -0
- package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/88e5ea5f3e78fa69353fe9e6a8d1a05391d54605e25600cb125531eb5a6432ff.json +1 -0
- package/bizar-dash/.bizar/graph/cache/stat-index.json +1 -0
- package/bizar-dash/.bizar/graph/graph.html +307 -0
- package/bizar-dash/.bizar/graph/graph.json +40267 -0
- package/bizar-dash/.bizar/graph/manifest.json +762 -0
- package/bizar-dash/.graphifyignore +40 -0
- package/bizar-dash/.obsidian/README.md +14 -0
- package/bizar-dash/.obsidian/daily/2026-06-25.md +9 -0
- package/bizar-dash/.obsidian/vault.json +7 -0
- package/bizar-dash/.od-skills/web-prototype-e1429c1737/SKILL.md +103 -0
- package/bizar-dash/.od-skills/web-prototype-e1429c1737/assets/template.html +338 -0
- package/bizar-dash/.od-skills/web-prototype-e1429c1737/example.html +81 -0
- package/bizar-dash/.od-skills/web-prototype-e1429c1737/open-design.json +172 -0
- package/bizar-dash/.od-skills/web-prototype-e1429c1737/references/checklist.md +44 -0
- package/bizar-dash/.od-skills/web-prototype-e1429c1737/references/layouts.md +247 -0
- package/bizar-dash/CHANGELOG.md +474 -0
- package/bizar-dash/DESIGN.md +136 -0
- package/bizar-dash/dist/assets/main-B3RgW6FS.js +331 -0
- package/bizar-dash/dist/assets/main-B3RgW6FS.js.map +1 -0
- package/bizar-dash/dist/assets/main-DlJ8wgLR.css +1 -0
- package/bizar-dash/dist/assets/mobile-CsZQAswA.css +1 -0
- package/bizar-dash/dist/assets/mobile-DbxIw8BG.js +2 -0
- package/bizar-dash/dist/assets/mobile-DbxIw8BG.js.map +1 -0
- package/bizar-dash/dist/assets/mobile-Dvq2d53Y.js +354 -0
- package/bizar-dash/dist/assets/mobile-Dvq2d53Y.js.map +1 -0
- package/bizar-dash/dist/index.html +40 -0
- package/bizar-dash/dist/mobile.html +22 -0
- package/bizar-dash/plans/debug-plan/comments.json +1 -0
- package/bizar-dash/plans/debug-plan/meta.json +10 -0
- package/bizar-dash/plans/debug-plan/plan.json +174 -0
- package/bizar-dash/scripts/smoke-bg-retry.mjs +246 -0
- package/bizar-dash/src/cli/dashboard-ports.mjs +488 -0
- package/bizar-dash/src/cli.mjs +522 -0
- package/bizar-dash/src/server/activity-log.mjs +174 -0
- package/bizar-dash/src/server/agents-store.mjs +465 -0
- package/bizar-dash/src/server/api.mjs +138 -0
- package/bizar-dash/src/server/artifact-template.html +810 -0
- package/bizar-dash/src/server/artifacts-store.mjs +733 -0
- package/bizar-dash/src/server/auth.mjs +381 -0
- package/bizar-dash/src/server/background-store.mjs +447 -0
- package/bizar-dash/src/server/bg-poller.mjs +305 -0
- package/bizar-dash/src/server/bg-retry.mjs +574 -0
- package/bizar-dash/src/server/browser.mjs +40 -0
- package/bizar-dash/src/server/diagnostics-store.mjs +148 -0
- package/bizar-dash/src/server/dialog-poller.mjs +105 -0
- package/bizar-dash/src/server/dialog-store.mjs +177 -0
- package/bizar-dash/src/server/glyphs/mdx-compiler.mjs +583 -0
- package/bizar-dash/src/server/lib/path-safe.mjs +283 -0
- package/bizar-dash/src/server/memory-git.mjs +329 -0
- package/bizar-dash/src/server/memory-lightrag.mjs +767 -0
- package/bizar-dash/src/server/memory-schema.mjs +145 -0
- package/bizar-dash/src/server/memory-secrets.mjs +84 -0
- package/bizar-dash/src/server/memory-store.mjs +471 -0
- package/bizar-dash/src/server/mod-security.mjs +398 -0
- package/bizar-dash/src/server/mods-loader.mjs +1067 -0
- package/bizar-dash/src/server/notifications-store.mjs +243 -0
- package/bizar-dash/src/server/obsidian-store.mjs +318 -0
- package/bizar-dash/src/server/opencode-runner.mjs +279 -0
- package/bizar-dash/src/server/pair-store.mjs +138 -0
- package/bizar-dash/src/server/projects-store.mjs +364 -0
- package/bizar-dash/src/server/providers-store.mjs +789 -0
- package/bizar-dash/src/server/routes/_shared.mjs +415 -0
- package/bizar-dash/src/server/routes/activity.mjs +110 -0
- package/bizar-dash/src/server/routes/agents.mjs +134 -0
- package/bizar-dash/src/server/routes/artifacts.mjs +279 -0
- package/bizar-dash/src/server/routes/auth.mjs +69 -0
- package/bizar-dash/src/server/routes/background.mjs +165 -0
- package/bizar-dash/src/server/routes/chat.mjs +485 -0
- package/bizar-dash/src/server/routes/config.mjs +129 -0
- package/bizar-dash/src/server/routes/diagnostics.mjs +50 -0
- package/bizar-dash/src/server/routes/dialogs.mjs +86 -0
- package/bizar-dash/src/server/routes/fs.mjs +429 -0
- package/bizar-dash/src/server/routes/history.mjs +78 -0
- package/bizar-dash/src/server/routes/memory.mjs +658 -0
- package/bizar-dash/src/server/routes/misc.mjs +89 -0
- package/bizar-dash/src/server/routes/mods.mjs +359 -0
- package/bizar-dash/src/server/routes/notifications.mjs +54 -0
- package/bizar-dash/src/server/routes/obsidian.mjs +239 -0
- package/bizar-dash/src/server/routes/opencode-sessions.mjs +55 -0
- package/bizar-dash/src/server/routes/overview.mjs +91 -0
- package/bizar-dash/src/server/routes/pair.mjs +67 -0
- package/bizar-dash/src/server/routes/projects.mjs +175 -0
- package/bizar-dash/src/server/routes/providers.mjs +47 -0
- package/bizar-dash/src/server/routes/schedules.mjs +104 -0
- package/bizar-dash/src/server/routes/settings.mjs +51 -0
- package/bizar-dash/src/server/routes/skills.mjs +76 -0
- package/bizar-dash/src/server/routes/tasks.mjs +468 -0
- package/bizar-dash/src/server/routes/themes.mjs +51 -0
- package/bizar-dash/src/server/routes-v2/auth.mjs +59 -0
- package/bizar-dash/src/server/routes-v2/events.mjs +107 -0
- package/bizar-dash/src/server/routes-v2/health.mjs +21 -0
- package/bizar-dash/src/server/routes-v2/index.mjs +90 -0
- package/bizar-dash/src/server/routes-v2/sessions.mjs +111 -0
- package/bizar-dash/src/server/schedules-runner.mjs +369 -0
- package/bizar-dash/src/server/schedules-store.mjs +268 -0
- package/bizar-dash/src/server/search-store.mjs +220 -0
- package/bizar-dash/src/server/serve-info.mjs +684 -0
- package/bizar-dash/src/server/server.mjs +796 -0
- package/bizar-dash/src/server/settings-store.mjs +45 -0
- package/bizar-dash/src/server/skills-store.mjs +300 -0
- package/bizar-dash/src/server/state.mjs +437 -0
- package/bizar-dash/src/server/tailscale-store.mjs +113 -0
- package/bizar-dash/src/server/task-delegator.mjs +824 -0
- package/bizar-dash/src/server/tasks-store.mjs +508 -0
- package/bizar-dash/src/server/tui.mjs +923 -0
- package/bizar-dash/src/server/update-store.mjs +168 -0
- package/bizar-dash/src/server/v2-auth-file.mjs +99 -0
- package/bizar-dash/src/server/v2-event-bus.mjs +128 -0
- package/bizar-dash/src/server/watcher.mjs +81 -0
- package/bizar-dash/src/server/yaml.mjs +238 -0
- package/bizar-dash/src/web/App.tsx +718 -0
- package/bizar-dash/src/web/MobileApp.tsx +408 -0
- package/bizar-dash/src/web/components/ArtifactCreateDialog.tsx +95 -0
- package/bizar-dash/src/web/components/ArtifactListDialog.tsx +55 -0
- package/bizar-dash/src/web/components/ArtifactViewer.tsx +143 -0
- package/bizar-dash/src/web/components/AuditDialog.tsx +79 -0
- package/bizar-dash/src/web/components/BgStatusBadge.tsx +32 -0
- package/bizar-dash/src/web/components/Button.tsx +56 -0
- package/bizar-dash/src/web/components/CanvasContextMenu.tsx +68 -0
- package/bizar-dash/src/web/components/Card.tsx +40 -0
- package/bizar-dash/src/web/components/CollapsibleSection.tsx +72 -0
- package/bizar-dash/src/web/components/CommandDialog.tsx +66 -0
- package/bizar-dash/src/web/components/EmptyState.tsx +30 -0
- package/bizar-dash/src/web/components/FileBrowser.tsx +633 -0
- package/bizar-dash/src/web/components/HelpDialog.tsx +55 -0
- package/bizar-dash/src/web/components/KillConfirmDialog.tsx +88 -0
- package/bizar-dash/src/web/components/Modal.tsx +215 -0
- package/bizar-dash/src/web/components/Notifications.tsx +241 -0
- package/bizar-dash/src/web/components/SearchModal.tsx +224 -0
- package/bizar-dash/src/web/components/Sidebar.tsx +66 -0
- package/bizar-dash/src/web/components/Spinner.tsx +19 -0
- package/bizar-dash/src/web/components/StatusBadge.tsx +25 -0
- package/bizar-dash/src/web/components/Tag.tsx +28 -0
- package/bizar-dash/src/web/components/Toast.tsx +150 -0
- package/bizar-dash/src/web/components/Topbar.tsx +368 -0
- package/bizar-dash/src/web/components/VisualPlanDialog.tsx +58 -0
- package/bizar-dash/src/web/components/chat/AgentChip.tsx +68 -0
- package/bizar-dash/src/web/components/chat/ChatBubble.tsx +60 -0
- package/bizar-dash/src/web/components/chat/ChatThread.tsx +88 -0
- package/bizar-dash/src/web/components/chat/ChatTopBar.tsx +50 -0
- package/bizar-dash/src/web/components/chat/Composer.tsx +164 -0
- package/bizar-dash/src/web/components/chat/ConfirmModal.tsx +54 -0
- package/bizar-dash/src/web/components/chat/EmptyState.tsx +21 -0
- package/bizar-dash/src/web/components/chat/FirstRunGreeting.tsx +18 -0
- package/bizar-dash/src/web/components/chat/FloatingComposer.tsx +32 -0
- package/bizar-dash/src/web/components/chat/InfoPanel.tsx +119 -0
- package/bizar-dash/src/web/components/chat/LoadingSkeleton.tsx +19 -0
- package/bizar-dash/src/web/components/chat/MessageBubble.tsx +60 -0
- package/bizar-dash/src/web/components/chat/SessionList.tsx +153 -0
- package/bizar-dash/src/web/components/chat/StreamingIndicator.tsx +9 -0
- package/bizar-dash/src/web/components/chat/SuggestionCards.tsx +55 -0
- package/bizar-dash/src/web/components/chat/WelcomeScreen.tsx +36 -0
- package/bizar-dash/src/web/components/chat/index.ts +20 -0
- package/bizar-dash/src/web/components/chat/useAutoGrowTextarea.ts +18 -0
- package/bizar-dash/src/web/components/chat/useChat.ts +219 -0
- package/bizar-dash/src/web/components/chat/useSlashCommands.ts +47 -0
- package/bizar-dash/src/web/index.html +37 -0
- package/bizar-dash/src/web/lib/api.ts +193 -0
- package/bizar-dash/src/web/lib/markdown.tsx +59 -0
- package/bizar-dash/src/web/lib/types.ts +668 -0
- package/bizar-dash/src/web/lib/utils.ts +114 -0
- package/bizar-dash/src/web/lib/ws.ts +176 -0
- package/bizar-dash/src/web/main.tsx +34 -0
- package/bizar-dash/src/web/mobile/MobileApp.tsx +278 -0
- package/bizar-dash/src/web/mobile/MobileBottomNav.tsx +38 -0
- package/bizar-dash/src/web/mobile/MobileTopbar.tsx +115 -0
- package/bizar-dash/src/web/mobile/components/MobileBottomSheet.tsx +182 -0
- package/bizar-dash/src/web/mobile/components/MobileListItem.tsx +64 -0
- package/bizar-dash/src/web/mobile/components/MobileModal.tsx +123 -0
- package/bizar-dash/src/web/mobile/views/MobileActivity.tsx +273 -0
- package/bizar-dash/src/web/mobile/views/MobileAgents.tsx +374 -0
- package/bizar-dash/src/web/mobile/views/MobileArtifactCanvas.tsx +439 -0
- package/bizar-dash/src/web/mobile/views/MobileArtifacts.tsx +143 -0
- package/bizar-dash/src/web/mobile/views/MobileChat.tsx +180 -0
- package/bizar-dash/src/web/mobile/views/MobileConfig.tsx +175 -0
- package/bizar-dash/src/web/mobile/views/MobileHistory.tsx +191 -0
- package/bizar-dash/src/web/mobile/views/MobileMods.tsx +149 -0
- package/bizar-dash/src/web/mobile/views/MobileMore.tsx +121 -0
- package/bizar-dash/src/web/mobile/views/MobileNotifications.tsx +137 -0
- package/bizar-dash/src/web/mobile/views/MobilePlans.tsx +26 -0
- package/bizar-dash/src/web/mobile/views/MobileSchedules.tsx +685 -0
- package/bizar-dash/src/web/mobile/views/MobileSearchModal.tsx +172 -0
- package/bizar-dash/src/web/mobile/views/MobileSettings.tsx +388 -0
- package/bizar-dash/src/web/mobile/views/MobileSkills.tsx +153 -0
- package/bizar-dash/src/web/mobile/views/MobileTasks.tsx +564 -0
- package/bizar-dash/src/web/mobile.html +20 -0
- package/bizar-dash/src/web/mobile.tsx +13 -0
- package/bizar-dash/src/web/styles/chat.css +198 -0
- package/bizar-dash/src/web/styles/glyphs.css +960 -0
- package/bizar-dash/src/web/styles/main.css +8855 -0
- package/bizar-dash/src/web/styles/mobile-chat.css +5 -0
- package/bizar-dash/src/web/styles/mobile.css +1960 -0
- package/bizar-dash/src/web/views/Activity.tsx +1203 -0
- package/bizar-dash/src/web/views/Agents.tsx +701 -0
- package/bizar-dash/src/web/views/Artifacts.tsx +1480 -0
- package/bizar-dash/src/web/views/BackgroundAgents.tsx +450 -0
- package/bizar-dash/src/web/views/Chat.tsx +190 -0
- package/bizar-dash/src/web/views/Config.tsx +2063 -0
- package/bizar-dash/src/web/views/History.tsx +335 -0
- package/bizar-dash/src/web/views/ModView.tsx +207 -0
- package/bizar-dash/src/web/views/Mods.tsx +693 -0
- package/bizar-dash/src/web/views/Overview.tsx +812 -0
- package/bizar-dash/src/web/views/Providers.tsx +302 -0
- package/bizar-dash/src/web/views/Schedules.tsx +802 -0
- package/bizar-dash/src/web/views/Settings.tsx +1787 -0
- package/bizar-dash/src/web/views/Skills.tsx +431 -0
- package/bizar-dash/src/web/views/Tasks.tsx +1523 -0
- package/bizar-dash/src/web/views/glyphs/GlyphRenderer.tsx +613 -0
- package/bizar-dash/src/web/views/glyphs/components.tsx +1098 -0
- package/bizar-dash/templates/mod/FORMAT.md +76 -0
- package/bizar-dash/templates/mod/hello-mod/README.md +19 -0
- package/bizar-dash/templates/mod/hello-mod/agents/greeter.md +8 -0
- package/bizar-dash/templates/mod/hello-mod/commands/hello.md +6 -0
- package/bizar-dash/templates/mod/hello-mod/mod.json +20 -0
- package/bizar-dash/templates/mod/hello-mod/routes/ping.mjs +9 -0
- package/bizar-dash/templates/mod/hello-mod/views/HelloView.tsx +10 -0
- package/bizar-dash/tests/dashboard-ports.test.mjs +138 -0
- package/bizar-dash/tests/graphify-mod-spawn.node.test.mjs +90 -0
- package/bizar-dash/tests/memory-cli.test.mjs +542 -0
- package/bizar-dash/tests/memory-config.test.mjs +422 -0
- package/bizar-dash/tests/memory-git.test.mjs +246 -0
- package/bizar-dash/tests/memory-lightrag.test.mjs +153 -0
- package/bizar-dash/tests/memory-protocol-drift.test.mjs +45 -0
- package/bizar-dash/tests/memory-schema.test.mjs +198 -0
- package/bizar-dash/tests/memory-secrets.test.mjs +121 -0
- package/bizar-dash/tests/memory-store.test.mjs +390 -0
- package/bizar-dash/tests/memory-sync.test.mjs +141 -0
- package/bizar-dash/tests/mod-instructions.node.test.mjs +188 -0
- package/bizar-dash/tests/mod-security.test.mjs +273 -0
- package/bizar-dash/tests/mod-upgrade.node.test.mjs +357 -0
- package/bizar-dash/tests/no-agent-browser.node.test.mjs +98 -0
- package/bizar-dash/tests/obsidian-back-compat.test.mjs +199 -0
- package/bizar-dash/tests/opencode-runner.test.mjs +172 -0
- package/bizar-dash/tests/path-safe.test.mjs +108 -0
- package/bizar-dash/tests/providers-store-backup-keys.node.test.mjs +333 -0
- package/bizar-dash/tests/smoke-v2.mjs +212 -0
- package/bizar-dash/tests/strip-thinking.test.mjs +175 -0
- package/bizar-dash/tests/submit-feedback.test.mjs +353 -0
- package/bizar-dash/tests/tmux-wrap.test.mjs +145 -0
- package/bizar-dash/tests/yaml.test.mjs +128 -0
- package/bizar-dash/tsconfig.json +23 -0
- package/bizar-dash/vite.config.ts +27 -0
- package/cli/atomic.mjs +73 -0
- package/cli/banner.mjs +1 -1
- package/cli/bin.mjs +44 -47
- package/cli/bootstrap.mjs +1 -1
- package/cli/copy.mjs +25 -16
- package/cli/dev-link.mjs +17 -1
- package/cli/doctor.mjs +4 -4
- package/cli/doctor.test.mjs +2 -2
- package/cli/init.mjs +135 -1
- package/cli/install.mjs +31 -39
- package/cli/memory-constants.mjs +59 -0
- package/cli/memory.mjs +1436 -0
- package/cli/service.mjs +5 -6
- package/cli/utils.mjs +6 -3
- package/config/AGENTS.md +7 -7
- package/config/agents/_shared/AGENT_BASELINE.md +59 -61
- package/config/opencode.json +13 -38
- package/config/skills/memory-protocol/SKILL.md +105 -0
- package/config/skills/obsidian/SKILL.md +270 -65
- package/install.sh +59 -19
- package/package.json +49 -14
- package/packages/sdk/LICENSE +21 -0
- package/packages/sdk/README.md +80 -0
- package/packages/sdk/src/client.ts +183 -0
- package/packages/sdk/src/errors.ts +120 -0
- package/packages/sdk/src/events.ts +153 -0
- package/packages/sdk/src/index.ts +63 -0
- package/packages/sdk/src/types.ts +176 -0
- package/packages/sdk/src/version.ts +4 -0
- package/packages/sdk/tests/client.test.ts +217 -0
- package/packages/sdk/tests/errors.test.ts +108 -0
- package/packages/sdk/tests/events.test.ts +139 -0
- package/packages/sdk/tests/fixtures/fetch-mock.ts +152 -0
- package/packages/sdk/tests/fixtures/sse-mock.ts +30 -0
- package/packages/sdk/tsconfig.json +25 -0
- package/packages/sdk/vitest.config.ts +9 -0
- package/plugins/bizar/LICENSE +21 -0
- package/plugins/bizar/README.md +448 -0
- package/plugins/bizar/dist/index.js +29901 -0
- package/plugins/bizar/index.ts +1491 -0
- package/plugins/bizar/scripts/check-forbidden-imports.sh +33 -0
- package/plugins/bizar/src/background-state.ts +551 -0
- package/plugins/bizar/src/background.ts +1250 -0
- package/plugins/bizar/src/commands-impl.ts +464 -0
- package/plugins/bizar/src/commands.ts +1168 -0
- package/plugins/bizar/src/dashboard-client.ts +233 -0
- package/plugins/bizar/src/event-stream.ts +606 -0
- package/plugins/bizar/src/fingerprint.ts +120 -0
- package/plugins/bizar/src/handoff.ts +79 -0
- package/plugins/bizar/src/http-client.ts +467 -0
- package/plugins/bizar/src/key-rotation.ts +218 -0
- package/plugins/bizar/src/logger.ts +144 -0
- package/plugins/bizar/src/loop.ts +176 -0
- package/plugins/bizar/src/opencode-runner.ts +390 -0
- package/plugins/bizar/src/options.ts +421 -0
- package/plugins/bizar/src/plan-fs.ts +323 -0
- package/plugins/bizar/src/reasoning-clean.ts +454 -0
- package/plugins/bizar/src/report.ts +178 -0
- package/plugins/bizar/src/research-prompt.ts +35 -0
- package/plugins/bizar/src/serve-info.ts +228 -0
- package/plugins/bizar/src/serve.ts +496 -0
- package/plugins/bizar/src/settings.ts +349 -0
- package/plugins/bizar/src/state.ts +298 -0
- package/plugins/bizar/src/tools/bg-collect.ts +104 -0
- package/plugins/bizar/src/tools/bg-get-comments.ts +239 -0
- package/plugins/bizar/src/tools/bg-kill.ts +87 -0
- package/plugins/bizar/src/tools/bg-spawn.ts +401 -0
- package/plugins/bizar/src/tools/bg-status.ts +99 -0
- package/plugins/bizar/src/tools/open-kb.ts +191 -0
- package/plugins/bizar/src/tools/plan-action.ts +767 -0
- package/plugins/bizar/src/tools/read-glyph-feedback.ts +170 -0
- package/plugins/bizar/src/tools/wait-for-feedback.ts +402 -0
- package/plugins/bizar/tests/attach-handler-bug.test.ts +169 -0
- package/plugins/bizar/tests/background-state.test.ts +277 -0
- package/plugins/bizar/tests/background.test.ts +402 -0
- package/plugins/bizar/tests/block.test.ts +195 -0
- package/plugins/bizar/tests/canonical-key-order.test.ts +75 -0
- package/plugins/bizar/tests/commands-impl.test.ts +442 -0
- package/plugins/bizar/tests/commands.test.ts +584 -0
- package/plugins/bizar/tests/config.test.ts +122 -0
- package/plugins/bizar/tests/dashboard-client.test.ts +159 -0
- package/plugins/bizar/tests/dispose.test.ts +336 -0
- package/plugins/bizar/tests/event-stream.test.ts +409 -0
- package/plugins/bizar/tests/event.test.ts +262 -0
- package/plugins/bizar/tests/fingerprint.test.ts +162 -0
- package/plugins/bizar/tests/http-client.test.ts +404 -0
- package/plugins/bizar/tests/init-helpers.test.ts +203 -0
- package/plugins/bizar/tests/integration/slash-command.test.ts +348 -0
- package/plugins/bizar/tests/integration/tool-routing.test.ts +314 -0
- package/plugins/bizar/tests/key-rotation.test.ts +396 -0
- package/plugins/bizar/tests/loop.test.ts +397 -0
- package/plugins/bizar/tests/options.test.ts +276 -0
- package/plugins/bizar/tests/reasoning-clean.test.ts +422 -0
- package/plugins/bizar/tests/serve.test.ts +339 -0
- package/plugins/bizar/tests/settings.test.ts +351 -0
- package/plugins/bizar/tests/stall-think.test.ts +750 -0
- package/plugins/bizar/tests/state.test.ts +276 -0
- package/plugins/bizar/tests/tools/bg-collect.test.ts +337 -0
- package/plugins/bizar/tests/tools/bg-get-comments.test.ts +485 -0
- package/plugins/bizar/tests/tools/bg-kill.test.ts +235 -0
- package/plugins/bizar/tests/tools/bg-spawn-delegation.test.ts +147 -0
- package/plugins/bizar/tests/tools/bg-spawn.test.ts +311 -0
- package/plugins/bizar/tests/tools/bg-status.test.ts +217 -0
- package/plugins/bizar/tests/tools/opencode-runner.test.ts +115 -0
- package/plugins/bizar/tests/tools/plan-action.test.ts +599 -0
- package/plugins/bizar/tests/tools/read-glyph-feedback.test.ts +253 -0
- package/plugins/bizar/tests/tools/wait-for-feedback.test.ts +390 -0
- package/plugins/bizar/tests/update-deadlock.test.ts +145 -0
- package/plugins/bizar/tsconfig.json +29 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/server/update-store.mjs
|
|
3
|
+
*
|
|
4
|
+
* v3.5.3 — Read installed package versions, check npm for latest,
|
|
5
|
+
* and apply updates for the three core bizarre packages.
|
|
6
|
+
*/
|
|
7
|
+
import { execSync, spawn } from 'node:child_process';
|
|
8
|
+
import { createRequire } from 'node:module';
|
|
9
|
+
|
|
10
|
+
const require = createRequire(import.meta.url);
|
|
11
|
+
|
|
12
|
+
const PACKAGES = [
|
|
13
|
+
{ id: 'bizar', name: '@polderlabs/bizar' },
|
|
14
|
+
{ id: 'bizar-dash', name: '@polderlabs/bizar-dash' },
|
|
15
|
+
{ id: 'bizar-plugin', name: '@polderlabs/bizar-plugin' },
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
export const updateStore = {
|
|
19
|
+
/**
|
|
20
|
+
* Get currently installed versions (read from each package's package.json).
|
|
21
|
+
* Returns null for any package that's not installed locally.
|
|
22
|
+
*/
|
|
23
|
+
current() {
|
|
24
|
+
const out = {};
|
|
25
|
+
for (const p of PACKAGES) {
|
|
26
|
+
try {
|
|
27
|
+
out[p.id] = require(`${p.name}/package.json`).version;
|
|
28
|
+
} catch {
|
|
29
|
+
out[p.id] = null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return out;
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Check npm registry for latest published versions.
|
|
37
|
+
* Uses `npm view <pkg> version` — works without auth.
|
|
38
|
+
* Returns null for any package that errors (e.g., offline).
|
|
39
|
+
*/
|
|
40
|
+
async latest() {
|
|
41
|
+
const out = {};
|
|
42
|
+
for (const p of PACKAGES) {
|
|
43
|
+
try {
|
|
44
|
+
const ver = execSync(`npm view ${p.name} version`, {
|
|
45
|
+
encoding: 'utf8',
|
|
46
|
+
timeout: 15000,
|
|
47
|
+
}).trim();
|
|
48
|
+
out[p.id] = ver;
|
|
49
|
+
} catch {
|
|
50
|
+
out[p.id] = null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return out;
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Determine if updates are available.
|
|
58
|
+
*/
|
|
59
|
+
hasUpdates(current, latest) {
|
|
60
|
+
if (!current || !latest) return false;
|
|
61
|
+
return PACKAGES.some((p) => {
|
|
62
|
+
if (!current[p.id] || !latest[p.id]) return false;
|
|
63
|
+
return latest[p.id] !== current[p.id];
|
|
64
|
+
});
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Run the update. Installs the specified packages at @latest.
|
|
69
|
+
* Streams progress via the broadcast callback.
|
|
70
|
+
*/
|
|
71
|
+
async apply({ packages, broadcast }) {
|
|
72
|
+
const targets = PACKAGES.filter((p) => packages.includes(p.id));
|
|
73
|
+
const results = {};
|
|
74
|
+
|
|
75
|
+
for (const p of targets) {
|
|
76
|
+
broadcast?.({ type: 'update:progress', pkg: p.id, status: 'installing' });
|
|
77
|
+
try {
|
|
78
|
+
const output = execSync(
|
|
79
|
+
`npm install -g ${p.name}@latest --ignore-scripts`,
|
|
80
|
+
{
|
|
81
|
+
encoding: 'utf8',
|
|
82
|
+
timeout: 180000,
|
|
83
|
+
},
|
|
84
|
+
);
|
|
85
|
+
results[p.id] = { ok: true, output: output.slice(-500) };
|
|
86
|
+
broadcast?.({ type: 'update:progress', pkg: p.id, status: 'done' });
|
|
87
|
+
} catch (err) {
|
|
88
|
+
results[p.id] = { ok: false, error: err.message };
|
|
89
|
+
broadcast?.({ type: 'update:progress', pkg: p.id, status: 'error', error: err.message });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return results;
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Apply updates for specified packages with live progress via broadcast.
|
|
98
|
+
* Streams events:
|
|
99
|
+
* { type: 'update:progress', pkg, status: 'starting' }
|
|
100
|
+
* { type: 'update:progress', pkg, status: 'installing' }
|
|
101
|
+
* { type: 'update:progress', pkg, status: 'done', newVersion }
|
|
102
|
+
* { type: 'update:progress', pkg, status: 'error', error }
|
|
103
|
+
* { type: 'update:complete', results, requiresRestart: true }
|
|
104
|
+
*/
|
|
105
|
+
async applyWithProgress({ packages, broadcast }) {
|
|
106
|
+
const results = {};
|
|
107
|
+
let allOk = true;
|
|
108
|
+
|
|
109
|
+
for (const pkg of packages) {
|
|
110
|
+
const pkgDef = PACKAGES.find((p) => p.id === pkg);
|
|
111
|
+
if (!pkgDef) continue;
|
|
112
|
+
|
|
113
|
+
broadcast?.({ type: 'update:progress', pkg, status: 'starting' });
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
broadcast?.({ type: 'update:progress', pkg, status: 'installing' });
|
|
117
|
+
|
|
118
|
+
// Use spawn so we can stream stdout/stderr
|
|
119
|
+
const child = spawn('npm', ['install', '-g', `${pkgDef.name}@latest`, '--ignore-scripts'], {
|
|
120
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Stream stdout/stderr to broadcast for live log display
|
|
124
|
+
child.stdout.on('data', (data) => {
|
|
125
|
+
const line = data.toString().trim();
|
|
126
|
+
if (line) broadcast?.({ type: 'update:log', pkg, line });
|
|
127
|
+
});
|
|
128
|
+
child.stderr.on('data', (data) => {
|
|
129
|
+
const line = data.toString().trim();
|
|
130
|
+
if (line) broadcast?.({ type: 'update:log', pkg, line });
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const exitCode = await new Promise((resolve) => {
|
|
134
|
+
child.on('close', resolve);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
if (exitCode !== 0) {
|
|
138
|
+
allOk = false;
|
|
139
|
+
results[pkg] = { ok: false, error: `npm exited with code ${exitCode}` };
|
|
140
|
+
broadcast?.({ type: 'update:progress', pkg, status: 'error', error: `exit ${exitCode}` });
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Get the new version
|
|
145
|
+
const newVersion = this.getInstalledVersion(pkg);
|
|
146
|
+
results[pkg] = { ok: true, newVersion };
|
|
147
|
+
broadcast?.({ type: 'update:progress', pkg, status: 'done', newVersion });
|
|
148
|
+
} catch (err) {
|
|
149
|
+
allOk = false;
|
|
150
|
+
results[pkg] = { ok: false, error: err.message };
|
|
151
|
+
broadcast?.({ type: 'update:progress', pkg, status: 'error', error: err.message });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
broadcast?.({ type: 'update:complete', results, requiresRestart: true, allOk });
|
|
156
|
+
return { results, requiresRestart: true };
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
getInstalledVersion(pkgId) {
|
|
160
|
+
const pkgDef = PACKAGES.find((p) => p.id === pkgId);
|
|
161
|
+
if (!pkgDef) return null;
|
|
162
|
+
try {
|
|
163
|
+
return require(`${pkgDef.name}/package.json`).version;
|
|
164
|
+
} catch {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/server/v2-auth-file.mjs
|
|
3
|
+
*
|
|
4
|
+
* v0.7.0-alpha.1 — Manages the dashboard's persistent password file.
|
|
5
|
+
*
|
|
6
|
+
* On first start: generate a 32-byte random password (base64), write to
|
|
7
|
+
* `~/.cache/bizarharness/dash-auth.json` with mode 0600.
|
|
8
|
+
*
|
|
9
|
+
* On subsequent starts: read the existing file and reuse the password.
|
|
10
|
+
*
|
|
11
|
+
* The plugin reads this same file to authenticate (or accepts
|
|
12
|
+
* `BIZAR_DASHBOARD_PASSWORD` env var override).
|
|
13
|
+
*
|
|
14
|
+
* Discovery paths (mirrors serve-info.mjs pattern):
|
|
15
|
+
* - ~/.cache/bizarharness/dash-auth.json (default)
|
|
16
|
+
* - ~/.cache/bizar/dash-auth.json (legacy, fallback)
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { randomBytes } from 'node:crypto';
|
|
20
|
+
import { writeFileSync, readFileSync, existsSync, mkdirSync, chmodSync, renameSync } from 'node:fs';
|
|
21
|
+
import { dirname, join } from 'node:path';
|
|
22
|
+
import { homedir } from 'node:os';
|
|
23
|
+
|
|
24
|
+
const HOME = homedir();
|
|
25
|
+
const DEFAULT_DIR = join(HOME, '.cache', 'bizarharness');
|
|
26
|
+
const DEFAULT_FILE = join(DEFAULT_DIR, 'dash-auth.json');
|
|
27
|
+
const LEGACY_FILE = join(HOME, '.cache', 'bizar', 'dash-auth.json');
|
|
28
|
+
|
|
29
|
+
export const V2_DEFAULT_PORT = 4098;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Load or create the dashboard auth record.
|
|
33
|
+
*
|
|
34
|
+
* @param {object} opts
|
|
35
|
+
* @param {string} [opts.file=DEFAULT_FILE] - explicit path
|
|
36
|
+
* @param {number} [opts.port=V2_DEFAULT_PORT] - port to record in auth file
|
|
37
|
+
* @returns {{ password: string, port: number, baseUrl: string, file: string, createdAt: number }}
|
|
38
|
+
*/
|
|
39
|
+
export function loadOrCreateAuth({ file = DEFAULT_FILE, port = V2_DEFAULT_PORT } = {}) {
|
|
40
|
+
// Prefer legacy file if it exists and default doesn't (migration).
|
|
41
|
+
const candidates = [file, LEGACY_FILE];
|
|
42
|
+
for (const candidate of candidates) {
|
|
43
|
+
if (existsSync(candidate)) {
|
|
44
|
+
try {
|
|
45
|
+
const raw = readFileSync(candidate, 'utf8');
|
|
46
|
+
const parsed = JSON.parse(raw);
|
|
47
|
+
if (typeof parsed.password === 'string' && parsed.password.length >= 16) {
|
|
48
|
+
// Reuse the persisted PASSWORD (idempotent auth), but always
|
|
49
|
+
// use the CURRENT port — the persisted port may be wrong
|
|
50
|
+
// (from an earlier bug or a different listen port).
|
|
51
|
+
const result = {
|
|
52
|
+
password: parsed.password,
|
|
53
|
+
port,
|
|
54
|
+
baseUrl: `http://127.0.0.1:${port}`,
|
|
55
|
+
file: candidate,
|
|
56
|
+
createdAt: typeof parsed.createdAt === 'number' ? parsed.createdAt : 0,
|
|
57
|
+
};
|
|
58
|
+
// Re-write the file to update the port (atomic). The persisted
|
|
59
|
+
// password and createdAt are preserved.
|
|
60
|
+
if (typeof parsed.port !== 'number' || parsed.port !== port) {
|
|
61
|
+
try {
|
|
62
|
+
writeAuthFile(candidate, result);
|
|
63
|
+
} catch {
|
|
64
|
+
// non-fatal — return the in-memory record even if rewrite fails
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
} catch {
|
|
70
|
+
// fall through to regeneration
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Generate a new password.
|
|
76
|
+
const password = randomBytes(24).toString('base64');
|
|
77
|
+
const createdAt = Date.now();
|
|
78
|
+
const record = { password, port, baseUrl: `http://127.0.0.1:${port}`, createdAt };
|
|
79
|
+
|
|
80
|
+
writeAuthFile(file, record);
|
|
81
|
+
|
|
82
|
+
return { ...record, file };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Atomically write the auth file with mode 0600.
|
|
87
|
+
*/
|
|
88
|
+
function writeAuthFile(file, record) {
|
|
89
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
90
|
+
const tmpFile = `${file}.tmp`;
|
|
91
|
+
writeFileSync(tmpFile, JSON.stringify(record, null, 2), { mode: 0o600 });
|
|
92
|
+
chmodSync(tmpFile, 0o600);
|
|
93
|
+
renameSync(tmpFile, file);
|
|
94
|
+
try {
|
|
95
|
+
chmodSync(file, 0o600);
|
|
96
|
+
} catch {
|
|
97
|
+
// non-fatal
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/server/v2-event-bus.mjs
|
|
3
|
+
*
|
|
4
|
+
* v0.7.0-alpha.1 — In-memory event bus for the v2 SSE protocol.
|
|
5
|
+
*
|
|
6
|
+
* Buffers the last N events for late subscribers (replay via `?since=`).
|
|
7
|
+
* New subscribers get the buffer + live events.
|
|
8
|
+
*
|
|
9
|
+
* Replaces the file-based serve.json bridge for plugin→dashboard
|
|
10
|
+
* communication (the file bridge remains as fallback for v1 consumers).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { EventEmitter } from 'node:events';
|
|
14
|
+
|
|
15
|
+
const BUFFER_LIMIT = 100;
|
|
16
|
+
|
|
17
|
+
export class V2EventBus {
|
|
18
|
+
constructor({ bufferLimit = BUFFER_LIMIT, logger = console } = {}) {
|
|
19
|
+
this.bufferLimit = bufferLimit;
|
|
20
|
+
this.logger = logger;
|
|
21
|
+
this.emitter = new EventEmitter();
|
|
22
|
+
this.emitter.setMaxListeners(0); // unlimited subscribers
|
|
23
|
+
this.buffer = []; // [{ seq, type, properties, publishedAt }]
|
|
24
|
+
this.seq = 0;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Publish an event. Returns the assigned sequence number.
|
|
29
|
+
*
|
|
30
|
+
* @param {{ type: string, properties: object }} event
|
|
31
|
+
* @returns {number} sequence number
|
|
32
|
+
*/
|
|
33
|
+
publish(event) {
|
|
34
|
+
if (!event || typeof event !== 'object' || typeof event.type !== 'string') {
|
|
35
|
+
throw new TypeError('V2EventBus.publish: event must have a string `type` field');
|
|
36
|
+
}
|
|
37
|
+
this.seq += 1;
|
|
38
|
+
const record = {
|
|
39
|
+
seq: this.seq,
|
|
40
|
+
type: event.type,
|
|
41
|
+
properties: event.properties ?? {},
|
|
42
|
+
publishedAt: Date.now(),
|
|
43
|
+
};
|
|
44
|
+
this.buffer.push(record);
|
|
45
|
+
if (this.buffer.length > this.bufferLimit) {
|
|
46
|
+
this.buffer.shift();
|
|
47
|
+
}
|
|
48
|
+
this.emitter.emit('event', record);
|
|
49
|
+
return record.seq;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Subscribe to live events.
|
|
54
|
+
*
|
|
55
|
+
* @param {object} opts
|
|
56
|
+
* @param {number} [opts.since] - replay events with seq > since (from buffer)
|
|
57
|
+
* @param {AbortSignal} [opts.signal] - abort signal to close subscription
|
|
58
|
+
* @returns {{ events: AsyncIterable, close: () => void }}
|
|
59
|
+
*/
|
|
60
|
+
subscribe({ since, signal } = {}) {
|
|
61
|
+
const replay = typeof since === 'number'
|
|
62
|
+
? this.buffer.filter((r) => r.seq > since)
|
|
63
|
+
: [];
|
|
64
|
+
const queue = [...replay];
|
|
65
|
+
const liveListeners = [];
|
|
66
|
+
let closed = false;
|
|
67
|
+
|
|
68
|
+
const onAbort = () => close();
|
|
69
|
+
if (signal) {
|
|
70
|
+
if (signal.aborted) {
|
|
71
|
+
// already aborted
|
|
72
|
+
return { events: emptyAsyncIterable(), close: () => {} };
|
|
73
|
+
}
|
|
74
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const onEvent = (record) => {
|
|
78
|
+
if (!closed) queue.push(record);
|
|
79
|
+
};
|
|
80
|
+
this.emitter.on('event', onEvent);
|
|
81
|
+
liveListeners.push(() => this.emitter.off('event', onEvent));
|
|
82
|
+
|
|
83
|
+
const close = () => {
|
|
84
|
+
if (closed) return;
|
|
85
|
+
closed = true;
|
|
86
|
+
for (const off of liveListeners) off();
|
|
87
|
+
if (signal) signal.removeEventListener('abort', onAbort);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
events: drainQueue(queue, close),
|
|
92
|
+
close,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Total events published since bus start. */
|
|
97
|
+
size() {
|
|
98
|
+
return this.seq;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Number of buffered events currently held for replay. */
|
|
102
|
+
bufferSize() {
|
|
103
|
+
return this.buffer.length;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function* drainQueue(queue, onEnd) {
|
|
108
|
+
try {
|
|
109
|
+
while (queue.length > 0) {
|
|
110
|
+
yield queue.shift();
|
|
111
|
+
}
|
|
112
|
+
// After replay, switch to live: block until queue has items or close()
|
|
113
|
+
while (true) {
|
|
114
|
+
if (queue.length > 0) {
|
|
115
|
+
yield queue.shift();
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
// wait briefly then check again
|
|
119
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
120
|
+
}
|
|
121
|
+
} finally {
|
|
122
|
+
if (onEnd) onEnd();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function* emptyAsyncIterable() {
|
|
127
|
+
// no-op
|
|
128
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/dashboard/watcher.mjs
|
|
3
|
+
*
|
|
4
|
+
* chokidar-backed file watcher. Translates low-level fs events into the
|
|
5
|
+
* shape the dashboard's WebSocket layer expects.
|
|
6
|
+
*
|
|
7
|
+
* The watcher ignores initial add events (the UI doesn't need a "you just
|
|
8
|
+
* started, here are 200 files that already existed" flood on connect).
|
|
9
|
+
*/
|
|
10
|
+
import chokidar from 'chokidar';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @param {object} opts
|
|
14
|
+
* @param {string[]} opts.paths - files or directories to watch
|
|
15
|
+
* @param {(event: 'add'|'change'|'unlink', path: string) => void} opts.onChange
|
|
16
|
+
* @param {object} [opts.options] - extra chokidar options
|
|
17
|
+
*/
|
|
18
|
+
export function createWatcher({ paths, onChange, options = {} }) {
|
|
19
|
+
if (!Array.isArray(paths) || paths.length === 0) {
|
|
20
|
+
throw new Error('createWatcher requires a non-empty paths array');
|
|
21
|
+
}
|
|
22
|
+
if (typeof onChange !== 'function') {
|
|
23
|
+
throw new Error('createWatcher requires an onChange callback');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const watcher = chokidar.watch(paths, {
|
|
27
|
+
ignoreInitial: true,
|
|
28
|
+
persistent: true,
|
|
29
|
+
awaitWriteFinish: { stabilityThreshold: 100, pollInterval: 50 },
|
|
30
|
+
// Ignore: any path segment named `node_modules` (anywhere), and any
|
|
31
|
+
// file/dir whose IMMEDIATE basename starts with `.` (covers .DS_Store,
|
|
32
|
+
// .swp, .bak, etc.). We must NOT walk the whole path — the watched
|
|
33
|
+
// root `~/.config/opencode/agents` contains the segment `.config`
|
|
34
|
+
// which would otherwise match and silently disable the watch.
|
|
35
|
+
ignored: (p) => {
|
|
36
|
+
const s = String(p);
|
|
37
|
+
const parts = s.split(/[\\/]/);
|
|
38
|
+
// Walk all segments — `node_modules` is fine to match anywhere.
|
|
39
|
+
if (parts.includes('node_modules')) return true;
|
|
40
|
+
// Last segment is the basename — check that ONLY, so `.config`
|
|
41
|
+
// in parent dirs doesn't disqualify the watched root.
|
|
42
|
+
const basename = parts[parts.length - 1] || '';
|
|
43
|
+
return basename.startsWith('.');
|
|
44
|
+
},
|
|
45
|
+
...options,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const safe = (event, p) => {
|
|
49
|
+
try {
|
|
50
|
+
onChange(event, p);
|
|
51
|
+
} catch (err) {
|
|
52
|
+
// A faulty onChange must not crash the watcher
|
|
53
|
+
console.error('[dashboard watcher] onChange error:', err);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
watcher.on('add', (p) => safe('add', p));
|
|
58
|
+
watcher.on('change', (p) => safe('change', p));
|
|
59
|
+
watcher.on('unlink', (p) => safe('unlink', p));
|
|
60
|
+
watcher.on('error', (err) => {
|
|
61
|
+
console.error('[dashboard watcher] chokidar error:', err);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
/** @returns {chokidar.FSWatcher} */
|
|
66
|
+
start() {
|
|
67
|
+
return watcher;
|
|
68
|
+
},
|
|
69
|
+
async stop() {
|
|
70
|
+
try {
|
|
71
|
+
await watcher.close();
|
|
72
|
+
} catch {
|
|
73
|
+
/* ignore */
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
/** Force a synthetic broadcast — useful after a self-mutation. */
|
|
77
|
+
poke(event = 'change', path = '<synthetic>') {
|
|
78
|
+
safe(event, path);
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|