@theowlops/boombot 0.0.0-stage → 1.1.1
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 +69 -3
- package/dist/LICENSE +21 -0
- package/dist/bin/boombot.js +16 -0
- package/dist/cli/api-key.js +123 -0
- package/dist/cli/bin-bun.js +17 -0
- package/dist/cli/bin.js +34 -0
- package/dist/cli/cli/cli-error.js +61 -0
- package/dist/cli/cli/dispatch.js +138 -0
- package/dist/cli/cli/help.js +83 -0
- package/dist/cli/cli/parser.js +89 -0
- package/dist/cli/cli/registry.js +185 -0
- package/dist/cli/cli/spec.js +41 -0
- package/dist/cli/cli/suggest.js +125 -0
- package/dist/cli/cli/version.js +30 -0
- package/dist/cli/cli/vocabulary.js +87 -0
- package/dist/cli/commands/doctor.js +365 -0
- package/dist/cli/commands/gateway.js +290 -0
- package/dist/cli/commands/migrate.js +270 -0
- package/dist/cli/commands/plugin.js +97 -0
- package/dist/cli/commands/setup.js +244 -0
- package/dist/cli/config-file.js +133 -0
- package/dist/cli/master-key.js +78 -0
- package/dist/cli/open-browser.js +59 -0
- package/dist/cli/web-assets.js +58 -0
- package/dist/gateway/auth.js +46 -0
- package/dist/gateway/config-format.js +202 -0
- package/dist/gateway/config.js +427 -0
- package/dist/gateway/context.js +1 -0
- package/dist/gateway/env-file.js +129 -0
- package/dist/gateway/events.js +172 -0
- package/dist/gateway/guard/tagging.js +505 -0
- package/dist/gateway/handlers/index.js +66 -0
- package/dist/gateway/handlers/memory.js +54 -0
- package/dist/gateway/handlers/providers.js +73 -0
- package/dist/gateway/handlers/registry.js +39 -0
- package/dist/gateway/handlers/sessions.js +188 -0
- package/dist/gateway/handlers/system.js +12 -0
- package/dist/gateway/handlers/tasks.js +191 -0
- package/dist/gateway/handlers/turn.js +100 -0
- package/dist/gateway/index.js +126 -0
- package/dist/gateway/main.js +23 -0
- package/dist/gateway/server.js +354 -0
- package/dist/gateway/services/context-limits.js +75 -0
- package/dist/gateway/services/history-normalizer.js +42 -0
- package/dist/gateway/services/image-pipeline.js +56 -0
- package/dist/gateway/services/key-roulette-client.js +43 -0
- package/dist/gateway/services/llm-client.js +593 -0
- package/dist/gateway/services/llm-decoder.js +200 -0
- package/dist/gateway/services/local-fallback.js +135 -0
- package/dist/gateway/services/memory.js +511 -0
- package/dist/gateway/services/persistent-memory.js +196 -0
- package/dist/gateway/services/providers.js +44 -0
- package/dist/gateway/services/tasks.js +237 -0
- package/dist/gateway/services/verification.js +124 -0
- package/dist/gateway/services/vision-payload.js +48 -0
- package/dist/gateway/services.js +257 -0
- package/dist/gateway/session-snapshot.js +19 -0
- package/dist/gateway/sessions.js +701 -0
- package/dist/gateway/static.js +239 -0
- package/dist/gateway/turns/clarify-tool.js +57 -0
- package/dist/gateway/turns/cronjob-tool.js +96 -0
- package/dist/gateway/turns/deferred-tools.js +154 -0
- package/dist/gateway/turns/execute-code-tool.js +84 -0
- package/dist/gateway/turns/file-tool-defs.js +178 -0
- package/dist/gateway/turns/file-tool-ops.js +178 -0
- package/dist/gateway/turns/file-tool-scan.js +182 -0
- package/dist/gateway/turns/file-tool.js +302 -0
- package/dist/gateway/turns/index.js +416 -0
- package/dist/gateway/turns/jev-middleware.js +91 -0
- package/dist/gateway/turns/local-reply.js +156 -0
- package/dist/gateway/turns/mcp-client.js +643 -0
- package/dist/gateway/turns/memory-tool.js +171 -0
- package/dist/gateway/turns/middleware.js +96 -0
- package/dist/gateway/turns/python-core.js +397 -0
- package/dist/gateway/turns/routing/index.js +3 -0
- package/dist/gateway/turns/routing/intent-classifier.js +60 -0
- package/dist/gateway/turns/routing/planner-adapter.js +91 -0
- package/dist/gateway/turns/routing/types.js +1 -0
- package/dist/gateway/turns/runtime-deps.js +228 -0
- package/dist/gateway/turns/self-config-tool.js +277 -0
- package/dist/gateway/turns/service-types.js +11 -0
- package/dist/gateway/turns/service.js +820 -0
- package/dist/gateway/turns/session-adapter.js +158 -0
- package/dist/gateway/turns/session-tool.js +49 -0
- package/dist/gateway/turns/shell-tool.js +190 -0
- package/dist/gateway/turns/skill-tool.js +239 -0
- package/dist/gateway/turns/spillover.js +201 -0
- package/dist/gateway/turns/subagent-tool.js +140 -0
- package/dist/gateway/turns/system-prompt.js +121 -0
- package/dist/gateway/turns/tool-schema.js +90 -0
- package/dist/gateway/turns/turn-utils.js +58 -0
- package/dist/gateway/turns/vision-tool.js +120 -0
- package/dist/gateway/types.js +1 -0
- package/dist/native/win32-x64/boombot-native-host.exe +0 -0
- package/dist/node_modules/@boombot/channels/handlers.d.ts +9 -0
- package/dist/node_modules/@boombot/channels/handlers.js +109 -0
- package/dist/node_modules/@boombot/channels/index.d.ts +12 -0
- package/dist/node_modules/@boombot/channels/index.js +12 -0
- package/dist/node_modules/@boombot/channels/package.json +9 -0
- package/dist/node_modules/@boombot/channels/service.d.ts +62 -0
- package/dist/node_modules/@boombot/channels/service.js +608 -0
- package/dist/node_modules/@boombot/channels/tool.d.ts +10 -0
- package/dist/node_modules/@boombot/channels/tool.js +46 -0
- package/dist/node_modules/@boombot/channels/transports/common/dedup.d.ts +17 -0
- package/dist/node_modules/@boombot/channels/transports/common/dedup.js +42 -0
- package/dist/node_modules/@boombot/channels/transports/common/handoff.d.ts +19 -0
- package/dist/node_modules/@boombot/channels/transports/common/handoff.js +32 -0
- package/dist/node_modules/@boombot/channels/transports/common/index.d.ts +3 -0
- package/dist/node_modules/@boombot/channels/transports/common/index.js +3 -0
- package/dist/node_modules/@boombot/channels/transports/common/types.d.ts +99 -0
- package/dist/node_modules/@boombot/channels/transports/common/types.js +54 -0
- package/dist/node_modules/@boombot/channels/transports/common.d.ts +2 -0
- package/dist/node_modules/@boombot/channels/transports/common.js +2 -0
- package/dist/node_modules/@boombot/channels/transports/dedup/index.d.ts +1 -0
- package/dist/node_modules/@boombot/channels/transports/dedup/index.js +1 -0
- package/dist/node_modules/@boombot/channels/transports/dedup.d.ts +2 -0
- package/dist/node_modules/@boombot/channels/transports/dedup.js +2 -0
- package/dist/node_modules/@boombot/channels/transports/discord/index.d.ts +1 -0
- package/dist/node_modules/@boombot/channels/transports/discord/index.js +1 -0
- package/dist/node_modules/@boombot/channels/transports/discord/transport.d.ts +36 -0
- package/dist/node_modules/@boombot/channels/transports/discord/transport.js +199 -0
- package/dist/node_modules/@boombot/channels/transports/discord.d.ts +2 -0
- package/dist/node_modules/@boombot/channels/transports/discord.js +2 -0
- package/dist/node_modules/@boombot/channels/transports/handoff.d.ts +2 -0
- package/dist/node_modules/@boombot/channels/transports/handoff.js +2 -0
- package/dist/node_modules/@boombot/channels/transports/messenger/index.d.ts +1 -0
- package/dist/node_modules/@boombot/channels/transports/messenger/index.js +1 -0
- package/dist/node_modules/@boombot/channels/transports/messenger/transport.d.ts +34 -0
- package/dist/node_modules/@boombot/channels/transports/messenger/transport.js +180 -0
- package/dist/node_modules/@boombot/channels/transports/messenger.d.ts +2 -0
- package/dist/node_modules/@boombot/channels/transports/messenger.js +2 -0
- package/dist/node_modules/@boombot/channels/transports/slack/index.d.ts +1 -0
- package/dist/node_modules/@boombot/channels/transports/slack/index.js +1 -0
- package/dist/node_modules/@boombot/channels/transports/slack/transport.d.ts +35 -0
- package/dist/node_modules/@boombot/channels/transports/slack/transport.js +183 -0
- package/dist/node_modules/@boombot/channels/transports/slack.d.ts +2 -0
- package/dist/node_modules/@boombot/channels/transports/slack.js +2 -0
- package/dist/node_modules/@boombot/channels/transports/streamer/index.d.ts +1 -0
- package/dist/node_modules/@boombot/channels/transports/streamer/index.js +1 -0
- package/dist/node_modules/@boombot/channels/transports/streamer/streamer.d.ts +31 -0
- package/dist/node_modules/@boombot/channels/transports/streamer/streamer.js +141 -0
- package/dist/node_modules/@boombot/channels/transports/streamer.d.ts +2 -0
- package/dist/node_modules/@boombot/channels/transports/streamer.js +2 -0
- package/dist/node_modules/@boombot/channels/transports/telegram/index.d.ts +1 -0
- package/dist/node_modules/@boombot/channels/transports/telegram/index.js +1 -0
- package/dist/node_modules/@boombot/channels/transports/telegram/transport.d.ts +75 -0
- package/dist/node_modules/@boombot/channels/transports/telegram/transport.js +395 -0
- package/dist/node_modules/@boombot/channels/transports/telegram.d.ts +2 -0
- package/dist/node_modules/@boombot/channels/transports/telegram.js +2 -0
- package/dist/node_modules/@boombot/channels/transports/tiktok/index.d.ts +1 -0
- package/dist/node_modules/@boombot/channels/transports/tiktok/index.js +1 -0
- package/dist/node_modules/@boombot/channels/transports/tiktok/transport.d.ts +27 -0
- package/dist/node_modules/@boombot/channels/transports/tiktok/transport.js +97 -0
- package/dist/node_modules/@boombot/channels/transports/tiktok.d.ts +2 -0
- package/dist/node_modules/@boombot/channels/transports/tiktok.js +2 -0
- package/dist/node_modules/@boombot/channels/transports/whatsapp/index.d.ts +1 -0
- package/dist/node_modules/@boombot/channels/transports/whatsapp/index.js +1 -0
- package/dist/node_modules/@boombot/channels/transports/whatsapp/transport.d.ts +38 -0
- package/dist/node_modules/@boombot/channels/transports/whatsapp/transport.js +189 -0
- package/dist/node_modules/@boombot/channels/transports/whatsapp.d.ts +2 -0
- package/dist/node_modules/@boombot/channels/transports/whatsapp.js +2 -0
- package/dist/node_modules/@boombot/channels/transports/zalo/index.d.ts +2 -0
- package/dist/node_modules/@boombot/channels/transports/zalo/index.js +2 -0
- package/dist/node_modules/@boombot/channels/transports/zalo/oa.d.ts +28 -0
- package/dist/node_modules/@boombot/channels/transports/zalo/oa.js +94 -0
- package/dist/node_modules/@boombot/channels/transports/zalo/personal.d.ts +106 -0
- package/dist/node_modules/@boombot/channels/transports/zalo/personal.js +171 -0
- package/dist/node_modules/@boombot/channels/transports/zalo-personal.d.ts +2 -0
- package/dist/node_modules/@boombot/channels/transports/zalo-personal.js +2 -0
- package/dist/node_modules/@boombot/channels/transports/zalo.d.ts +2 -0
- package/dist/node_modules/@boombot/channels/transports/zalo.js +2 -0
- package/dist/node_modules/@boombot/core/agent-core/index.d.ts +12 -0
- package/dist/node_modules/@boombot/core/agent-core/index.js +12 -0
- package/dist/node_modules/@boombot/core/agent-core/middleware.d.ts +60 -0
- package/dist/node_modules/@boombot/core/agent-core/middleware.js +152 -0
- package/dist/node_modules/@boombot/core/agent-core/routing.d.ts +128 -0
- package/dist/node_modules/@boombot/core/agent-core/routing.js +437 -0
- package/dist/node_modules/@boombot/core/agent-core/runtime-deps.d.ts +88 -0
- package/dist/node_modules/@boombot/core/agent-core/runtime-deps.js +8 -0
- package/dist/node_modules/@boombot/core/agent-core/tool-errors.d.ts +44 -0
- package/dist/node_modules/@boombot/core/agent-core/tool-errors.js +60 -0
- package/dist/node_modules/@boombot/core/agent-core/turn-loop.d.ts +21 -0
- package/dist/node_modules/@boombot/core/agent-core/turn-loop.js +561 -0
- package/dist/node_modules/@boombot/core/agent-core/types.d.ts +189 -0
- package/dist/node_modules/@boombot/core/agent-core/types.js +7 -0
- package/dist/node_modules/@boombot/core/compaction/compactor.d.ts +19 -0
- package/dist/node_modules/@boombot/core/compaction/compactor.js +127 -0
- package/dist/node_modules/@boombot/core/compaction/estimator.d.ts +10 -0
- package/dist/node_modules/@boombot/core/compaction/estimator.js +48 -0
- package/dist/node_modules/@boombot/core/compaction/index.d.ts +3 -0
- package/dist/node_modules/@boombot/core/compaction/index.js +3 -0
- package/dist/node_modules/@boombot/core/compaction/types.d.ts +31 -0
- package/dist/node_modules/@boombot/core/compaction/types.js +7 -0
- package/dist/node_modules/@boombot/core/index.d.ts +9 -0
- package/dist/node_modules/@boombot/core/index.js +9 -0
- package/dist/node_modules/@boombot/core/jev/index.d.ts +2 -0
- package/dist/node_modules/@boombot/core/jev/index.js +2 -0
- package/dist/node_modules/@boombot/core/jev/jev.d.ts +30 -0
- package/dist/node_modules/@boombot/core/jev/jev.js +164 -0
- package/dist/node_modules/@boombot/core/jev/types.d.ts +22 -0
- package/dist/node_modules/@boombot/core/jev/types.js +1 -0
- package/dist/node_modules/@boombot/core/memory/consolidation/budget.d.ts +63 -0
- package/dist/node_modules/@boombot/core/memory/consolidation/budget.js +94 -0
- package/dist/node_modules/@boombot/core/memory/consolidation/consolidator.d.ts +152 -0
- package/dist/node_modules/@boombot/core/memory/consolidation/consolidator.js +318 -0
- package/dist/node_modules/@boombot/core/memory/consolidation/index.d.ts +12 -0
- package/dist/node_modules/@boombot/core/memory/consolidation/index.js +12 -0
- package/dist/node_modules/@boombot/core/memory/curation.d.ts +86 -0
- package/dist/node_modules/@boombot/core/memory/curation.js +120 -0
- package/dist/node_modules/@boombot/core/memory/errors.d.ts +12 -0
- package/dist/node_modules/@boombot/core/memory/errors.js +20 -0
- package/dist/node_modules/@boombot/core/memory/hybrid-search.d.ts +29 -0
- package/dist/node_modules/@boombot/core/memory/hybrid-search.js +52 -0
- package/dist/node_modules/@boombot/core/memory/index.d.ts +23 -0
- package/dist/node_modules/@boombot/core/memory/index.js +23 -0
- package/dist/node_modules/@boombot/core/memory/indexer.d.ts +47 -0
- package/dist/node_modules/@boombot/core/memory/indexer.js +138 -0
- package/dist/node_modules/@boombot/core/memory/lifecycle.d.ts +12 -0
- package/dist/node_modules/@boombot/core/memory/lifecycle.js +66 -0
- package/dist/node_modules/@boombot/core/memory/provider.d.ts +84 -0
- package/dist/node_modules/@boombot/core/memory/provider.js +117 -0
- package/dist/node_modules/@boombot/core/memory/recall.d.ts +7 -0
- package/dist/node_modules/@boombot/core/memory/recall.js +111 -0
- package/dist/node_modules/@boombot/core/memory/scope-resolver.d.ts +27 -0
- package/dist/node_modules/@boombot/core/memory/scope-resolver.js +60 -0
- package/dist/node_modules/@boombot/core/memory/search.d.ts +37 -0
- package/dist/node_modules/@boombot/core/memory/search.js +158 -0
- package/dist/node_modules/@boombot/core/memory/store.d.ts +60 -0
- package/dist/node_modules/@boombot/core/memory/store.js +289 -0
- package/dist/node_modules/@boombot/core/memory/types.d.ts +121 -0
- package/dist/node_modules/@boombot/core/memory/types.js +21 -0
- package/dist/node_modules/@boombot/core/memory/vector-index.d.ts +34 -0
- package/dist/node_modules/@boombot/core/memory/vector-index.js +65 -0
- package/dist/node_modules/@boombot/core/package.json +9 -0
- package/dist/node_modules/@boombot/core/reflection/classifier.d.ts +13 -0
- package/dist/node_modules/@boombot/core/reflection/classifier.js +77 -0
- package/dist/node_modules/@boombot/core/reflection/index.d.ts +2 -0
- package/dist/node_modules/@boombot/core/reflection/index.js +2 -0
- package/dist/node_modules/@boombot/core/reflection/types.d.ts +13 -0
- package/dist/node_modules/@boombot/core/reflection/types.js +4 -0
- package/dist/node_modules/@boombot/core/research/config.d.ts +89 -0
- package/dist/node_modules/@boombot/core/research/config.js +206 -0
- package/dist/node_modules/@boombot/core/research/crawl.d.ts +36 -0
- package/dist/node_modules/@boombot/core/research/crawl.js +179 -0
- package/dist/node_modules/@boombot/core/research/factory.d.ts +26 -0
- package/dist/node_modules/@boombot/core/research/factory.js +46 -0
- package/dist/node_modules/@boombot/core/research/http.d.ts +87 -0
- package/dist/node_modules/@boombot/core/research/http.js +250 -0
- package/dist/node_modules/@boombot/core/research/index.d.ts +17 -0
- package/dist/node_modules/@boombot/core/research/index.js +10 -0
- package/dist/node_modules/@boombot/core/research/orchestrator.d.ts +113 -0
- package/dist/node_modules/@boombot/core/research/orchestrator.js +252 -0
- package/dist/node_modules/@boombot/core/research/parse/docx.d.ts +24 -0
- package/dist/node_modules/@boombot/core/research/parse/docx.js +183 -0
- package/dist/node_modules/@boombot/core/research/parse/guards.d.ts +89 -0
- package/dist/node_modules/@boombot/core/research/parse/guards.js +171 -0
- package/dist/node_modules/@boombot/core/research/parse/index.d.ts +68 -0
- package/dist/node_modules/@boombot/core/research/parse/index.js +219 -0
- package/dist/node_modules/@boombot/core/research/parse/pdf.d.ts +38 -0
- package/dist/node_modules/@boombot/core/research/parse/pdf.js +567 -0
- package/dist/node_modules/@boombot/core/research/providers/arxiv.d.ts +9 -0
- package/dist/node_modules/@boombot/core/research/providers/arxiv.js +72 -0
- package/dist/node_modules/@boombot/core/research/providers/brave.d.ts +11 -0
- package/dist/node_modules/@boombot/core/research/providers/brave.js +36 -0
- package/dist/node_modules/@boombot/core/research/providers/duckduckgo.d.ts +9 -0
- package/dist/node_modules/@boombot/core/research/providers/duckduckgo.js +78 -0
- package/dist/node_modules/@boombot/core/research/providers/exa.d.ts +11 -0
- package/dist/node_modules/@boombot/core/research/providers/exa.js +34 -0
- package/dist/node_modules/@boombot/core/research/providers/firecrawl.d.ts +16 -0
- package/dist/node_modules/@boombot/core/research/providers/firecrawl.js +47 -0
- package/dist/node_modules/@boombot/core/research/providers/index.d.ts +17 -0
- package/dist/node_modules/@boombot/core/research/providers/index.js +11 -0
- package/dist/node_modules/@boombot/core/research/providers/jina.d.ts +13 -0
- package/dist/node_modules/@boombot/core/research/providers/jina.js +114 -0
- package/dist/node_modules/@boombot/core/research/providers/openalex.d.ts +14 -0
- package/dist/node_modules/@boombot/core/research/providers/openalex.js +66 -0
- package/dist/node_modules/@boombot/core/research/providers/semantic-scholar.d.ts +9 -0
- package/dist/node_modules/@boombot/core/research/providers/semantic-scholar.js +56 -0
- package/dist/node_modules/@boombot/core/research/providers/tavily.d.ts +11 -0
- package/dist/node_modules/@boombot/core/research/providers/tavily.js +35 -0
- package/dist/node_modules/@boombot/core/research/providers/wikipedia.d.ts +12 -0
- package/dist/node_modules/@boombot/core/research/providers/wikipedia.js +47 -0
- package/dist/node_modules/@boombot/core/research/tools.d.ts +27 -0
- package/dist/node_modules/@boombot/core/research/tools.js +251 -0
- package/dist/node_modules/@boombot/core/research/types.d.ts +133 -0
- package/dist/node_modules/@boombot/core/research/types.js +8 -0
- package/dist/node_modules/@boombot/core/research/wiring.d.ts +25 -0
- package/dist/node_modules/@boombot/core/research/wiring.js +51 -0
- package/dist/node_modules/@boombot/core/scheduler/cron.d.ts +34 -0
- package/dist/node_modules/@boombot/core/scheduler/cron.js +147 -0
- package/dist/node_modules/@boombot/core/scheduler/evaluator.d.ts +42 -0
- package/dist/node_modules/@boombot/core/scheduler/evaluator.js +148 -0
- package/dist/node_modules/@boombot/core/scheduler/index.d.ts +15 -0
- package/dist/node_modules/@boombot/core/scheduler/index.js +15 -0
- package/dist/node_modules/@boombot/core/scheduler/registry.d.ts +81 -0
- package/dist/node_modules/@boombot/core/scheduler/registry.js +263 -0
- package/dist/node_modules/@boombot/core/scheduler/runner.d.ts +59 -0
- package/dist/node_modules/@boombot/core/scheduler/runner.js +257 -0
- package/dist/node_modules/@boombot/core/scheduler/store.d.ts +150 -0
- package/dist/node_modules/@boombot/core/scheduler/store.js +526 -0
- package/dist/node_modules/@boombot/core/scheduler/timezone.d.ts +35 -0
- package/dist/node_modules/@boombot/core/scheduler/timezone.js +96 -0
- package/dist/node_modules/@boombot/core/scheduler/types.d.ts +157 -0
- package/dist/node_modules/@boombot/core/scheduler/types.js +89 -0
- package/dist/node_modules/@boombot/core/task-runner/index.d.ts +7 -0
- package/dist/node_modules/@boombot/core/task-runner/index.js +5 -0
- package/dist/node_modules/@boombot/core/task-runner/planner.d.ts +37 -0
- package/dist/node_modules/@boombot/core/task-runner/planner.js +154 -0
- package/dist/node_modules/@boombot/core/task-runner/runner.d.ts +8 -0
- package/dist/node_modules/@boombot/core/task-runner/runner.js +564 -0
- package/dist/node_modules/@boombot/core/task-runner/store.d.ts +28 -0
- package/dist/node_modules/@boombot/core/task-runner/store.js +126 -0
- package/dist/node_modules/@boombot/core/task-runner/types.d.ts +138 -0
- package/dist/node_modules/@boombot/core/task-runner/types.js +11 -0
- package/dist/node_modules/@boombot/core/workflow/dag.d.ts +16 -0
- package/dist/node_modules/@boombot/core/workflow/dag.js +109 -0
- package/dist/node_modules/@boombot/core/workflow/engine.d.ts +34 -0
- package/dist/node_modules/@boombot/core/workflow/engine.js +304 -0
- package/dist/node_modules/@boombot/core/workflow/index.d.ts +3 -0
- package/dist/node_modules/@boombot/core/workflow/index.js +3 -0
- package/dist/node_modules/@boombot/core/workflow/types.d.ts +98 -0
- package/dist/node_modules/@boombot/core/workflow/types.js +21 -0
- package/dist/node_modules/@boombot/cronjobs/handlers.d.ts +11 -0
- package/dist/node_modules/@boombot/cronjobs/handlers.js +174 -0
- package/dist/node_modules/@boombot/cronjobs/index.d.ts +2 -0
- package/dist/node_modules/@boombot/cronjobs/index.js +2 -0
- package/dist/node_modules/@boombot/cronjobs/package.json +9 -0
- package/dist/node_modules/@boombot/cronjobs/service.d.ts +53 -0
- package/dist/node_modules/@boombot/cronjobs/service.js +200 -0
- package/dist/node_modules/@boombot/plugins/index.d.ts +2 -0
- package/dist/node_modules/@boombot/plugins/index.js +2 -0
- package/dist/node_modules/@boombot/plugins/installer.d.ts +45 -0
- package/dist/node_modules/@boombot/plugins/installer.js +487 -0
- package/dist/node_modules/@boombot/plugins/manager.d.ts +23 -0
- package/dist/node_modules/@boombot/plugins/manager.js +243 -0
- package/dist/node_modules/@boombot/plugins/package.json +9 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-channels.d.ts +125 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-channels.js +76 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-plugins.d.ts +220 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-plugins.js +60 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-scheduler.d.ts +33 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-scheduler.js +25 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/commands.d.ts +4349 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/commands.js +563 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/envelope.d.ts +32 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/envelope.js +61 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/errors.d.ts +13 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/errors.js +16 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/events.d.ts +567 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/events.js +395 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/index.d.ts +20 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/index.js +13 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/types.d.ts +91 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/types.js +7 -0
- package/dist/node_modules/@boombot/protocol/index.d.ts +4 -0
- package/dist/node_modules/@boombot/protocol/index.js +3 -0
- package/dist/node_modules/@boombot/protocol/package.json +9 -0
- package/dist/node_modules/@boombot/protocol/plugin-sdk/channel.d.ts +41 -0
- package/dist/node_modules/@boombot/protocol/plugin-sdk/channel.js +17 -0
- package/dist/node_modules/@boombot/protocol/plugin-sdk/index.d.ts +10 -0
- package/dist/node_modules/@boombot/protocol/plugin-sdk/index.js +10 -0
- package/dist/node_modules/@boombot/protocol/plugin-sdk/manifest.d.ts +58 -0
- package/dist/node_modules/@boombot/protocol/plugin-sdk/manifest.js +116 -0
- package/dist/node_modules/@boombot/protocol/plugin-sdk/plugin.d.ts +25 -0
- package/dist/node_modules/@boombot/protocol/plugin-sdk/plugin.js +1 -0
- package/dist/node_modules/@boombot/protocol/providers/cipher.d.ts +7 -0
- package/dist/node_modules/@boombot/protocol/providers/cipher.js +52 -0
- package/dist/node_modules/@boombot/protocol/providers/index.d.ts +19 -0
- package/dist/node_modules/@boombot/protocol/providers/index.js +19 -0
- package/dist/node_modules/@boombot/protocol/providers/key-roulette.d.ts +79 -0
- package/dist/node_modules/@boombot/protocol/providers/key-roulette.js +81 -0
- package/dist/node_modules/@boombot/protocol/providers/probe.d.ts +29 -0
- package/dist/node_modules/@boombot/protocol/providers/probe.js +96 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/anthropic.d.ts +11 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/anthropic.js +11 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/custom.d.ts +22 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/custom.js +222 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/deepseek.d.ts +7 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/deepseek.js +9 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/google.d.ts +10 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/google.js +10 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/index.d.ts +31 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/index.js +56 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/local.d.ts +10 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/local.js +11 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/mistral.d.ts +7 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/mistral.js +9 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/openai-compatible.d.ts +10 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/openai-compatible.js +10 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/openai.d.ts +7 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/openai.js +11 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/types.d.ts +58 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/types.js +1 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/xai.d.ts +7 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/xai.js +10 -0
- package/dist/node_modules/@boombot/protocol/providers/request-profile.d.ts +102 -0
- package/dist/node_modules/@boombot/protocol/providers/request-profile.js +118 -0
- package/dist/node_modules/@boombot/protocol/providers/service.d.ts +2 -0
- package/dist/node_modules/@boombot/protocol/providers/service.js +418 -0
- package/dist/node_modules/@boombot/protocol/providers/types.d.ts +141 -0
- package/dist/node_modules/@boombot/protocol/providers/types.js +16 -0
- package/dist/node_modules/@boombot/sandbox/fs-path.d.ts +13 -0
- package/dist/node_modules/@boombot/sandbox/fs-path.js +79 -0
- package/dist/node_modules/@boombot/sandbox/index.d.ts +13 -0
- package/dist/node_modules/@boombot/sandbox/index.js +13 -0
- package/dist/node_modules/@boombot/sandbox/native.d.ts +39 -0
- package/dist/node_modules/@boombot/sandbox/native.js +224 -0
- package/dist/node_modules/@boombot/sandbox/package.json +9 -0
- package/dist/node_modules/@boombot/sandbox/sandbox.d.ts +92 -0
- package/dist/node_modules/@boombot/sandbox/sandbox.js +229 -0
- package/dist/node_modules/@boombot/security/harness-guard/boundary.d.ts +22 -0
- package/dist/node_modules/@boombot/security/harness-guard/boundary.js +105 -0
- package/dist/node_modules/@boombot/security/harness-guard/gates.d.ts +90 -0
- package/dist/node_modules/@boombot/security/harness-guard/gates.js +791 -0
- package/dist/node_modules/@boombot/security/harness-guard/index.d.ts +23 -0
- package/dist/node_modules/@boombot/security/harness-guard/index.js +23 -0
- package/dist/node_modules/@boombot/security/harness-guard/policy.d.ts +20 -0
- package/dist/node_modules/@boombot/security/harness-guard/policy.js +70 -0
- package/dist/node_modules/@boombot/security/harness-guard/sanitizer.d.ts +53 -0
- package/dist/node_modules/@boombot/security/harness-guard/sanitizer.js +145 -0
- package/dist/node_modules/@boombot/security/harness-guard/types.d.ts +71 -0
- package/dist/node_modules/@boombot/security/harness-guard/types.js +10 -0
- package/dist/node_modules/@boombot/security/harness-guard/verifier.d.ts +59 -0
- package/dist/node_modules/@boombot/security/harness-guard/verifier.js +169 -0
- package/dist/node_modules/@boombot/security/index.d.ts +5 -0
- package/dist/node_modules/@boombot/security/index.js +9 -0
- package/dist/node_modules/@boombot/security/package.json +9 -0
- package/dist/node_modules/@boombot/security/policy/approval.d.ts +117 -0
- package/dist/node_modules/@boombot/security/policy/approval.js +223 -0
- package/dist/node_modules/@boombot/security/reasoning-guard/claim-extractor.d.ts +10 -0
- package/dist/node_modules/@boombot/security/reasoning-guard/claim-extractor.js +124 -0
- package/dist/node_modules/@boombot/security/reasoning-guard/evidence-ledger.d.ts +25 -0
- package/dist/node_modules/@boombot/security/reasoning-guard/evidence-ledger.js +70 -0
- package/dist/node_modules/@boombot/security/reasoning-guard/gate.d.ts +38 -0
- package/dist/node_modules/@boombot/security/reasoning-guard/gate.js +61 -0
- package/dist/node_modules/@boombot/security/reasoning-guard/grounding-check.d.ts +12 -0
- package/dist/node_modules/@boombot/security/reasoning-guard/grounding-check.js +234 -0
- package/dist/node_modules/@boombot/security/reasoning-guard/index.d.ts +17 -0
- package/dist/node_modules/@boombot/security/reasoning-guard/index.js +13 -0
- package/dist/node_modules/@boombot/security/reasoning-guard/policy.d.ts +15 -0
- package/dist/node_modules/@boombot/security/reasoning-guard/policy.js +39 -0
- package/dist/node_modules/@boombot/security/reasoning-guard/types.d.ts +46 -0
- package/dist/node_modules/@boombot/security/reasoning-guard/types.js +7 -0
- package/dist/node_modules/@boombot/security/security/approval.d.ts +63 -0
- package/dist/node_modules/@boombot/security/security/approval.js +140 -0
- package/dist/node_modules/@boombot/security/security/audit.d.ts +39 -0
- package/dist/node_modules/@boombot/security/security/audit.js +70 -0
- package/dist/node_modules/@boombot/security/security/index.d.ts +12 -0
- package/dist/node_modules/@boombot/security/security/index.js +12 -0
- package/dist/node_modules/@boombot/security/security/trust.d.ts +79 -0
- package/dist/node_modules/@boombot/security/security/trust.js +87 -0
- package/dist/package.json +5 -0
- package/dist/proto/agent_core.proto +56 -0
- package/dist/python/agent-core-python/pyproject.toml +40 -0
- package/dist/python/agent-core-python/src/boombot_agent/__init__.py +5 -0
- package/dist/python/agent-core-python/src/boombot_agent/generated/__init__.py +0 -0
- package/dist/python/agent-core-python/src/boombot_agent/generated/agent_core_pb2.py +54 -0
- package/dist/python/agent-core-python/src/boombot_agent/generated/agent_core_pb2_grpc.py +140 -0
- package/dist/python/agent-core-python/src/boombot_agent/rpc.py +265 -0
- package/dist/python/agent-core-python/src/boombot_agent/turn.py +577 -0
- package/dist/python/agent-core-python/src/boombot_ml/__init__.py +12 -0
- package/dist/python/agent-core-python/src/boombot_ml/embeddings.py +58 -0
- package/dist/python/agent-core-python/src/boombot_ml/training.py +156 -0
- package/dist/python/agent-core-python/uv.lock +2856 -0
- package/dist/web/assets/ChatView-BZrSD-64.js +262 -0
- package/dist/web/assets/ChatView-Cahbjez6.css +1 -0
- package/dist/web/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/dist/web/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/dist/web/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/dist/web/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/dist/web/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/dist/web/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/dist/web/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/dist/web/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/dist/web/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/dist/web/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/dist/web/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/dist/web/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/dist/web/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/dist/web/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/dist/web/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/dist/web/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/dist/web/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/dist/web/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/dist/web/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/dist/web/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/dist/web/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/dist/web/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/dist/web/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/dist/web/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/dist/web/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/dist/web/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/dist/web/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/dist/web/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/dist/web/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/dist/web/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/dist/web/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/dist/web/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/dist/web/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/dist/web/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/dist/web/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/dist/web/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/dist/web/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/dist/web/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/dist/web/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/dist/web/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/dist/web/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/dist/web/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/dist/web/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/dist/web/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/dist/web/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/dist/web/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/dist/web/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/dist/web/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/dist/web/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/dist/web/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/dist/web/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/dist/web/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/dist/web/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/dist/web/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/dist/web/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/dist/web/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/dist/web/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/dist/web/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/dist/web/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/dist/web/assets/MemoryView-BJbXqqs7.css +1 -0
- package/dist/web/assets/MemoryView-BfMq_6CD.js +1 -0
- package/dist/web/assets/SchedulesView-B22QmPR-.js +1 -0
- package/dist/web/assets/SchedulesView-C6XK7A8a.css +1 -0
- package/dist/web/assets/SessionsView-C1dqdhxI.css +1 -0
- package/dist/web/assets/SessionsView-DS0-aH2a.js +1 -0
- package/dist/web/assets/TasksView-Dtasg4hd.css +1 -0
- package/dist/web/assets/TasksView-ey0FLsW8.js +2 -0
- package/dist/web/assets/Textarea-DN3Zybl1.js +1 -0
- package/dist/web/assets/Tooltip-Bn2cqFPz.css +1 -0
- package/dist/web/assets/Tooltip-MzV2Gqnc.js +1 -0
- package/dist/web/assets/browser-Bnt73-Ub.js +8 -0
- package/dist/web/assets/index-BL8bi7Yt.css +1 -0
- package/dist/web/assets/index-CuPR73Tn.js +73 -0
- package/dist/web/favicon.svg +4 -0
- package/dist/web/index.html +26 -0
- package/dist/web/logo.jpg +0 -0
- package/dist/web/logo.png +0 -0
- package/package.json +51 -4
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { hashApiKey } from './auth.js';
|
|
3
|
+
import { coerceChannelSection, defaultConfigDir, defaultConfigPath, readConfigObject, resolveConfigFilePath, } from './config-format.js';
|
|
4
|
+
const DEFAULT_HOST = '127.0.0.1';
|
|
5
|
+
const DEFAULT_PORT = 8787;
|
|
6
|
+
/**
|
|
7
|
+
* Default config path (`~/.boombot/config.yml`). The real definition lives in
|
|
8
|
+
* `config-format.ts`, next to the file probe order; re-exported here so old
|
|
9
|
+
* call sites doing `import { defaultConfigPath } from './config.js'` don't break.
|
|
10
|
+
*/
|
|
11
|
+
export { defaultConfigPath };
|
|
12
|
+
/** Default data directory: `~/.boombot/data`. */
|
|
13
|
+
export function defaultDataDir(env = process.env) {
|
|
14
|
+
return join(defaultConfigDir(env), 'data');
|
|
15
|
+
}
|
|
16
|
+
function parsePort(raw) {
|
|
17
|
+
const n = typeof raw === 'number'
|
|
18
|
+
? raw
|
|
19
|
+
: typeof raw === 'string'
|
|
20
|
+
? Number.parseInt(raw, 10)
|
|
21
|
+
: Number.NaN;
|
|
22
|
+
if (!Number.isInteger(n) || n <= 0 || n > 65535)
|
|
23
|
+
return DEFAULT_PORT;
|
|
24
|
+
return n;
|
|
25
|
+
}
|
|
26
|
+
const CHANNEL_KINDS = [
|
|
27
|
+
'telegram',
|
|
28
|
+
'discord',
|
|
29
|
+
'slack',
|
|
30
|
+
'whatsapp',
|
|
31
|
+
'zalo',
|
|
32
|
+
'messenger',
|
|
33
|
+
'tiktok',
|
|
34
|
+
];
|
|
35
|
+
/**
|
|
36
|
+
* Reads the `channels` section.
|
|
37
|
+
*
|
|
38
|
+
* Rejects unknown channels: a misspelled channel name (`telego:`) used to be
|
|
39
|
+
* passed straight into the config, and the only symptom was that channel never
|
|
40
|
+
* running — no error, no warning. Each valid channel also goes through
|
|
41
|
+
* `coerceChannelSection` for YAML coercion and typo catching.
|
|
42
|
+
*/
|
|
43
|
+
function parseChannels(raw, configPath) {
|
|
44
|
+
if (raw === undefined)
|
|
45
|
+
return undefined;
|
|
46
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
|
|
47
|
+
throw new Error('Gateway config "channels" must be an object');
|
|
48
|
+
}
|
|
49
|
+
const channels = {};
|
|
50
|
+
for (const [kind, section] of Object.entries(raw)) {
|
|
51
|
+
if (!CHANNEL_KINDS.includes(kind)) {
|
|
52
|
+
throw new Error(`Gateway config at ${configPath}: unknown channel "${kind}" (expected one of ${CHANNEL_KINDS.join(', ')})`);
|
|
53
|
+
}
|
|
54
|
+
channels[kind] = coerceChannelSection(kind, section, configPath);
|
|
55
|
+
}
|
|
56
|
+
return channels;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* `ChannelConnectionConfig` fields loadable from the environment, per channel.
|
|
60
|
+
* Variable names derive from the rule `BOOMBOT_<KIND>_<FIELD_SNAKE>`, e.g.
|
|
61
|
+
* `BOOMBOT_SLACK_SIGNING_SECRET`, `BOOMBOT_MESSENGER_PAGE_ACCESS_TOKEN`.
|
|
62
|
+
*
|
|
63
|
+
* Deliberately excludes `enabled` and `allowlistChatIds`: they are structure,
|
|
64
|
+
* not secrets, and `allowlistChatIds` is an array — stuffing it into one env
|
|
65
|
+
* var would require inventing a string-splitting format, i.e. one more thing
|
|
66
|
+
* to mistype with no security gain.
|
|
67
|
+
*
|
|
68
|
+
* `accessToken` exists on both whatsapp and tiktok, so the channel prefix in
|
|
69
|
+
* the variable name is mandatory; without it the two channels would fight over
|
|
70
|
+
* one variable.
|
|
71
|
+
*/
|
|
72
|
+
const CHANNEL_ENV_FIELDS = [
|
|
73
|
+
['telegram', 'botToken'],
|
|
74
|
+
['discord', 'botToken'],
|
|
75
|
+
['discord', 'guildId'],
|
|
76
|
+
['discord', 'publicKey'],
|
|
77
|
+
['slack', 'botToken'],
|
|
78
|
+
['slack', 'signingSecret'],
|
|
79
|
+
['slack', 'appToken'],
|
|
80
|
+
['whatsapp', 'phoneNumberId'],
|
|
81
|
+
['whatsapp', 'accessToken'],
|
|
82
|
+
['whatsapp', 'verifyToken'],
|
|
83
|
+
['whatsapp', 'appSecret'],
|
|
84
|
+
['zalo', 'oaToken'],
|
|
85
|
+
['zalo', 'appId'],
|
|
86
|
+
['zalo', 'appSecret'],
|
|
87
|
+
['zalo', 'bridgeUrl'],
|
|
88
|
+
['messenger', 'pageAccessToken'],
|
|
89
|
+
['messenger', 'appSecret'],
|
|
90
|
+
['tiktok', 'appId'],
|
|
91
|
+
['tiktok', 'clientSecret'],
|
|
92
|
+
['tiktok', 'accessToken'],
|
|
93
|
+
];
|
|
94
|
+
function camelToSnake(field) {
|
|
95
|
+
return field.replace(/[A-Z]/g, (char) => `_${char.toLowerCase()}`);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Environment variable name for one field of one channel. Exported so
|
|
99
|
+
* `boombot setup` (writing) and `boombot doctor` (reading) share one rule —
|
|
100
|
+
* two places inventing their own names would drift apart sooner or later.
|
|
101
|
+
*/
|
|
102
|
+
export function channelEnvVar(kind, field) {
|
|
103
|
+
return `BOOMBOT_${kind.toUpperCase()}_${camelToSnake(field).toUpperCase()}`;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Assigns a dynamic-keyed field onto a channel section.
|
|
107
|
+
*
|
|
108
|
+
* `field` only comes from `CHANNEL_ENV_FIELDS`, where every value is a key
|
|
109
|
+
* that `ChannelConnectionConfig` declares as `string | undefined`, so the
|
|
110
|
+
* assignment is always semantically correct. TS can't verify dynamic keys, so
|
|
111
|
+
* it goes through `unknown` — the only place in this file that needs it. The
|
|
112
|
+
* section is copied before mutation so the caller's object is untouched.
|
|
113
|
+
*/
|
|
114
|
+
function setChannelField(channels, kind, field, value) {
|
|
115
|
+
const section = { ...channels[kind] };
|
|
116
|
+
section[field] = value;
|
|
117
|
+
return { ...channels, [kind]: section };
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Merges channel credentials from the environment over the file's `channels`.
|
|
121
|
+
* Environment values win; fields absent from the environment keep the file's
|
|
122
|
+
* values.
|
|
123
|
+
*
|
|
124
|
+
* A channel never mentioned in `config.json` is still created when the
|
|
125
|
+
* environment has a token — that's the point: `config.json` should never
|
|
126
|
+
* mention any secret. With nothing from the environment, the old `channels`
|
|
127
|
+
* object is returned as-is (identity preserved, no empty object created).
|
|
128
|
+
*/
|
|
129
|
+
function mergeChannelEnv(channels, get) {
|
|
130
|
+
let merged = { ...channels };
|
|
131
|
+
let touched = false;
|
|
132
|
+
for (const [kind, field] of CHANNEL_ENV_FIELDS) {
|
|
133
|
+
const value = get(channelEnvVar(kind, field));
|
|
134
|
+
if (value === undefined || value === '')
|
|
135
|
+
continue;
|
|
136
|
+
merged = setChannelField(merged, kind, field, value);
|
|
137
|
+
touched = true;
|
|
138
|
+
}
|
|
139
|
+
return touched ? merged : channels;
|
|
140
|
+
}
|
|
141
|
+
const GUARD_MODES = ['strict', 'warn', 'off'];
|
|
142
|
+
function parseGuardMode(raw, envValue, name) {
|
|
143
|
+
const fromEnv = typeof envValue === 'string' && envValue.length > 0 ? envValue : undefined;
|
|
144
|
+
const fromFile = typeof raw === 'string'
|
|
145
|
+
? raw
|
|
146
|
+
: typeof raw === 'object' && raw !== null && !Array.isArray(raw)
|
|
147
|
+
? raw.mode
|
|
148
|
+
: undefined;
|
|
149
|
+
const value = fromEnv ?? (typeof fromFile === 'string' ? fromFile : undefined) ?? 'warn';
|
|
150
|
+
if (!GUARD_MODES.includes(value)) {
|
|
151
|
+
throw new Error(`Gateway config "${name}" mode must be one of ${GUARD_MODES.join(', ')} (got ${JSON.stringify(value)})`);
|
|
152
|
+
}
|
|
153
|
+
return value;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Parse the `shell` section: `{ enabled?: boolean; projectRoot?: boolean }`.
|
|
157
|
+
* Env `BOOMBOT_SHELL_ENABLED=1`/`true` forces `enabled` on and
|
|
158
|
+
* `BOOMBOT_SHELL_PROJECT_ROOT=1`/`true` forces `projectRoot` on; anything
|
|
159
|
+
* else leaves the file value. Both default off — fail-closed.
|
|
160
|
+
*/
|
|
161
|
+
function parseShellConfig(raw, envEnabled, envProjectRoot, envBackend) {
|
|
162
|
+
const truthy = (value) => typeof value === 'string' && /^(1|true)$/i.test(value.trim());
|
|
163
|
+
let fromFileEnabled;
|
|
164
|
+
let fromFileProjectRoot;
|
|
165
|
+
let fromFileBackend;
|
|
166
|
+
if (typeof raw === 'object' && raw !== null && !Array.isArray(raw)) {
|
|
167
|
+
const section = raw;
|
|
168
|
+
if (typeof section.enabled === 'boolean')
|
|
169
|
+
fromFileEnabled = section.enabled;
|
|
170
|
+
else if (section.enabled !== undefined) {
|
|
171
|
+
throw new Error('Gateway config "shell.enabled" must be a boolean');
|
|
172
|
+
}
|
|
173
|
+
if (typeof section.projectRoot === 'boolean')
|
|
174
|
+
fromFileProjectRoot = section.projectRoot;
|
|
175
|
+
else if (section.projectRoot !== undefined) {
|
|
176
|
+
throw new Error('Gateway config "shell.projectRoot" must be a boolean');
|
|
177
|
+
}
|
|
178
|
+
fromFileBackend = section.backend;
|
|
179
|
+
}
|
|
180
|
+
else if (raw !== undefined) {
|
|
181
|
+
throw new Error('Gateway config "shell" must be an object');
|
|
182
|
+
}
|
|
183
|
+
const enabled = truthy(envEnabled) || fromFileEnabled === true;
|
|
184
|
+
const backend = envBackend ?? fromFileBackend ?? 'auto';
|
|
185
|
+
if (!['auto', 'native', 'subprocess'].includes(String(backend))) {
|
|
186
|
+
throw new Error('Gateway config "shell.backend" must be auto, native, or subprocess');
|
|
187
|
+
}
|
|
188
|
+
if (!enabled) {
|
|
189
|
+
// Fail-closed: without `enabled` there is no shell tool to point anywhere.
|
|
190
|
+
return undefined;
|
|
191
|
+
}
|
|
192
|
+
const projectRoot = truthy(envProjectRoot) || fromFileProjectRoot === true;
|
|
193
|
+
return {
|
|
194
|
+
enabled: true,
|
|
195
|
+
...(projectRoot ? { projectRoot: true } : {}),
|
|
196
|
+
...(backend === 'auto' ? {} : { backend: backend }),
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Parse the `files` section: `{ root?: string }`. Env `BOOMBOT_FILES_ROOT`
|
|
201
|
+
* wins over the file value. The path is deliberately left unresolved here:
|
|
202
|
+
* a relative value has to be relative to the directory the gateway was
|
|
203
|
+
* started in, not to the config file, so `registerTurnTools` resolves it.
|
|
204
|
+
*/
|
|
205
|
+
function parseFilesConfig(raw, envRoot) {
|
|
206
|
+
let fromFile;
|
|
207
|
+
if (typeof raw === 'object' && raw !== null && !Array.isArray(raw)) {
|
|
208
|
+
const root = raw.root;
|
|
209
|
+
if (typeof root === 'string' && root.trim() !== '')
|
|
210
|
+
fromFile = root;
|
|
211
|
+
else if (root !== undefined) {
|
|
212
|
+
throw new Error('Gateway config "files.root" must be a non-empty string');
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
else if (raw !== undefined) {
|
|
216
|
+
throw new Error('Gateway config "files" must be an object');
|
|
217
|
+
}
|
|
218
|
+
const root = envRoot ?? fromFile;
|
|
219
|
+
return root === undefined ? undefined : { root };
|
|
220
|
+
}
|
|
221
|
+
function parseMcpConfig(raw, env) {
|
|
222
|
+
if (raw === undefined)
|
|
223
|
+
return undefined;
|
|
224
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
|
|
225
|
+
throw new Error('Gateway config "mcp" must be an object');
|
|
226
|
+
}
|
|
227
|
+
const rawServers = raw.servers;
|
|
228
|
+
if (typeof rawServers !== 'object' || rawServers === null || Array.isArray(rawServers)) {
|
|
229
|
+
throw new Error('Gateway config "mcp.servers" must be an object');
|
|
230
|
+
}
|
|
231
|
+
const servers = {};
|
|
232
|
+
for (const [name, value] of Object.entries(rawServers)) {
|
|
233
|
+
if (!/^[a-z0-9][a-z0-9-]{0,31}$/.test(name)) {
|
|
234
|
+
throw new Error(`Gateway config MCP server name ${JSON.stringify(name)} is invalid`);
|
|
235
|
+
}
|
|
236
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
237
|
+
throw new Error(`Gateway config "mcp.servers.${name}" must be an object`);
|
|
238
|
+
}
|
|
239
|
+
const section = value;
|
|
240
|
+
if (section['command'] !== undefined &&
|
|
241
|
+
(typeof section['command'] !== 'string' || section['command'].trim() === '')) {
|
|
242
|
+
throw new Error(`Gateway config "mcp.servers.${name}.command" must be a non-empty string`);
|
|
243
|
+
}
|
|
244
|
+
const command = typeof section['command'] === 'string' && section['command'].trim() !== ''
|
|
245
|
+
? section['command'].trim()
|
|
246
|
+
: undefined;
|
|
247
|
+
let url;
|
|
248
|
+
if (typeof section['url'] === 'string' && section['url'].trim() !== '') {
|
|
249
|
+
try {
|
|
250
|
+
const parsed = new URL(section['url']);
|
|
251
|
+
if ((parsed.protocol !== 'http:' && parsed.protocol !== 'https:') ||
|
|
252
|
+
parsed.username !== '' || parsed.password !== '')
|
|
253
|
+
throw new Error();
|
|
254
|
+
url = parsed.toString();
|
|
255
|
+
}
|
|
256
|
+
catch {
|
|
257
|
+
throw new Error(`Gateway config "mcp.servers.${name}.url" must be an absolute HTTP(S) URL`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
else if (section['url'] !== undefined) {
|
|
261
|
+
throw new Error(`Gateway config "mcp.servers.${name}.url" must be an absolute HTTP(S) URL`);
|
|
262
|
+
}
|
|
263
|
+
if ((command === undefined) === (url === undefined)) {
|
|
264
|
+
throw new Error(`Gateway config "mcp.servers.${name}" must define exactly one of command or url`);
|
|
265
|
+
}
|
|
266
|
+
if (section['args'] !== undefined &&
|
|
267
|
+
(!Array.isArray(section['args']) || !section['args'].every((item) => typeof item === 'string'))) {
|
|
268
|
+
throw new Error(`Gateway config "mcp.servers.${name}.args" must be an array of strings`);
|
|
269
|
+
}
|
|
270
|
+
if (section['cwd'] !== undefined &&
|
|
271
|
+
(typeof section['cwd'] !== 'string' || section['cwd'].trim() === '')) {
|
|
272
|
+
throw new Error(`Gateway config "mcp.servers.${name}.cwd" must be a non-empty string`);
|
|
273
|
+
}
|
|
274
|
+
if (section['enabled'] !== undefined && typeof section['enabled'] !== 'boolean') {
|
|
275
|
+
throw new Error(`Gateway config "mcp.servers.${name}.enabled" must be a boolean`);
|
|
276
|
+
}
|
|
277
|
+
const rawEnv = section['env'];
|
|
278
|
+
if (rawEnv !== undefined &&
|
|
279
|
+
(typeof rawEnv !== 'object' || rawEnv === null || Array.isArray(rawEnv) ||
|
|
280
|
+
!Object.values(rawEnv).every((item) => typeof item === 'string'))) {
|
|
281
|
+
throw new Error(`Gateway config "mcp.servers.${name}.env" must contain only string values`);
|
|
282
|
+
}
|
|
283
|
+
const rawHeaders = section['headers'];
|
|
284
|
+
if (rawHeaders !== undefined &&
|
|
285
|
+
(typeof rawHeaders !== 'object' || rawHeaders === null || Array.isArray(rawHeaders) ||
|
|
286
|
+
!Object.values(rawHeaders).every((item) => typeof item === 'string'))) {
|
|
287
|
+
throw new Error(`Gateway config "mcp.servers.${name}.headers" must contain only string values`);
|
|
288
|
+
}
|
|
289
|
+
const enabled = typeof section['enabled'] === 'boolean' ? { enabled: section['enabled'] } : {};
|
|
290
|
+
if (command !== undefined) {
|
|
291
|
+
if (rawHeaders !== undefined) {
|
|
292
|
+
throw new Error(`Gateway config "mcp.servers.${name}.headers" requires url transport`);
|
|
293
|
+
}
|
|
294
|
+
servers[name] = {
|
|
295
|
+
command,
|
|
296
|
+
...(Array.isArray(section['args']) ? { args: section['args'] } : {}),
|
|
297
|
+
...(typeof section['cwd'] === 'string' ? { cwd: section['cwd'] } : {}),
|
|
298
|
+
...(rawEnv === undefined ? {} : { env: rawEnv }),
|
|
299
|
+
...enabled,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
if (section['args'] !== undefined || section['cwd'] !== undefined || rawEnv !== undefined) {
|
|
304
|
+
throw new Error(`Gateway config "mcp.servers.${name}" cannot mix url with stdio options`);
|
|
305
|
+
}
|
|
306
|
+
servers[name] = {
|
|
307
|
+
url: url,
|
|
308
|
+
...(rawHeaders === undefined ? {} : {
|
|
309
|
+
headers: Object.fromEntries(Object.entries(rawHeaders).map(([header, value]) => {
|
|
310
|
+
const variable = /^\$\{([A-Z_][A-Z0-9_]*)\}$/u.exec(value)?.[1];
|
|
311
|
+
if (variable === undefined)
|
|
312
|
+
return [header, value];
|
|
313
|
+
const resolved = env[variable];
|
|
314
|
+
if (resolved === undefined || resolved === '') {
|
|
315
|
+
throw new Error(`Gateway config "mcp.servers.${name}.headers.${header}" requires environment variable ${variable}`);
|
|
316
|
+
}
|
|
317
|
+
return [header, resolved];
|
|
318
|
+
})),
|
|
319
|
+
}),
|
|
320
|
+
...enabled,
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return { servers };
|
|
325
|
+
}
|
|
326
|
+
function parsePluginsConfig(raw) {
|
|
327
|
+
if (raw === undefined)
|
|
328
|
+
return undefined;
|
|
329
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
|
|
330
|
+
throw new Error('Gateway config "plugins" must be an object');
|
|
331
|
+
}
|
|
332
|
+
const section = raw;
|
|
333
|
+
if (section['root'] !== undefined &&
|
|
334
|
+
(typeof section['root'] !== 'string' || section['root'].trim() === '')) {
|
|
335
|
+
throw new Error('Gateway config "plugins.root" must be a non-empty string');
|
|
336
|
+
}
|
|
337
|
+
if (section['enabled'] !== undefined && typeof section['enabled'] !== 'boolean') {
|
|
338
|
+
throw new Error('Gateway config "plugins.enabled" must be a boolean');
|
|
339
|
+
}
|
|
340
|
+
if (section['config'] !== undefined &&
|
|
341
|
+
(typeof section['config'] !== 'object' || section['config'] === null || Array.isArray(section['config']))) {
|
|
342
|
+
throw new Error('Gateway config "plugins.config" must be an object');
|
|
343
|
+
}
|
|
344
|
+
return {
|
|
345
|
+
...(typeof section['root'] === 'string' ? { root: section['root'] } : {}),
|
|
346
|
+
...(typeof section['enabled'] === 'boolean' ? { enabled: section['enabled'] } : {}),
|
|
347
|
+
...(section['config'] === undefined
|
|
348
|
+
? {}
|
|
349
|
+
: { config: section['config'] }),
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Operator key: prefers the plaintext copy in the environment/`.env`, hashed
|
|
354
|
+
* immediately at boot. This lets `boombot setup` write the key to `.env` and
|
|
355
|
+
* the operator can still read it back, instead of only a sha256 as before —
|
|
356
|
+
* losing the only printed copy meant losing access forever.
|
|
357
|
+
*
|
|
358
|
+
* An empty or whitespace-only key in the environment counts as UNSET, falling
|
|
359
|
+
* back to the file's hash. Treating it as valid would hash the empty string —
|
|
360
|
+
* i.e. a key anyone can guess. Trimming stray whitespace is also deliberate:
|
|
361
|
+
* keys from `generateApiKey` are hex, so leading/trailing whitespace can only
|
|
362
|
+
* be a typing mistake in `.env`, and it fails silently in exactly the
|
|
363
|
+
* mysterious-401 way.
|
|
364
|
+
*/
|
|
365
|
+
function resolveApiKeyHash(fileHash, envKey) {
|
|
366
|
+
const fromFile = typeof fileHash === 'string' && fileHash.length > 0 ? fileHash : undefined;
|
|
367
|
+
const plain = envKey?.trim();
|
|
368
|
+
return plain !== undefined && plain !== '' ? hashApiKey(plain) : fromFile;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Load the gateway config. `env` defaults to `process.env`; tests inject a
|
|
372
|
+
* plain object. Never logs secret values.
|
|
373
|
+
*/
|
|
374
|
+
export function loadConfig(env = process.env) {
|
|
375
|
+
/*
|
|
376
|
+
* This function does NOT read the `.env` file: `loadEnvFile` (env-file.ts)
|
|
377
|
+
* loads it into `process.env` at boot, BEFORE config is read. That gives the
|
|
378
|
+
* "real env vars > `.env` > config.json" precedence for free, while this
|
|
379
|
+
* function stays pure over its `env` parameter — so tests injecting an object
|
|
380
|
+
* never have to worry about the real `.env` in the test machine's home.
|
|
381
|
+
*
|
|
382
|
+
* Empty strings count as unset, so `BOOMBOT_PORT=` can't silently disable
|
|
383
|
+
* the file's value.
|
|
384
|
+
*/
|
|
385
|
+
const get = (key) => {
|
|
386
|
+
const value = env[key];
|
|
387
|
+
return value !== undefined && value !== '' ? value : undefined;
|
|
388
|
+
};
|
|
389
|
+
/*
|
|
390
|
+
* These two values read `env` directly, NOT through `get()`: `$BOOMBOT_CONFIG`
|
|
391
|
+
* picks the config file itself, so letting it come from the `.env` file is a
|
|
392
|
+
* chicken-and-egg problem — you must know which file to read before you can
|
|
393
|
+
* read any file. Real environment variables are the only source for these two.
|
|
394
|
+
*/
|
|
395
|
+
const configPath = resolveConfigFilePath(env);
|
|
396
|
+
const file = readConfigObject(configPath);
|
|
397
|
+
const fileHost = typeof file.host === 'string' && file.host.length > 0 ? file.host : undefined;
|
|
398
|
+
const host = get('BOOMBOT_HOST') ?? fileHost ?? DEFAULT_HOST;
|
|
399
|
+
const port = parsePort(get('BOOMBOT_PORT') ?? file.port);
|
|
400
|
+
const fileDataDir = typeof file.dataDir === 'string' && file.dataDir.length > 0 ? file.dataDir : undefined;
|
|
401
|
+
const dataDir = get('BOOMBOT_DATA_DIR') ?? fileDataDir ?? defaultDataDir(env);
|
|
402
|
+
const apiKeyHash = resolveApiKeyHash(file.apiKeyHash, get('BOOMBOT_API_KEY'));
|
|
403
|
+
const channels = mergeChannelEnv(parseChannels(file.channels, configPath), get);
|
|
404
|
+
const reasoningGuard = {
|
|
405
|
+
mode: parseGuardMode(file.reasoningGuard, get('BOOMBOT_REASONING_GUARD_MODE'), 'reasoningGuard'),
|
|
406
|
+
};
|
|
407
|
+
const harnessGuard = {
|
|
408
|
+
mode: parseGuardMode(file.harnessGuard, get('BOOMBOT_GUARD_MODE'), 'harnessGuard'),
|
|
409
|
+
};
|
|
410
|
+
const shell = parseShellConfig(file.shell, get('BOOMBOT_SHELL_ENABLED'), get('BOOMBOT_SHELL_PROJECT_ROOT'), get('BOOMBOT_SHELL_BACKEND'));
|
|
411
|
+
const files = parseFilesConfig(file.files, get('BOOMBOT_FILES_ROOT'));
|
|
412
|
+
const mcp = parseMcpConfig(file.mcp, env);
|
|
413
|
+
const plugins = parsePluginsConfig(file.plugins);
|
|
414
|
+
return {
|
|
415
|
+
host,
|
|
416
|
+
port,
|
|
417
|
+
dataDir,
|
|
418
|
+
apiKeyHash,
|
|
419
|
+
channels,
|
|
420
|
+
reasoningGuard,
|
|
421
|
+
harnessGuard,
|
|
422
|
+
shell,
|
|
423
|
+
files,
|
|
424
|
+
mcp,
|
|
425
|
+
plugins,
|
|
426
|
+
};
|
|
427
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { defaultConfigDir } from './config-format.js';
|
|
4
|
+
/**
|
|
5
|
+
* BoomBot's `.env` config layer.
|
|
6
|
+
*
|
|
7
|
+
* Why this file exists: channel tokens (bot token, signing secret, access
|
|
8
|
+
* token, app secret) sit in plaintext next to everything else in `config.json` —
|
|
9
|
+
* exactly the file people copy to other machines, paste into issues, and
|
|
10
|
+
* commit by accident. Splitting them into their own file, mode 0600, easy to
|
|
11
|
+
* gitignore and easy to rotate, is the correct operational shape; `config.json`
|
|
12
|
+
* then only keeps non-secret config (host, port, dataDir, guard mode, channel
|
|
13
|
+
* `enabled`/`allowlistChatIds`).
|
|
14
|
+
*
|
|
15
|
+
* Stated plainly so nobody feels safe by mistake: `.env` encrypts **nothing**.
|
|
16
|
+
* It's a text file with tight permissions — anyone who can read the disk can
|
|
17
|
+
* read it just like `config.json`, and on Windows `chmod` is a no-op (see
|
|
18
|
+
* SECURITY.md §5). What's actually encrypted at rest is the provider key
|
|
19
|
+
* (`providers.json` + `master.key`); don't downgrade those to here.
|
|
20
|
+
*
|
|
21
|
+
* Value precedence, high → low:
|
|
22
|
+
* 1. the process's real environment variables (`BOOMBOT_*`);
|
|
23
|
+
* 2. the `.env` file;
|
|
24
|
+
* 3. `~/.boombot/config.json`.
|
|
25
|
+
*
|
|
26
|
+
* This module only READS the file; merging the precedence lives in `loadConfig`
|
|
27
|
+
* in `config.ts`, so that function stays pure and testable by injecting a fake
|
|
28
|
+
* `env` without touching the global `process.env`.
|
|
29
|
+
*/
|
|
30
|
+
/** Default env file location: `~/.boombot/.env` (or `$BOOMBOT_ENV_FILE`). */
|
|
31
|
+
export function defaultEnvPath() {
|
|
32
|
+
return join(defaultConfigDir(), '.env');
|
|
33
|
+
}
|
|
34
|
+
const KEY_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
35
|
+
const ESCAPES = {
|
|
36
|
+
n: '\n',
|
|
37
|
+
r: '\r',
|
|
38
|
+
t: '\t',
|
|
39
|
+
'"': '"',
|
|
40
|
+
'\\': '\\',
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* One `.env` line: `KEY=value`, optional `export` prefix, value may be wrapped
|
|
44
|
+
* in single or double quotes. Returns `null` for blank lines and comments.
|
|
45
|
+
*/
|
|
46
|
+
function parseEnvLine(line, lineNumber, path) {
|
|
47
|
+
const trimmed = line.trim();
|
|
48
|
+
if (trimmed === '' || trimmed.startsWith('#'))
|
|
49
|
+
return null;
|
|
50
|
+
const body = trimmed.startsWith('export ')
|
|
51
|
+
? trimmed.slice('export '.length).trim()
|
|
52
|
+
: trimmed;
|
|
53
|
+
const eq = body.indexOf('=');
|
|
54
|
+
if (eq === -1) {
|
|
55
|
+
throw new Error(`Env file at ${path}:${lineNumber} is not a KEY=value line`);
|
|
56
|
+
}
|
|
57
|
+
const key = body.slice(0, eq).trim();
|
|
58
|
+
if (!KEY_PATTERN.test(key)) {
|
|
59
|
+
throw new Error(`Env file at ${path}:${lineNumber} has an invalid key: ${JSON.stringify(key)}`);
|
|
60
|
+
}
|
|
61
|
+
return { key, value: unquote(body.slice(eq + 1).trim()) };
|
|
62
|
+
}
|
|
63
|
+
function unquote(rawValue) {
|
|
64
|
+
const quote = rawValue[0];
|
|
65
|
+
if ((quote === '"' || quote === "'") && rawValue.length >= 2 && rawValue.endsWith(quote)) {
|
|
66
|
+
const inner = rawValue.slice(1, -1);
|
|
67
|
+
// Single quotes are absolute literals (as in shell): no escape processing.
|
|
68
|
+
return quote === '"' ? inner.replace(/\\([nrt"\\])/g, (_, code) => ESCAPES[code] ?? code) : inner;
|
|
69
|
+
}
|
|
70
|
+
/*
|
|
71
|
+
* Unquoted: `#` only starts a comment after whitespace. Cutting at every `#`
|
|
72
|
+
* would truncate a secret containing that character, and what's lost is the
|
|
73
|
+
* token — failing silently, the hardest kind of bug to trace.
|
|
74
|
+
*/
|
|
75
|
+
const comment = rawValue.search(/\s#/);
|
|
76
|
+
return comment === -1 ? rawValue : rawValue.slice(0, comment).trimEnd();
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Reads a `.env` file into a flat map. A missing file is normal — returns `{}`
|
|
80
|
+
* because `.env` is optional. But a PRESENT file with a bad line throws with
|
|
81
|
+
* the line number, never silently skipped: a swallowed typo would surface as
|
|
82
|
+
* "channel not running" with an error message unrelated to the cause.
|
|
83
|
+
*
|
|
84
|
+
* Later lines override earlier ones with the same key. Real environment
|
|
85
|
+
* variables overriding the file is `loadConfig`'s job, not this module's.
|
|
86
|
+
*/
|
|
87
|
+
export function readEnvFile(path) {
|
|
88
|
+
let raw;
|
|
89
|
+
try {
|
|
90
|
+
raw = readFileSync(path, 'utf8');
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
if (error.code === 'ENOENT')
|
|
94
|
+
return {};
|
|
95
|
+
throw new Error(`Cannot read env file at ${path}: ${error.message}`);
|
|
96
|
+
}
|
|
97
|
+
const values = {};
|
|
98
|
+
const lines = raw.split(/\r?\n/);
|
|
99
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
100
|
+
const parsed = parseEnvLine(lines[index] ?? '', index + 1, path);
|
|
101
|
+
if (parsed)
|
|
102
|
+
values[parsed.key] = parsed.value;
|
|
103
|
+
}
|
|
104
|
+
return values;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Loads the `.env` file into `process.env`, setting only keys NOT already in
|
|
108
|
+
* the environment. That is the entire mechanism guaranteeing "real env vars
|
|
109
|
+
* beat `.env`" — no intermediate merging layer needed.
|
|
110
|
+
*
|
|
111
|
+
* Called exactly once at the top of `main.ts`, before any config read. Returns
|
|
112
|
+
* the number of keys set so the caller can log "loaded .env (n keys)" without
|
|
113
|
+
* re-reading the file; never log values.
|
|
114
|
+
*
|
|
115
|
+
* Writing straight into `process.env` is deliberate and is what dotenv does:
|
|
116
|
+
* everything downstream — `loadConfig`, each channel's transport, sub-services —
|
|
117
|
+
* just reads environment variables and never needs to know about this file.
|
|
118
|
+
*/
|
|
119
|
+
export function loadEnvFile(path = defaultEnvPath()) {
|
|
120
|
+
const values = readEnvFile(path);
|
|
121
|
+
let applied = 0;
|
|
122
|
+
for (const [key, value] of Object.entries(values)) {
|
|
123
|
+
if (process.env[key] !== undefined)
|
|
124
|
+
continue;
|
|
125
|
+
process.env[key] = value;
|
|
126
|
+
applied += 1;
|
|
127
|
+
}
|
|
128
|
+
return applied;
|
|
129
|
+
}
|