profclaw 0.0.1-beta.3 → 2.3.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/.env.example +14 -2
- package/CHANGELOG.md +54 -34
- package/CONTRIBUTING.md +23 -5
- package/Dockerfile +39 -23
- package/README.md +271 -72
- package/dist/agents/agent-summary.d.ts +44 -0
- package/dist/agents/agent-summary.js +176 -0
- package/dist/agents/auto-memory.d.ts +59 -0
- package/dist/agents/auto-memory.js +310 -0
- package/dist/agents/checkpoint-manager.d.ts +76 -0
- package/dist/agents/checkpoint-manager.js +156 -0
- package/dist/agents/circuit-breaker.d.ts +54 -0
- package/dist/agents/circuit-breaker.js +153 -0
- package/dist/agents/context-compactor.d.ts +70 -0
- package/dist/agents/context-compactor.js +361 -0
- package/dist/agents/conversation-branch.d.ts +55 -0
- package/dist/agents/conversation-branch.js +115 -0
- package/dist/agents/error-recovery.d.ts +44 -0
- package/dist/agents/error-recovery.js +197 -0
- package/dist/agents/events.d.ts +76 -0
- package/dist/agents/events.js +8 -0
- package/dist/agents/executor.d.ts +35 -1
- package/dist/agents/executor.js +573 -124
- package/dist/agents/file-snapshots.d.ts +72 -0
- package/dist/agents/file-snapshots.js +182 -0
- package/dist/agents/index.d.ts +2 -0
- package/dist/agents/orchestrator.d.ts +183 -0
- package/dist/agents/orchestrator.js +495 -0
- package/dist/agents/permissions.d.ts +63 -0
- package/dist/agents/permissions.js +138 -0
- package/dist/agents/plan-mode.d.ts +71 -0
- package/dist/agents/plan-mode.js +186 -0
- package/dist/agents/prompt-suggestions.d.ts +33 -0
- package/dist/agents/prompt-suggestions.js +208 -0
- package/dist/agents/rate-limit-monitor.d.ts +56 -0
- package/dist/agents/rate-limit-monitor.js +224 -0
- package/dist/agents/result-store.d.ts +37 -0
- package/dist/agents/result-store.js +104 -0
- package/dist/agents/session-diff.d.ts +43 -0
- package/dist/agents/session-diff.js +258 -0
- package/dist/agents/tool-loader.d.ts +65 -0
- package/dist/agents/tool-loader.js +273 -0
- package/dist/agents/transcript.d.ts +96 -0
- package/dist/agents/transcript.js +267 -0
- package/dist/agents/worktree-manager.d.ts +80 -0
- package/dist/agents/worktree-manager.js +203 -0
- package/dist/auth/middleware.js +14 -0
- package/dist/chat/agentic-executor.d.ts +7 -0
- package/dist/chat/agentic-executor.js +253 -30
- package/dist/chat/conversations.d.ts +2 -0
- package/dist/chat/conversations.js +8 -0
- package/dist/chat/execution/executor.d.ts +8 -0
- package/dist/chat/execution/executor.js +157 -103
- package/dist/chat/execution/index.js +5 -1
- package/dist/chat/execution/tool-router.js +40 -14
- package/dist/chat/execution/tools/canvas.d.ts +2 -2
- package/dist/chat/execution/tools/code-tools.d.ts +66 -0
- package/dist/chat/execution/tools/code-tools.js +353 -0
- package/dist/chat/execution/tools/complete-task.d.ts +2 -2
- package/dist/chat/execution/tools/cron-tool.d.ts +26 -10
- package/dist/chat/execution/tools/cron-tool.js +48 -3
- package/dist/chat/execution/tools/decompose-task.d.ts +58 -0
- package/dist/chat/execution/tools/decompose-task.js +166 -0
- package/dist/chat/execution/tools/exec.d.ts +5 -2
- package/dist/chat/execution/tools/exec.js +294 -155
- package/dist/chat/execution/tools/feed-tools.d.ts +77 -0
- package/dist/chat/execution/tools/feed-tools.js +142 -0
- package/dist/chat/execution/tools/file-ops.d.ts +1 -1
- package/dist/chat/execution/tools/github.d.ts +2 -2
- package/dist/chat/execution/tools/index.d.ts +15 -3
- package/dist/chat/execution/tools/index.js +125 -12
- package/dist/chat/execution/tools/integrations.d.ts +8 -8
- package/dist/chat/execution/tools/multi-patch.d.ts +53 -0
- package/dist/chat/execution/tools/multi-patch.js +373 -0
- package/dist/chat/execution/tools/openai-image-gen.d.ts +2 -2
- package/dist/chat/execution/tools/plan-tools.d.ts +80 -0
- package/dist/chat/execution/tools/plan-tools.js +134 -0
- package/dist/chat/execution/tools/profclaw-ops.d.ts +6 -6
- package/dist/chat/execution/tools/repl-tool.d.ts +46 -0
- package/dist/chat/execution/tools/repl-tool.js +207 -0
- package/dist/chat/execution/tools/sessions-send.d.ts +2 -2
- package/dist/chat/execution/tools/slack-actions.d.ts +2 -2
- package/dist/chat/execution/tools/system.d.ts +4 -4
- package/dist/chat/execution/tools/todo-tool.d.ts +72 -0
- package/dist/chat/execution/tools/todo-tool.js +152 -0
- package/dist/chat/execution/tools/tool-search.d.ts +50 -0
- package/dist/chat/execution/tools/tool-search.js +155 -0
- package/dist/chat/execution/types.d.ts +21 -14
- package/dist/chat/execution/workflows/executor.d.ts +11 -0
- package/dist/chat/execution/workflows/executor.js +99 -1
- package/dist/chat/execution/workflows/types.d.ts +35 -2
- package/dist/chat/format/index.d.ts +1 -0
- package/dist/chat/format/index.js +1 -0
- package/dist/chat/format/markdown-convert.d.ts +23 -0
- package/dist/chat/format/markdown-convert.js +149 -0
- package/dist/chat/index.d.ts +1 -1
- package/dist/chat/index.js +1 -1
- package/dist/chat/message-handler.js +58 -6
- package/dist/chat/proactive/index.d.ts +62 -0
- package/dist/chat/proactive/index.js +147 -0
- package/dist/chat/prompt-adapter.js +2 -0
- package/dist/chat/providers/irc/index.d.ts +2 -2
- package/dist/chat/providers/slack/index.d.ts +2 -2
- package/dist/chat/providers/telegram/index.d.ts +2 -0
- package/dist/chat/providers/telegram/index.js +21 -2
- package/dist/chat/system-prompts.d.ts +3 -3
- package/dist/chat/system-prompts.js +116 -61
- package/dist/chat/tools.d.ts +6 -6
- package/dist/cli/commands/auth.js +64 -1
- package/dist/cli/commands/chat.js +1327 -138
- package/dist/cli/commands/doctor.js +222 -16
- package/dist/cli/commands/history.d.ts +3 -0
- package/dist/cli/commands/history.js +138 -0
- package/dist/cli/commands/import.d.ts +44 -0
- package/dist/cli/commands/import.js +526 -0
- package/dist/cli/commands/init.d.ts +13 -0
- package/dist/cli/commands/init.js +381 -0
- package/dist/cli/commands/models.js +39 -26
- package/dist/cli/commands/onboard.js +263 -98
- package/dist/cli/commands/plan.d.ts +13 -0
- package/dist/cli/commands/plan.js +184 -0
- package/dist/cli/commands/serve.d.ts +7 -0
- package/dist/cli/commands/serve.js +77 -18
- package/dist/cli/commands/setup.js +375 -91
- package/dist/cli/commands/status.js +54 -0
- package/dist/cli/commands/tools.js +13 -13
- package/dist/cli/commands/tui.js +26 -1
- package/dist/cli/index.js +19 -6
- package/dist/cli/ink/App.d.ts +13 -0
- package/dist/cli/ink/App.js +6 -0
- package/dist/cli/ink/ChatApp.d.ts +60 -0
- package/dist/cli/ink/ChatApp.js +325 -0
- package/dist/cli/ink/DashboardApp.d.ts +3 -0
- package/dist/cli/ink/DashboardApp.js +143 -0
- package/dist/cli/ink/components/AgentStatus.d.ts +17 -0
- package/dist/cli/ink/components/AgentStatus.js +33 -0
- package/dist/cli/ink/components/ChannelList.d.ts +17 -0
- package/dist/cli/ink/components/ChannelList.js +33 -0
- package/dist/cli/ink/components/ConnectionStatus.d.ts +19 -0
- package/dist/cli/ink/components/ConnectionStatus.js +50 -0
- package/dist/cli/ink/components/CostBar.d.ts +19 -0
- package/dist/cli/ink/components/CostBar.js +35 -0
- package/dist/cli/ink/components/HookStatus.d.ts +15 -0
- package/dist/cli/ink/components/HookStatus.js +7 -0
- package/dist/cli/ink/components/PermissionPrompt.d.ts +21 -0
- package/dist/cli/ink/components/PermissionPrompt.js +51 -0
- package/dist/cli/ink/components/PlanView.d.ts +23 -0
- package/dist/cli/ink/components/PlanView.js +26 -0
- package/dist/cli/ink/components/ProviderList.d.ts +16 -0
- package/dist/cli/ink/components/ProviderList.js +16 -0
- package/dist/cli/ink/components/SessionHeader.d.ts +15 -0
- package/dist/cli/ink/components/SessionHeader.js +15 -0
- package/dist/cli/ink/components/StreamingMessage.d.ts +20 -0
- package/dist/cli/ink/components/StreamingMessage.js +27 -0
- package/dist/cli/ink/components/SuggestionBar.d.ts +15 -0
- package/dist/cli/ink/components/SuggestionBar.js +28 -0
- package/dist/cli/ink/components/SystemHealth.d.ts +20 -0
- package/dist/cli/ink/components/SystemHealth.js +39 -0
- package/dist/cli/ink/components/TaskList.d.ts +17 -0
- package/dist/cli/ink/components/TaskList.js +59 -0
- package/dist/cli/ink/components/ToolCall.d.ts +17 -0
- package/dist/cli/ink/components/ToolCall.js +43 -0
- package/dist/cli/ink/index.d.ts +23 -0
- package/dist/cli/ink/index.js +15 -0
- package/dist/cli/ink/keybindings.d.ts +35 -0
- package/dist/cli/ink/keybindings.js +105 -0
- package/dist/cli/ink/markdown-renderer.d.ts +47 -0
- package/dist/cli/ink/markdown-renderer.js +380 -0
- package/dist/cli/ink/output-styles.d.ts +39 -0
- package/dist/cli/ink/output-styles.js +119 -0
- package/dist/cli/interactive/auto-serve.d.ts +38 -0
- package/dist/cli/interactive/auto-serve.js +209 -0
- package/dist/cli/interactive/index.d.ts +25 -0
- package/dist/cli/interactive/index.js +23 -0
- package/dist/cli/interactive/picker.d.ts +28 -0
- package/dist/cli/interactive/picker.js +153 -0
- package/dist/cli/interactive/renderer.d.ts +26 -0
- package/dist/cli/interactive/renderer.js +555 -0
- package/dist/cli/interactive/repl.d.ts +14 -0
- package/dist/cli/interactive/repl.js +1516 -0
- package/dist/cli/interactive/stream-client.d.ts +59 -0
- package/dist/cli/interactive/stream-client.js +506 -0
- package/dist/cli/interactive/types.d.ts +121 -0
- package/dist/cli/interactive/types.js +10 -0
- package/dist/cli/providers.d.ts +15 -0
- package/dist/cli/providers.js +41 -0
- package/dist/cli/utils/api.d.ts +9 -0
- package/dist/cli/utils/api.js +58 -2
- package/dist/core/deployment.js +2 -0
- package/dist/cron/natural-language.d.ts +54 -0
- package/dist/cron/natural-language.js +569 -0
- package/dist/cron/scheduler.d.ts +34 -4
- package/dist/cron/scheduler.js +141 -9
- package/dist/cron/templates.d.ts +1 -1
- package/dist/cron/templates.js +219 -0
- package/dist/feeds/feed-engine.d.ts +149 -0
- package/dist/feeds/feed-engine.js +600 -0
- package/dist/feeds/index.d.ts +9 -0
- package/dist/feeds/index.js +8 -0
- package/dist/gateway/router.js +6 -1
- package/dist/hooks/built-in/audit-log.d.ts +11 -0
- package/dist/hooks/built-in/audit-log.js +84 -0
- package/dist/hooks/built-in/cost-warning.d.ts +10 -0
- package/dist/hooks/built-in/cost-warning.js +59 -0
- package/dist/hooks/built-in/dangerous-tool.d.ts +32 -0
- package/dist/hooks/built-in/dangerous-tool.js +80 -0
- package/dist/hooks/built-in/index.d.ts +14 -0
- package/dist/hooks/built-in/index.js +20 -0
- package/dist/hooks/index.d.ts +3 -0
- package/dist/hooks/index.js +6 -0
- package/dist/hooks/loader.d.ts +17 -0
- package/dist/hooks/loader.js +142 -0
- package/dist/hooks/registry.d.ts +62 -0
- package/dist/hooks/registry.js +127 -0
- package/dist/integrations/web-search.js +6 -3
- package/dist/memory/index.d.ts +1 -0
- package/dist/memory/index.js +2 -0
- package/dist/memory/memory-service.js +14 -13
- package/dist/memory/observational.d.ts +62 -0
- package/dist/memory/observational.js +396 -0
- package/dist/middleware/content-filter.d.ts +22 -0
- package/dist/middleware/content-filter.js +69 -0
- package/dist/middleware/request-validator.d.ts +159 -0
- package/dist/middleware/request-validator.js +93 -0
- package/dist/plugins/sdk.d.ts +22 -0
- package/dist/plugins/sdk.js +17 -1
- package/dist/projects/types.d.ts +10 -10
- package/dist/providers/ai-sdk.d.ts +11 -0
- package/dist/providers/ai-sdk.js +97 -18
- package/dist/providers/core/models.js +67 -1
- package/dist/providers/core/types.d.ts +3 -0
- package/dist/providers/index.d.ts +1 -0
- package/dist/providers/index.js +1 -0
- package/dist/providers/smart-router.d.ts +118 -0
- package/dist/providers/smart-router.js +685 -0
- package/dist/providers/types.d.ts +16 -16
- package/dist/routes/agents.js +50 -0
- package/dist/routes/auth.js +8 -1
- package/dist/routes/channels.d.ts +12 -0
- package/dist/routes/channels.js +166 -0
- package/dist/routes/chat.js +259 -14
- package/dist/routes/costs.js +32 -0
- package/dist/routes/cron.js +80 -5
- package/dist/routes/feeds.d.ts +10 -0
- package/dist/routes/feeds.js +168 -0
- package/dist/routes/mcp.js +21 -0
- package/dist/routes/memory.js +130 -0
- package/dist/routes/skills.js +129 -0
- package/dist/routes/teams.d.ts +9 -0
- package/dist/routes/teams.js +210 -0
- package/dist/routes/tools.js +14 -0
- package/dist/routes/users.js +2 -0
- package/dist/server/route-loader.js +4 -1
- package/dist/server/stream-bridge.d.ts +19 -0
- package/dist/server/stream-bridge.js +38 -0
- package/dist/server.js +421 -141
- package/dist/skills/marketplace.d.ts +87 -0
- package/dist/skills/marketplace.js +268 -0
- package/dist/storage/index.d.ts +14 -0
- package/dist/storage/index.js +64 -3
- package/dist/storage/schema.d.ts +4 -4
- package/dist/storage/schema.js +1 -1
- package/dist/teams/index.d.ts +110 -0
- package/dist/teams/index.js +365 -0
- package/dist/tickets/types.d.ts +39 -39
- package/dist/types/summary.d.ts +5 -5
- package/dist/utils/auto-update.d.ts +23 -0
- package/dist/utils/auto-update.js +157 -0
- package/dist/utils/offline-detect.d.ts +55 -0
- package/dist/utils/offline-detect.js +121 -0
- package/dist/utils/pid-file.d.ts +36 -0
- package/dist/utils/pid-file.js +125 -0
- package/dist/utils/prevent-sleep.d.ts +27 -0
- package/dist/utils/prevent-sleep.js +95 -0
- package/dist/utils/sanitizer.d.ts +57 -0
- package/dist/utils/sanitizer.js +174 -0
- package/docker-compose.yml +13 -137
- package/package.json +31 -6
- package/skills/deploy-cloudflare/SKILL.md +62 -0
- package/skills/deploy-vercel/SKILL.md +67 -0
- package/skills/docker-deploy/SKILL.md +158 -0
- package/skills/full-stack-builder/SKILL.md +92 -0
- package/skills/git-workflow/SKILL.md +3 -3
- package/skills/humanizer/SKILL.md +488 -0
- package/skills/profclaw-assistant/SKILL.md +85 -56
- package/ui/dist/assets/ActivityView-Da6HQ8ws.js +1 -0
- package/ui/dist/assets/AgentList-H58R0kX3.js +1 -0
- package/ui/dist/assets/AnalyticsDashboard-B28AeRsc.js +1 -0
- package/ui/dist/assets/AreaChart-DwLzeAQ8.js +1 -0
- package/ui/dist/assets/CartesianChart-piKQsTAR.js +36 -0
- package/ui/dist/assets/ChatView-Dbj8rxU5.js +10 -0
- package/ui/dist/assets/CostsDashboard-C6F5df0F.js +1 -0
- package/ui/dist/assets/EstimateSelect-wga0rUKX.js +1 -0
- package/ui/dist/assets/FeedDashboard-Ef6FEzEf.js +1 -0
- package/ui/dist/assets/FloatingChatbot-0yhkUeCq.js +2 -0
- package/ui/dist/assets/InviteCodeManagement-tUXfx4Bc.js +2 -0
- package/ui/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/ui/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/ui/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/ui/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/ui/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/ui/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/ui/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/ui/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/ui/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/ui/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/ui/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/ui/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/ui/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/ui/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/ui/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/ui/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/ui/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/ui/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/ui/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/ui/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/ui/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/ui/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/ui/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/ui/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/ui/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/ui/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/ui/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/ui/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/ui/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/ui/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/ui/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/ui/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/ui/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/ui/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/ui/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/ui/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/ui/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/ui/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/ui/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/ui/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/ui/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/ui/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/ui/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/ui/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/ui/dist/assets/MarketplacePage-DGrLtolR.js +1 -0
- package/ui/dist/assets/{ProjectDetail-DlYUcOss.js → ProjectDetail-BgWvA6UP.js} +2 -2
- package/ui/dist/assets/ProjectIcon-TDuliqwg.js +1 -0
- package/ui/dist/assets/ProjectList-DXgM4187.js +1 -0
- package/ui/dist/assets/{Settings-CgPqzUvj.js → Settings-xA8Zhi3p.js} +3 -3
- package/ui/dist/assets/StatusIndicator-B8vfFAxg.js +1 -0
- package/ui/dist/assets/{SummaryDetail-CwLAfHgJ.js → SummaryDetail-eedRfTtF.js} +2 -2
- package/ui/dist/assets/SummaryList-CLNX2P84.js +1 -0
- package/ui/dist/assets/TaskDetail-BtWclNiR.js +1 -0
- package/ui/dist/assets/TaskList-JjODAgSc.js +1 -0
- package/ui/dist/assets/TeamsPage-CBK5iAyx.js +1 -0
- package/ui/dist/assets/TicketBoard-C9aJNNYp.js +1 -0
- package/ui/dist/assets/TicketDetail-CV5JmL31.js +1 -0
- package/ui/dist/assets/TicketList-68V4lzUz.js +1 -0
- package/ui/dist/assets/UserManagement-DKTb6UpE.js +2 -0
- package/ui/dist/assets/ViewSwitcher-BvdQht2Y.js +1 -0
- package/ui/dist/assets/alert-dialog-O6qBOv_f.js +1 -0
- package/ui/dist/assets/{archive-CGck7gxR.js → archive-CX7UvZpf.js} +1 -1
- package/ui/dist/assets/badge-BbfnwDJP.js +1 -0
- package/ui/dist/assets/{bug-DqSGPUUE.js → bug-3WX0394-.js} +1 -1
- package/ui/dist/assets/{calendar-BuZ4zbPW.js → calendar-buq77TaS.js} +1 -1
- package/ui/dist/assets/{chevron-up-DcJ4LL_N.js → chevron-up-4ymsBJRo.js} +1 -1
- package/ui/dist/assets/{circle-x-C78PNZjF.js → circle-x-ByXtwR6Y.js} +1 -1
- package/ui/dist/assets/constants-CVdDh7p0.js +1 -0
- package/ui/dist/assets/{cpu-Ck6AoG8c.js → cpu-CiuoCeDY.js} +1 -1
- package/ui/dist/assets/{database-CA2w8Lvt.js → database-BWXSBg5a.js} +1 -1
- package/ui/dist/assets/dialog-lDg4NvMV.js +1 -0
- package/ui/dist/assets/dollar-sign-Dmijd6DP.js +1 -0
- package/ui/dist/assets/{ellipsis-FhHKRbfl.js → ellipsis-vT-5SVIp.js} +1 -1
- package/ui/dist/assets/{ellipsis-vertical-CO0UqE4-.js → ellipsis-vertical-B4tAX_bL.js} +1 -1
- package/ui/dist/assets/feature-markdown-DBo5Q7uq.js +289 -0
- package/ui/dist/assets/{file-code-ChnhYYj1.js → file-code-ChOWdKXf.js} +1 -1
- package/ui/dist/assets/{folder-open-CHwA1-ve.js → folder-open-CkOUbIiP.js} +1 -1
- package/ui/dist/assets/{funnel-ByEjRFNB.js → funnel-uZkoSPP-.js} +1 -1
- package/ui/dist/assets/{git-branch-GTOPQgU4.js → git-branch-CwvUxj7C.js} +1 -1
- package/ui/dist/assets/{git-commit-horizontal-F5M84Kut.js → git-commit-horizontal-lxPzCvhD.js} +1 -1
- package/ui/dist/assets/{globe-CQWfr7hT.js → globe-D_WpJdsj.js} +1 -1
- package/ui/dist/assets/{hash-nKC9gtLg.js → hash-CA0hJ7vh.js} +1 -1
- package/ui/dist/assets/{history-DfFSA9xJ.js → history-CEWMLELs.js} +1 -1
- package/ui/dist/assets/inbox-ehtOptTV.js +1 -0
- package/ui/dist/assets/{index-a4Fxq0Hb.js → index-C1rhbml4.js} +3 -3
- package/ui/dist/assets/index-CR8gfCHv.js +1 -0
- package/ui/dist/assets/index-DVXMpWai.js +1 -0
- package/ui/dist/assets/index-DaDJMM2v.js +1 -0
- package/ui/dist/assets/index-Djdq_0xu.js +1 -0
- package/ui/dist/assets/index-M46Gcb8K.css +1 -0
- package/ui/dist/assets/index-sEBN7w8J.js +109 -0
- package/ui/dist/assets/layers-C6Mhf8Nk.js +1 -0
- package/ui/dist/assets/{layout-dashboard-WmgjMhWF.js → layout-dashboard-BRUM0kpT.js} +1 -1
- package/ui/dist/assets/{lightbulb-Bo8hEr7B.js → lightbulb-CN2q1O5l.js} +1 -1
- package/ui/dist/assets/{link-2-YqrpQXBH.js → link-2-CC4E28yJ.js} +1 -1
- package/ui/dist/assets/markdown-renderer-C6ENbB0u.js +10 -0
- package/ui/dist/assets/markdown-renderer-CfVKi3_s.css +1 -0
- package/ui/dist/assets/{message-square-CD0H5hot.js → message-square-Bbb241am.js} +1 -1
- package/ui/dist/assets/package-CwT3IzFx.js +1 -0
- package/ui/dist/assets/{pencil-DpFnj5dn.js → pencil-CDmiHzoj.js} +1 -1
- package/ui/dist/assets/{play-DVgea_jT.js → play-tjy5jMQX.js} +1 -1
- package/ui/dist/assets/popover-CFHZf3pE.js +1 -0
- package/ui/dist/assets/progress-BboUHuLf.js +1 -0
- package/ui/dist/assets/{rocket-BFxPYGx9.js → rocket--8ZHgdXJ.js} +1 -1
- package/ui/dist/assets/select-n108rXny.js +1 -0
- package/ui/dist/assets/{send-eYnW_7n-.js → send-Cu7MR2Qh.js} +1 -1
- package/ui/dist/assets/server-DmodxZp-.js +1 -0
- package/ui/dist/assets/{settings-2-scXUOuE_.js → settings-2-CkRNqkxj.js} +1 -1
- package/ui/dist/assets/sheet-7aUJkyzU.js +1 -0
- package/ui/dist/assets/{shield-check-PEr7nmVw.js → shield-check-ViI3nMKE.js} +1 -1
- package/ui/dist/assets/{shield-off-CLtXio62.js → shield-off-DN3tagkn.js} +1 -1
- package/ui/dist/assets/skeleton-C0E1Nfro.js +1 -0
- package/ui/dist/assets/{smartphone-DusFfY0U.js → smartphone-5FghiXpA.js} +1 -1
- package/ui/dist/assets/{square-e1yxB2eR.js → square-DiNSx8K7.js} +1 -1
- package/ui/dist/assets/star-DhdFVidS.js +1 -0
- package/ui/dist/assets/switch-CIgVbZqQ.js +1 -0
- package/ui/dist/assets/table-BC56FVMV.js +1 -0
- package/ui/dist/assets/{tag-tAwMmQ9G.js → tag-CH_vQliw.js} +1 -1
- package/ui/dist/assets/textarea-CxDiKFXZ.js +1 -0
- package/ui/dist/assets/{timer-Vel1N3RP.js → timer-CdGVTb60.js} +1 -1
- package/ui/dist/assets/tooltip-BuW9A5UO.js +1 -0
- package/ui/dist/assets/{trash-2-eM-CNwsZ.js → trash-2-CucZJL0N.js} +1 -1
- package/ui/dist/assets/trending-down-BCezC1z2.js +1 -0
- package/ui/dist/assets/{trending-up-BGafmjRo.js → trending-up-C5OYX2k2.js} +1 -1
- package/ui/dist/assets/vendor-query-C0p7b53x.js +4 -0
- package/ui/dist/assets/vendor-react-Mc5_kfYG.js +12 -0
- package/ui/dist/assets/vendor-ui-CXPAUBFv.js +51 -0
- package/ui/dist/assets/{wand-sparkles-DPvcd9Pi.js → wand-sparkles-BaH6pJnc.js} +1 -1
- package/ui/dist/assets/{wrench-B8s3zYVV.js → wrench-CjxjP6ok.js} +1 -1
- package/ui/dist/index.html +5 -2
- package/dist/chat/auto-reply.d.ts +0 -90
- package/dist/chat/auto-reply.js +0 -272
- package/dist/chat/execution/pty.d.ts +0 -92
- package/dist/chat/execution/pty.js +0 -285
- package/dist/chat/execution/tools/glinr-ops.d.ts +0 -202
- package/dist/chat/execution/tools/glinr-ops.js +0 -694
- package/dist/cli/commands/browser.d.ts +0 -3
- package/dist/cli/commands/browser.js +0 -138
- package/dist/cli/commands/canvas.d.ts +0 -3
- package/dist/cli/commands/canvas.js +0 -155
- package/dist/cli/commands/nodes.d.ts +0 -3
- package/dist/cli/commands/nodes.js +0 -193
- package/dist/diagnostics/exporters.d.ts +0 -21
- package/dist/diagnostics/exporters.js +0 -138
- package/dist/diagnostics/index.d.ts +0 -10
- package/dist/diagnostics/index.js +0 -9
- package/dist/diagnostics/middleware.d.ts +0 -12
- package/dist/diagnostics/middleware.js +0 -58
- package/dist/diagnostics/tracer.d.ts +0 -133
- package/dist/diagnostics/tracer.js +0 -257
- package/dist/discovery/index.d.ts +0 -8
- package/dist/discovery/index.js +0 -7
- package/dist/discovery/mdns.d.ts +0 -40
- package/dist/discovery/mdns.js +0 -249
- package/dist/plugins/registry.d.ts +0 -122
- package/dist/plugins/registry.js +0 -352
- package/dist/plugins/sandbox.d.ts +0 -77
- package/dist/plugins/sandbox.js +0 -287
- package/dist/plugins/search/brave.d.ts +0 -9
- package/dist/plugins/search/brave.js +0 -157
- package/dist/plugins/search/duckduckgo.d.ts +0 -9
- package/dist/plugins/search/duckduckgo.js +0 -171
- package/dist/plugins/search/index.d.ts +0 -13
- package/dist/plugins/search/index.js +0 -23
- package/dist/plugins/search/searxng.d.ts +0 -9
- package/dist/plugins/search/searxng.js +0 -170
- package/dist/plugins/search/serper.d.ts +0 -9
- package/dist/plugins/search/serper.js +0 -135
- package/dist/plugins/search/tavily.d.ts +0 -9
- package/dist/plugins/search/tavily.js +0 -146
- package/dist/providers/adapters/anthropic.d.ts +0 -26
- package/dist/providers/adapters/anthropic.js +0 -306
- package/dist/providers/adapters/base.d.ts +0 -53
- package/dist/providers/adapters/base.js +0 -99
- package/dist/providers/adapters/ollama.d.ts +0 -30
- package/dist/providers/adapters/ollama.js +0 -292
- package/dist/utils/ai-cache.d.ts +0 -72
- package/dist/utils/ai-cache.js +0 -204
- package/dist/utils/file-lock.d.ts +0 -85
- package/dist/utils/file-lock.js +0 -204
- package/ui/dist/assets/ActivityView-DIPQSJ_d.js +0 -1
- package/ui/dist/assets/AgentList-CqyaYrcq.js +0 -1
- package/ui/dist/assets/AnalyticsDashboard-C-mjuGHT.js +0 -1
- package/ui/dist/assets/AreaChart-CfYWmbtr.js +0 -1
- package/ui/dist/assets/CartesianChart-BaQ3It0c.js +0 -36
- package/ui/dist/assets/ChatView-DxxQy6pl.js +0 -10
- package/ui/dist/assets/CostsDashboard-CDBBcGkg.js +0 -1
- package/ui/dist/assets/EstimateSelect-CDjf5qKl.js +0 -1
- package/ui/dist/assets/FloatingChatbot-CZHjvJIe.js +0 -2
- package/ui/dist/assets/InviteCodeManagement-DTm-IPTm.js +0 -2
- package/ui/dist/assets/ProjectIcon-BMRpnM04.js +0 -1
- package/ui/dist/assets/ProjectList-D85TN0i0.js +0 -4
- package/ui/dist/assets/StatusIndicator-yig0uZEA.js +0 -1
- package/ui/dist/assets/SummaryList-BvS5Idnu.js +0 -1
- package/ui/dist/assets/TaskDetail-zarC1fOt.js +0 -1
- package/ui/dist/assets/TaskList-CyJq3ANE.js +0 -1
- package/ui/dist/assets/TicketBoard-BlWr3-iH.js +0 -1
- package/ui/dist/assets/TicketDetail-DmqWKj2W.js +0 -1
- package/ui/dist/assets/TicketList-Scj4pMWa.js +0 -1
- package/ui/dist/assets/UserManagement-BH7uG7dw.js +0 -2
- package/ui/dist/assets/ViewSwitcher-3HzZUdWn.js +0 -1
- package/ui/dist/assets/alert-dialog-DWPb2IfQ.js +0 -7
- package/ui/dist/assets/badge-DEmc3Z_l.js +0 -1
- package/ui/dist/assets/brain-CfFdLVqb.js +0 -1
- package/ui/dist/assets/constants-DhDjZe4P.js +0 -1
- package/ui/dist/assets/dialog-BOdGWhgE.js +0 -1
- package/ui/dist/assets/index--JkIxVXb.js +0 -67
- package/ui/dist/assets/index-Ca3vk7Gq.js +0 -1
- package/ui/dist/assets/index-DHGvP9mp.js +0 -1
- package/ui/dist/assets/index-DeI8lKRR.js +0 -1
- package/ui/dist/assets/index-WclMPci1.css +0 -1
- package/ui/dist/assets/index-Z4-RtZcs.js +0 -1
- package/ui/dist/assets/layers-DFeLExSa.js +0 -1
- package/ui/dist/assets/markdown-renderer-Croje6Xe.js +0 -36
- package/ui/dist/assets/popover-JXecfWG5.js +0 -1
- package/ui/dist/assets/progress-C1ghf1Fu.js +0 -1
- package/ui/dist/assets/select-MADP8m6Q.js +0 -1
- package/ui/dist/assets/server-CPMaysH8.js +0 -1
- package/ui/dist/assets/sheet-r6NSOxi-.js +0 -1
- package/ui/dist/assets/skeleton-C9oEzNlU.js +0 -1
- package/ui/dist/assets/switch-tNs0n-kz.js +0 -1
- package/ui/dist/assets/table-CXxtLicg.js +0 -1
- package/ui/dist/assets/textarea-BjiEeIWh.js +0 -1
- package/ui/dist/assets/tooltip-ZMwtfr1J.js +0 -1
package/dist/server.js
CHANGED
|
@@ -6,15 +6,23 @@ import { createServer } from 'net';
|
|
|
6
6
|
// Core imports - keep static only for truly essential boot utilities
|
|
7
7
|
import { loadConfig } from './utils/config-loader.js';
|
|
8
8
|
import { createContextualLogger } from './utils/logger.js';
|
|
9
|
+
import { writePidFile, removePidFile } from './utils/pid-file.js';
|
|
9
10
|
const appLog = createContextualLogger('Server');
|
|
10
11
|
import { validateEnvironment, getConfiguredIntegrations, getConfiguredAIProviders } from './utils/env-validator.js';
|
|
11
12
|
import { initApiTokensTable, tokenAuthMiddleware } from './auth/api-tokens.js';
|
|
12
13
|
import { authMiddleware } from './auth/middleware.js';
|
|
13
14
|
import { rateLimit } from './middleware/rate-limit.js';
|
|
14
15
|
import { CreateTaskSchema } from './types/task.js';
|
|
15
|
-
import { getMode, getModeLabel, hasCapability } from './core/deployment.js';
|
|
16
|
-
import { registerRouteModules } from './server/route-loader.js';
|
|
16
|
+
import { getMode, getModeLabel, hasCapability, getCapabilities } from './core/deployment.js';
|
|
17
|
+
import { getRouteDefinitionsForMode, registerRouteModules } from './server/route-loader.js';
|
|
18
|
+
import { validateBody, GatewayExecuteSchema } from './middleware/request-validator.js';
|
|
17
19
|
const VERSION = '2.0.0';
|
|
20
|
+
// Module-level startup state flags
|
|
21
|
+
let degradedMode = false;
|
|
22
|
+
let inMemoryStorageReminderInterval = null;
|
|
23
|
+
// Runtime tracking state
|
|
24
|
+
let runningPort = 0;
|
|
25
|
+
let lastErrorMessage;
|
|
18
26
|
let cachedSettings = null;
|
|
19
27
|
function getAppSettings() {
|
|
20
28
|
if (!cachedSettings) {
|
|
@@ -87,6 +95,13 @@ app.use('*', cors({
|
|
|
87
95
|
// Authentication middleware — applied to all /api/* routes
|
|
88
96
|
// Skips public routes (auth, setup, webhooks, messaging channels)
|
|
89
97
|
app.use('/api/*', authMiddleware());
|
|
98
|
+
// WS-1.4: In-memory storage warning header — set on every response when storage is ephemeral
|
|
99
|
+
app.use('*', async (c, next) => {
|
|
100
|
+
await next();
|
|
101
|
+
if (degradedMode) {
|
|
102
|
+
c.res.headers.set('X-ProfClaw-Storage', 'ephemeral');
|
|
103
|
+
}
|
|
104
|
+
});
|
|
90
105
|
// HTTP rate limiting (sliding window, per-IP)
|
|
91
106
|
const apiRateLimiter = rateLimit({
|
|
92
107
|
maxRequests: parseInt(process.env['API_RATE_LIMIT'] ?? '100', 10),
|
|
@@ -100,131 +115,332 @@ app.use('/api/*', apiRateLimiter);
|
|
|
100
115
|
app.use('/auth/*', authRateLimiter);
|
|
101
116
|
// === Core Routes ===
|
|
102
117
|
// Health check
|
|
118
|
+
const serverStartTime = Date.now();
|
|
119
|
+
async function checkDatabaseHealth() {
|
|
120
|
+
const start = Date.now();
|
|
121
|
+
try {
|
|
122
|
+
const { getDb } = await import('./storage/index.js');
|
|
123
|
+
const db = getDb();
|
|
124
|
+
if (!db)
|
|
125
|
+
return { status: 'error', message: 'Database not initialised' };
|
|
126
|
+
// Simple liveness query
|
|
127
|
+
await db.run('SELECT 1');
|
|
128
|
+
return { status: 'ok', latencyMs: Date.now() - start };
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
return {
|
|
132
|
+
status: 'error',
|
|
133
|
+
latencyMs: Date.now() - start,
|
|
134
|
+
message: err instanceof Error ? err.message : 'Database check failed',
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async function checkQueueHealth() {
|
|
139
|
+
try {
|
|
140
|
+
const { getQueueType } = await import('./queue/index.js');
|
|
141
|
+
const queueType = getQueueType();
|
|
142
|
+
if (!queueType)
|
|
143
|
+
return { status: 'warning', message: 'Queue not initialised' };
|
|
144
|
+
if (queueType === 'redis') {
|
|
145
|
+
// Test Redis reachability with a fresh connection
|
|
146
|
+
const redisUrl = process.env.REDIS_URL || 'redis://localhost:6379';
|
|
147
|
+
const start = Date.now();
|
|
148
|
+
try {
|
|
149
|
+
const IORedis = (await import('ioredis')).default;
|
|
150
|
+
const probe = new IORedis(redisUrl, { lazyConnect: true, connectTimeout: 2000, maxRetriesPerRequest: 0 });
|
|
151
|
+
await probe.connect();
|
|
152
|
+
await probe.ping();
|
|
153
|
+
await probe.disconnect();
|
|
154
|
+
return { status: 'ok', type: 'redis', latencyMs: Date.now() - start };
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return { status: 'error', type: 'redis', latencyMs: Date.now() - start, message: 'Redis unreachable' };
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return { status: 'ok', type: queueType };
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
return { status: 'ok', type: 'in-memory' };
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
async function checkOllamaHealth() {
|
|
167
|
+
const ollamaUrl = process.env.OLLAMA_BASE_URL || 'http://localhost:11434';
|
|
168
|
+
const start = Date.now();
|
|
169
|
+
try {
|
|
170
|
+
const res = await fetch(`${ollamaUrl}/api/tags`, {
|
|
171
|
+
signal: AbortSignal.timeout(3000),
|
|
172
|
+
});
|
|
173
|
+
if (!res.ok)
|
|
174
|
+
return { status: 'error', latencyMs: Date.now() - start, message: `HTTP ${res.status}` };
|
|
175
|
+
const data = await res.json();
|
|
176
|
+
const modelCount = Array.isArray(data.models) ? data.models.length : 0;
|
|
177
|
+
return { status: 'ok', latencyMs: Date.now() - start, models: modelCount };
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
// Ollama is optional — not reachable means degraded, not unhealthy
|
|
181
|
+
return { status: 'warning', latencyMs: Date.now() - start, message: 'Ollama not reachable' };
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
async function checkDiskHealth() {
|
|
185
|
+
try {
|
|
186
|
+
const { statfs } = await import('node:fs/promises');
|
|
187
|
+
const stats = await statfs(process.cwd());
|
|
188
|
+
const freeBytes = stats.bfree * stats.bsize;
|
|
189
|
+
const freeGb = freeBytes / (1024 ** 3);
|
|
190
|
+
const WARN_THRESHOLD_BYTES = 100 * 1024 * 1024; // 100 MB
|
|
191
|
+
const status = freeBytes < WARN_THRESHOLD_BYTES ? 'warning' : 'ok';
|
|
192
|
+
return {
|
|
193
|
+
status,
|
|
194
|
+
freeGb: Math.round(freeGb * 100) / 100,
|
|
195
|
+
...(status === 'warning' && { message: 'Low disk space (< 100 MB free)' }),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
return { status: 'warning', message: 'Disk check unavailable' };
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
function checkMemoryHealth() {
|
|
203
|
+
const WARN_THRESHOLD = 90;
|
|
204
|
+
const mode = getMode();
|
|
205
|
+
const mem = process.memoryUsage();
|
|
206
|
+
const rssMb = Math.round(mem.rss / 1024 / 1024);
|
|
207
|
+
// Mode-specific RSS caps (absolute memory usage is more actionable than heap ratio)
|
|
208
|
+
const RSS_CAP_MB = { pico: 128, mini: 256, pro: 512 };
|
|
209
|
+
const capMb = RSS_CAP_MB[mode] ?? 512;
|
|
210
|
+
// Use RSS vs mode cap — heap ratio is misleading at small heap sizes
|
|
211
|
+
const usedPercent = Math.round((rssMb / capMb) * 100);
|
|
212
|
+
const status = usedPercent > WARN_THRESHOLD ? 'warning' : 'ok';
|
|
213
|
+
return {
|
|
214
|
+
status,
|
|
215
|
+
usedPercent,
|
|
216
|
+
...(status === 'warning' && { message: `RSS ${rssMb}MB exceeds ${WARN_THRESHOLD}% of ${capMb}MB cap (${mode} mode)` }),
|
|
217
|
+
};
|
|
218
|
+
}
|
|
103
219
|
app.get('/health', async (c) => {
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
220
|
+
const [database, queue, ollama, disk] = await Promise.all([
|
|
221
|
+
checkDatabaseHealth(),
|
|
222
|
+
checkQueueHealth(),
|
|
223
|
+
checkOllamaHealth(),
|
|
224
|
+
checkDiskHealth(),
|
|
225
|
+
]);
|
|
226
|
+
const memory = checkMemoryHealth();
|
|
227
|
+
const services = { database, queue, ollama, disk, memory };
|
|
228
|
+
const statuses = Object.values(services).map((s) => s.status);
|
|
229
|
+
const overallStatus = statuses.some((s) => s === 'error')
|
|
230
|
+
? 'unhealthy'
|
|
231
|
+
: statuses.some((s) => s === 'warning')
|
|
232
|
+
? 'degraded'
|
|
233
|
+
: 'ok';
|
|
234
|
+
// Gather queue depth
|
|
235
|
+
let queueDepth = { pending: 0, running: 0, total: 0 };
|
|
236
|
+
try {
|
|
237
|
+
const { getTasks } = await import('./queue/index.js');
|
|
238
|
+
const pending = getTasks({ status: 'pending' }).length + getTasks({ status: 'queued' }).length;
|
|
239
|
+
const running = getTasks({ status: 'in_progress' }).length;
|
|
240
|
+
queueDepth = { pending, running, total: pending + running };
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
// Queue not yet initialised — use zeroes
|
|
244
|
+
}
|
|
245
|
+
// Gather active agent sessions
|
|
246
|
+
let activeSessions = 0;
|
|
247
|
+
try {
|
|
248
|
+
const { getAgentRegistry } = await import('./adapters/registry.js');
|
|
249
|
+
activeSessions = getAgentRegistry().getActiveAdapters().length;
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
// Registry not yet initialised
|
|
253
|
+
}
|
|
254
|
+
const memUsage = process.memoryUsage();
|
|
255
|
+
const { loadavg: osLoadavg } = await import('node:os');
|
|
256
|
+
const body = {
|
|
257
|
+
status: overallStatus,
|
|
258
|
+
services,
|
|
110
259
|
version: VERSION,
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
260
|
+
uptime: Math.floor((Date.now() - serverStartTime) / 1000),
|
|
261
|
+
sseClients: sseConnections.size,
|
|
262
|
+
activeSessions,
|
|
263
|
+
queueDepth,
|
|
264
|
+
process: {
|
|
265
|
+
pid: process.pid,
|
|
266
|
+
port: runningPort,
|
|
267
|
+
memoryMb: Math.round(memUsage.rss / 1024 / 1024),
|
|
268
|
+
cpuLoadAvg: process.platform !== 'win32' ? osLoadavg() : [0, 0, 0],
|
|
269
|
+
},
|
|
270
|
+
...(lastErrorMessage !== undefined && { lastError: lastErrorMessage }),
|
|
271
|
+
};
|
|
272
|
+
const httpStatus = overallStatus === 'unhealthy' ? 503 : 200;
|
|
273
|
+
return c.json(body, httpStatus);
|
|
116
274
|
});
|
|
117
|
-
// API info
|
|
118
|
-
app.get('/', (c) => {
|
|
275
|
+
// API info (mode-aware)
|
|
276
|
+
app.get('/', async (c) => {
|
|
277
|
+
// WS-1.3: First-run setup redirect — send to /setup when no admin users exist
|
|
278
|
+
try {
|
|
279
|
+
const [{ getDb }, { users }, { eq }] = await Promise.all([
|
|
280
|
+
import('./storage/index.js'),
|
|
281
|
+
import('./storage/schema.js'),
|
|
282
|
+
import('drizzle-orm'),
|
|
283
|
+
]);
|
|
284
|
+
const db = getDb();
|
|
285
|
+
if (db) {
|
|
286
|
+
const adminCount = await db
|
|
287
|
+
.select({ id: users.id })
|
|
288
|
+
.from(users)
|
|
289
|
+
.where(eq(users.role, 'admin'))
|
|
290
|
+
.limit(1);
|
|
291
|
+
if (adminCount.length === 0) {
|
|
292
|
+
return c.redirect('/setup', 302);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
catch {
|
|
297
|
+
// DB may not be ready; fall through to normal response
|
|
298
|
+
}
|
|
299
|
+
// If UI is built and web_ui capability is enabled, serve the dashboard
|
|
300
|
+
if (hasCapability('web_ui')) {
|
|
301
|
+
const { existsSync } = await import('node:fs');
|
|
302
|
+
const { join } = await import('node:path');
|
|
303
|
+
const { readFile } = await import('node:fs/promises');
|
|
304
|
+
const indexPath = join(process.cwd(), 'ui', 'dist', 'index.html');
|
|
305
|
+
if (existsSync(indexPath)) {
|
|
306
|
+
const html = await readFile(indexPath, 'utf-8');
|
|
307
|
+
return c.html(html);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
// Fallback: API info JSON
|
|
311
|
+
const mode = getMode();
|
|
312
|
+
const routes = getRouteDefinitionsForMode(mode);
|
|
313
|
+
const routeIds = new Set(routes.map((r) => r.id));
|
|
314
|
+
// Build endpoints dynamically based on available routes
|
|
315
|
+
const endpoints = {
|
|
316
|
+
health: 'GET /health',
|
|
317
|
+
};
|
|
318
|
+
// Core routes (always available)
|
|
319
|
+
if (routeIds.has('tasks')) {
|
|
320
|
+
endpoints.tasks = {
|
|
321
|
+
list: 'GET /api/tasks',
|
|
322
|
+
create: 'POST /api/tasks',
|
|
323
|
+
get: 'GET /api/tasks/:id',
|
|
324
|
+
cancel: 'POST /api/tasks/:id/cancel',
|
|
325
|
+
retry: 'POST /api/tasks/:id/retry',
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
if (routeIds.has('agents'))
|
|
329
|
+
endpoints.agents = 'GET /api/agents';
|
|
330
|
+
if (routeIds.has('chat')) {
|
|
331
|
+
endpoints.chat = {
|
|
332
|
+
models: 'GET /api/chat/models',
|
|
333
|
+
providers: 'GET /api/chat/providers',
|
|
334
|
+
send: 'POST /api/chat/send',
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
if (routeIds.has('gateway')) {
|
|
338
|
+
endpoints.gateway = {
|
|
339
|
+
execute: 'POST /api/gateway/execute',
|
|
340
|
+
agents: 'GET /api/gateway/agents',
|
|
341
|
+
workflows: 'GET /api/gateway/workflows',
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
if (routeIds.has('hooks')) {
|
|
345
|
+
endpoints.hooks = {
|
|
346
|
+
toolUse: 'POST /api/hook/tool-use',
|
|
347
|
+
sessionEnd: 'POST /api/hook/session-end',
|
|
348
|
+
sessions: 'GET /api/hook/sessions',
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
if (routeIds.has('tools')) {
|
|
352
|
+
endpoints.tools = {
|
|
353
|
+
list: 'GET /api/tools',
|
|
354
|
+
execute: 'POST /api/tools/execute',
|
|
355
|
+
security: 'GET /api/tools/security',
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
if (routeIds.has('settings')) {
|
|
359
|
+
endpoints.settings = {
|
|
360
|
+
get: 'GET /api/settings',
|
|
361
|
+
update: 'PATCH /api/settings',
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
if (routeIds.has('auth'))
|
|
365
|
+
endpoints.auth = 'POST /auth/login';
|
|
366
|
+
// Dashboard/web UI routes
|
|
367
|
+
if (routeIds.has('costs'))
|
|
368
|
+
endpoints.costs = 'GET /api/costs/summary';
|
|
369
|
+
if (routeIds.has('memory'))
|
|
370
|
+
endpoints.memory = 'GET /api/memory';
|
|
371
|
+
if (routeIds.has('skills'))
|
|
372
|
+
endpoints.skills = 'GET /api/skills';
|
|
373
|
+
if (routeIds.has('mcp')) {
|
|
374
|
+
endpoints.mcp = {
|
|
375
|
+
status: 'GET /api/mcp',
|
|
376
|
+
tools: 'GET /api/mcp/tools',
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
if (routeIds.has('tunnels'))
|
|
380
|
+
endpoints.tunnels = 'GET /api/tunnels';
|
|
381
|
+
if (routeIds.has('teams')) {
|
|
382
|
+
endpoints.teams = {
|
|
383
|
+
list: 'GET /api/teams?userId=<id>',
|
|
384
|
+
create: 'POST /api/teams',
|
|
385
|
+
members: 'GET /api/teams/:id/members',
|
|
386
|
+
usage: 'GET /api/teams/:id/usage',
|
|
387
|
+
invites: 'POST /api/teams/:id/invites',
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
// Web UI data routes
|
|
391
|
+
if (routeIds.has('dlq'))
|
|
392
|
+
endpoints.deadLetterQueue = 'GET /api/dlq';
|
|
393
|
+
if (routeIds.has('summaries')) {
|
|
394
|
+
endpoints.summaries = {
|
|
395
|
+
list: 'GET /api/summaries',
|
|
396
|
+
search: 'GET /api/summaries/search',
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
if (routeIds.has('search')) {
|
|
400
|
+
endpoints.search = {
|
|
401
|
+
semantic: 'GET /api/search/semantic?q={query}',
|
|
402
|
+
text: 'GET /api/search/text?q={query}',
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
if (routeIds.has('stats'))
|
|
406
|
+
endpoints.stats = 'GET /api/stats';
|
|
407
|
+
// Integration routes
|
|
408
|
+
if (routeIds.has('tickets')) {
|
|
409
|
+
endpoints.tickets = {
|
|
410
|
+
list: 'GET /api/tickets',
|
|
411
|
+
create: 'POST /api/tickets',
|
|
412
|
+
get: 'GET /api/tickets/:id',
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
if (routeIds.has('projects')) {
|
|
416
|
+
endpoints.projects = {
|
|
417
|
+
list: 'GET /api/projects',
|
|
418
|
+
create: 'POST /api/projects',
|
|
419
|
+
get: 'GET /api/projects/:id',
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
if (routeIds.has('webhooks')) {
|
|
423
|
+
endpoints.webhooks = {
|
|
424
|
+
github: 'POST /webhooks/github',
|
|
425
|
+
jira: 'POST /webhooks/jira',
|
|
426
|
+
linear: 'POST /webhooks/linear',
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
// Cron routes
|
|
430
|
+
if (routeIds.has('cron'))
|
|
431
|
+
endpoints.cron = 'GET /api/cron';
|
|
432
|
+
// Channel routes
|
|
433
|
+
if (routeIds.has('channels'))
|
|
434
|
+
endpoints.channels = 'GET /api/channels';
|
|
435
|
+
// Sync routes
|
|
436
|
+
if (routeIds.has('sync'))
|
|
437
|
+
endpoints.sync = 'GET /api/sync';
|
|
119
438
|
return c.json({
|
|
120
439
|
name: 'profClaw',
|
|
121
440
|
version: VERSION,
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
endpoints
|
|
125
|
-
health: 'GET /health',
|
|
126
|
-
tasks: 'GET /api/tasks',
|
|
127
|
-
createTask: 'POST /api/tasks',
|
|
128
|
-
getTask: 'GET /api/tasks/:id',
|
|
129
|
-
cancelTask: 'POST /api/tasks/:id/cancel',
|
|
130
|
-
retryTask: 'POST /api/tasks/:id/retry',
|
|
131
|
-
agents: 'GET /api/agents',
|
|
132
|
-
deadLetterQueue: 'GET /api/dlq',
|
|
133
|
-
retryDlqTask: 'POST /api/dlq/:id/retry',
|
|
134
|
-
webhooks: {
|
|
135
|
-
github: 'POST /webhooks/github',
|
|
136
|
-
jira: 'POST /webhooks/jira',
|
|
137
|
-
linear: 'POST /webhooks/linear',
|
|
138
|
-
},
|
|
139
|
-
hooks: {
|
|
140
|
-
toolUse: 'POST /api/hook/tool-use',
|
|
141
|
-
sessionEnd: 'POST /api/hook/session-end',
|
|
142
|
-
promptSubmit: 'POST /api/hook/prompt-submit',
|
|
143
|
-
sessions: 'GET /api/hook/sessions',
|
|
144
|
-
sessionEvents: 'GET /api/hook/sessions/:sessionId/events',
|
|
145
|
-
sessionSummary: 'GET /api/hook/sessions/:sessionId/summary',
|
|
146
|
-
},
|
|
147
|
-
agentWebhooks: {
|
|
148
|
-
openclaw: 'POST /api/hook/webhook/openclaw',
|
|
149
|
-
generic: 'POST /api/hook/webhook/agent',
|
|
150
|
-
reports: 'GET /api/hook/webhook/reports',
|
|
151
|
-
taskReports: 'GET /api/hook/webhook/tasks/:taskId/reports',
|
|
152
|
-
},
|
|
153
|
-
costs: {
|
|
154
|
-
summary: 'GET /api/costs/summary',
|
|
155
|
-
budget: 'GET /api/costs/budget',
|
|
156
|
-
},
|
|
157
|
-
summaries: {
|
|
158
|
-
list: 'GET /api/summaries',
|
|
159
|
-
create: 'POST /api/summaries',
|
|
160
|
-
get: 'GET /api/summaries/:id',
|
|
161
|
-
delete: 'DELETE /api/summaries/:id',
|
|
162
|
-
search: 'GET /api/summaries/search',
|
|
163
|
-
stats: 'GET /api/summaries/stats',
|
|
164
|
-
recent: 'GET /api/summaries/recent',
|
|
165
|
-
byTask: 'GET /api/tasks/:taskId/summaries',
|
|
166
|
-
},
|
|
167
|
-
settings: {
|
|
168
|
-
get: 'GET /api/settings',
|
|
169
|
-
update: 'PATCH /api/settings',
|
|
170
|
-
reset: 'POST /api/settings/reset',
|
|
171
|
-
},
|
|
172
|
-
gateway: {
|
|
173
|
-
execute: 'POST /api/gateway/execute',
|
|
174
|
-
agents: 'GET /api/gateway/agents',
|
|
175
|
-
workflows: 'GET /api/gateway/workflows',
|
|
176
|
-
workflow: 'GET /api/gateway/workflows/:type',
|
|
177
|
-
config: 'GET /api/gateway/config',
|
|
178
|
-
updateConfig: 'PATCH /api/gateway/config',
|
|
179
|
-
},
|
|
180
|
-
search: {
|
|
181
|
-
semantic: 'GET /api/search/semantic?q={query}',
|
|
182
|
-
text: 'GET /api/search/text?q={query}',
|
|
183
|
-
capabilities: 'GET /api/search/capabilities',
|
|
184
|
-
},
|
|
185
|
-
tickets: {
|
|
186
|
-
list: 'GET /api/tickets',
|
|
187
|
-
create: 'POST /api/tickets',
|
|
188
|
-
get: 'GET /api/tickets/:id',
|
|
189
|
-
update: 'PATCH /api/tickets/:id',
|
|
190
|
-
delete: 'DELETE /api/tickets/:id',
|
|
191
|
-
transition: 'POST /api/tickets/:id/transition',
|
|
192
|
-
assignAgent: 'POST /api/tickets/:id/assign-agent',
|
|
193
|
-
comments: 'GET /api/tickets/:id/comments',
|
|
194
|
-
addComment: 'POST /api/tickets/:id/comments',
|
|
195
|
-
links: 'GET /api/tickets/:id/links',
|
|
196
|
-
addLink: 'POST /api/tickets/:id/links',
|
|
197
|
-
history: 'GET /api/tickets/:id/history',
|
|
198
|
-
},
|
|
199
|
-
projects: {
|
|
200
|
-
list: 'GET /api/projects',
|
|
201
|
-
create: 'POST /api/projects',
|
|
202
|
-
get: 'GET /api/projects/:id',
|
|
203
|
-
update: 'PATCH /api/projects/:id',
|
|
204
|
-
archive: 'POST /api/projects/:id/archive',
|
|
205
|
-
delete: 'DELETE /api/projects/:id',
|
|
206
|
-
byKey: 'GET /api/projects/by-key/:key',
|
|
207
|
-
default: 'GET /api/projects/default',
|
|
208
|
-
migrate: 'POST /api/projects/migrate',
|
|
209
|
-
externalLinks: 'GET /api/projects/:id/external-links',
|
|
210
|
-
addExternalLink: 'POST /api/projects/:id/external-links',
|
|
211
|
-
},
|
|
212
|
-
sprints: {
|
|
213
|
-
list: 'GET /api/projects/:projectId/sprints',
|
|
214
|
-
create: 'POST /api/projects/:projectId/sprints',
|
|
215
|
-
get: 'GET /api/projects/:projectId/sprints/:sprintId',
|
|
216
|
-
update: 'PATCH /api/projects/:projectId/sprints/:sprintId',
|
|
217
|
-
start: 'POST /api/projects/:projectId/sprints/:sprintId/start',
|
|
218
|
-
complete: 'POST /api/projects/:projectId/sprints/:sprintId/complete',
|
|
219
|
-
cancel: 'POST /api/projects/:projectId/sprints/:sprintId/cancel',
|
|
220
|
-
delete: 'DELETE /api/projects/:projectId/sprints/:sprintId',
|
|
221
|
-
active: 'GET /api/projects/:projectId/sprints/active',
|
|
222
|
-
tickets: 'GET /api/projects/:projectId/sprints/:sprintId/tickets',
|
|
223
|
-
addTickets: 'POST /api/projects/:projectId/sprints/:sprintId/tickets',
|
|
224
|
-
removeTicket: 'DELETE /api/projects/:projectId/sprints/:sprintId/tickets/:ticketId',
|
|
225
|
-
reorderTickets: 'PUT /api/projects/:projectId/sprints/:sprintId/tickets/reorder',
|
|
226
|
-
},
|
|
227
|
-
},
|
|
441
|
+
mode,
|
|
442
|
+
capabilities: getCapabilities(),
|
|
443
|
+
endpoints,
|
|
228
444
|
});
|
|
229
445
|
});
|
|
230
446
|
// Task statistics
|
|
@@ -395,9 +611,10 @@ app.get('/api/events', (c) => {
|
|
|
395
611
|
});
|
|
396
612
|
});
|
|
397
613
|
// === Protected Gateway Endpoint ===
|
|
398
|
-
app.post('/api/gateway/execute-secure', tokenAuthMiddleware(['gateway:execute']), async (c) => {
|
|
614
|
+
app.post('/api/gateway/execute-secure', tokenAuthMiddleware(['gateway:execute']), validateBody(GatewayExecuteSchema), async (c) => {
|
|
399
615
|
try {
|
|
400
|
-
|
|
616
|
+
// validateBody middleware stores the parsed result; cast via raw context map
|
|
617
|
+
const body = c.get('validatedBody');
|
|
401
618
|
const { getGateway } = await import('./gateway/index.js');
|
|
402
619
|
const gateway = getGateway();
|
|
403
620
|
const request = {
|
|
@@ -436,6 +653,7 @@ app.post('/api/plugins/:id/toggle', async (c) => {
|
|
|
436
653
|
return c.redirect(`/api/settings/plugins/${id}/toggle`, 307);
|
|
437
654
|
});
|
|
438
655
|
// === Server Startup ===
|
|
656
|
+
const MAX_PORT_ATTEMPTS = 3;
|
|
439
657
|
function checkPortAvailable(port) {
|
|
440
658
|
return new Promise((resolve) => {
|
|
441
659
|
const server = createServer();
|
|
@@ -449,6 +667,16 @@ function checkPortAvailable(port) {
|
|
|
449
667
|
server.listen(port, '127.0.0.1');
|
|
450
668
|
});
|
|
451
669
|
}
|
|
670
|
+
async function resolvePort(startPort) {
|
|
671
|
+
for (let offset = 0; offset < MAX_PORT_ATTEMPTS; offset++) {
|
|
672
|
+
const candidate = startPort + offset;
|
|
673
|
+
const available = await checkPortAvailable(candidate);
|
|
674
|
+
if (available) {
|
|
675
|
+
return candidate;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
return null;
|
|
679
|
+
}
|
|
452
680
|
async function main() {
|
|
453
681
|
// Only start IF we are the main module actually being executed
|
|
454
682
|
const isMain = process.argv[1]?.endsWith('server.ts') || process.argv[1]?.endsWith('server.js');
|
|
@@ -457,15 +685,18 @@ async function main() {
|
|
|
457
685
|
}
|
|
458
686
|
const mode = getMode();
|
|
459
687
|
const appSettings = getAppSettings();
|
|
460
|
-
const
|
|
688
|
+
const configuredPort = getPort();
|
|
461
689
|
const ENABLE_CRON = isCronEnabled();
|
|
462
690
|
appLog.info('profClaw starting', { version: VERSION, mode: getModeLabel() });
|
|
463
|
-
//
|
|
464
|
-
const
|
|
465
|
-
if (
|
|
466
|
-
appLog.error(`
|
|
691
|
+
// Bind-or-retry: find an available port
|
|
692
|
+
const PORT = await resolvePort(configuredPort);
|
|
693
|
+
if (PORT === null) {
|
|
694
|
+
appLog.error(`All ports ${configuredPort}–${configuredPort + MAX_PORT_ATTEMPTS - 1} are in use`, new Error(`EADDRINUSE: ports ${configuredPort}-${configuredPort + MAX_PORT_ATTEMPTS - 1}`));
|
|
467
695
|
process.exit(1);
|
|
468
696
|
}
|
|
697
|
+
if (PORT !== configuredPort) {
|
|
698
|
+
appLog.warn(`Port ${configuredPort} in use, started on port ${PORT}`);
|
|
699
|
+
}
|
|
469
700
|
// Validate environment variables (fail fast if critical vars missing)
|
|
470
701
|
const envResult = validateEnvironment({
|
|
471
702
|
exitOnError: process.env.NODE_ENV === 'production',
|
|
@@ -545,6 +776,15 @@ async function main() {
|
|
|
545
776
|
appLog.error('Failed to initialize task queue', error instanceof Error ? error : new Error(String(error)));
|
|
546
777
|
appLog.warn('Tasks will not be processed');
|
|
547
778
|
}
|
|
779
|
+
// Register SSE broadcaster for agent summary updates
|
|
780
|
+
try {
|
|
781
|
+
const { getAgentSummaryTracker } = await import('./agents/agent-summary.js');
|
|
782
|
+
getAgentSummaryTracker().registerSSEBroadcaster(broadcastEvent);
|
|
783
|
+
appLog.info('Agent summary SSE broadcaster registered');
|
|
784
|
+
}
|
|
785
|
+
catch (error) {
|
|
786
|
+
appLog.error('Failed to register agent summary broadcaster', error instanceof Error ? error : new Error(String(error)));
|
|
787
|
+
}
|
|
548
788
|
// Initialize sync engine (pro mode or if explicitly configured)
|
|
549
789
|
if (hasCapability('sync_engine')) {
|
|
550
790
|
try {
|
|
@@ -561,12 +801,10 @@ async function main() {
|
|
|
561
801
|
appLog.warn('Sync engine initialization failed', { error: error instanceof Error ? error.message : String(error) });
|
|
562
802
|
}
|
|
563
803
|
}
|
|
564
|
-
// Initialize cost tracking (
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
appLog.info('Token tracker initialized');
|
|
569
|
-
}
|
|
804
|
+
// Initialize cost tracking (all modes — lightweight, needed for /api/costs)
|
|
805
|
+
const { initTokenTracker } = await import('./costs/token-tracker.js');
|
|
806
|
+
initTokenTracker();
|
|
807
|
+
appLog.info('Token tracker initialized');
|
|
570
808
|
// Initialize agents from config
|
|
571
809
|
const { getAgentRegistry } = await import('./adapters/registry.js');
|
|
572
810
|
const registry = getAgentRegistry();
|
|
@@ -605,11 +843,13 @@ async function main() {
|
|
|
605
843
|
}
|
|
606
844
|
}
|
|
607
845
|
}
|
|
608
|
-
// Auto-discover agents if enabled
|
|
609
|
-
|
|
846
|
+
// Auto-discover agents if enabled
|
|
847
|
+
// Pico mode: only auto-discover Ollama (cheap fetch, essential for pico users)
|
|
848
|
+
// Mini/Pro: full auto-discovery (Claude Code CLI check, etc.)
|
|
849
|
+
const autoDiscover = mode === 'pico' ? true : (process.env.AUTO_DISCOVER_AGENTS !== undefined
|
|
610
850
|
? process.env.AUTO_DISCOVER_AGENTS === 'true'
|
|
611
851
|
: (appSettings.agents?.autoDiscover ?? false));
|
|
612
|
-
if (autoDiscover && !registry.getActiveAdapters().some(a => a.type === 'claude-code')) {
|
|
852
|
+
if (autoDiscover && mode !== 'pico' && !registry.getActiveAdapters().some(a => a.type === 'claude-code')) {
|
|
613
853
|
try {
|
|
614
854
|
const { execSync } = await import('child_process');
|
|
615
855
|
execSync('which claude', { stdio: 'ignore' });
|
|
@@ -655,6 +895,32 @@ async function main() {
|
|
|
655
895
|
// Ollama not running
|
|
656
896
|
}
|
|
657
897
|
}
|
|
898
|
+
// WS-1.2: Provider validation — warn (or exit in strict mode) when no AI adapters are configured
|
|
899
|
+
if (registry.getActiveAdapters().length === 0) {
|
|
900
|
+
appLog.warn('No AI providers configured. Run `profclaw setup` or set ANTHROPIC_API_KEY / OPENAI_API_KEY');
|
|
901
|
+
if (process.env.PROFCLAW_STRICT_MODE === 'true') {
|
|
902
|
+
appLog.error('Strict mode enabled: refusing to start without an AI provider');
|
|
903
|
+
process.exit(1);
|
|
904
|
+
}
|
|
905
|
+
degradedMode = true;
|
|
906
|
+
}
|
|
907
|
+
// WS-1.4: In-memory storage — warn on startup and remind every 60 seconds
|
|
908
|
+
{
|
|
909
|
+
const { isStorageInMemory } = await import('./storage/index.js');
|
|
910
|
+
if (isStorageInMemory()) {
|
|
911
|
+
degradedMode = true;
|
|
912
|
+
appLog.warn('╔══════════════════════════════════════════════════════════╗');
|
|
913
|
+
appLog.warn('║ WARNING: Running with in-memory storage (ephemeral) ║');
|
|
914
|
+
appLog.warn('║ All data will be lost when the server restarts. ║');
|
|
915
|
+
appLog.warn('║ Set STORAGE_TIER=local or DATABASE_URL to persist data. ║');
|
|
916
|
+
appLog.warn('╚══════════════════════════════════════════════════════════╝');
|
|
917
|
+
inMemoryStorageReminderInterval = setInterval(() => {
|
|
918
|
+
appLog.warn('Reminder: server is running with ephemeral in-memory storage — data will not survive a restart');
|
|
919
|
+
}, 60_000);
|
|
920
|
+
// Allow Node to exit cleanly without waiting for this interval
|
|
921
|
+
inMemoryStorageReminderInterval.unref();
|
|
922
|
+
}
|
|
923
|
+
}
|
|
658
924
|
// Start cron jobs (if mode supports it and enabled)
|
|
659
925
|
if (ENABLE_CRON && hasCapability('cron')) {
|
|
660
926
|
const { startAllCronJobs } = await import('./cron/index.js');
|
|
@@ -714,6 +980,10 @@ async function main() {
|
|
|
714
980
|
fetch: app.fetch,
|
|
715
981
|
port: PORT,
|
|
716
982
|
});
|
|
983
|
+
// Record runtime port for health endpoint
|
|
984
|
+
runningPort = PORT;
|
|
985
|
+
// Record PID so the CLI can detect/kill existing instances
|
|
986
|
+
writePidFile(PORT);
|
|
717
987
|
appLog.info(`profClaw ready on http://localhost:${PORT}`, { port: PORT, mode: getModeLabel() });
|
|
718
988
|
if (ENABLE_CRON) {
|
|
719
989
|
const heartbeatMs = parseInt(process.env.POLL_INTERVAL_HEARTBEAT || '30000', 10);
|
|
@@ -784,7 +1054,12 @@ async function gracefulShutdown(signal) {
|
|
|
784
1054
|
sseConnections.clear();
|
|
785
1055
|
clearInterval(sseCleanupInterval);
|
|
786
1056
|
appLog.info('SSE connections closed');
|
|
787
|
-
// 2. Stop
|
|
1057
|
+
// 2. Stop in-memory storage reminder interval
|
|
1058
|
+
if (inMemoryStorageReminderInterval !== null) {
|
|
1059
|
+
clearInterval(inMemoryStorageReminderInterval);
|
|
1060
|
+
inMemoryStorageReminderInterval = null;
|
|
1061
|
+
}
|
|
1062
|
+
// 3. Stop cron jobs
|
|
788
1063
|
try {
|
|
789
1064
|
const { stopAllCronJobs } = await import('./cron/index.js');
|
|
790
1065
|
stopAllCronJobs();
|
|
@@ -793,7 +1068,7 @@ async function gracefulShutdown(signal) {
|
|
|
793
1068
|
catch {
|
|
794
1069
|
// Cron may not be initialized
|
|
795
1070
|
}
|
|
796
|
-
//
|
|
1071
|
+
// 4. Drain task queue
|
|
797
1072
|
try {
|
|
798
1073
|
const { closeQueue } = await import('./queue/index.js');
|
|
799
1074
|
await closeQueue();
|
|
@@ -802,7 +1077,7 @@ async function gracefulShutdown(signal) {
|
|
|
802
1077
|
catch {
|
|
803
1078
|
// Queue may not be initialized
|
|
804
1079
|
}
|
|
805
|
-
//
|
|
1080
|
+
// 5. Destroy HTTP rate limiters
|
|
806
1081
|
try {
|
|
807
1082
|
apiRateLimiter.destroy();
|
|
808
1083
|
authRateLimiter.destroy();
|
|
@@ -811,6 +1086,8 @@ async function gracefulShutdown(signal) {
|
|
|
811
1086
|
catch {
|
|
812
1087
|
// Rate limiters may not be initialized
|
|
813
1088
|
}
|
|
1089
|
+
// Remove PID file so new instances know the slot is free
|
|
1090
|
+
removePidFile();
|
|
814
1091
|
appLog.info('Clean exit');
|
|
815
1092
|
clearTimeout(forceExitTimer);
|
|
816
1093
|
process.exit(0);
|
|
@@ -819,12 +1096,15 @@ process.on('SIGINT', () => { gracefulShutdown('SIGINT'); });
|
|
|
819
1096
|
process.on('SIGTERM', () => { gracefulShutdown('SIGTERM'); });
|
|
820
1097
|
// Catch unhandled errors to log before crashing
|
|
821
1098
|
process.on('uncaughtException', (err) => {
|
|
1099
|
+
lastErrorMessage = err.message;
|
|
822
1100
|
appLog.error('Uncaught exception', err);
|
|
823
1101
|
// Let the process crash (daemon/serve will restart it)
|
|
824
1102
|
process.exit(1);
|
|
825
1103
|
});
|
|
826
1104
|
process.on('unhandledRejection', (reason) => {
|
|
827
|
-
|
|
1105
|
+
const errMsg = reason instanceof Error ? reason.message : String(reason);
|
|
1106
|
+
lastErrorMessage = errMsg;
|
|
1107
|
+
appLog.error('Unhandled rejection', reason instanceof Error ? reason : new Error(errMsg));
|
|
828
1108
|
// Don't exit for unhandled rejections - log and continue
|
|
829
1109
|
// This prevents a single failed promise from taking down the server
|
|
830
1110
|
});
|