@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,447 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/server/background-store.mjs
|
|
3
|
+
*
|
|
4
|
+
* v3.2.0 — Bridge from the dashboard to the opencode plugin's
|
|
5
|
+
* background-agent infrastructure.
|
|
6
|
+
*
|
|
7
|
+
* What this store actually does:
|
|
8
|
+
* 1. Lists bg instances by walking the plugin's bg state directory
|
|
9
|
+
* (defaults to `~/.cache/bizar/bg/` per the plugin spec).
|
|
10
|
+
* 2. Optionally attaches tmux session hints when a tmux session
|
|
11
|
+
* named `bizar-bg-<id>` happens to exist. The plugin does NOT
|
|
12
|
+
* itself use tmux — instances are HTTP sessions — but tmux
|
|
13
|
+
* sessions may be attached by operators running the plugin
|
|
14
|
+
* under `bizar serve` for live monitoring. We surface them
|
|
15
|
+
* when present and degrade gracefully when not.
|
|
16
|
+
* 3. Supports best-effort `sendMessage` / `kill` via tmux when the
|
|
17
|
+
* session exists. Both operations are no-ops when there's no
|
|
18
|
+
* tmux session — they NEVER fail the request.
|
|
19
|
+
*
|
|
20
|
+
* All I/O is defensive: missing files, corrupt JSON, missing tmux —
|
|
21
|
+
* everything is handled silently and the API returns an empty list
|
|
22
|
+
* rather than a 500.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import {
|
|
26
|
+
readFileSync,
|
|
27
|
+
existsSync,
|
|
28
|
+
readdirSync,
|
|
29
|
+
statSync,
|
|
30
|
+
unlinkSync,
|
|
31
|
+
} from 'node:fs';
|
|
32
|
+
import { join } from 'node:path';
|
|
33
|
+
import { homedir } from 'node:os';
|
|
34
|
+
import { execFileSync } from 'node:child_process';
|
|
35
|
+
|
|
36
|
+
const HOME = homedir();
|
|
37
|
+
|
|
38
|
+
// Bg state directories, ordered by likelihood. First match wins.
|
|
39
|
+
// Matches plugins/bizar/src/background-state.ts (default `~/.cache/bizar`).
|
|
40
|
+
const BG_DIRS = [
|
|
41
|
+
join(HOME, '.cache', 'bizar', 'bg'),
|
|
42
|
+
join(HOME, '.config', 'opencode', 'bg'),
|
|
43
|
+
join(HOME, '.bizar', 'bg'),
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
function pickBgDir() {
|
|
47
|
+
for (const dir of BG_DIRS) {
|
|
48
|
+
if (existsSync(dir)) return dir;
|
|
49
|
+
}
|
|
50
|
+
return BG_DIRS[0];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function safeReadJSON(file, fallback = null) {
|
|
54
|
+
try {
|
|
55
|
+
if (!existsSync(file)) return fallback;
|
|
56
|
+
const text = readFileSync(file, 'utf8');
|
|
57
|
+
if (!text.trim()) return fallback;
|
|
58
|
+
return JSON.parse(text);
|
|
59
|
+
} catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function tmuxSessionFor(instanceId) {
|
|
65
|
+
return `bizar-bg-${instanceId}`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function tmuxHasSession(sessionName) {
|
|
69
|
+
try {
|
|
70
|
+
execFileSync('tmux', ['has-session', '-t', sessionName], { stdio: 'ignore' });
|
|
71
|
+
return true;
|
|
72
|
+
} catch {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const backgroundStore = {
|
|
78
|
+
BG_DIRS,
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* List all bg instances across the candidate dirs. Each entry is
|
|
82
|
+
* the raw plugin state file enriched with tmux hints (when
|
|
83
|
+
* applicable).
|
|
84
|
+
*/
|
|
85
|
+
list() {
|
|
86
|
+
const out = [];
|
|
87
|
+
for (const dir of BG_DIRS) {
|
|
88
|
+
if (!existsSync(dir)) continue;
|
|
89
|
+
let files;
|
|
90
|
+
try {
|
|
91
|
+
files = readdirSync(dir).filter((f) => f.endsWith('.json'));
|
|
92
|
+
} catch {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
for (const f of files) {
|
|
96
|
+
const full = join(dir, f);
|
|
97
|
+
let st;
|
|
98
|
+
try {
|
|
99
|
+
st = statSync(full);
|
|
100
|
+
} catch {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const data = safeReadJSON(full, null);
|
|
104
|
+
if (!data) continue;
|
|
105
|
+
const instanceId = data.instanceId || f.replace(/\.json$/, '');
|
|
106
|
+
const sessionName = tmuxSessionFor(instanceId);
|
|
107
|
+
const tmuxActive = tmuxHasSession(sessionName);
|
|
108
|
+
out.push({
|
|
109
|
+
...data,
|
|
110
|
+
_bgDir: dir,
|
|
111
|
+
_mtime: st.mtimeMs,
|
|
112
|
+
_file: full,
|
|
113
|
+
tmuxSession: sessionName,
|
|
114
|
+
tmuxActive,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Sort: pending/running first, then by start time desc.
|
|
119
|
+
out.sort((a, b) => {
|
|
120
|
+
const order = { pending: 0, running: 1, done: 2, failed: 3, killed: 4, timed_out: 5 };
|
|
121
|
+
const ao = order[a.status] ?? 9;
|
|
122
|
+
const bo = order[b.status] ?? 9;
|
|
123
|
+
if (ao !== bo) return ao - bo;
|
|
124
|
+
return (b.startedAt || b._mtime || 0) - (a.startedAt || a._mtime || 0);
|
|
125
|
+
});
|
|
126
|
+
return out;
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Read a single bg instance by id. Searches all candidate dirs.
|
|
131
|
+
*/
|
|
132
|
+
get(instanceId) {
|
|
133
|
+
for (const dir of BG_DIRS) {
|
|
134
|
+
const file = join(dir, `${instanceId}.json`);
|
|
135
|
+
if (existsSync(file)) {
|
|
136
|
+
const data = safeReadJSON(file, null);
|
|
137
|
+
if (data) {
|
|
138
|
+
const sessionName = tmuxSessionFor(instanceId);
|
|
139
|
+
return {
|
|
140
|
+
...data,
|
|
141
|
+
_bgDir: dir,
|
|
142
|
+
_file: file,
|
|
143
|
+
tmuxSession: sessionName,
|
|
144
|
+
tmuxActive: tmuxHasSession(sessionName),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Best-effort: send a line of text into the tmux session for an
|
|
154
|
+
* instance. No-op when there's no session.
|
|
155
|
+
*/
|
|
156
|
+
sendMessage(instanceId, message) {
|
|
157
|
+
const session = tmuxSessionFor(instanceId);
|
|
158
|
+
if (!tmuxHasSession(session)) {
|
|
159
|
+
return { ok: false, error: 'no tmux session for instance', session };
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
const msg = String(message || '');
|
|
163
|
+
// Use tmux's literal mode (`-l`) and pass the message as a single
|
|
164
|
+
// arg via execFileSync — no shell interpolation, so payloads like
|
|
165
|
+
// `"; cat /etc/passwd #` cannot break out.
|
|
166
|
+
execFileSync(
|
|
167
|
+
'tmux',
|
|
168
|
+
['send-keys', '-l', '-t', session, msg, 'Enter'],
|
|
169
|
+
{ stdio: 'pipe', timeout: 5_000 },
|
|
170
|
+
);
|
|
171
|
+
return { ok: true, session };
|
|
172
|
+
} catch (err) {
|
|
173
|
+
return { ok: false, error: err.message, session };
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Best-effort: kill the tmux session for an instance.
|
|
179
|
+
* The plugin's session is HTTP-based, so this only stops the
|
|
180
|
+
* terminal attach; the underlying opencode session continues
|
|
181
|
+
* unless the operator also aborts it.
|
|
182
|
+
*/
|
|
183
|
+
async kill(instanceId) {
|
|
184
|
+
const steps = [];
|
|
185
|
+
const inst = this.get(instanceId);
|
|
186
|
+
if (!inst) {
|
|
187
|
+
return { ok: false, error: 'instance not found', instanceId, steps };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
let ok = true;
|
|
191
|
+
const session = tmuxSessionFor(instanceId);
|
|
192
|
+
|
|
193
|
+
if (inst.sessionId) {
|
|
194
|
+
try {
|
|
195
|
+
const { readServeInfo, abortSession } = await import('./serve-info.mjs');
|
|
196
|
+
const serveInfo = readServeInfo();
|
|
197
|
+
if (!serveInfo) {
|
|
198
|
+
ok = false;
|
|
199
|
+
steps.push({ step: 'abort-session', ok: false, note: 'serve-info unavailable' });
|
|
200
|
+
} else {
|
|
201
|
+
const aborted = await abortSession(
|
|
202
|
+
serveInfo,
|
|
203
|
+
inst.sessionId,
|
|
204
|
+
inst.worktree || serveInfo.worktree,
|
|
205
|
+
);
|
|
206
|
+
steps.push({ step: 'abort-session', ...aborted });
|
|
207
|
+
if (!aborted.ok) ok = false;
|
|
208
|
+
}
|
|
209
|
+
} catch (err) {
|
|
210
|
+
ok = false;
|
|
211
|
+
steps.push({ step: 'abort-session', ok: false, error: err instanceof Error ? err.message : String(err) });
|
|
212
|
+
}
|
|
213
|
+
} else {
|
|
214
|
+
steps.push({ step: 'abort-session', ok: true, note: 'no opencode session id present' });
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (inst._file && existsSync(inst._file)) {
|
|
218
|
+
try {
|
|
219
|
+
unlinkSync(inst._file);
|
|
220
|
+
steps.push({ step: 'delete-state-file', ok: true, file: inst._file });
|
|
221
|
+
} catch (err) {
|
|
222
|
+
ok = false;
|
|
223
|
+
steps.push({ step: 'delete-state-file', ok: false, error: err instanceof Error ? err.message : String(err) });
|
|
224
|
+
}
|
|
225
|
+
} else {
|
|
226
|
+
steps.push({ step: 'delete-state-file', ok: true, note: 'state file already absent' });
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (tmuxHasSession(session)) {
|
|
230
|
+
try {
|
|
231
|
+
execFileSync('tmux', ['kill-session', '-t', session], { stdio: 'pipe', timeout: 5_000 });
|
|
232
|
+
steps.push({ step: 'kill-tmux', ok: true, session });
|
|
233
|
+
} catch (err) {
|
|
234
|
+
ok = false;
|
|
235
|
+
steps.push({ step: 'kill-tmux', ok: false, session, error: err instanceof Error ? err.message : String(err) });
|
|
236
|
+
}
|
|
237
|
+
} else {
|
|
238
|
+
steps.push({ step: 'kill-tmux', ok: true, session, note: 'no tmux session present' });
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return { ok, instanceId, session, steps };
|
|
242
|
+
},
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* v3.5.5 — Return tmux session metadata for the API layer to
|
|
246
|
+
* expose as `GET /api/background/:id/tmux`. Always returns the
|
|
247
|
+
* computed session name so the UI can render an "Attach" button
|
|
248
|
+
* even when the session doesn't exist (the button just opens a
|
|
249
|
+
* new detached session that shares the same name).
|
|
250
|
+
*
|
|
251
|
+
* @param {string} instanceId
|
|
252
|
+
* @returns {{ exists: boolean, session: string, attachCommand: string, attachUrl: string }}
|
|
253
|
+
*/
|
|
254
|
+
tmuxAttachInfo(instanceId) {
|
|
255
|
+
const session = tmuxSessionFor(instanceId);
|
|
256
|
+
return {
|
|
257
|
+
exists: tmuxHasSession(session),
|
|
258
|
+
session,
|
|
259
|
+
attachCommand: `tmux attach -t ${session}`,
|
|
260
|
+
attachUrl: `tmux://${session}`,
|
|
261
|
+
};
|
|
262
|
+
},
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* v3.5.5 — Spawn a tmux session that wraps the agent run. Used
|
|
266
|
+
* by the task delegator so operators can `tmux attach -t …` and
|
|
267
|
+
* watch the opencode process in real time. The session is named
|
|
268
|
+
* `bizar-bg-<id>` and runs the supplied shell command inside
|
|
269
|
+
* the optional `cwd`.
|
|
270
|
+
*
|
|
271
|
+
* Returns `{ ok, session, error? }`. Never throws — failures are
|
|
272
|
+
* logged and reported so the dispatch path can keep going.
|
|
273
|
+
*
|
|
274
|
+
* @param {string} instanceId
|
|
275
|
+
* @param {string} command
|
|
276
|
+
* @param {string} [cwd]
|
|
277
|
+
*/
|
|
278
|
+
spawnTmuxFor(instanceId, command, cwd) {
|
|
279
|
+
const session = tmuxSessionFor(instanceId);
|
|
280
|
+
if (tmuxHasSession(session)) {
|
|
281
|
+
return { ok: true, session, note: 'session already existed' };
|
|
282
|
+
}
|
|
283
|
+
const spec =
|
|
284
|
+
command && typeof command === 'object' && !Array.isArray(command)
|
|
285
|
+
? command
|
|
286
|
+
: { command, args: [] };
|
|
287
|
+
const executable = typeof spec.command === 'string' ? spec.command.trim() : '';
|
|
288
|
+
const args = Array.isArray(spec.args) ? spec.args.map((arg) => String(arg)) : [];
|
|
289
|
+
const workdir = cwd && typeof cwd === 'string' ? cwd : '';
|
|
290
|
+
if (!executable) {
|
|
291
|
+
return {
|
|
292
|
+
ok: false,
|
|
293
|
+
session,
|
|
294
|
+
error: 'command is required',
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
try {
|
|
298
|
+
const tmuxArgs = ['new-session', '-d', '-s', session, '-x', '220', '-y', '50'];
|
|
299
|
+
if (workdir) tmuxArgs.push('-c', workdir);
|
|
300
|
+
tmuxArgs.push(executable, ...args);
|
|
301
|
+
execFileSync(
|
|
302
|
+
'tmux',
|
|
303
|
+
tmuxArgs,
|
|
304
|
+
{ stdio: 'ignore', timeout: 5_000 },
|
|
305
|
+
);
|
|
306
|
+
return { ok: true, session };
|
|
307
|
+
} catch (err) {
|
|
308
|
+
return { ok: false, session, error: err instanceof Error ? err.message : String(err) };
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Capture the tail of the tmux session for an instance.
|
|
314
|
+
*/
|
|
315
|
+
captureOutput(instanceId, lines = 50) {
|
|
316
|
+
const session = tmuxSessionFor(instanceId);
|
|
317
|
+
if (!tmuxHasSession(session)) {
|
|
318
|
+
return { ok: false, error: 'no tmux session for instance', session, output: '' };
|
|
319
|
+
}
|
|
320
|
+
try {
|
|
321
|
+
const safeLines = String(Math.max(1, Math.min(500, lines)));
|
|
322
|
+
const out = execFileSync(
|
|
323
|
+
'tmux',
|
|
324
|
+
['capture-pane', '-t', session, '-p', '-S', `-${safeLines}`],
|
|
325
|
+
{ encoding: 'utf8', timeout: 5_000 },
|
|
326
|
+
);
|
|
327
|
+
return { ok: true, session, output: out };
|
|
328
|
+
} catch (err) {
|
|
329
|
+
return { ok: false, error: err.message, session, output: '' };
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* v3.11.1 — Kill the tmux session for an instance if one exists.
|
|
335
|
+
* No-op when the session is absent or tmux is not installed. Returns
|
|
336
|
+
* `{ ok, session, killed, error? }` so callers can log a structured
|
|
337
|
+
* result. Used by the bg-retry janitor and `cleanup()` to ensure
|
|
338
|
+
* terminal instances don't leave dangling tmux panes.
|
|
339
|
+
*
|
|
340
|
+
* @param {string} instanceId
|
|
341
|
+
* @returns {{ ok: boolean, session: string, killed: boolean, error?: string }}
|
|
342
|
+
*/
|
|
343
|
+
killTmuxFor(instanceId) {
|
|
344
|
+
const session = tmuxSessionFor(instanceId);
|
|
345
|
+
if (!tmuxHasSession(session)) {
|
|
346
|
+
return { ok: true, session, killed: false, note: 'no session present' };
|
|
347
|
+
}
|
|
348
|
+
try {
|
|
349
|
+
execFileSync('tmux', ['kill-session', '-t', session], { stdio: 'pipe', timeout: 5_000 });
|
|
350
|
+
return { ok: true, session, killed: true };
|
|
351
|
+
} catch (err) {
|
|
352
|
+
return {
|
|
353
|
+
ok: false,
|
|
354
|
+
session,
|
|
355
|
+
killed: false,
|
|
356
|
+
error: err instanceof Error ? err.message : String(err),
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
},
|
|
360
|
+
|
|
361
|
+
/** The attach command (for the UI to display). */
|
|
362
|
+
attachCommand(instanceId) {
|
|
363
|
+
return `tmux attach -t ${tmuxSessionFor(instanceId)}`;
|
|
364
|
+
},
|
|
365
|
+
|
|
366
|
+
/** Diagnostics: which bg dir is being used right now. */
|
|
367
|
+
status() {
|
|
368
|
+
const dir = pickBgDir();
|
|
369
|
+
const exists = existsSync(dir);
|
|
370
|
+
let count = 0;
|
|
371
|
+
if (exists) {
|
|
372
|
+
try {
|
|
373
|
+
count = readdirSync(dir).filter((f) => f.endsWith('.json')).length;
|
|
374
|
+
} catch {
|
|
375
|
+
count = 0;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return { dir, exists, count };
|
|
379
|
+
},
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* v0.5.5 — Delete state files for terminal instances older than
|
|
383
|
+
* `maxAgeDays`. Iterates all BG_DIRS. Non-terminal instances are
|
|
384
|
+
* NEVER removed. Returns the total deleted count.
|
|
385
|
+
*
|
|
386
|
+
* v3.11.1 — Also kills the tmux session for each terminal instance
|
|
387
|
+
* (even those NOT yet old enough for state-file deletion) so the
|
|
388
|
+
* operator's `tmux ls` doesn't grow unboundedly. Returns
|
|
389
|
+
* `{ deleted, tmuxKilled }`.
|
|
390
|
+
*/
|
|
391
|
+
cleanup(maxAgeDays = 7) {
|
|
392
|
+
let deleted = 0;
|
|
393
|
+
let tmuxKilled = 0;
|
|
394
|
+
const cutoff = Date.now() - (maxAgeDays * 24 * 60 * 60 * 1000);
|
|
395
|
+
const TERMINAL = new Set(['done', 'failed', 'killed', 'timed_out']);
|
|
396
|
+
const seen = new Set();
|
|
397
|
+
for (const dir of BG_DIRS) {
|
|
398
|
+
if (!existsSync(dir)) continue;
|
|
399
|
+
let files;
|
|
400
|
+
try {
|
|
401
|
+
files = readdirSync(dir).filter((f) => f.endsWith('.json'));
|
|
402
|
+
} catch {
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
for (const f of files) {
|
|
406
|
+
const full = join(dir, f);
|
|
407
|
+
const data = safeReadJSON(full, null);
|
|
408
|
+
if (!data) continue;
|
|
409
|
+
if (!TERMINAL.has(data.status)) continue;
|
|
410
|
+
|
|
411
|
+
// Kill the tmux session for every terminal instance, regardless
|
|
412
|
+
// of age — prevents tmux-session pile-up in long-running hosts.
|
|
413
|
+
const instanceId = data.instanceId || f.replace(/\.json$/, '');
|
|
414
|
+
if (!seen.has(instanceId)) {
|
|
415
|
+
seen.add(instanceId);
|
|
416
|
+
const tmuxRes = this.killTmuxFor(instanceId);
|
|
417
|
+
if (tmuxRes.killed) tmuxKilled += 1;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const ts = new Date(data.updatedAt || data.completedAt || 0).getTime();
|
|
421
|
+
if (ts < cutoff) {
|
|
422
|
+
try {
|
|
423
|
+
unlinkSync(full);
|
|
424
|
+
deleted += 1;
|
|
425
|
+
} catch {
|
|
426
|
+
// race — file already gone, skip
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return { deleted, tmuxKilled };
|
|
432
|
+
},
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* v0.5.5 — List all instances with summary status counts. Wraps
|
|
436
|
+
* `list()` and computes a breakdown.
|
|
437
|
+
*/
|
|
438
|
+
listWithStatusCounts() {
|
|
439
|
+
const instances = this.list();
|
|
440
|
+
const counts = { pending: 0, running: 0, done: 0, failed: 0, killed: 0, timed_out: 0 };
|
|
441
|
+
for (const inst of instances) {
|
|
442
|
+
const s = inst.status;
|
|
443
|
+
if (s in counts) counts[s] += 1;
|
|
444
|
+
}
|
|
445
|
+
return { instances, counts, total: instances.length };
|
|
446
|
+
},
|
|
447
|
+
};
|