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,681 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Microsoft Teams Channel Adapter
|
|
3
|
+
*
|
|
4
|
+
* Implements the ChannelAdapter interface using Microsoft Bot Framework SDK.
|
|
5
|
+
* Supports direct messages, channel mentions, and group chats.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
CloudAdapter,
|
|
10
|
+
ConfigurationBotFrameworkAuthentication,
|
|
11
|
+
TurnContext,
|
|
12
|
+
Activity,
|
|
13
|
+
ActivityTypes,
|
|
14
|
+
ConversationReference,
|
|
15
|
+
MessageFactory,
|
|
16
|
+
} from 'botbuilder';
|
|
17
|
+
import * as http from 'http';
|
|
18
|
+
import * as fs from 'fs';
|
|
19
|
+
import * as path from 'path';
|
|
20
|
+
import {
|
|
21
|
+
ChannelAdapter,
|
|
22
|
+
ChannelStatus,
|
|
23
|
+
IncomingMessage,
|
|
24
|
+
OutgoingMessage,
|
|
25
|
+
MessageHandler,
|
|
26
|
+
ErrorHandler,
|
|
27
|
+
StatusHandler,
|
|
28
|
+
ChannelInfo,
|
|
29
|
+
TeamsConfig,
|
|
30
|
+
} from './types';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Simple TTL cache for message deduplication
|
|
34
|
+
*/
|
|
35
|
+
class MessageDeduplicationCache {
|
|
36
|
+
private cache: Map<string, number> = new Map();
|
|
37
|
+
private readonly ttlMs: number;
|
|
38
|
+
|
|
39
|
+
constructor(ttlMs: number = 60000) {
|
|
40
|
+
this.ttlMs = ttlMs;
|
|
41
|
+
// Cleanup expired entries every minute
|
|
42
|
+
setInterval(() => this.cleanup(), 60000);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
has(messageId: string): boolean {
|
|
46
|
+
const timestamp = this.cache.get(messageId);
|
|
47
|
+
if (!timestamp) return false;
|
|
48
|
+
if (Date.now() - timestamp > this.ttlMs) {
|
|
49
|
+
this.cache.delete(messageId);
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
add(messageId: string): void {
|
|
56
|
+
this.cache.set(messageId, Date.now());
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private cleanup(): void {
|
|
60
|
+
const now = Date.now();
|
|
61
|
+
for (const [key, timestamp] of this.cache.entries()) {
|
|
62
|
+
if (now - timestamp > this.ttlMs) {
|
|
63
|
+
this.cache.delete(key);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export class TeamsAdapter implements ChannelAdapter {
|
|
70
|
+
readonly type = 'teams' as const;
|
|
71
|
+
|
|
72
|
+
private adapter: CloudAdapter | null = null;
|
|
73
|
+
private server: http.Server | null = null;
|
|
74
|
+
private _status: ChannelStatus = 'disconnected';
|
|
75
|
+
private _botUsername?: string;
|
|
76
|
+
private _botId?: string;
|
|
77
|
+
private messageHandlers: MessageHandler[] = [];
|
|
78
|
+
private errorHandlers: ErrorHandler[] = [];
|
|
79
|
+
private statusHandlers: StatusHandler[] = [];
|
|
80
|
+
private config: TeamsConfig;
|
|
81
|
+
private conversationReferences: Map<string, Partial<ConversationReference>> = new Map();
|
|
82
|
+
private deduplicationCache: MessageDeduplicationCache;
|
|
83
|
+
private reconnectAttempts = 0;
|
|
84
|
+
private reconnectTimer: NodeJS.Timeout | null = null;
|
|
85
|
+
|
|
86
|
+
constructor(config: TeamsConfig) {
|
|
87
|
+
this.config = config;
|
|
88
|
+
this.deduplicationCache = new MessageDeduplicationCache();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
get status(): ChannelStatus {
|
|
92
|
+
return this._status;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
get botUsername(): string | undefined {
|
|
96
|
+
return this._botUsername;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Connect to Microsoft Teams via Bot Framework
|
|
101
|
+
*/
|
|
102
|
+
async connect(): Promise<void> {
|
|
103
|
+
if (this._status === 'connected' || this._status === 'connecting') {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
this.setStatus('connecting');
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
// Create Bot Framework authentication
|
|
111
|
+
const botFrameworkAuth = new ConfigurationBotFrameworkAuthentication({
|
|
112
|
+
MicrosoftAppId: this.config.appId,
|
|
113
|
+
MicrosoftAppPassword: this.config.appPassword,
|
|
114
|
+
MicrosoftAppTenantId: this.config.tenantId,
|
|
115
|
+
MicrosoftAppType: this.config.tenantId ? 'SingleTenant' : 'MultiTenant',
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Create the adapter
|
|
119
|
+
this.adapter = new CloudAdapter(botFrameworkAuth);
|
|
120
|
+
|
|
121
|
+
// Set up error handling for the adapter
|
|
122
|
+
this.adapter.onTurnError = async (context: TurnContext, error: Error) => {
|
|
123
|
+
console.error('Teams adapter turn error:', error);
|
|
124
|
+
this.handleError(error, 'turnError');
|
|
125
|
+
|
|
126
|
+
// Send error message to user
|
|
127
|
+
try {
|
|
128
|
+
await context.sendActivity('Sorry, something went wrong processing your message.');
|
|
129
|
+
} catch (sendError) {
|
|
130
|
+
console.error('Failed to send error message:', sendError);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// Set bot info from config
|
|
135
|
+
this._botUsername = this.config.displayName || 'Teams Bot';
|
|
136
|
+
|
|
137
|
+
// Start the HTTP server for receiving webhooks
|
|
138
|
+
await this.startWebhookServer();
|
|
139
|
+
|
|
140
|
+
console.log(`Teams bot "${this._botUsername}" is connected on port ${this.config.webhookPort || 3978}`);
|
|
141
|
+
this.setStatus('connected');
|
|
142
|
+
this.reconnectAttempts = 0;
|
|
143
|
+
} catch (error) {
|
|
144
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
145
|
+
this.setStatus('error', err);
|
|
146
|
+
this.scheduleReconnect();
|
|
147
|
+
throw err;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Start the webhook server to receive messages from Teams
|
|
153
|
+
*/
|
|
154
|
+
private async startWebhookServer(): Promise<void> {
|
|
155
|
+
const port = this.config.webhookPort || 3978;
|
|
156
|
+
|
|
157
|
+
return new Promise((resolve, reject) => {
|
|
158
|
+
this.server = http.createServer(async (req, res) => {
|
|
159
|
+
if (req.method === 'POST' && req.url === '/api/messages') {
|
|
160
|
+
let body = '';
|
|
161
|
+
req.on('data', chunk => {
|
|
162
|
+
body += chunk.toString();
|
|
163
|
+
});
|
|
164
|
+
req.on('end', async () => {
|
|
165
|
+
try {
|
|
166
|
+
await this.processIncomingActivity(req, res, body);
|
|
167
|
+
} catch (error) {
|
|
168
|
+
console.error('Error processing Teams message:', error);
|
|
169
|
+
res.writeHead(500);
|
|
170
|
+
res.end('Internal Server Error');
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
} else if (req.method === 'GET' && req.url === '/api/health') {
|
|
174
|
+
// Health check endpoint
|
|
175
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
176
|
+
res.end(JSON.stringify({ status: 'ok', bot: this._botUsername }));
|
|
177
|
+
} else {
|
|
178
|
+
res.writeHead(404);
|
|
179
|
+
res.end('Not Found');
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
this.server.on('error', (error: NodeJS.ErrnoException) => {
|
|
184
|
+
if (error.code === 'EADDRINUSE') {
|
|
185
|
+
reject(new Error(`Port ${port} is already in use. Please choose a different webhook port.`));
|
|
186
|
+
} else if (error.code === 'EACCES') {
|
|
187
|
+
reject(new Error(`Permission denied to use port ${port}. Try a port above 1024.`));
|
|
188
|
+
} else {
|
|
189
|
+
reject(error);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
this.server.listen(port, () => {
|
|
194
|
+
console.log(`Teams webhook server listening on port ${port}`);
|
|
195
|
+
resolve();
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Process incoming activity from Teams
|
|
202
|
+
*/
|
|
203
|
+
private async processIncomingActivity(
|
|
204
|
+
req: http.IncomingMessage,
|
|
205
|
+
res: http.ServerResponse,
|
|
206
|
+
body: string
|
|
207
|
+
): Promise<void> {
|
|
208
|
+
if (!this.adapter) {
|
|
209
|
+
res.writeHead(500);
|
|
210
|
+
res.end('Adapter not initialized');
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
const activity: Activity = JSON.parse(body);
|
|
216
|
+
|
|
217
|
+
// Create a fake request object for the adapter
|
|
218
|
+
const fakeReq = {
|
|
219
|
+
body: activity,
|
|
220
|
+
headers: req.headers,
|
|
221
|
+
method: req.method,
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
await this.adapter.process(fakeReq as any, res as any, async (context: TurnContext) => {
|
|
225
|
+
await this.handleActivity(context);
|
|
226
|
+
});
|
|
227
|
+
} catch (error) {
|
|
228
|
+
console.error('Error parsing Teams activity:', error);
|
|
229
|
+
res.writeHead(400);
|
|
230
|
+
res.end('Bad Request');
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Handle incoming activity from Teams
|
|
236
|
+
*/
|
|
237
|
+
private async handleActivity(context: TurnContext): Promise<void> {
|
|
238
|
+
const activity = context.activity;
|
|
239
|
+
|
|
240
|
+
// Store conversation reference for proactive messaging
|
|
241
|
+
const conversationRef = TurnContext.getConversationReference(activity);
|
|
242
|
+
this.conversationReferences.set(activity.conversation.id, conversationRef);
|
|
243
|
+
|
|
244
|
+
// Handle different activity types
|
|
245
|
+
switch (activity.type) {
|
|
246
|
+
case ActivityTypes.Message:
|
|
247
|
+
await this.handleMessage(context, activity);
|
|
248
|
+
break;
|
|
249
|
+
|
|
250
|
+
case ActivityTypes.ConversationUpdate:
|
|
251
|
+
await this.handleConversationUpdate(context, activity);
|
|
252
|
+
break;
|
|
253
|
+
|
|
254
|
+
case ActivityTypes.MessageReaction:
|
|
255
|
+
// Handle reactions if needed
|
|
256
|
+
break;
|
|
257
|
+
|
|
258
|
+
default:
|
|
259
|
+
// Log unhandled activity types for debugging
|
|
260
|
+
console.log(`Unhandled activity type: ${activity.type}`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Handle incoming message activity
|
|
266
|
+
*/
|
|
267
|
+
private async handleMessage(context: TurnContext, activity: Activity): Promise<void> {
|
|
268
|
+
// Skip if no text content
|
|
269
|
+
if (!activity.text) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Deduplication check
|
|
274
|
+
const messageId = activity.id || `${activity.conversation.id}-${activity.timestamp}`;
|
|
275
|
+
if (this.config.deduplicationEnabled !== false && this.deduplicationCache.has(messageId)) {
|
|
276
|
+
console.log(`Skipping duplicate Teams message: ${messageId}`);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
this.deduplicationCache.add(messageId);
|
|
280
|
+
|
|
281
|
+
// Remove bot mention from text
|
|
282
|
+
let text = activity.text;
|
|
283
|
+
if (activity.entities) {
|
|
284
|
+
for (const entity of activity.entities) {
|
|
285
|
+
if (entity.type === 'mention' && entity.mentioned?.id === activity.recipient?.id) {
|
|
286
|
+
// Remove the mention from the text
|
|
287
|
+
const mentionText = entity.text || '';
|
|
288
|
+
text = text.replace(mentionText, '').trim();
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Skip empty messages after removing mentions
|
|
294
|
+
if (!text.trim()) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Get user info
|
|
299
|
+
const userName = activity.from?.name || 'Unknown User';
|
|
300
|
+
const userId = activity.from?.id || '';
|
|
301
|
+
|
|
302
|
+
// Map to IncomingMessage format
|
|
303
|
+
const incomingMessage: IncomingMessage = {
|
|
304
|
+
messageId: messageId,
|
|
305
|
+
channel: 'teams',
|
|
306
|
+
userId: userId,
|
|
307
|
+
userName: userName,
|
|
308
|
+
chatId: activity.conversation.id,
|
|
309
|
+
text: text.trim(),
|
|
310
|
+
timestamp: activity.timestamp ? new Date(activity.timestamp) : new Date(),
|
|
311
|
+
replyTo: activity.replyToId,
|
|
312
|
+
threadId: activity.conversation.id,
|
|
313
|
+
raw: activity,
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
console.log(`Processing Teams message from ${userName}: ${text.slice(0, 50)}...`);
|
|
317
|
+
await this.handleIncomingMessage(incomingMessage);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Handle conversation update activity (new members, etc.)
|
|
322
|
+
*/
|
|
323
|
+
private async handleConversationUpdate(context: TurnContext, activity: Activity): Promise<void> {
|
|
324
|
+
// Welcome new members
|
|
325
|
+
if (activity.membersAdded) {
|
|
326
|
+
for (const member of activity.membersAdded) {
|
|
327
|
+
// Don't welcome the bot itself
|
|
328
|
+
if (member.id !== activity.recipient?.id) {
|
|
329
|
+
// Could send a welcome message here if desired
|
|
330
|
+
console.log(`New member added to conversation: ${member.name}`);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Schedule reconnection with exponential backoff
|
|
338
|
+
*/
|
|
339
|
+
private scheduleReconnect(): void {
|
|
340
|
+
if (!this.config.autoReconnect) return;
|
|
341
|
+
|
|
342
|
+
const maxAttempts = this.config.maxReconnectAttempts || 5;
|
|
343
|
+
if (this.reconnectAttempts >= maxAttempts) {
|
|
344
|
+
console.error(`Teams: Max reconnection attempts (${maxAttempts}) reached`);
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
|
|
349
|
+
this.reconnectAttempts++;
|
|
350
|
+
|
|
351
|
+
console.log(`Teams: Scheduling reconnection attempt ${this.reconnectAttempts} in ${delay}ms`);
|
|
352
|
+
|
|
353
|
+
if (this.reconnectTimer) {
|
|
354
|
+
clearTimeout(this.reconnectTimer);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
this.reconnectTimer = setTimeout(async () => {
|
|
358
|
+
try {
|
|
359
|
+
await this.connect();
|
|
360
|
+
} catch (error) {
|
|
361
|
+
console.error('Teams reconnection failed:', error);
|
|
362
|
+
}
|
|
363
|
+
}, delay);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Disconnect from Teams
|
|
368
|
+
*/
|
|
369
|
+
async disconnect(): Promise<void> {
|
|
370
|
+
if (this.reconnectTimer) {
|
|
371
|
+
clearTimeout(this.reconnectTimer);
|
|
372
|
+
this.reconnectTimer = null;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (this.server) {
|
|
376
|
+
await new Promise<void>((resolve) => {
|
|
377
|
+
this.server!.close(() => resolve());
|
|
378
|
+
});
|
|
379
|
+
this.server = null;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
this.adapter = null;
|
|
383
|
+
this._botUsername = undefined;
|
|
384
|
+
this._botId = undefined;
|
|
385
|
+
this.conversationReferences.clear();
|
|
386
|
+
this.setStatus('disconnected');
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Send a message to a Teams conversation
|
|
391
|
+
*/
|
|
392
|
+
async sendMessage(message: OutgoingMessage): Promise<string> {
|
|
393
|
+
if (!this.adapter || this._status !== 'connected') {
|
|
394
|
+
throw new Error('Teams bot is not connected');
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Get conversation reference
|
|
398
|
+
const conversationRef = this.conversationReferences.get(message.chatId);
|
|
399
|
+
if (!conversationRef) {
|
|
400
|
+
throw new Error(`No conversation reference found for chat: ${message.chatId}`);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Process text for Teams compatibility
|
|
404
|
+
let processedText = message.text;
|
|
405
|
+
if (message.parseMode === 'markdown') {
|
|
406
|
+
processedText = this.convertMarkdownForTeams(message.text);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Teams has a 28KB limit per message, but practical limit is ~4000 chars for readability
|
|
410
|
+
const chunks = this.splitMessage(processedText, 4000);
|
|
411
|
+
let lastMessageId = '';
|
|
412
|
+
|
|
413
|
+
try {
|
|
414
|
+
for (const chunk of chunks) {
|
|
415
|
+
await this.adapter.continueConversationAsync(
|
|
416
|
+
this.config.appId,
|
|
417
|
+
conversationRef as ConversationReference,
|
|
418
|
+
async (context: TurnContext) => {
|
|
419
|
+
const response = await context.sendActivity(MessageFactory.text(chunk));
|
|
420
|
+
lastMessageId = response?.id || '';
|
|
421
|
+
}
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
} catch (error: unknown) {
|
|
425
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
426
|
+
console.error('Error sending Teams message:', errorMessage);
|
|
427
|
+
throw error;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return lastMessageId;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Convert GitHub-flavored markdown to Teams format
|
|
435
|
+
* Teams supports a subset of markdown
|
|
436
|
+
*/
|
|
437
|
+
private convertMarkdownForTeams(text: string): string {
|
|
438
|
+
let result = text;
|
|
439
|
+
|
|
440
|
+
// Teams supports basic markdown: **bold**, *italic*, ~~strikethrough~~, `code`, ```code blocks```
|
|
441
|
+
// No conversion needed for these
|
|
442
|
+
|
|
443
|
+
// Convert horizontal rules
|
|
444
|
+
result = result.replace(/^[-*]{3,}$/gm, '───────────────────');
|
|
445
|
+
|
|
446
|
+
return result;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Split message into chunks respecting Teams message limit
|
|
451
|
+
*/
|
|
452
|
+
private splitMessage(text: string, maxLength: number): string[] {
|
|
453
|
+
if (text.length <= maxLength) {
|
|
454
|
+
return [text];
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const chunks: string[] = [];
|
|
458
|
+
let remaining = text;
|
|
459
|
+
|
|
460
|
+
while (remaining.length > 0) {
|
|
461
|
+
if (remaining.length <= maxLength) {
|
|
462
|
+
chunks.push(remaining);
|
|
463
|
+
break;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Find a good breaking point (newline or space)
|
|
467
|
+
let breakIndex = remaining.lastIndexOf('\n', maxLength);
|
|
468
|
+
if (breakIndex === -1 || breakIndex < maxLength / 2) {
|
|
469
|
+
breakIndex = remaining.lastIndexOf(' ', maxLength);
|
|
470
|
+
}
|
|
471
|
+
if (breakIndex === -1 || breakIndex < maxLength / 2) {
|
|
472
|
+
breakIndex = maxLength;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
chunks.push(remaining.substring(0, breakIndex));
|
|
476
|
+
remaining = remaining.substring(breakIndex).trimStart();
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
return chunks;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Edit an existing message
|
|
484
|
+
*/
|
|
485
|
+
async editMessage(chatId: string, messageId: string, text: string): Promise<void> {
|
|
486
|
+
if (!this.adapter || this._status !== 'connected') {
|
|
487
|
+
throw new Error('Teams bot is not connected');
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
const conversationRef = this.conversationReferences.get(chatId);
|
|
491
|
+
if (!conversationRef) {
|
|
492
|
+
throw new Error(`No conversation reference found for chat: ${chatId}`);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
await this.adapter.continueConversationAsync(
|
|
496
|
+
this.config.appId,
|
|
497
|
+
conversationRef as ConversationReference,
|
|
498
|
+
async (context: TurnContext) => {
|
|
499
|
+
const activity = MessageFactory.text(text);
|
|
500
|
+
activity.id = messageId;
|
|
501
|
+
await context.updateActivity(activity);
|
|
502
|
+
}
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* Delete a message
|
|
508
|
+
*/
|
|
509
|
+
async deleteMessage(chatId: string, messageId: string): Promise<void> {
|
|
510
|
+
if (!this.adapter || this._status !== 'connected') {
|
|
511
|
+
throw new Error('Teams bot is not connected');
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const conversationRef = this.conversationReferences.get(chatId);
|
|
515
|
+
if (!conversationRef) {
|
|
516
|
+
throw new Error(`No conversation reference found for chat: ${chatId}`);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
await this.adapter.continueConversationAsync(
|
|
520
|
+
this.config.appId,
|
|
521
|
+
conversationRef as ConversationReference,
|
|
522
|
+
async (context: TurnContext) => {
|
|
523
|
+
await context.deleteActivity(messageId);
|
|
524
|
+
}
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Send a document/file to a conversation
|
|
530
|
+
*/
|
|
531
|
+
async sendDocument(chatId: string, filePath: string, caption?: string): Promise<string> {
|
|
532
|
+
if (!this.adapter || this._status !== 'connected') {
|
|
533
|
+
throw new Error('Teams bot is not connected');
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Check if file exists
|
|
537
|
+
if (!fs.existsSync(filePath)) {
|
|
538
|
+
throw new Error(`File not found: ${filePath}`);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
const conversationRef = this.conversationReferences.get(chatId);
|
|
542
|
+
if (!conversationRef) {
|
|
543
|
+
throw new Error(`No conversation reference found for chat: ${chatId}`);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
const fileName = path.basename(filePath);
|
|
547
|
+
const fileBuffer = fs.readFileSync(filePath);
|
|
548
|
+
const base64Content = fileBuffer.toString('base64');
|
|
549
|
+
const contentType = this.getContentType(fileName);
|
|
550
|
+
|
|
551
|
+
let lastMessageId = '';
|
|
552
|
+
|
|
553
|
+
await this.adapter.continueConversationAsync(
|
|
554
|
+
this.config.appId,
|
|
555
|
+
conversationRef as ConversationReference,
|
|
556
|
+
async (context: TurnContext) => {
|
|
557
|
+
// Create attachment
|
|
558
|
+
const attachment = {
|
|
559
|
+
name: fileName,
|
|
560
|
+
contentType: contentType,
|
|
561
|
+
contentUrl: `data:${contentType};base64,${base64Content}`,
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
const activity = MessageFactory.attachment(attachment, caption);
|
|
565
|
+
const response = await context.sendActivity(activity);
|
|
566
|
+
lastMessageId = response?.id || '';
|
|
567
|
+
}
|
|
568
|
+
);
|
|
569
|
+
|
|
570
|
+
return lastMessageId;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Get content type from file extension
|
|
575
|
+
*/
|
|
576
|
+
private getContentType(fileName: string): string {
|
|
577
|
+
const ext = path.extname(fileName).toLowerCase();
|
|
578
|
+
const contentTypes: Record<string, string> = {
|
|
579
|
+
'.pdf': 'application/pdf',
|
|
580
|
+
'.doc': 'application/msword',
|
|
581
|
+
'.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
582
|
+
'.xls': 'application/vnd.ms-excel',
|
|
583
|
+
'.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
584
|
+
'.ppt': 'application/vnd.ms-powerpoint',
|
|
585
|
+
'.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
586
|
+
'.png': 'image/png',
|
|
587
|
+
'.jpg': 'image/jpeg',
|
|
588
|
+
'.jpeg': 'image/jpeg',
|
|
589
|
+
'.gif': 'image/gif',
|
|
590
|
+
'.txt': 'text/plain',
|
|
591
|
+
'.json': 'application/json',
|
|
592
|
+
'.xml': 'application/xml',
|
|
593
|
+
'.zip': 'application/zip',
|
|
594
|
+
};
|
|
595
|
+
return contentTypes[ext] || 'application/octet-stream';
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Register a message handler
|
|
600
|
+
*/
|
|
601
|
+
onMessage(handler: MessageHandler): void {
|
|
602
|
+
this.messageHandlers.push(handler);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* Register an error handler
|
|
607
|
+
*/
|
|
608
|
+
onError(handler: ErrorHandler): void {
|
|
609
|
+
this.errorHandlers.push(handler);
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* Register a status change handler
|
|
614
|
+
*/
|
|
615
|
+
onStatusChange(handler: StatusHandler): void {
|
|
616
|
+
this.statusHandlers.push(handler);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* Get channel info
|
|
621
|
+
*/
|
|
622
|
+
async getInfo(): Promise<ChannelInfo> {
|
|
623
|
+
return {
|
|
624
|
+
type: 'teams',
|
|
625
|
+
status: this._status,
|
|
626
|
+
botId: this.config.appId,
|
|
627
|
+
botUsername: this._botUsername,
|
|
628
|
+
botDisplayName: this._botUsername,
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// Private helper methods
|
|
633
|
+
|
|
634
|
+
private async handleIncomingMessage(message: IncomingMessage): Promise<void> {
|
|
635
|
+
for (const handler of this.messageHandlers) {
|
|
636
|
+
try {
|
|
637
|
+
await handler(message);
|
|
638
|
+
} catch (error) {
|
|
639
|
+
console.error('Error in message handler:', error);
|
|
640
|
+
this.handleError(
|
|
641
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
642
|
+
'messageHandler'
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
private handleError(error: Error, context?: string): void {
|
|
649
|
+
for (const handler of this.errorHandlers) {
|
|
650
|
+
try {
|
|
651
|
+
handler(error, context);
|
|
652
|
+
} catch (e) {
|
|
653
|
+
console.error('Error in error handler:', e);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
private setStatus(status: ChannelStatus, error?: Error): void {
|
|
659
|
+
this._status = status;
|
|
660
|
+
for (const handler of this.statusHandlers) {
|
|
661
|
+
try {
|
|
662
|
+
handler(status, error);
|
|
663
|
+
} catch (e) {
|
|
664
|
+
console.error('Error in status handler:', e);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Create a Teams adapter from configuration
|
|
672
|
+
*/
|
|
673
|
+
export function createTeamsAdapter(config: TeamsConfig): TeamsAdapter {
|
|
674
|
+
if (!config.appId) {
|
|
675
|
+
throw new Error('Microsoft App ID is required');
|
|
676
|
+
}
|
|
677
|
+
if (!config.appPassword) {
|
|
678
|
+
throw new Error('Microsoft App Password is required');
|
|
679
|
+
}
|
|
680
|
+
return new TeamsAdapter(config);
|
|
681
|
+
}
|