cowork-os 0.3.21
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/LICENSE +21 -0
- package/README.md +1638 -0
- package/bin/cowork.js +42 -0
- package/build/entitlements.mac.plist +16 -0
- package/build/icon.icns +0 -0
- package/build/icon.png +0 -0
- package/dist/electron/electron/activity/ActivityRepository.js +190 -0
- package/dist/electron/electron/agent/browser/browser-service.js +639 -0
- package/dist/electron/electron/agent/context-manager.js +225 -0
- package/dist/electron/electron/agent/custom-skill-loader.js +566 -0
- package/dist/electron/electron/agent/daemon.js +975 -0
- package/dist/electron/electron/agent/executor.js +3561 -0
- package/dist/electron/electron/agent/llm/anthropic-provider.js +155 -0
- package/dist/electron/electron/agent/llm/bedrock-provider.js +202 -0
- package/dist/electron/electron/agent/llm/gemini-provider.js +375 -0
- package/dist/electron/electron/agent/llm/index.js +34 -0
- package/dist/electron/electron/agent/llm/ollama-provider.js +263 -0
- package/dist/electron/electron/agent/llm/openai-oauth.js +101 -0
- package/dist/electron/electron/agent/llm/openai-provider.js +657 -0
- package/dist/electron/electron/agent/llm/openrouter-provider.js +232 -0
- package/dist/electron/electron/agent/llm/pricing.js +160 -0
- package/dist/electron/electron/agent/llm/provider-factory.js +880 -0
- package/dist/electron/electron/agent/llm/types.js +178 -0
- package/dist/electron/electron/agent/queue-manager.js +378 -0
- package/dist/electron/electron/agent/sandbox/docker-sandbox.js +402 -0
- package/dist/electron/electron/agent/sandbox/macos-sandbox.js +407 -0
- package/dist/electron/electron/agent/sandbox/runner.js +410 -0
- package/dist/electron/electron/agent/sandbox/sandbox-factory.js +228 -0
- package/dist/electron/electron/agent/sandbox/security-utils.js +258 -0
- package/dist/electron/electron/agent/search/brave-provider.js +119 -0
- package/dist/electron/electron/agent/search/google-provider.js +100 -0
- package/dist/electron/electron/agent/search/index.js +28 -0
- package/dist/electron/electron/agent/search/provider-factory.js +395 -0
- package/dist/electron/electron/agent/search/serpapi-provider.js +112 -0
- package/dist/electron/electron/agent/search/tavily-provider.js +90 -0
- package/dist/electron/electron/agent/search/types.js +40 -0
- package/dist/electron/electron/agent/security/index.js +12 -0
- package/dist/electron/electron/agent/security/input-sanitizer.js +303 -0
- package/dist/electron/electron/agent/security/output-filter.js +217 -0
- package/dist/electron/electron/agent/skill-eligibility.js +281 -0
- package/dist/electron/electron/agent/skill-registry.js +396 -0
- package/dist/electron/electron/agent/skills/document.js +878 -0
- package/dist/electron/electron/agent/skills/image-generator.js +225 -0
- package/dist/electron/electron/agent/skills/organizer.js +141 -0
- package/dist/electron/electron/agent/skills/presentation.js +367 -0
- package/dist/electron/electron/agent/skills/spreadsheet.js +165 -0
- package/dist/electron/electron/agent/tools/browser-tools.js +523 -0
- package/dist/electron/electron/agent/tools/builtin-settings.js +384 -0
- package/dist/electron/electron/agent/tools/canvas-tools.js +530 -0
- package/dist/electron/electron/agent/tools/cron-tools.js +577 -0
- package/dist/electron/electron/agent/tools/edit-tools.js +194 -0
- package/dist/electron/electron/agent/tools/file-tools.js +719 -0
- package/dist/electron/electron/agent/tools/glob-tools.js +283 -0
- package/dist/electron/electron/agent/tools/grep-tools.js +387 -0
- package/dist/electron/electron/agent/tools/image-tools.js +111 -0
- package/dist/electron/electron/agent/tools/mention-tools.js +282 -0
- package/dist/electron/electron/agent/tools/node-tools.js +476 -0
- package/dist/electron/electron/agent/tools/registry.js +2719 -0
- package/dist/electron/electron/agent/tools/search-tools.js +91 -0
- package/dist/electron/electron/agent/tools/shell-tools.js +574 -0
- package/dist/electron/electron/agent/tools/skill-tools.js +274 -0
- package/dist/electron/electron/agent/tools/system-tools.js +578 -0
- package/dist/electron/electron/agent/tools/web-fetch-tools.js +444 -0
- package/dist/electron/electron/agent/tools/x-tools.js +264 -0
- package/dist/electron/electron/agents/AgentRoleRepository.js +420 -0
- package/dist/electron/electron/agents/HeartbeatService.js +356 -0
- package/dist/electron/electron/agents/MentionRepository.js +197 -0
- package/dist/electron/electron/agents/TaskSubscriptionRepository.js +168 -0
- package/dist/electron/electron/agents/WorkingStateRepository.js +229 -0
- package/dist/electron/electron/canvas/canvas-manager.js +714 -0
- package/dist/electron/electron/canvas/canvas-preload.js +53 -0
- package/dist/electron/electron/canvas/canvas-protocol.js +195 -0
- package/dist/electron/electron/canvas/canvas-store.js +174 -0
- package/dist/electron/electron/canvas/index.js +13 -0
- package/dist/electron/electron/control-plane/client.js +364 -0
- package/dist/electron/electron/control-plane/handlers.js +572 -0
- package/dist/electron/electron/control-plane/index.js +41 -0
- package/dist/electron/electron/control-plane/node-manager.js +264 -0
- package/dist/electron/electron/control-plane/protocol.js +194 -0
- package/dist/electron/electron/control-plane/remote-client.js +437 -0
- package/dist/electron/electron/control-plane/server.js +640 -0
- package/dist/electron/electron/control-plane/settings.js +369 -0
- package/dist/electron/electron/control-plane/ssh-tunnel.js +549 -0
- package/dist/electron/electron/cron/index.js +30 -0
- package/dist/electron/electron/cron/schedule.js +190 -0
- package/dist/electron/electron/cron/service.js +614 -0
- package/dist/electron/electron/cron/store.js +155 -0
- package/dist/electron/electron/cron/types.js +82 -0
- package/dist/electron/electron/cron/webhook.js +258 -0
- package/dist/electron/electron/database/SecureSettingsRepository.js +444 -0
- package/dist/electron/electron/database/TaskLabelRepository.js +120 -0
- package/dist/electron/electron/database/repositories.js +1781 -0
- package/dist/electron/electron/database/schema.js +978 -0
- package/dist/electron/electron/extensions/index.js +33 -0
- package/dist/electron/electron/extensions/loader.js +313 -0
- package/dist/electron/electron/extensions/registry.js +485 -0
- package/dist/electron/electron/extensions/types.js +11 -0
- package/dist/electron/electron/gateway/channel-registry.js +1102 -0
- package/dist/electron/electron/gateway/channels/bluebubbles-client.js +479 -0
- package/dist/electron/electron/gateway/channels/bluebubbles.js +432 -0
- package/dist/electron/electron/gateway/channels/discord.js +975 -0
- package/dist/electron/electron/gateway/channels/email-client.js +593 -0
- package/dist/electron/electron/gateway/channels/email.js +443 -0
- package/dist/electron/electron/gateway/channels/google-chat.js +631 -0
- package/dist/electron/electron/gateway/channels/imessage-client.js +363 -0
- package/dist/electron/electron/gateway/channels/imessage.js +465 -0
- package/dist/electron/electron/gateway/channels/index.js +36 -0
- package/dist/electron/electron/gateway/channels/line-client.js +470 -0
- package/dist/electron/electron/gateway/channels/line.js +479 -0
- package/dist/electron/electron/gateway/channels/matrix-client.js +432 -0
- package/dist/electron/electron/gateway/channels/matrix.js +592 -0
- package/dist/electron/electron/gateway/channels/mattermost-client.js +394 -0
- package/dist/electron/electron/gateway/channels/mattermost.js +496 -0
- package/dist/electron/electron/gateway/channels/signal-client.js +500 -0
- package/dist/electron/electron/gateway/channels/signal.js +582 -0
- package/dist/electron/electron/gateway/channels/slack.js +415 -0
- package/dist/electron/electron/gateway/channels/teams.js +596 -0
- package/dist/electron/electron/gateway/channels/telegram.js +1390 -0
- package/dist/electron/electron/gateway/channels/twitch-client.js +502 -0
- package/dist/electron/electron/gateway/channels/twitch.js +396 -0
- package/dist/electron/electron/gateway/channels/types.js +8 -0
- package/dist/electron/electron/gateway/channels/whatsapp.js +953 -0
- package/dist/electron/electron/gateway/context-policy.js +268 -0
- package/dist/electron/electron/gateway/index.js +1063 -0
- package/dist/electron/electron/gateway/infrastructure.js +496 -0
- package/dist/electron/electron/gateway/router.js +2700 -0
- package/dist/electron/electron/gateway/security.js +375 -0
- package/dist/electron/electron/gateway/session.js +115 -0
- package/dist/electron/electron/gateway/tunnel.js +503 -0
- package/dist/electron/electron/guardrails/guardrail-manager.js +348 -0
- package/dist/electron/electron/hooks/gmail-watcher.js +300 -0
- package/dist/electron/electron/hooks/index.js +46 -0
- package/dist/electron/electron/hooks/mappings.js +381 -0
- package/dist/electron/electron/hooks/server.js +480 -0
- package/dist/electron/electron/hooks/settings.js +447 -0
- package/dist/electron/electron/hooks/types.js +41 -0
- package/dist/electron/electron/ipc/canvas-handlers.js +158 -0
- package/dist/electron/electron/ipc/handlers.js +3138 -0
- package/dist/electron/electron/ipc/mission-control-handlers.js +141 -0
- package/dist/electron/electron/main.js +448 -0
- package/dist/electron/electron/mcp/client/MCPClientManager.js +330 -0
- package/dist/electron/electron/mcp/client/MCPServerConnection.js +437 -0
- package/dist/electron/electron/mcp/client/transports/SSETransport.js +304 -0
- package/dist/electron/electron/mcp/client/transports/StdioTransport.js +307 -0
- package/dist/electron/electron/mcp/client/transports/WebSocketTransport.js +329 -0
- package/dist/electron/electron/mcp/host/MCPHostServer.js +354 -0
- package/dist/electron/electron/mcp/host/ToolAdapter.js +100 -0
- package/dist/electron/electron/mcp/registry/MCPRegistryManager.js +497 -0
- package/dist/electron/electron/mcp/settings.js +446 -0
- package/dist/electron/electron/mcp/types.js +59 -0
- package/dist/electron/electron/memory/MemoryService.js +435 -0
- package/dist/electron/electron/notifications/index.js +17 -0
- package/dist/electron/electron/notifications/service.js +118 -0
- package/dist/electron/electron/notifications/store.js +144 -0
- package/dist/electron/electron/preload.js +842 -0
- package/dist/electron/electron/reports/StandupReportService.js +272 -0
- package/dist/electron/electron/security/concurrency.js +293 -0
- package/dist/electron/electron/security/index.js +15 -0
- package/dist/electron/electron/security/policy-manager.js +435 -0
- package/dist/electron/electron/settings/appearance-manager.js +193 -0
- package/dist/electron/electron/settings/personality-manager.js +724 -0
- package/dist/electron/electron/settings/x-manager.js +58 -0
- package/dist/electron/electron/tailscale/exposure.js +188 -0
- package/dist/electron/electron/tailscale/index.js +28 -0
- package/dist/electron/electron/tailscale/settings.js +205 -0
- package/dist/electron/electron/tailscale/tailscale.js +355 -0
- package/dist/electron/electron/tray/QuickInputWindow.js +568 -0
- package/dist/electron/electron/tray/TrayManager.js +895 -0
- package/dist/electron/electron/tray/index.js +9 -0
- package/dist/electron/electron/updater/index.js +6 -0
- package/dist/electron/electron/updater/update-manager.js +418 -0
- package/dist/electron/electron/utils/env-migration.js +209 -0
- package/dist/electron/electron/utils/process.js +102 -0
- package/dist/electron/electron/utils/rate-limiter.js +104 -0
- package/dist/electron/electron/utils/validation.js +419 -0
- package/dist/electron/electron/utils/x-cli.js +177 -0
- package/dist/electron/electron/voice/VoiceService.js +507 -0
- package/dist/electron/electron/voice/index.js +14 -0
- package/dist/electron/electron/voice/voice-settings-manager.js +359 -0
- package/dist/electron/shared/channelMessages.js +170 -0
- package/dist/electron/shared/types.js +1185 -0
- package/package.json +159 -0
- package/resources/skills/1password.json +10 -0
- package/resources/skills/add-documentation.json +31 -0
- package/resources/skills/analyze-csv.json +17 -0
- package/resources/skills/apple-notes.json +10 -0
- package/resources/skills/apple-reminders.json +10 -0
- package/resources/skills/auto-commenter.json +10 -0
- package/resources/skills/bear-notes.json +10 -0
- package/resources/skills/bird.json +35 -0
- package/resources/skills/blogwatcher.json +10 -0
- package/resources/skills/blucli.json +10 -0
- package/resources/skills/bluebubbles.json +10 -0
- package/resources/skills/camsnap.json +10 -0
- package/resources/skills/clean-imports.json +18 -0
- package/resources/skills/code-review.json +18 -0
- package/resources/skills/coding-agent.json +10 -0
- package/resources/skills/compare-files.json +23 -0
- package/resources/skills/convert-code.json +34 -0
- package/resources/skills/create-changelog.json +24 -0
- package/resources/skills/debug-error.json +17 -0
- package/resources/skills/dependency-check.json +10 -0
- package/resources/skills/discord.json +10 -0
- package/resources/skills/eightctl.json +10 -0
- package/resources/skills/explain-code.json +29 -0
- package/resources/skills/extract-todos.json +18 -0
- package/resources/skills/food-order.json +10 -0
- package/resources/skills/gemini.json +10 -0
- package/resources/skills/generate-readme.json +10 -0
- package/resources/skills/gifgrep.json +10 -0
- package/resources/skills/git-commit.json +10 -0
- package/resources/skills/github.json +10 -0
- package/resources/skills/gog.json +10 -0
- package/resources/skills/goplaces.json +10 -0
- package/resources/skills/himalaya.json +10 -0
- package/resources/skills/imsg.json +10 -0
- package/resources/skills/karpathy-guidelines.json +12 -0
- package/resources/skills/last30days.json +26 -0
- package/resources/skills/local-places.json +10 -0
- package/resources/skills/mcporter.json +10 -0
- package/resources/skills/model-usage.json +10 -0
- package/resources/skills/nano-banana-pro.json +10 -0
- package/resources/skills/nano-pdf.json +10 -0
- package/resources/skills/notion.json +10 -0
- package/resources/skills/obsidian.json +10 -0
- package/resources/skills/openai-image-gen.json +10 -0
- package/resources/skills/openai-whisper-api.json +10 -0
- package/resources/skills/openai-whisper.json +10 -0
- package/resources/skills/openhue.json +10 -0
- package/resources/skills/oracle.json +10 -0
- package/resources/skills/ordercli.json +10 -0
- package/resources/skills/peekaboo.json +10 -0
- package/resources/skills/project-structure.json +10 -0
- package/resources/skills/proofread.json +17 -0
- package/resources/skills/refactor-code.json +31 -0
- package/resources/skills/rename-symbol.json +23 -0
- package/resources/skills/sag.json +10 -0
- package/resources/skills/security-audit.json +18 -0
- package/resources/skills/session-logs.json +10 -0
- package/resources/skills/sherpa-onnx-tts.json +10 -0
- package/resources/skills/skill-creator.json +15 -0
- package/resources/skills/skill-hub.json +29 -0
- package/resources/skills/slack.json +10 -0
- package/resources/skills/songsee.json +10 -0
- package/resources/skills/sonoscli.json +10 -0
- package/resources/skills/spotify-player.json +10 -0
- package/resources/skills/startup-cfo.json +55 -0
- package/resources/skills/summarize-folder.json +18 -0
- package/resources/skills/summarize.json +10 -0
- package/resources/skills/things-mac.json +10 -0
- package/resources/skills/tmux.json +10 -0
- package/resources/skills/translate.json +36 -0
- package/resources/skills/trello.json +10 -0
- package/resources/skills/video-frames.json +10 -0
- package/resources/skills/voice-call.json +10 -0
- package/resources/skills/wacli.json +10 -0
- package/resources/skills/weather.json +10 -0
- package/resources/skills/write-tests.json +31 -0
- package/src/electron/activity/ActivityRepository.ts +238 -0
- package/src/electron/agent/browser/browser-service.ts +721 -0
- package/src/electron/agent/context-manager.ts +257 -0
- package/src/electron/agent/custom-skill-loader.ts +634 -0
- package/src/electron/agent/daemon.ts +1097 -0
- package/src/electron/agent/executor.ts +4017 -0
- package/src/electron/agent/llm/anthropic-provider.ts +175 -0
- package/src/electron/agent/llm/bedrock-provider.ts +236 -0
- package/src/electron/agent/llm/gemini-provider.ts +422 -0
- package/src/electron/agent/llm/index.ts +9 -0
- package/src/electron/agent/llm/ollama-provider.ts +347 -0
- package/src/electron/agent/llm/openai-oauth.ts +127 -0
- package/src/electron/agent/llm/openai-provider.ts +686 -0
- package/src/electron/agent/llm/openrouter-provider.ts +273 -0
- package/src/electron/agent/llm/pricing.ts +180 -0
- package/src/electron/agent/llm/provider-factory.ts +971 -0
- package/src/electron/agent/llm/types.ts +291 -0
- package/src/electron/agent/queue-manager.ts +408 -0
- package/src/electron/agent/sandbox/docker-sandbox.ts +453 -0
- package/src/electron/agent/sandbox/macos-sandbox.ts +426 -0
- package/src/electron/agent/sandbox/runner.ts +453 -0
- package/src/electron/agent/sandbox/sandbox-factory.ts +337 -0
- package/src/electron/agent/sandbox/security-utils.ts +251 -0
- package/src/electron/agent/search/brave-provider.ts +141 -0
- package/src/electron/agent/search/google-provider.ts +131 -0
- package/src/electron/agent/search/index.ts +6 -0
- package/src/electron/agent/search/provider-factory.ts +450 -0
- package/src/electron/agent/search/serpapi-provider.ts +138 -0
- package/src/electron/agent/search/tavily-provider.ts +108 -0
- package/src/electron/agent/search/types.ts +118 -0
- package/src/electron/agent/security/index.ts +20 -0
- package/src/electron/agent/security/input-sanitizer.ts +380 -0
- package/src/electron/agent/security/output-filter.ts +259 -0
- package/src/electron/agent/skill-eligibility.ts +334 -0
- package/src/electron/agent/skill-registry.ts +457 -0
- package/src/electron/agent/skills/document.ts +1070 -0
- package/src/electron/agent/skills/image-generator.ts +272 -0
- package/src/electron/agent/skills/organizer.ts +131 -0
- package/src/electron/agent/skills/presentation.ts +418 -0
- package/src/electron/agent/skills/spreadsheet.ts +166 -0
- package/src/electron/agent/tools/browser-tools.ts +546 -0
- package/src/electron/agent/tools/builtin-settings.ts +422 -0
- package/src/electron/agent/tools/canvas-tools.ts +572 -0
- package/src/electron/agent/tools/cron-tools.ts +723 -0
- package/src/electron/agent/tools/edit-tools.ts +196 -0
- package/src/electron/agent/tools/file-tools.ts +811 -0
- package/src/electron/agent/tools/glob-tools.ts +303 -0
- package/src/electron/agent/tools/grep-tools.ts +432 -0
- package/src/electron/agent/tools/image-tools.ts +126 -0
- package/src/electron/agent/tools/mention-tools.ts +371 -0
- package/src/electron/agent/tools/node-tools.ts +550 -0
- package/src/electron/agent/tools/registry.ts +3052 -0
- package/src/electron/agent/tools/search-tools.ts +111 -0
- package/src/electron/agent/tools/shell-tools.ts +651 -0
- package/src/electron/agent/tools/skill-tools.ts +340 -0
- package/src/electron/agent/tools/system-tools.ts +665 -0
- package/src/electron/agent/tools/web-fetch-tools.ts +528 -0
- package/src/electron/agent/tools/x-tools.ts +267 -0
- package/src/electron/agents/AgentRoleRepository.ts +557 -0
- package/src/electron/agents/HeartbeatService.ts +469 -0
- package/src/electron/agents/MentionRepository.ts +242 -0
- package/src/electron/agents/TaskSubscriptionRepository.ts +231 -0
- package/src/electron/agents/WorkingStateRepository.ts +278 -0
- package/src/electron/canvas/canvas-manager.ts +818 -0
- package/src/electron/canvas/canvas-preload.ts +102 -0
- package/src/electron/canvas/canvas-protocol.ts +174 -0
- package/src/electron/canvas/canvas-store.ts +200 -0
- package/src/electron/canvas/index.ts +8 -0
- package/src/electron/control-plane/client.ts +527 -0
- package/src/electron/control-plane/handlers.ts +723 -0
- package/src/electron/control-plane/index.ts +51 -0
- package/src/electron/control-plane/node-manager.ts +322 -0
- package/src/electron/control-plane/protocol.ts +269 -0
- package/src/electron/control-plane/remote-client.ts +517 -0
- package/src/electron/control-plane/server.ts +853 -0
- package/src/electron/control-plane/settings.ts +401 -0
- package/src/electron/control-plane/ssh-tunnel.ts +624 -0
- package/src/electron/cron/index.ts +9 -0
- package/src/electron/cron/schedule.ts +217 -0
- package/src/electron/cron/service.ts +743 -0
- package/src/electron/cron/store.ts +165 -0
- package/src/electron/cron/types.ts +291 -0
- package/src/electron/cron/webhook.ts +303 -0
- package/src/electron/database/SecureSettingsRepository.ts +514 -0
- package/src/electron/database/TaskLabelRepository.ts +148 -0
- package/src/electron/database/repositories.ts +2397 -0
- package/src/electron/database/schema.ts +1017 -0
- package/src/electron/extensions/index.ts +18 -0
- package/src/electron/extensions/loader.ts +336 -0
- package/src/electron/extensions/registry.ts +546 -0
- package/src/electron/extensions/types.ts +372 -0
- package/src/electron/gateway/channel-registry.ts +1267 -0
- package/src/electron/gateway/channels/bluebubbles-client.ts +641 -0
- package/src/electron/gateway/channels/bluebubbles.ts +509 -0
- package/src/electron/gateway/channels/discord.ts +1150 -0
- package/src/electron/gateway/channels/email-client.ts +708 -0
- package/src/electron/gateway/channels/email.ts +516 -0
- package/src/electron/gateway/channels/google-chat.ts +760 -0
- package/src/electron/gateway/channels/imessage-client.ts +473 -0
- package/src/electron/gateway/channels/imessage.ts +520 -0
- package/src/electron/gateway/channels/index.ts +21 -0
- package/src/electron/gateway/channels/line-client.ts +598 -0
- package/src/electron/gateway/channels/line.ts +559 -0
- package/src/electron/gateway/channels/matrix-client.ts +632 -0
- package/src/electron/gateway/channels/matrix.ts +655 -0
- package/src/electron/gateway/channels/mattermost-client.ts +526 -0
- package/src/electron/gateway/channels/mattermost.ts +550 -0
- package/src/electron/gateway/channels/signal-client.ts +722 -0
- package/src/electron/gateway/channels/signal.ts +666 -0
- package/src/electron/gateway/channels/slack.ts +458 -0
- package/src/electron/gateway/channels/teams.ts +681 -0
- package/src/electron/gateway/channels/telegram.ts +1727 -0
- package/src/electron/gateway/channels/twitch-client.ts +665 -0
- package/src/electron/gateway/channels/twitch.ts +468 -0
- package/src/electron/gateway/channels/types.ts +1002 -0
- package/src/electron/gateway/channels/whatsapp.ts +1101 -0
- package/src/electron/gateway/context-policy.ts +382 -0
- package/src/electron/gateway/index.ts +1274 -0
- package/src/electron/gateway/infrastructure.ts +645 -0
- package/src/electron/gateway/router.ts +3206 -0
- package/src/electron/gateway/security.ts +422 -0
- package/src/electron/gateway/session.ts +144 -0
- package/src/electron/gateway/tunnel.ts +626 -0
- package/src/electron/guardrails/guardrail-manager.ts +380 -0
- package/src/electron/hooks/gmail-watcher.ts +355 -0
- package/src/electron/hooks/index.ts +30 -0
- package/src/electron/hooks/mappings.ts +404 -0
- package/src/electron/hooks/server.ts +574 -0
- package/src/electron/hooks/settings.ts +466 -0
- package/src/electron/hooks/types.ts +245 -0
- package/src/electron/ipc/canvas-handlers.ts +223 -0
- package/src/electron/ipc/handlers.ts +3661 -0
- package/src/electron/ipc/mission-control-handlers.ts +182 -0
- package/src/electron/main.ts +496 -0
- package/src/electron/mcp/client/MCPClientManager.ts +406 -0
- package/src/electron/mcp/client/MCPServerConnection.ts +514 -0
- package/src/electron/mcp/client/transports/SSETransport.ts +360 -0
- package/src/electron/mcp/client/transports/StdioTransport.ts +355 -0
- package/src/electron/mcp/client/transports/WebSocketTransport.ts +384 -0
- package/src/electron/mcp/host/MCPHostServer.ts +388 -0
- package/src/electron/mcp/host/ToolAdapter.ts +140 -0
- package/src/electron/mcp/registry/MCPRegistryManager.ts +565 -0
- package/src/electron/mcp/settings.ts +468 -0
- package/src/electron/mcp/types.ts +371 -0
- package/src/electron/memory/MemoryService.ts +523 -0
- package/src/electron/notifications/index.ts +16 -0
- package/src/electron/notifications/service.ts +161 -0
- package/src/electron/notifications/store.ts +163 -0
- package/src/electron/preload.ts +2845 -0
- package/src/electron/reports/StandupReportService.ts +356 -0
- package/src/electron/security/concurrency.ts +333 -0
- package/src/electron/security/index.ts +17 -0
- package/src/electron/security/policy-manager.ts +539 -0
- package/src/electron/settings/appearance-manager.ts +182 -0
- package/src/electron/settings/personality-manager.ts +800 -0
- package/src/electron/settings/x-manager.ts +62 -0
- package/src/electron/tailscale/exposure.ts +262 -0
- package/src/electron/tailscale/index.ts +34 -0
- package/src/electron/tailscale/settings.ts +218 -0
- package/src/electron/tailscale/tailscale.ts +379 -0
- package/src/electron/tray/QuickInputWindow.ts +609 -0
- package/src/electron/tray/TrayManager.ts +1005 -0
- package/src/electron/tray/index.ts +6 -0
- package/src/electron/updater/index.ts +1 -0
- package/src/electron/updater/update-manager.ts +447 -0
- package/src/electron/utils/env-migration.ts +203 -0
- package/src/electron/utils/process.ts +124 -0
- package/src/electron/utils/rate-limiter.ts +130 -0
- package/src/electron/utils/validation.ts +493 -0
- package/src/electron/utils/x-cli.ts +198 -0
- package/src/electron/voice/VoiceService.ts +583 -0
- package/src/electron/voice/index.ts +9 -0
- package/src/electron/voice/voice-settings-manager.ts +403 -0
- package/src/renderer/App.tsx +775 -0
- package/src/renderer/components/ActivityFeed.tsx +407 -0
- package/src/renderer/components/ActivityFeedItem.tsx +285 -0
- package/src/renderer/components/AgentRoleCard.tsx +343 -0
- package/src/renderer/components/AgentRoleEditor.tsx +805 -0
- package/src/renderer/components/AgentSquadSettings.tsx +295 -0
- package/src/renderer/components/AgentWorkingStatePanel.tsx +411 -0
- package/src/renderer/components/AppearanceSettings.tsx +122 -0
- package/src/renderer/components/ApprovalDialog.tsx +100 -0
- package/src/renderer/components/BlueBubblesSettings.tsx +505 -0
- package/src/renderer/components/BuiltinToolsSettings.tsx +307 -0
- package/src/renderer/components/CanvasPreview.tsx +1189 -0
- package/src/renderer/components/CommandOutput.tsx +202 -0
- package/src/renderer/components/ContextPolicySettings.tsx +523 -0
- package/src/renderer/components/ControlPlaneSettings.tsx +1134 -0
- package/src/renderer/components/DisclaimerModal.tsx +124 -0
- package/src/renderer/components/DiscordSettings.tsx +436 -0
- package/src/renderer/components/EmailSettings.tsx +606 -0
- package/src/renderer/components/ExtensionsSettings.tsx +542 -0
- package/src/renderer/components/FileViewer.tsx +224 -0
- package/src/renderer/components/GoogleChatSettings.tsx +535 -0
- package/src/renderer/components/GuardrailSettings.tsx +487 -0
- package/src/renderer/components/HooksSettings.tsx +581 -0
- package/src/renderer/components/ImessageSettings.tsx +484 -0
- package/src/renderer/components/LineSettings.tsx +483 -0
- package/src/renderer/components/MCPRegistryBrowser.tsx +386 -0
- package/src/renderer/components/MCPSettings.tsx +943 -0
- package/src/renderer/components/MainContent.tsx +2433 -0
- package/src/renderer/components/MatrixSettings.tsx +510 -0
- package/src/renderer/components/MattermostSettings.tsx +473 -0
- package/src/renderer/components/MemorySettings.tsx +247 -0
- package/src/renderer/components/MentionBadge.tsx +87 -0
- package/src/renderer/components/MentionInput.tsx +409 -0
- package/src/renderer/components/MentionList.tsx +476 -0
- package/src/renderer/components/MissionControlPanel.tsx +1995 -0
- package/src/renderer/components/NodesSettings.tsx +316 -0
- package/src/renderer/components/NotificationPanel.tsx +481 -0
- package/src/renderer/components/Onboarding/AwakeningOrb.tsx +44 -0
- package/src/renderer/components/Onboarding/Onboarding.tsx +443 -0
- package/src/renderer/components/Onboarding/TypewriterText.tsx +102 -0
- package/src/renderer/components/Onboarding/index.ts +3 -0
- package/src/renderer/components/OnboardingModal.tsx +698 -0
- package/src/renderer/components/PairingCodeDisplay.tsx +324 -0
- package/src/renderer/components/PersonalitySettings.tsx +597 -0
- package/src/renderer/components/QueueSettings.tsx +119 -0
- package/src/renderer/components/QuickTaskFAB.tsx +71 -0
- package/src/renderer/components/RightPanel.tsx +413 -0
- package/src/renderer/components/ScheduledTasksSettings.tsx +1328 -0
- package/src/renderer/components/SearchSettings.tsx +328 -0
- package/src/renderer/components/Settings.tsx +1504 -0
- package/src/renderer/components/Sidebar.tsx +344 -0
- package/src/renderer/components/SignalSettings.tsx +673 -0
- package/src/renderer/components/SkillHubBrowser.tsx +458 -0
- package/src/renderer/components/SkillParameterModal.tsx +185 -0
- package/src/renderer/components/SkillsSettings.tsx +451 -0
- package/src/renderer/components/SlackSettings.tsx +442 -0
- package/src/renderer/components/StandupReportViewer.tsx +614 -0
- package/src/renderer/components/TaskBoard.tsx +498 -0
- package/src/renderer/components/TaskBoardCard.tsx +357 -0
- package/src/renderer/components/TaskBoardColumn.tsx +211 -0
- package/src/renderer/components/TaskLabelManager.tsx +472 -0
- package/src/renderer/components/TaskQueuePanel.tsx +144 -0
- package/src/renderer/components/TaskQuickActions.tsx +492 -0
- package/src/renderer/components/TaskTimeline.tsx +216 -0
- package/src/renderer/components/TaskView.tsx +162 -0
- package/src/renderer/components/TeamsSettings.tsx +518 -0
- package/src/renderer/components/TelegramSettings.tsx +421 -0
- package/src/renderer/components/Toast.tsx +76 -0
- package/src/renderer/components/TraySettings.tsx +189 -0
- package/src/renderer/components/TwitchSettings.tsx +511 -0
- package/src/renderer/components/UpdateSettings.tsx +295 -0
- package/src/renderer/components/VoiceIndicator.tsx +270 -0
- package/src/renderer/components/VoiceSettings.tsx +867 -0
- package/src/renderer/components/WhatsAppSettings.tsx +721 -0
- package/src/renderer/components/WorkingStateEditor.tsx +309 -0
- package/src/renderer/components/WorkingStateHistory.tsx +481 -0
- package/src/renderer/components/WorkspaceSelector.tsx +150 -0
- package/src/renderer/components/XSettings.tsx +311 -0
- package/src/renderer/global.d.ts +9 -0
- package/src/renderer/hooks/useAgentContext.ts +153 -0
- package/src/renderer/hooks/useOnboardingFlow.ts +548 -0
- package/src/renderer/hooks/useVoiceInput.ts +268 -0
- package/src/renderer/index.html +12 -0
- package/src/renderer/main.tsx +10 -0
- package/src/renderer/public/cowork-os-logo.png +0 -0
- package/src/renderer/quick-input.html +164 -0
- package/src/renderer/styles/index.css +14504 -0
- package/src/renderer/utils/agentMessages.ts +749 -0
- package/src/renderer/utils/voice-directives.ts +169 -0
- package/src/shared/channelMessages.ts +213 -0
- package/src/shared/types.ts +3608 -0
- package/tsconfig.electron.json +26 -0
- package/tsconfig.json +26 -0
- package/tsconfig.node.json +10 -0
- package/vite.config.ts +23 -0
|
@@ -0,0 +1,1274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel Gateway
|
|
3
|
+
*
|
|
4
|
+
* Main entry point for multi-channel messaging support.
|
|
5
|
+
* Manages channel adapters, routing, and sessions.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { BrowserWindow } from 'electron';
|
|
9
|
+
import Database from 'better-sqlite3';
|
|
10
|
+
import { MessageRouter, RouterConfig } from './router';
|
|
11
|
+
import { SecurityManager } from './security';
|
|
12
|
+
import { SessionManager } from './session';
|
|
13
|
+
import {
|
|
14
|
+
ChannelAdapter,
|
|
15
|
+
ChannelType,
|
|
16
|
+
TelegramConfig,
|
|
17
|
+
DiscordConfig,
|
|
18
|
+
SlackConfig,
|
|
19
|
+
WhatsAppConfig,
|
|
20
|
+
ImessageConfig,
|
|
21
|
+
SignalConfig,
|
|
22
|
+
MattermostConfig,
|
|
23
|
+
MatrixConfig,
|
|
24
|
+
TwitchConfig,
|
|
25
|
+
LineConfig,
|
|
26
|
+
BlueBubblesConfig,
|
|
27
|
+
EmailConfig,
|
|
28
|
+
GatewayEventHandler,
|
|
29
|
+
} from './channels/types';
|
|
30
|
+
import { TelegramAdapter, createTelegramAdapter } from './channels/telegram';
|
|
31
|
+
import { DiscordAdapter, createDiscordAdapter } from './channels/discord';
|
|
32
|
+
import { SlackAdapter, createSlackAdapter } from './channels/slack';
|
|
33
|
+
import { WhatsAppAdapter, createWhatsAppAdapter } from './channels/whatsapp';
|
|
34
|
+
import { ImessageAdapter, createImessageAdapter } from './channels/imessage';
|
|
35
|
+
import { SignalAdapter, createSignalAdapter } from './channels/signal';
|
|
36
|
+
import { MattermostAdapter, createMattermostAdapter } from './channels/mattermost';
|
|
37
|
+
import { MatrixAdapter, createMatrixAdapter } from './channels/matrix';
|
|
38
|
+
import { TwitchAdapter, createTwitchAdapter } from './channels/twitch';
|
|
39
|
+
import { LineAdapter, createLineAdapter } from './channels/line';
|
|
40
|
+
import { BlueBubblesAdapter, createBlueBubblesAdapter } from './channels/bluebubbles';
|
|
41
|
+
import { EmailAdapter, createEmailAdapter } from './channels/email';
|
|
42
|
+
import {
|
|
43
|
+
ChannelRepository,
|
|
44
|
+
ChannelUserRepository,
|
|
45
|
+
ChannelSessionRepository,
|
|
46
|
+
ChannelMessageRepository,
|
|
47
|
+
Channel,
|
|
48
|
+
} from '../database/repositories';
|
|
49
|
+
import { AgentDaemon } from '../agent/daemon';
|
|
50
|
+
import { PersonalityManager } from '../settings/personality-manager';
|
|
51
|
+
import {
|
|
52
|
+
getChannelMessage,
|
|
53
|
+
DEFAULT_CHANNEL_CONTEXT,
|
|
54
|
+
type ChannelMessageContext,
|
|
55
|
+
} from '../../shared/channelMessages';
|
|
56
|
+
import { DEFAULT_QUIRKS } from '../../shared/types';
|
|
57
|
+
|
|
58
|
+
export interface GatewayConfig {
|
|
59
|
+
/** Router configuration */
|
|
60
|
+
router?: RouterConfig;
|
|
61
|
+
/** Auto-connect enabled channels on startup */
|
|
62
|
+
autoConnect?: boolean;
|
|
63
|
+
/** Agent daemon for task execution */
|
|
64
|
+
agentDaemon?: AgentDaemon;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const DEFAULT_CONFIG: GatewayConfig = {
|
|
68
|
+
autoConnect: true,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Channel Gateway - Main class for managing multi-channel messaging
|
|
73
|
+
*/
|
|
74
|
+
export class ChannelGateway {
|
|
75
|
+
private db: Database.Database;
|
|
76
|
+
private router: MessageRouter;
|
|
77
|
+
private securityManager: SecurityManager;
|
|
78
|
+
private sessionManager: SessionManager;
|
|
79
|
+
private channelRepo: ChannelRepository;
|
|
80
|
+
private userRepo: ChannelUserRepository;
|
|
81
|
+
private sessionRepo: ChannelSessionRepository;
|
|
82
|
+
private messageRepo: ChannelMessageRepository;
|
|
83
|
+
private config: GatewayConfig;
|
|
84
|
+
private initialized = false;
|
|
85
|
+
private agentDaemon?: AgentDaemon;
|
|
86
|
+
private daemonListeners: Array<{ event: string; handler: (...args: any[]) => void }> = [];
|
|
87
|
+
|
|
88
|
+
constructor(db: Database.Database, config: GatewayConfig = {}) {
|
|
89
|
+
this.db = db;
|
|
90
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
91
|
+
|
|
92
|
+
// Initialize components
|
|
93
|
+
this.router = new MessageRouter(db, config.router, config.agentDaemon);
|
|
94
|
+
this.securityManager = new SecurityManager(db);
|
|
95
|
+
this.sessionManager = new SessionManager(db);
|
|
96
|
+
this.channelRepo = new ChannelRepository(db);
|
|
97
|
+
this.userRepo = new ChannelUserRepository(db);
|
|
98
|
+
this.sessionRepo = new ChannelSessionRepository(db);
|
|
99
|
+
this.messageRepo = new ChannelMessageRepository(db);
|
|
100
|
+
|
|
101
|
+
// Listen for agent daemon events to send responses back to channels
|
|
102
|
+
if (config.agentDaemon) {
|
|
103
|
+
this.agentDaemon = config.agentDaemon;
|
|
104
|
+
this.setupAgentDaemonListeners(config.agentDaemon);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Get the channel message context from personality settings
|
|
110
|
+
*/
|
|
111
|
+
private getMessageContext(): ChannelMessageContext {
|
|
112
|
+
try {
|
|
113
|
+
if (PersonalityManager.isInitialized()) {
|
|
114
|
+
const settings = PersonalityManager.loadSettings();
|
|
115
|
+
return {
|
|
116
|
+
agentName: settings.agentName || 'CoWork',
|
|
117
|
+
userName: settings.relationship?.userName,
|
|
118
|
+
personality: settings.activePersonality || 'professional',
|
|
119
|
+
emojiUsage: settings.responseStyle?.emojiUsage || 'minimal',
|
|
120
|
+
quirks: settings.quirks || DEFAULT_QUIRKS,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
} catch (error) {
|
|
124
|
+
console.error('[ChannelGateway] Failed to load personality settings:', error);
|
|
125
|
+
}
|
|
126
|
+
return DEFAULT_CHANNEL_CONTEXT;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Set up listeners for agent daemon events
|
|
131
|
+
*/
|
|
132
|
+
private setupAgentDaemonListeners(agentDaemon: AgentDaemon): void {
|
|
133
|
+
// Track the last assistant message for each task to send as completion result
|
|
134
|
+
const lastMessages = new Map<string, string>();
|
|
135
|
+
|
|
136
|
+
// Listen for assistant messages (streaming responses)
|
|
137
|
+
// Note: daemon emits { taskId, message } not { taskId, content }
|
|
138
|
+
const onAssistantMessage = (data: { taskId: string; message?: string }) => {
|
|
139
|
+
const message = data.message;
|
|
140
|
+
if (message && message.length > 10) {
|
|
141
|
+
// Keep the BEST (longest substantive) answer, not just the last one
|
|
142
|
+
// This prevents confused step messages from overwriting good answers
|
|
143
|
+
const existingMessage = lastMessages.get(data.taskId);
|
|
144
|
+
const isConfusedMessage = message.toLowerCase().includes("don't have") ||
|
|
145
|
+
message.toLowerCase().includes("please provide") ||
|
|
146
|
+
message.toLowerCase().includes("i cannot") ||
|
|
147
|
+
message.toLowerCase().includes("not available");
|
|
148
|
+
|
|
149
|
+
// Only overwrite if new message is better (longer and not confused)
|
|
150
|
+
if (!existingMessage || (!isConfusedMessage && message.length >= existingMessage.length)) {
|
|
151
|
+
lastMessages.set(data.taskId, message);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Always stream updates to channel (so user sees progress)
|
|
155
|
+
this.router.sendTaskUpdate(data.taskId, message);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
agentDaemon.on('assistant_message', onAssistantMessage);
|
|
159
|
+
this.daemonListeners.push({ event: 'assistant_message', handler: onAssistantMessage });
|
|
160
|
+
|
|
161
|
+
// Listen for task completion
|
|
162
|
+
const onTaskCompleted = (data: { taskId: string; message?: string }) => {
|
|
163
|
+
// Use the last assistant message as the result
|
|
164
|
+
const result = lastMessages.get(data.taskId);
|
|
165
|
+
this.router.handleTaskCompletion(data.taskId, result);
|
|
166
|
+
lastMessages.delete(data.taskId);
|
|
167
|
+
};
|
|
168
|
+
agentDaemon.on('task_completed', onTaskCompleted);
|
|
169
|
+
this.daemonListeners.push({ event: 'task_completed', handler: onTaskCompleted });
|
|
170
|
+
|
|
171
|
+
// Listen for task errors
|
|
172
|
+
// Note: daemon emits { taskId, error } or { taskId, message }
|
|
173
|
+
const onError = (data: { taskId: string; error?: string; message?: string }) => {
|
|
174
|
+
const errorMsg = data.error || data.message || 'Unknown error';
|
|
175
|
+
this.router.handleTaskFailure(data.taskId, errorMsg);
|
|
176
|
+
lastMessages.delete(data.taskId);
|
|
177
|
+
};
|
|
178
|
+
agentDaemon.on('error', onError);
|
|
179
|
+
this.daemonListeners.push({ event: 'error', handler: onError });
|
|
180
|
+
|
|
181
|
+
// Listen for tool errors (individual tool execution failures)
|
|
182
|
+
const onToolError = (data: { taskId: string; tool?: string; error?: string }) => {
|
|
183
|
+
const toolName = data.tool || 'Unknown tool';
|
|
184
|
+
const errorMsg = data.error || 'Unknown error';
|
|
185
|
+
const message = getChannelMessage('toolError', this.getMessageContext(), { tool: toolName, error: errorMsg });
|
|
186
|
+
this.router.sendTaskUpdate(data.taskId, message);
|
|
187
|
+
};
|
|
188
|
+
agentDaemon.on('tool_error', onToolError);
|
|
189
|
+
this.daemonListeners.push({ event: 'tool_error', handler: onToolError });
|
|
190
|
+
|
|
191
|
+
// Listen for follow-up message completion
|
|
192
|
+
// Track if any assistant messages were sent during follow-up
|
|
193
|
+
const followUpMessagesSent = new Map<string, boolean>();
|
|
194
|
+
const originalOnAssistantMessage = onAssistantMessage;
|
|
195
|
+
// Override to track follow-up messages
|
|
196
|
+
agentDaemon.off('assistant_message', onAssistantMessage);
|
|
197
|
+
const trackingAssistantMessage = (data: { taskId: string; message?: string }) => {
|
|
198
|
+
followUpMessagesSent.set(data.taskId, true);
|
|
199
|
+
originalOnAssistantMessage(data);
|
|
200
|
+
};
|
|
201
|
+
agentDaemon.on('assistant_message', trackingAssistantMessage);
|
|
202
|
+
// Update the stored handler
|
|
203
|
+
const assistantIdx = this.daemonListeners.findIndex(l => l.event === 'assistant_message');
|
|
204
|
+
if (assistantIdx >= 0) {
|
|
205
|
+
this.daemonListeners[assistantIdx] = { event: 'assistant_message', handler: trackingAssistantMessage };
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const onFollowUpCompleted = async (data: { taskId: string }) => {
|
|
209
|
+
// If no assistant messages were sent during the follow-up, send a confirmation
|
|
210
|
+
if (!followUpMessagesSent.get(data.taskId)) {
|
|
211
|
+
const message = getChannelMessage('followUpProcessed', this.getMessageContext());
|
|
212
|
+
this.router.sendTaskUpdate(data.taskId, message);
|
|
213
|
+
}
|
|
214
|
+
followUpMessagesSent.delete(data.taskId);
|
|
215
|
+
|
|
216
|
+
// Send any artifacts (images, screenshots) created during the follow-up
|
|
217
|
+
await this.router.sendArtifacts(data.taskId);
|
|
218
|
+
};
|
|
219
|
+
agentDaemon.on('follow_up_completed', onFollowUpCompleted);
|
|
220
|
+
this.daemonListeners.push({ event: 'follow_up_completed', handler: onFollowUpCompleted });
|
|
221
|
+
|
|
222
|
+
// Listen for follow-up failures
|
|
223
|
+
const onFollowUpFailed = (data: { taskId: string; error?: string }) => {
|
|
224
|
+
const errorMsg = data.error || 'Unknown error';
|
|
225
|
+
const message = getChannelMessage('followUpFailed', this.getMessageContext(), { error: errorMsg });
|
|
226
|
+
this.router.sendTaskUpdate(data.taskId, message);
|
|
227
|
+
followUpMessagesSent.delete(data.taskId);
|
|
228
|
+
};
|
|
229
|
+
agentDaemon.on('follow_up_failed', onFollowUpFailed);
|
|
230
|
+
this.daemonListeners.push({ event: 'follow_up_failed', handler: onFollowUpFailed });
|
|
231
|
+
|
|
232
|
+
// Listen for approval requests - forward to Discord/Telegram
|
|
233
|
+
const onApprovalRequested = (data: { taskId: string; approval: any }) => {
|
|
234
|
+
this.router.sendApprovalRequest(data.taskId, data.approval);
|
|
235
|
+
};
|
|
236
|
+
agentDaemon.on('approval_requested', onApprovalRequested);
|
|
237
|
+
this.daemonListeners.push({ event: 'approval_requested', handler: onApprovalRequested });
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Initialize the gateway
|
|
242
|
+
*/
|
|
243
|
+
async initialize(mainWindow?: BrowserWindow): Promise<void> {
|
|
244
|
+
if (this.initialized) return;
|
|
245
|
+
|
|
246
|
+
if (mainWindow) {
|
|
247
|
+
this.router.setMainWindow(mainWindow);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Load and register enabled channels
|
|
251
|
+
await this.loadChannels();
|
|
252
|
+
|
|
253
|
+
// Auto-connect if configured
|
|
254
|
+
if (this.config.autoConnect) {
|
|
255
|
+
await this.router.connectAll();
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
this.initialized = true;
|
|
259
|
+
console.log('Channel Gateway initialized');
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Set the main window for IPC communication
|
|
264
|
+
*/
|
|
265
|
+
setMainWindow(window: BrowserWindow): void {
|
|
266
|
+
this.router.setMainWindow(window);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Shutdown the gateway
|
|
271
|
+
*/
|
|
272
|
+
async shutdown(): Promise<void> {
|
|
273
|
+
// Clean up daemon event listeners
|
|
274
|
+
if (this.agentDaemon) {
|
|
275
|
+
for (const { event, handler } of this.daemonListeners) {
|
|
276
|
+
this.agentDaemon.off(event, handler);
|
|
277
|
+
}
|
|
278
|
+
this.daemonListeners = [];
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
await this.router.disconnectAll();
|
|
282
|
+
this.initialized = false;
|
|
283
|
+
console.log('Channel Gateway shutdown');
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Channel Management
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Add a new Telegram channel
|
|
290
|
+
*/
|
|
291
|
+
async addTelegramChannel(
|
|
292
|
+
name: string,
|
|
293
|
+
botToken: string,
|
|
294
|
+
securityMode: 'open' | 'allowlist' | 'pairing' = 'pairing'
|
|
295
|
+
): Promise<Channel> {
|
|
296
|
+
// Check if Telegram channel already exists
|
|
297
|
+
const existing = this.channelRepo.findByType('telegram');
|
|
298
|
+
if (existing) {
|
|
299
|
+
throw new Error('Telegram channel already configured. Update or remove it first.');
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Create channel record
|
|
303
|
+
const channel = this.channelRepo.create({
|
|
304
|
+
type: 'telegram',
|
|
305
|
+
name,
|
|
306
|
+
enabled: false, // Don't enable until tested
|
|
307
|
+
config: { botToken },
|
|
308
|
+
securityConfig: {
|
|
309
|
+
mode: securityMode,
|
|
310
|
+
pairingCodeTTL: 300, // 5 minutes
|
|
311
|
+
maxPairingAttempts: 5,
|
|
312
|
+
rateLimitPerMinute: 30,
|
|
313
|
+
},
|
|
314
|
+
status: 'disconnected',
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
return channel;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Add a new Discord channel
|
|
322
|
+
*/
|
|
323
|
+
async addDiscordChannel(
|
|
324
|
+
name: string,
|
|
325
|
+
botToken: string,
|
|
326
|
+
applicationId: string,
|
|
327
|
+
guildIds?: string[],
|
|
328
|
+
securityMode: 'open' | 'allowlist' | 'pairing' = 'pairing'
|
|
329
|
+
): Promise<Channel> {
|
|
330
|
+
// Check if Discord channel already exists
|
|
331
|
+
const existing = this.channelRepo.findByType('discord');
|
|
332
|
+
if (existing) {
|
|
333
|
+
throw new Error('Discord channel already configured. Update or remove it first.');
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Create channel record
|
|
337
|
+
const channel = this.channelRepo.create({
|
|
338
|
+
type: 'discord',
|
|
339
|
+
name,
|
|
340
|
+
enabled: false, // Don't enable until tested
|
|
341
|
+
config: { botToken, applicationId, guildIds },
|
|
342
|
+
securityConfig: {
|
|
343
|
+
mode: securityMode,
|
|
344
|
+
pairingCodeTTL: 300, // 5 minutes
|
|
345
|
+
maxPairingAttempts: 5,
|
|
346
|
+
rateLimitPerMinute: 30,
|
|
347
|
+
},
|
|
348
|
+
status: 'disconnected',
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
return channel;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Add a new Slack channel
|
|
356
|
+
*/
|
|
357
|
+
async addSlackChannel(
|
|
358
|
+
name: string,
|
|
359
|
+
botToken: string,
|
|
360
|
+
appToken: string,
|
|
361
|
+
signingSecret?: string,
|
|
362
|
+
securityMode: 'open' | 'allowlist' | 'pairing' = 'pairing'
|
|
363
|
+
): Promise<Channel> {
|
|
364
|
+
// Check if Slack channel already exists
|
|
365
|
+
const existing = this.channelRepo.findByType('slack');
|
|
366
|
+
if (existing) {
|
|
367
|
+
throw new Error('Slack channel already configured. Update or remove it first.');
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Create channel record
|
|
371
|
+
const channel = this.channelRepo.create({
|
|
372
|
+
type: 'slack',
|
|
373
|
+
name,
|
|
374
|
+
enabled: false, // Don't enable until tested
|
|
375
|
+
config: { botToken, appToken, signingSecret },
|
|
376
|
+
securityConfig: {
|
|
377
|
+
mode: securityMode,
|
|
378
|
+
pairingCodeTTL: 300, // 5 minutes
|
|
379
|
+
maxPairingAttempts: 5,
|
|
380
|
+
rateLimitPerMinute: 30,
|
|
381
|
+
},
|
|
382
|
+
status: 'disconnected',
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
return channel;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Add a new WhatsApp channel
|
|
390
|
+
*/
|
|
391
|
+
async addWhatsAppChannel(
|
|
392
|
+
name: string,
|
|
393
|
+
allowedNumbers?: string[],
|
|
394
|
+
securityMode: 'open' | 'allowlist' | 'pairing' = 'pairing',
|
|
395
|
+
selfChatMode: boolean = true,
|
|
396
|
+
responsePrefix: string = '🤖'
|
|
397
|
+
): Promise<Channel> {
|
|
398
|
+
// Check if WhatsApp channel already exists
|
|
399
|
+
const existing = this.channelRepo.findByType('whatsapp');
|
|
400
|
+
if (existing) {
|
|
401
|
+
throw new Error('WhatsApp channel already configured. Update or remove it first.');
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Create channel record
|
|
405
|
+
const channel = this.channelRepo.create({
|
|
406
|
+
type: 'whatsapp',
|
|
407
|
+
name,
|
|
408
|
+
enabled: false, // Don't enable until QR code is scanned
|
|
409
|
+
config: {
|
|
410
|
+
allowedNumbers,
|
|
411
|
+
selfChatMode,
|
|
412
|
+
responsePrefix,
|
|
413
|
+
},
|
|
414
|
+
securityConfig: {
|
|
415
|
+
mode: securityMode,
|
|
416
|
+
allowedUsers: allowedNumbers,
|
|
417
|
+
pairingCodeTTL: 300, // 5 minutes
|
|
418
|
+
maxPairingAttempts: 5,
|
|
419
|
+
rateLimitPerMinute: 30,
|
|
420
|
+
},
|
|
421
|
+
status: 'disconnected',
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
return channel;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Add a new iMessage channel
|
|
429
|
+
*/
|
|
430
|
+
async addImessageChannel(
|
|
431
|
+
name: string,
|
|
432
|
+
cliPath?: string,
|
|
433
|
+
dbPath?: string,
|
|
434
|
+
allowedContacts?: string[],
|
|
435
|
+
securityMode: 'open' | 'allowlist' | 'pairing' = 'pairing',
|
|
436
|
+
dmPolicy: 'open' | 'allowlist' | 'pairing' | 'disabled' = 'pairing',
|
|
437
|
+
groupPolicy: 'open' | 'allowlist' | 'disabled' = 'allowlist'
|
|
438
|
+
): Promise<Channel> {
|
|
439
|
+
// Check if iMessage channel already exists
|
|
440
|
+
const existing = this.channelRepo.findByType('imessage');
|
|
441
|
+
if (existing) {
|
|
442
|
+
throw new Error('iMessage channel already configured. Update or remove it first.');
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Create channel record
|
|
446
|
+
const channel = this.channelRepo.create({
|
|
447
|
+
type: 'imessage',
|
|
448
|
+
name,
|
|
449
|
+
enabled: false, // Don't enable until connected
|
|
450
|
+
config: {
|
|
451
|
+
cliPath,
|
|
452
|
+
dbPath,
|
|
453
|
+
allowedContacts,
|
|
454
|
+
dmPolicy,
|
|
455
|
+
groupPolicy,
|
|
456
|
+
},
|
|
457
|
+
securityConfig: {
|
|
458
|
+
mode: securityMode,
|
|
459
|
+
allowedUsers: allowedContacts,
|
|
460
|
+
pairingCodeTTL: 300, // 5 minutes
|
|
461
|
+
maxPairingAttempts: 5,
|
|
462
|
+
rateLimitPerMinute: 30,
|
|
463
|
+
},
|
|
464
|
+
status: 'disconnected',
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
return channel;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Add a new Signal channel
|
|
472
|
+
*/
|
|
473
|
+
async addSignalChannel(
|
|
474
|
+
name: string,
|
|
475
|
+
phoneNumber: string,
|
|
476
|
+
dataDir?: string,
|
|
477
|
+
securityMode: 'open' | 'allowlist' | 'pairing' = 'pairing',
|
|
478
|
+
mode: 'native' | 'daemon' = 'native',
|
|
479
|
+
trustMode: 'tofu' | 'always' | 'manual' = 'tofu',
|
|
480
|
+
dmPolicy: 'open' | 'allowlist' | 'pairing' | 'disabled' = 'pairing',
|
|
481
|
+
groupPolicy: 'open' | 'allowlist' | 'disabled' = 'allowlist',
|
|
482
|
+
sendReadReceipts: boolean = true,
|
|
483
|
+
sendTypingIndicators: boolean = true
|
|
484
|
+
): Promise<Channel> {
|
|
485
|
+
// Check if Signal channel already exists
|
|
486
|
+
const existing = this.channelRepo.findByType('signal');
|
|
487
|
+
if (existing) {
|
|
488
|
+
throw new Error('Signal channel already configured. Update or remove it first.');
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// Create channel record
|
|
492
|
+
const channel = this.channelRepo.create({
|
|
493
|
+
type: 'signal',
|
|
494
|
+
name,
|
|
495
|
+
enabled: false, // Don't enable until connected
|
|
496
|
+
config: {
|
|
497
|
+
phoneNumber,
|
|
498
|
+
dataDir,
|
|
499
|
+
mode,
|
|
500
|
+
trustMode,
|
|
501
|
+
dmPolicy,
|
|
502
|
+
groupPolicy,
|
|
503
|
+
sendReadReceipts,
|
|
504
|
+
sendTypingIndicators,
|
|
505
|
+
},
|
|
506
|
+
securityConfig: {
|
|
507
|
+
mode: securityMode,
|
|
508
|
+
allowedUsers: [],
|
|
509
|
+
pairingCodeTTL: 300, // 5 minutes
|
|
510
|
+
maxPairingAttempts: 5,
|
|
511
|
+
rateLimitPerMinute: 30,
|
|
512
|
+
},
|
|
513
|
+
status: 'disconnected',
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
return channel;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Add a new Mattermost channel
|
|
521
|
+
*/
|
|
522
|
+
async addMattermostChannel(
|
|
523
|
+
name: string,
|
|
524
|
+
serverUrl: string,
|
|
525
|
+
token: string,
|
|
526
|
+
teamId?: string,
|
|
527
|
+
securityMode: 'open' | 'allowlist' | 'pairing' = 'pairing'
|
|
528
|
+
): Promise<Channel> {
|
|
529
|
+
// Check if Mattermost channel already exists
|
|
530
|
+
const existing = this.channelRepo.findByType('mattermost');
|
|
531
|
+
if (existing) {
|
|
532
|
+
throw new Error('Mattermost channel already configured. Update or remove it first.');
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// Create channel record
|
|
536
|
+
const channel = this.channelRepo.create({
|
|
537
|
+
type: 'mattermost',
|
|
538
|
+
name,
|
|
539
|
+
enabled: false, // Don't enable until connected
|
|
540
|
+
config: {
|
|
541
|
+
serverUrl,
|
|
542
|
+
token,
|
|
543
|
+
teamId,
|
|
544
|
+
},
|
|
545
|
+
securityConfig: {
|
|
546
|
+
mode: securityMode,
|
|
547
|
+
allowedUsers: [],
|
|
548
|
+
pairingCodeTTL: 300, // 5 minutes
|
|
549
|
+
maxPairingAttempts: 5,
|
|
550
|
+
rateLimitPerMinute: 30,
|
|
551
|
+
},
|
|
552
|
+
status: 'disconnected',
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
return channel;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* Add a new Matrix channel
|
|
560
|
+
*/
|
|
561
|
+
async addMatrixChannel(
|
|
562
|
+
name: string,
|
|
563
|
+
homeserver: string,
|
|
564
|
+
userId: string,
|
|
565
|
+
accessToken: string,
|
|
566
|
+
deviceId?: string,
|
|
567
|
+
roomIds?: string[],
|
|
568
|
+
securityMode: 'open' | 'allowlist' | 'pairing' = 'pairing'
|
|
569
|
+
): Promise<Channel> {
|
|
570
|
+
// Check if Matrix channel already exists
|
|
571
|
+
const existing = this.channelRepo.findByType('matrix');
|
|
572
|
+
if (existing) {
|
|
573
|
+
throw new Error('Matrix channel already configured. Update or remove it first.');
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// Create channel record
|
|
577
|
+
const channel = this.channelRepo.create({
|
|
578
|
+
type: 'matrix',
|
|
579
|
+
name,
|
|
580
|
+
enabled: false, // Don't enable until connected
|
|
581
|
+
config: {
|
|
582
|
+
homeserver,
|
|
583
|
+
userId,
|
|
584
|
+
accessToken,
|
|
585
|
+
deviceId,
|
|
586
|
+
roomIds,
|
|
587
|
+
},
|
|
588
|
+
securityConfig: {
|
|
589
|
+
mode: securityMode,
|
|
590
|
+
allowedUsers: [],
|
|
591
|
+
pairingCodeTTL: 300, // 5 minutes
|
|
592
|
+
maxPairingAttempts: 5,
|
|
593
|
+
rateLimitPerMinute: 30,
|
|
594
|
+
},
|
|
595
|
+
status: 'disconnected',
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
return channel;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* Add a new Twitch channel
|
|
603
|
+
*/
|
|
604
|
+
async addTwitchChannel(
|
|
605
|
+
name: string,
|
|
606
|
+
username: string,
|
|
607
|
+
oauthToken: string,
|
|
608
|
+
channels: string[],
|
|
609
|
+
allowWhispers: boolean = false,
|
|
610
|
+
securityMode: 'open' | 'allowlist' | 'pairing' = 'pairing'
|
|
611
|
+
): Promise<Channel> {
|
|
612
|
+
// Check if Twitch channel already exists
|
|
613
|
+
const existing = this.channelRepo.findByType('twitch');
|
|
614
|
+
if (existing) {
|
|
615
|
+
throw new Error('Twitch channel already configured. Update or remove it first.');
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// Create channel record
|
|
619
|
+
const channel = this.channelRepo.create({
|
|
620
|
+
type: 'twitch',
|
|
621
|
+
name,
|
|
622
|
+
enabled: false, // Don't enable until connected
|
|
623
|
+
config: {
|
|
624
|
+
username,
|
|
625
|
+
oauthToken,
|
|
626
|
+
channels,
|
|
627
|
+
allowWhispers,
|
|
628
|
+
},
|
|
629
|
+
securityConfig: {
|
|
630
|
+
mode: securityMode,
|
|
631
|
+
allowedUsers: [],
|
|
632
|
+
pairingCodeTTL: 300, // 5 minutes
|
|
633
|
+
maxPairingAttempts: 5,
|
|
634
|
+
rateLimitPerMinute: 30,
|
|
635
|
+
},
|
|
636
|
+
status: 'disconnected',
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
return channel;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Add a new LINE channel
|
|
644
|
+
*/
|
|
645
|
+
async addLineChannel(
|
|
646
|
+
name: string,
|
|
647
|
+
channelAccessToken: string,
|
|
648
|
+
channelSecret: string,
|
|
649
|
+
webhookPort: number = 3100,
|
|
650
|
+
securityMode: 'open' | 'allowlist' | 'pairing' = 'pairing'
|
|
651
|
+
): Promise<Channel> {
|
|
652
|
+
// Check if LINE channel already exists
|
|
653
|
+
const existing = this.channelRepo.findByType('line');
|
|
654
|
+
if (existing) {
|
|
655
|
+
throw new Error('LINE channel already configured. Update or remove it first.');
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// Create channel record
|
|
659
|
+
const channel = this.channelRepo.create({
|
|
660
|
+
type: 'line',
|
|
661
|
+
name,
|
|
662
|
+
enabled: false, // Don't enable until connected
|
|
663
|
+
config: {
|
|
664
|
+
channelAccessToken,
|
|
665
|
+
channelSecret,
|
|
666
|
+
webhookPort,
|
|
667
|
+
},
|
|
668
|
+
securityConfig: {
|
|
669
|
+
mode: securityMode,
|
|
670
|
+
allowedUsers: [],
|
|
671
|
+
pairingCodeTTL: 300,
|
|
672
|
+
maxPairingAttempts: 5,
|
|
673
|
+
rateLimitPerMinute: 30,
|
|
674
|
+
},
|
|
675
|
+
status: 'disconnected',
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
return channel;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* Add a new BlueBubbles channel
|
|
683
|
+
*/
|
|
684
|
+
async addBlueBubblesChannel(
|
|
685
|
+
name: string,
|
|
686
|
+
serverUrl: string,
|
|
687
|
+
password: string,
|
|
688
|
+
webhookPort: number = 3101,
|
|
689
|
+
allowedContacts?: string[],
|
|
690
|
+
securityMode: 'open' | 'allowlist' | 'pairing' = 'pairing'
|
|
691
|
+
): Promise<Channel> {
|
|
692
|
+
// Check if BlueBubbles channel already exists
|
|
693
|
+
const existing = this.channelRepo.findByType('bluebubbles');
|
|
694
|
+
if (existing) {
|
|
695
|
+
throw new Error('BlueBubbles channel already configured. Update or remove it first.');
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// Create channel record
|
|
699
|
+
const channel = this.channelRepo.create({
|
|
700
|
+
type: 'bluebubbles',
|
|
701
|
+
name,
|
|
702
|
+
enabled: false, // Don't enable until connected
|
|
703
|
+
config: {
|
|
704
|
+
serverUrl,
|
|
705
|
+
password,
|
|
706
|
+
webhookPort,
|
|
707
|
+
allowedContacts,
|
|
708
|
+
},
|
|
709
|
+
securityConfig: {
|
|
710
|
+
mode: securityMode,
|
|
711
|
+
allowedUsers: allowedContacts || [],
|
|
712
|
+
pairingCodeTTL: 300,
|
|
713
|
+
maxPairingAttempts: 5,
|
|
714
|
+
rateLimitPerMinute: 30,
|
|
715
|
+
},
|
|
716
|
+
status: 'disconnected',
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
return channel;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* Add a new Email channel
|
|
724
|
+
*/
|
|
725
|
+
async addEmailChannel(
|
|
726
|
+
name: string,
|
|
727
|
+
email: string,
|
|
728
|
+
password: string,
|
|
729
|
+
imapHost: string,
|
|
730
|
+
smtpHost: string,
|
|
731
|
+
displayName?: string,
|
|
732
|
+
allowedSenders?: string[],
|
|
733
|
+
subjectFilter?: string,
|
|
734
|
+
securityMode: 'open' | 'allowlist' | 'pairing' = 'pairing'
|
|
735
|
+
): Promise<Channel> {
|
|
736
|
+
// Check if Email channel already exists
|
|
737
|
+
const existing = this.channelRepo.findByType('email');
|
|
738
|
+
if (existing) {
|
|
739
|
+
throw new Error('Email channel already configured. Update or remove it first.');
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// Create channel record
|
|
743
|
+
const channel = this.channelRepo.create({
|
|
744
|
+
type: 'email',
|
|
745
|
+
name,
|
|
746
|
+
enabled: false, // Don't enable until connected
|
|
747
|
+
config: {
|
|
748
|
+
email,
|
|
749
|
+
password,
|
|
750
|
+
imapHost,
|
|
751
|
+
smtpHost,
|
|
752
|
+
displayName,
|
|
753
|
+
allowedSenders,
|
|
754
|
+
subjectFilter,
|
|
755
|
+
},
|
|
756
|
+
securityConfig: {
|
|
757
|
+
mode: securityMode,
|
|
758
|
+
allowedUsers: allowedSenders || [],
|
|
759
|
+
pairingCodeTTL: 300,
|
|
760
|
+
maxPairingAttempts: 5,
|
|
761
|
+
rateLimitPerMinute: 30,
|
|
762
|
+
},
|
|
763
|
+
status: 'disconnected',
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
return channel;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* Update a channel configuration
|
|
771
|
+
*/
|
|
772
|
+
updateChannel(channelId: string, updates: Partial<Channel>): void {
|
|
773
|
+
this.channelRepo.update(channelId, updates);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
/**
|
|
777
|
+
* Enable a channel and connect
|
|
778
|
+
*/
|
|
779
|
+
async enableChannel(channelId: string): Promise<void> {
|
|
780
|
+
const channel = this.channelRepo.findById(channelId);
|
|
781
|
+
if (!channel) {
|
|
782
|
+
throw new Error('Channel not found');
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
// Create and register adapter if not already done
|
|
786
|
+
let adapter = this.router.getAdapter(channel.type as ChannelType);
|
|
787
|
+
if (!adapter) {
|
|
788
|
+
adapter = this.createAdapterForChannel(channel);
|
|
789
|
+
this.router.registerAdapter(adapter);
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
// Update channel state
|
|
793
|
+
this.channelRepo.update(channelId, { enabled: true });
|
|
794
|
+
|
|
795
|
+
// Connect
|
|
796
|
+
await adapter.connect();
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
/**
|
|
800
|
+
* Disable a channel and disconnect
|
|
801
|
+
*/
|
|
802
|
+
async disableChannel(channelId: string): Promise<void> {
|
|
803
|
+
const channel = this.channelRepo.findById(channelId);
|
|
804
|
+
if (!channel) {
|
|
805
|
+
throw new Error('Channel not found');
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
const adapter = this.router.getAdapter(channel.type as ChannelType);
|
|
809
|
+
if (adapter) {
|
|
810
|
+
await adapter.disconnect();
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
this.channelRepo.update(channelId, { enabled: false, status: 'disconnected' });
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/**
|
|
817
|
+
* Enable WhatsApp channel and set up QR code forwarding
|
|
818
|
+
* This method connects the WhatsApp adapter and forwards QR codes to the renderer
|
|
819
|
+
*/
|
|
820
|
+
async enableWhatsAppWithQRForwarding(channelId: string): Promise<void> {
|
|
821
|
+
const channel = this.channelRepo.findById(channelId);
|
|
822
|
+
if (!channel || channel.type !== 'whatsapp') {
|
|
823
|
+
throw new Error('WhatsApp channel not found');
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// Create and register adapter if not already done
|
|
827
|
+
let adapter = this.router.getAdapter('whatsapp') as WhatsAppAdapter | undefined;
|
|
828
|
+
if (!adapter) {
|
|
829
|
+
adapter = this.createAdapterForChannel(channel) as WhatsAppAdapter;
|
|
830
|
+
this.router.registerAdapter(adapter);
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
// Set up QR code forwarding to renderer
|
|
834
|
+
const mainWindow = this.router.getMainWindow();
|
|
835
|
+
if (mainWindow && !mainWindow.isDestroyed()) {
|
|
836
|
+
adapter.onQrCode((qr: string) => {
|
|
837
|
+
console.log('WhatsApp QR code received, forwarding to renderer');
|
|
838
|
+
if (!mainWindow.isDestroyed()) {
|
|
839
|
+
mainWindow.webContents.send('whatsapp:qr-code', qr);
|
|
840
|
+
}
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
adapter.onStatusChange((status, error) => {
|
|
844
|
+
console.log(`WhatsApp status changed to: ${status}`);
|
|
845
|
+
if (!mainWindow.isDestroyed()) {
|
|
846
|
+
mainWindow.webContents.send('whatsapp:status', { status, error: error?.message });
|
|
847
|
+
if (status === 'connected') {
|
|
848
|
+
mainWindow.webContents.send('whatsapp:connected');
|
|
849
|
+
// Update channel status in database
|
|
850
|
+
this.channelRepo.update(channelId, {
|
|
851
|
+
enabled: true,
|
|
852
|
+
status: 'connected',
|
|
853
|
+
botUsername: adapter?.botUsername,
|
|
854
|
+
});
|
|
855
|
+
} else if (status === 'error') {
|
|
856
|
+
this.channelRepo.update(channelId, { status: 'error' });
|
|
857
|
+
} else if (status === 'disconnected') {
|
|
858
|
+
this.channelRepo.update(channelId, { status: 'disconnected' });
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// Update channel state to connecting
|
|
865
|
+
this.channelRepo.update(channelId, { enabled: true, status: 'connecting' });
|
|
866
|
+
|
|
867
|
+
// Connect (this will trigger QR code generation)
|
|
868
|
+
await adapter.connect();
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
/**
|
|
872
|
+
* Get WhatsApp channel info including QR code
|
|
873
|
+
*/
|
|
874
|
+
async getWhatsAppInfo(): Promise<{ qrCode?: string; phoneNumber?: string; status?: string }> {
|
|
875
|
+
const channel = this.channelRepo.findByType('whatsapp');
|
|
876
|
+
if (!channel) {
|
|
877
|
+
return {};
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
const adapter = this.router.getAdapter('whatsapp') as WhatsAppAdapter | undefined;
|
|
881
|
+
if (!adapter) {
|
|
882
|
+
return { status: channel.status };
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
return {
|
|
886
|
+
qrCode: adapter.qrCode,
|
|
887
|
+
phoneNumber: adapter.botUsername,
|
|
888
|
+
status: adapter.status,
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
/**
|
|
893
|
+
* Logout from WhatsApp and clear credentials
|
|
894
|
+
*/
|
|
895
|
+
async whatsAppLogout(): Promise<void> {
|
|
896
|
+
const adapter = this.router.getAdapter('whatsapp') as WhatsAppAdapter | undefined;
|
|
897
|
+
if (adapter) {
|
|
898
|
+
await adapter.logout();
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
const channel = this.channelRepo.findByType('whatsapp');
|
|
902
|
+
if (channel) {
|
|
903
|
+
this.channelRepo.update(channel.id, { enabled: false, status: 'disconnected', botUsername: undefined });
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
/**
|
|
908
|
+
* Remove a channel
|
|
909
|
+
*/
|
|
910
|
+
async removeChannel(channelId: string): Promise<void> {
|
|
911
|
+
await this.disableChannel(channelId);
|
|
912
|
+
|
|
913
|
+
// Delete associated data first (to avoid foreign key constraint errors)
|
|
914
|
+
this.messageRepo.deleteByChannelId(channelId);
|
|
915
|
+
this.sessionRepo.deleteByChannelId(channelId);
|
|
916
|
+
this.userRepo.deleteByChannelId(channelId);
|
|
917
|
+
|
|
918
|
+
// Now delete the channel
|
|
919
|
+
this.channelRepo.delete(channelId);
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
/**
|
|
923
|
+
* Test a channel connection without enabling it
|
|
924
|
+
*/
|
|
925
|
+
async testChannel(channelId: string): Promise<{ success: boolean; error?: string; botUsername?: string }> {
|
|
926
|
+
const channel = this.channelRepo.findById(channelId);
|
|
927
|
+
if (!channel) {
|
|
928
|
+
return { success: false, error: 'Channel not found' };
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
try {
|
|
932
|
+
const adapter = this.createAdapterForChannel(channel);
|
|
933
|
+
await adapter.connect();
|
|
934
|
+
const info = await adapter.getInfo();
|
|
935
|
+
await adapter.disconnect();
|
|
936
|
+
|
|
937
|
+
return {
|
|
938
|
+
success: true,
|
|
939
|
+
botUsername: info.botUsername,
|
|
940
|
+
};
|
|
941
|
+
} catch (error) {
|
|
942
|
+
return {
|
|
943
|
+
success: false,
|
|
944
|
+
error: error instanceof Error ? error.message : String(error),
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
/**
|
|
950
|
+
* Get all channels
|
|
951
|
+
*/
|
|
952
|
+
getChannels(): Channel[] {
|
|
953
|
+
return this.channelRepo.findAll();
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
/**
|
|
957
|
+
* Get a channel by ID
|
|
958
|
+
*/
|
|
959
|
+
getChannel(channelId: string): Channel | undefined {
|
|
960
|
+
return this.channelRepo.findById(channelId);
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
/**
|
|
964
|
+
* Get channel by type
|
|
965
|
+
*/
|
|
966
|
+
getChannelByType(type: string): Channel | undefined {
|
|
967
|
+
return this.channelRepo.findByType(type);
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
// User Management
|
|
971
|
+
|
|
972
|
+
/**
|
|
973
|
+
* Generate a pairing code for a user
|
|
974
|
+
*/
|
|
975
|
+
generatePairingCode(channelId: string, userId?: string, displayName?: string): string {
|
|
976
|
+
const channel = this.channelRepo.findById(channelId);
|
|
977
|
+
if (!channel) {
|
|
978
|
+
throw new Error('Channel not found');
|
|
979
|
+
}
|
|
980
|
+
return this.securityManager.generatePairingCode(channel, userId, displayName);
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
/**
|
|
984
|
+
* Grant access to a user
|
|
985
|
+
*/
|
|
986
|
+
grantUserAccess(channelId: string, userId: string, displayName?: string): void {
|
|
987
|
+
this.securityManager.grantAccess(channelId, userId, displayName);
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
/**
|
|
991
|
+
* Revoke user access
|
|
992
|
+
*/
|
|
993
|
+
revokeUserAccess(channelId: string, userId: string): void {
|
|
994
|
+
this.securityManager.revokeAccess(channelId, userId);
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
/**
|
|
998
|
+
* Get users for a channel
|
|
999
|
+
* Automatically cleans up expired pending pairing entries
|
|
1000
|
+
*/
|
|
1001
|
+
getChannelUsers(channelId: string): ReturnType<typeof this.userRepo.findByChannelId> {
|
|
1002
|
+
// Use securityManager to trigger cleanup of expired pending entries
|
|
1003
|
+
return this.securityManager.getChannelUsers(channelId);
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
// Messaging
|
|
1007
|
+
|
|
1008
|
+
/**
|
|
1009
|
+
* Send a message to a channel chat
|
|
1010
|
+
*/
|
|
1011
|
+
async sendMessage(
|
|
1012
|
+
channelType: ChannelType,
|
|
1013
|
+
chatId: string,
|
|
1014
|
+
text: string,
|
|
1015
|
+
options?: { replyTo?: string; parseMode?: 'text' | 'markdown' | 'html' }
|
|
1016
|
+
): Promise<string> {
|
|
1017
|
+
return this.router.sendMessage(channelType, {
|
|
1018
|
+
chatId,
|
|
1019
|
+
text,
|
|
1020
|
+
replyTo: options?.replyTo,
|
|
1021
|
+
parseMode: options?.parseMode,
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
/**
|
|
1026
|
+
* Send a message to a session's chat
|
|
1027
|
+
*/
|
|
1028
|
+
async sendMessageToSession(
|
|
1029
|
+
sessionId: string,
|
|
1030
|
+
text: string,
|
|
1031
|
+
options?: { replyTo?: string; parseMode?: 'text' | 'markdown' | 'html' }
|
|
1032
|
+
): Promise<string | null> {
|
|
1033
|
+
const session = this.sessionManager.getSession(sessionId);
|
|
1034
|
+
if (!session) {
|
|
1035
|
+
console.error('Session not found:', sessionId);
|
|
1036
|
+
return null;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
const channel = this.channelRepo.findById(session.channelId);
|
|
1040
|
+
if (!channel) {
|
|
1041
|
+
console.error('Channel not found:', session.channelId);
|
|
1042
|
+
return null;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
return this.router.sendMessage(channel.type as ChannelType, {
|
|
1046
|
+
chatId: session.chatId,
|
|
1047
|
+
text,
|
|
1048
|
+
replyTo: options?.replyTo,
|
|
1049
|
+
parseMode: options?.parseMode,
|
|
1050
|
+
});
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
// Events
|
|
1054
|
+
|
|
1055
|
+
/**
|
|
1056
|
+
* Register an event handler
|
|
1057
|
+
*/
|
|
1058
|
+
onEvent(handler: GatewayEventHandler): void {
|
|
1059
|
+
this.router.onEvent(handler);
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
// Task response methods
|
|
1063
|
+
|
|
1064
|
+
/**
|
|
1065
|
+
* Send a task update to the channel
|
|
1066
|
+
*/
|
|
1067
|
+
async sendTaskUpdate(taskId: string, text: string): Promise<void> {
|
|
1068
|
+
return this.router.sendTaskUpdate(taskId, text);
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
/**
|
|
1072
|
+
* Handle task completion
|
|
1073
|
+
*/
|
|
1074
|
+
async handleTaskCompletion(taskId: string, result?: string): Promise<void> {
|
|
1075
|
+
return this.router.handleTaskCompletion(taskId, result);
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
/**
|
|
1079
|
+
* Handle task failure
|
|
1080
|
+
*/
|
|
1081
|
+
async handleTaskFailure(taskId: string, error: string): Promise<void> {
|
|
1082
|
+
return this.router.handleTaskFailure(taskId, error);
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
// Private methods
|
|
1086
|
+
|
|
1087
|
+
/**
|
|
1088
|
+
* Load and register channel adapters
|
|
1089
|
+
*/
|
|
1090
|
+
private async loadChannels(): Promise<void> {
|
|
1091
|
+
const channels = this.channelRepo.findAll();
|
|
1092
|
+
|
|
1093
|
+
for (const channel of channels) {
|
|
1094
|
+
try {
|
|
1095
|
+
const adapter = this.createAdapterForChannel(channel);
|
|
1096
|
+
this.router.registerAdapter(adapter);
|
|
1097
|
+
} catch (error) {
|
|
1098
|
+
console.error(`Failed to create adapter for channel ${channel.type}:`, error);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
/**
|
|
1104
|
+
* Create an adapter for a channel
|
|
1105
|
+
*/
|
|
1106
|
+
private createAdapterForChannel(channel: Channel): ChannelAdapter {
|
|
1107
|
+
switch (channel.type) {
|
|
1108
|
+
case 'telegram':
|
|
1109
|
+
return createTelegramAdapter({
|
|
1110
|
+
enabled: channel.enabled,
|
|
1111
|
+
botToken: channel.config.botToken as string,
|
|
1112
|
+
webhookUrl: channel.config.webhookUrl as string | undefined,
|
|
1113
|
+
});
|
|
1114
|
+
|
|
1115
|
+
case 'discord':
|
|
1116
|
+
return createDiscordAdapter({
|
|
1117
|
+
enabled: channel.enabled,
|
|
1118
|
+
botToken: channel.config.botToken as string,
|
|
1119
|
+
applicationId: channel.config.applicationId as string,
|
|
1120
|
+
guildIds: channel.config.guildIds as string[] | undefined,
|
|
1121
|
+
});
|
|
1122
|
+
|
|
1123
|
+
case 'slack':
|
|
1124
|
+
return createSlackAdapter({
|
|
1125
|
+
enabled: channel.enabled,
|
|
1126
|
+
botToken: channel.config.botToken as string,
|
|
1127
|
+
appToken: channel.config.appToken as string,
|
|
1128
|
+
signingSecret: channel.config.signingSecret as string | undefined,
|
|
1129
|
+
});
|
|
1130
|
+
|
|
1131
|
+
case 'whatsapp':
|
|
1132
|
+
return createWhatsAppAdapter({
|
|
1133
|
+
enabled: channel.enabled,
|
|
1134
|
+
allowedNumbers: channel.config.allowedNumbers as string[] | undefined,
|
|
1135
|
+
printQrToTerminal: true, // For debugging
|
|
1136
|
+
selfChatMode: channel.config.selfChatMode as boolean | undefined ?? true,
|
|
1137
|
+
responsePrefix: channel.config.responsePrefix as string | undefined ?? '🤖',
|
|
1138
|
+
});
|
|
1139
|
+
|
|
1140
|
+
case 'imessage':
|
|
1141
|
+
return createImessageAdapter({
|
|
1142
|
+
enabled: channel.enabled,
|
|
1143
|
+
cliPath: channel.config.cliPath as string | undefined,
|
|
1144
|
+
dbPath: channel.config.dbPath as string | undefined,
|
|
1145
|
+
dmPolicy: channel.config.dmPolicy as 'open' | 'allowlist' | 'pairing' | 'disabled' | undefined,
|
|
1146
|
+
groupPolicy: channel.config.groupPolicy as 'open' | 'allowlist' | 'disabled' | undefined,
|
|
1147
|
+
allowedContacts: channel.config.allowedContacts as string[] | undefined,
|
|
1148
|
+
responsePrefix: channel.config.responsePrefix as string | undefined,
|
|
1149
|
+
});
|
|
1150
|
+
|
|
1151
|
+
case 'signal':
|
|
1152
|
+
return createSignalAdapter({
|
|
1153
|
+
enabled: channel.enabled,
|
|
1154
|
+
phoneNumber: channel.config.phoneNumber as string,
|
|
1155
|
+
cliPath: channel.config.cliPath as string | undefined,
|
|
1156
|
+
dataDir: channel.config.dataDir as string | undefined,
|
|
1157
|
+
mode: channel.config.mode as 'native' | 'daemon' | undefined,
|
|
1158
|
+
socketPath: channel.config.socketPath as string | undefined,
|
|
1159
|
+
trustMode: channel.config.trustMode as 'tofu' | 'always' | 'manual' | undefined,
|
|
1160
|
+
dmPolicy: channel.config.dmPolicy as 'open' | 'allowlist' | 'pairing' | 'disabled' | undefined,
|
|
1161
|
+
groupPolicy: channel.config.groupPolicy as 'open' | 'allowlist' | 'disabled' | undefined,
|
|
1162
|
+
allowedNumbers: channel.config.allowedNumbers as string[] | undefined,
|
|
1163
|
+
sendReadReceipts: channel.config.sendReadReceipts as boolean | undefined,
|
|
1164
|
+
sendTypingIndicators: channel.config.sendTypingIndicators as boolean | undefined,
|
|
1165
|
+
responsePrefix: channel.config.responsePrefix as string | undefined,
|
|
1166
|
+
});
|
|
1167
|
+
|
|
1168
|
+
case 'mattermost':
|
|
1169
|
+
return createMattermostAdapter({
|
|
1170
|
+
enabled: channel.enabled,
|
|
1171
|
+
serverUrl: channel.config.serverUrl as string,
|
|
1172
|
+
token: channel.config.token as string,
|
|
1173
|
+
teamId: channel.config.teamId as string | undefined,
|
|
1174
|
+
responsePrefix: channel.config.responsePrefix as string | undefined,
|
|
1175
|
+
});
|
|
1176
|
+
|
|
1177
|
+
case 'matrix':
|
|
1178
|
+
return createMatrixAdapter({
|
|
1179
|
+
enabled: channel.enabled,
|
|
1180
|
+
homeserver: channel.config.homeserver as string,
|
|
1181
|
+
userId: channel.config.userId as string,
|
|
1182
|
+
accessToken: channel.config.accessToken as string,
|
|
1183
|
+
deviceId: channel.config.deviceId as string | undefined,
|
|
1184
|
+
roomIds: channel.config.roomIds as string[] | undefined,
|
|
1185
|
+
sendTypingIndicators: channel.config.sendTypingIndicators as boolean | undefined,
|
|
1186
|
+
sendReadReceipts: channel.config.sendReadReceipts as boolean | undefined,
|
|
1187
|
+
responsePrefix: channel.config.responsePrefix as string | undefined,
|
|
1188
|
+
});
|
|
1189
|
+
|
|
1190
|
+
case 'twitch':
|
|
1191
|
+
return createTwitchAdapter({
|
|
1192
|
+
enabled: channel.enabled,
|
|
1193
|
+
username: channel.config.username as string,
|
|
1194
|
+
oauthToken: channel.config.oauthToken as string,
|
|
1195
|
+
channels: channel.config.channels as string[],
|
|
1196
|
+
allowWhispers: channel.config.allowWhispers as boolean | undefined,
|
|
1197
|
+
responsePrefix: channel.config.responsePrefix as string | undefined,
|
|
1198
|
+
});
|
|
1199
|
+
|
|
1200
|
+
case 'line':
|
|
1201
|
+
return createLineAdapter({
|
|
1202
|
+
enabled: channel.enabled,
|
|
1203
|
+
channelAccessToken: channel.config.channelAccessToken as string,
|
|
1204
|
+
channelSecret: channel.config.channelSecret as string,
|
|
1205
|
+
webhookPort: channel.config.webhookPort as number | undefined,
|
|
1206
|
+
webhookPath: channel.config.webhookPath as string | undefined,
|
|
1207
|
+
responsePrefix: channel.config.responsePrefix as string | undefined,
|
|
1208
|
+
});
|
|
1209
|
+
|
|
1210
|
+
case 'bluebubbles':
|
|
1211
|
+
return createBlueBubblesAdapter({
|
|
1212
|
+
enabled: channel.enabled,
|
|
1213
|
+
serverUrl: channel.config.serverUrl as string,
|
|
1214
|
+
password: channel.config.password as string,
|
|
1215
|
+
webhookPort: channel.config.webhookPort as number | undefined,
|
|
1216
|
+
webhookPath: channel.config.webhookPath as string | undefined,
|
|
1217
|
+
pollInterval: channel.config.pollInterval as number | undefined,
|
|
1218
|
+
allowedContacts: channel.config.allowedContacts as string[] | undefined,
|
|
1219
|
+
responsePrefix: channel.config.responsePrefix as string | undefined,
|
|
1220
|
+
});
|
|
1221
|
+
|
|
1222
|
+
case 'email':
|
|
1223
|
+
return createEmailAdapter({
|
|
1224
|
+
enabled: channel.enabled,
|
|
1225
|
+
imapHost: channel.config.imapHost as string,
|
|
1226
|
+
imapPort: channel.config.imapPort as number | undefined,
|
|
1227
|
+
imapSecure: channel.config.imapSecure as boolean | undefined,
|
|
1228
|
+
smtpHost: channel.config.smtpHost as string,
|
|
1229
|
+
smtpPort: channel.config.smtpPort as number | undefined,
|
|
1230
|
+
smtpSecure: channel.config.smtpSecure as boolean | undefined,
|
|
1231
|
+
email: channel.config.email as string,
|
|
1232
|
+
password: channel.config.password as string,
|
|
1233
|
+
displayName: channel.config.displayName as string | undefined,
|
|
1234
|
+
mailbox: channel.config.mailbox as string | undefined,
|
|
1235
|
+
pollInterval: channel.config.pollInterval as number | undefined,
|
|
1236
|
+
markAsRead: channel.config.markAsRead as boolean | undefined,
|
|
1237
|
+
allowedSenders: channel.config.allowedSenders as string[] | undefined,
|
|
1238
|
+
subjectFilter: channel.config.subjectFilter as string | undefined,
|
|
1239
|
+
responsePrefix: channel.config.responsePrefix as string | undefined,
|
|
1240
|
+
});
|
|
1241
|
+
|
|
1242
|
+
default:
|
|
1243
|
+
throw new Error(`Unsupported channel type: ${channel.type}`);
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
// Re-export types and components
|
|
1249
|
+
export * from './channels/types';
|
|
1250
|
+
export * from './router';
|
|
1251
|
+
export * from './session';
|
|
1252
|
+
export * from './security';
|
|
1253
|
+
export * from './channel-registry';
|
|
1254
|
+
export { TelegramAdapter, createTelegramAdapter } from './channels/telegram';
|
|
1255
|
+
export { DiscordAdapter, createDiscordAdapter } from './channels/discord';
|
|
1256
|
+
export { SlackAdapter, createSlackAdapter } from './channels/slack';
|
|
1257
|
+
export { WhatsAppAdapter, createWhatsAppAdapter } from './channels/whatsapp';
|
|
1258
|
+
export { ImessageAdapter, createImessageAdapter } from './channels/imessage';
|
|
1259
|
+
export { SignalAdapter, createSignalAdapter } from './channels/signal';
|
|
1260
|
+
export { SignalClient } from './channels/signal-client';
|
|
1261
|
+
export { MattermostAdapter, createMattermostAdapter } from './channels/mattermost';
|
|
1262
|
+
export { MattermostClient } from './channels/mattermost-client';
|
|
1263
|
+
export { MatrixAdapter, createMatrixAdapter } from './channels/matrix';
|
|
1264
|
+
export { MatrixClient } from './channels/matrix-client';
|
|
1265
|
+
export { TwitchAdapter, createTwitchAdapter } from './channels/twitch';
|
|
1266
|
+
export { TwitchClient } from './channels/twitch-client';
|
|
1267
|
+
export { LineAdapter, createLineAdapter } from './channels/line';
|
|
1268
|
+
export { LineClient } from './channels/line-client';
|
|
1269
|
+
export { BlueBubblesAdapter, createBlueBubblesAdapter } from './channels/bluebubbles';
|
|
1270
|
+
export { BlueBubblesClient } from './channels/bluebubbles-client';
|
|
1271
|
+
export { EmailAdapter, createEmailAdapter } from './channels/email';
|
|
1272
|
+
export { EmailClient } from './channels/email-client';
|
|
1273
|
+
export { TunnelManager, getAvailableTunnelProviders, createAutoTunnel } from './tunnel';
|
|
1274
|
+
export type { TunnelProvider, TunnelStatus, TunnelConfig, TunnelInfo } from './tunnel';
|