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,356 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
3
|
+
import { StandupReport, Task, BoardColumn } from '../../shared/types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Query for standup reports
|
|
7
|
+
*/
|
|
8
|
+
export interface StandupListQuery {
|
|
9
|
+
workspaceId: string;
|
|
10
|
+
limit?: number;
|
|
11
|
+
startDate?: string; // YYYY-MM-DD
|
|
12
|
+
endDate?: string; // YYYY-MM-DD
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Channel configuration for report delivery
|
|
17
|
+
*/
|
|
18
|
+
export interface DeliveryConfig {
|
|
19
|
+
channelType: string; // telegram, discord, slack, etc.
|
|
20
|
+
channelId: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Service for generating and managing daily standup reports
|
|
25
|
+
*/
|
|
26
|
+
export class StandupReportService {
|
|
27
|
+
constructor(
|
|
28
|
+
private db: Database.Database,
|
|
29
|
+
private deliverToChannel?: (report: StandupReport, config: DeliveryConfig) => Promise<void>
|
|
30
|
+
) {}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Generate a standup report for a workspace
|
|
34
|
+
* Aggregates task status from the past 24 hours
|
|
35
|
+
*/
|
|
36
|
+
async generateReport(workspaceId: string, date: Date = new Date()): Promise<StandupReport> {
|
|
37
|
+
const reportDate = this.formatDate(date);
|
|
38
|
+
const yesterday = date.getTime() - 24 * 60 * 60 * 1000;
|
|
39
|
+
|
|
40
|
+
// Check if report already exists for this date
|
|
41
|
+
const existing = this.getByDate(workspaceId, reportDate);
|
|
42
|
+
if (existing) {
|
|
43
|
+
return existing;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Get completed tasks (done column, updated in last 24h)
|
|
47
|
+
const completedTasks = this.getTasksByColumn(workspaceId, 'done', yesterday);
|
|
48
|
+
|
|
49
|
+
// Get in-progress tasks
|
|
50
|
+
const inProgressTasks = this.getTasksByColumn(workspaceId, 'in_progress');
|
|
51
|
+
|
|
52
|
+
// Get blocked tasks (status = 'blocked' or 'failed')
|
|
53
|
+
const blockedTasks = this.getBlockedTasks(workspaceId);
|
|
54
|
+
|
|
55
|
+
// Generate summary
|
|
56
|
+
const summary = this.buildSummary(completedTasks, inProgressTasks, blockedTasks);
|
|
57
|
+
|
|
58
|
+
const report: StandupReport = {
|
|
59
|
+
id: uuidv4(),
|
|
60
|
+
workspaceId,
|
|
61
|
+
reportDate,
|
|
62
|
+
completedTaskIds: completedTasks.map(t => t.id),
|
|
63
|
+
inProgressTaskIds: inProgressTasks.map(t => t.id),
|
|
64
|
+
blockedTaskIds: blockedTasks.map(t => t.id),
|
|
65
|
+
summary,
|
|
66
|
+
createdAt: Date.now(),
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// Save to database
|
|
70
|
+
this.save(report);
|
|
71
|
+
|
|
72
|
+
return report;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Deliver a standup report to a configured channel
|
|
77
|
+
*/
|
|
78
|
+
async deliverReport(report: StandupReport, config: DeliveryConfig): Promise<void> {
|
|
79
|
+
if (!this.deliverToChannel) {
|
|
80
|
+
throw new Error('No delivery handler configured');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
await this.deliverToChannel(report, config);
|
|
84
|
+
|
|
85
|
+
// Update report with delivery info
|
|
86
|
+
const deliveredToChannel = `${config.channelType}:${config.channelId}`;
|
|
87
|
+
const stmt = this.db.prepare(
|
|
88
|
+
'UPDATE standup_reports SET delivered_to_channel = ? WHERE id = ?'
|
|
89
|
+
);
|
|
90
|
+
stmt.run(deliveredToChannel, report.id);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get the latest standup report for a workspace
|
|
95
|
+
*/
|
|
96
|
+
getLatest(workspaceId: string): StandupReport | undefined {
|
|
97
|
+
const stmt = this.db.prepare(`
|
|
98
|
+
SELECT * FROM standup_reports
|
|
99
|
+
WHERE workspace_id = ?
|
|
100
|
+
ORDER BY created_at DESC
|
|
101
|
+
LIMIT 1
|
|
102
|
+
`);
|
|
103
|
+
const row = stmt.get(workspaceId) as any;
|
|
104
|
+
return row ? this.mapRowToReport(row) : undefined;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Get a standup report by date
|
|
109
|
+
*/
|
|
110
|
+
getByDate(workspaceId: string, reportDate: string): StandupReport | undefined {
|
|
111
|
+
const stmt = this.db.prepare(`
|
|
112
|
+
SELECT * FROM standup_reports
|
|
113
|
+
WHERE workspace_id = ? AND report_date = ?
|
|
114
|
+
`);
|
|
115
|
+
const row = stmt.get(workspaceId, reportDate) as any;
|
|
116
|
+
return row ? this.mapRowToReport(row) : undefined;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* List standup reports for a workspace
|
|
121
|
+
*/
|
|
122
|
+
list(query: StandupListQuery): StandupReport[] {
|
|
123
|
+
const conditions: string[] = ['workspace_id = ?'];
|
|
124
|
+
const params: any[] = [query.workspaceId];
|
|
125
|
+
|
|
126
|
+
if (query.startDate) {
|
|
127
|
+
conditions.push('report_date >= ?');
|
|
128
|
+
params.push(query.startDate);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (query.endDate) {
|
|
132
|
+
conditions.push('report_date <= ?');
|
|
133
|
+
params.push(query.endDate);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
let sql = `SELECT * FROM standup_reports WHERE ${conditions.join(' AND ')} ORDER BY report_date DESC`;
|
|
137
|
+
|
|
138
|
+
if (query.limit) {
|
|
139
|
+
sql += ` LIMIT ${query.limit}`;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const stmt = this.db.prepare(sql);
|
|
143
|
+
const rows = stmt.all(...params) as any[];
|
|
144
|
+
return rows.map(row => this.mapRowToReport(row));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Find a report by ID
|
|
149
|
+
*/
|
|
150
|
+
findById(id: string): StandupReport | undefined {
|
|
151
|
+
const stmt = this.db.prepare('SELECT * FROM standup_reports WHERE id = ?');
|
|
152
|
+
const row = stmt.get(id) as any;
|
|
153
|
+
return row ? this.mapRowToReport(row) : undefined;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Delete old reports (cleanup)
|
|
158
|
+
*/
|
|
159
|
+
deleteOlderThan(workspaceId: string, daysToKeep: number): number {
|
|
160
|
+
const cutoffDate = new Date();
|
|
161
|
+
cutoffDate.setDate(cutoffDate.getDate() - daysToKeep);
|
|
162
|
+
const cutoffStr = this.formatDate(cutoffDate);
|
|
163
|
+
|
|
164
|
+
const stmt = this.db.prepare(
|
|
165
|
+
'DELETE FROM standup_reports WHERE workspace_id = ? AND report_date < ?'
|
|
166
|
+
);
|
|
167
|
+
const result = stmt.run(workspaceId, cutoffStr);
|
|
168
|
+
return result.changes;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Format the standup report as a message for delivery
|
|
173
|
+
*/
|
|
174
|
+
formatReportMessage(report: StandupReport, tasks: Map<string, Task>): string {
|
|
175
|
+
const lines: string[] = [
|
|
176
|
+
`**Daily Standup Report - ${report.reportDate}**`,
|
|
177
|
+
'',
|
|
178
|
+
];
|
|
179
|
+
|
|
180
|
+
// Completed section
|
|
181
|
+
if (report.completedTaskIds.length > 0) {
|
|
182
|
+
lines.push('**Completed Today:**');
|
|
183
|
+
for (const taskId of report.completedTaskIds) {
|
|
184
|
+
const task = tasks.get(taskId);
|
|
185
|
+
if (task) {
|
|
186
|
+
lines.push(`- ${task.title}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
lines.push('');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// In Progress section
|
|
193
|
+
if (report.inProgressTaskIds.length > 0) {
|
|
194
|
+
lines.push('**In Progress:**');
|
|
195
|
+
for (const taskId of report.inProgressTaskIds) {
|
|
196
|
+
const task = tasks.get(taskId);
|
|
197
|
+
if (task) {
|
|
198
|
+
lines.push(`- ${task.title}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
lines.push('');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Blocked section
|
|
205
|
+
if (report.blockedTaskIds.length > 0) {
|
|
206
|
+
lines.push('**Blocked:**');
|
|
207
|
+
for (const taskId of report.blockedTaskIds) {
|
|
208
|
+
const task = tasks.get(taskId);
|
|
209
|
+
if (task) {
|
|
210
|
+
lines.push(`- ${task.title}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
lines.push('');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Summary
|
|
217
|
+
lines.push('**Summary:**');
|
|
218
|
+
lines.push(report.summary);
|
|
219
|
+
|
|
220
|
+
return lines.join('\n');
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Save a report to the database
|
|
225
|
+
*/
|
|
226
|
+
private save(report: StandupReport): void {
|
|
227
|
+
const stmt = this.db.prepare(`
|
|
228
|
+
INSERT INTO standup_reports (
|
|
229
|
+
id, workspace_id, report_date, completed_task_ids,
|
|
230
|
+
in_progress_task_ids, blocked_task_ids, summary,
|
|
231
|
+
delivered_to_channel, created_at
|
|
232
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
233
|
+
`);
|
|
234
|
+
|
|
235
|
+
stmt.run(
|
|
236
|
+
report.id,
|
|
237
|
+
report.workspaceId,
|
|
238
|
+
report.reportDate,
|
|
239
|
+
JSON.stringify(report.completedTaskIds),
|
|
240
|
+
JSON.stringify(report.inProgressTaskIds),
|
|
241
|
+
JSON.stringify(report.blockedTaskIds),
|
|
242
|
+
report.summary,
|
|
243
|
+
report.deliveredToChannel || null,
|
|
244
|
+
report.createdAt
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Get tasks by board column with optional time filter
|
|
250
|
+
*/
|
|
251
|
+
private getTasksByColumn(
|
|
252
|
+
workspaceId: string,
|
|
253
|
+
column: BoardColumn,
|
|
254
|
+
updatedAfter?: number
|
|
255
|
+
): Task[] {
|
|
256
|
+
let sql = `
|
|
257
|
+
SELECT * FROM tasks
|
|
258
|
+
WHERE workspace_id = ? AND board_column = ?
|
|
259
|
+
`;
|
|
260
|
+
const params: any[] = [workspaceId, column];
|
|
261
|
+
|
|
262
|
+
if (updatedAfter) {
|
|
263
|
+
sql += ' AND updated_at >= ?';
|
|
264
|
+
params.push(updatedAfter);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
sql += ' ORDER BY updated_at DESC';
|
|
268
|
+
|
|
269
|
+
const stmt = this.db.prepare(sql);
|
|
270
|
+
const rows = stmt.all(...params) as any[];
|
|
271
|
+
return rows.map(row => this.mapRowToTask(row));
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Get blocked tasks (status = blocked or failed)
|
|
276
|
+
*/
|
|
277
|
+
private getBlockedTasks(workspaceId: string): Task[] {
|
|
278
|
+
const stmt = this.db.prepare(`
|
|
279
|
+
SELECT * FROM tasks
|
|
280
|
+
WHERE workspace_id = ? AND status IN ('blocked', 'failed')
|
|
281
|
+
ORDER BY updated_at DESC
|
|
282
|
+
`);
|
|
283
|
+
const rows = stmt.all(workspaceId) as any[];
|
|
284
|
+
return rows.map(row => this.mapRowToTask(row));
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Build a summary from task lists
|
|
289
|
+
*/
|
|
290
|
+
private buildSummary(
|
|
291
|
+
completed: Task[],
|
|
292
|
+
inProgress: Task[],
|
|
293
|
+
blocked: Task[]
|
|
294
|
+
): string {
|
|
295
|
+
const parts: string[] = [];
|
|
296
|
+
|
|
297
|
+
if (completed.length > 0) {
|
|
298
|
+
parts.push(`${completed.length} task${completed.length === 1 ? '' : 's'} completed`);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (inProgress.length > 0) {
|
|
302
|
+
parts.push(`${inProgress.length} task${inProgress.length === 1 ? '' : 's'} in progress`);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (blocked.length > 0) {
|
|
306
|
+
parts.push(`${blocked.length} task${blocked.length === 1 ? '' : 's'} blocked`);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (parts.length === 0) {
|
|
310
|
+
return 'No task activity today.';
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return parts.join(', ') + '.';
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Format date as YYYY-MM-DD
|
|
318
|
+
*/
|
|
319
|
+
private formatDate(date: Date): string {
|
|
320
|
+
return date.toISOString().split('T')[0];
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Map database row to StandupReport
|
|
325
|
+
*/
|
|
326
|
+
private mapRowToReport(row: any): StandupReport {
|
|
327
|
+
return {
|
|
328
|
+
id: row.id,
|
|
329
|
+
workspaceId: row.workspace_id,
|
|
330
|
+
reportDate: row.report_date,
|
|
331
|
+
completedTaskIds: JSON.parse(row.completed_task_ids || '[]'),
|
|
332
|
+
inProgressTaskIds: JSON.parse(row.in_progress_task_ids || '[]'),
|
|
333
|
+
blockedTaskIds: JSON.parse(row.blocked_task_ids || '[]'),
|
|
334
|
+
summary: row.summary,
|
|
335
|
+
deliveredToChannel: row.delivered_to_channel || undefined,
|
|
336
|
+
createdAt: row.created_at,
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Map database row to Task (minimal mapping for standup)
|
|
342
|
+
*/
|
|
343
|
+
private mapRowToTask(row: any): Task {
|
|
344
|
+
return {
|
|
345
|
+
id: row.id,
|
|
346
|
+
title: row.title,
|
|
347
|
+
prompt: row.prompt,
|
|
348
|
+
status: row.status,
|
|
349
|
+
workspaceId: row.workspace_id,
|
|
350
|
+
createdAt: row.created_at,
|
|
351
|
+
updatedAt: row.updated_at,
|
|
352
|
+
boardColumn: row.board_column,
|
|
353
|
+
assignedAgentRoleId: row.assigned_agent_role_id,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
}
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Concurrency Safety Module
|
|
3
|
+
*
|
|
4
|
+
* Provides mutex locks and idempotency guarantees for critical operations.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Simple async mutex for protecting critical sections
|
|
9
|
+
* Prevents race conditions in pairing, approval, and state changes
|
|
10
|
+
*/
|
|
11
|
+
export class AsyncMutex {
|
|
12
|
+
private locked = false;
|
|
13
|
+
private queue: Array<() => void> = [];
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Acquire the lock. Returns a release function.
|
|
17
|
+
*/
|
|
18
|
+
async acquire(): Promise<() => void> {
|
|
19
|
+
return new Promise((resolve) => {
|
|
20
|
+
const tryAcquire = () => {
|
|
21
|
+
if (!this.locked) {
|
|
22
|
+
this.locked = true;
|
|
23
|
+
resolve(() => this.release());
|
|
24
|
+
} else {
|
|
25
|
+
this.queue.push(tryAcquire);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
tryAcquire();
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Release the lock
|
|
34
|
+
*/
|
|
35
|
+
private release(): void {
|
|
36
|
+
this.locked = false;
|
|
37
|
+
const next = this.queue.shift();
|
|
38
|
+
if (next) {
|
|
39
|
+
next();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Execute a function with the lock held
|
|
45
|
+
*/
|
|
46
|
+
async withLock<T>(fn: () => Promise<T>): Promise<T> {
|
|
47
|
+
const release = await this.acquire();
|
|
48
|
+
try {
|
|
49
|
+
return await fn();
|
|
50
|
+
} finally {
|
|
51
|
+
release();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Check if the lock is currently held
|
|
57
|
+
*/
|
|
58
|
+
isLocked(): boolean {
|
|
59
|
+
return this.locked;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Named mutex manager for managing multiple locks by key
|
|
65
|
+
*/
|
|
66
|
+
export class NamedMutexManager {
|
|
67
|
+
private mutexes: Map<string, AsyncMutex> = new Map();
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get or create a mutex for a given key
|
|
71
|
+
*/
|
|
72
|
+
getMutex(key: string): AsyncMutex {
|
|
73
|
+
let mutex = this.mutexes.get(key);
|
|
74
|
+
if (!mutex) {
|
|
75
|
+
mutex = new AsyncMutex();
|
|
76
|
+
this.mutexes.set(key, mutex);
|
|
77
|
+
}
|
|
78
|
+
return mutex;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Execute a function with the named lock held
|
|
83
|
+
*/
|
|
84
|
+
async withLock<T>(key: string, fn: () => Promise<T>): Promise<T> {
|
|
85
|
+
return this.getMutex(key).withLock(fn);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Clean up unused mutexes
|
|
90
|
+
*/
|
|
91
|
+
cleanup(): void {
|
|
92
|
+
for (const [key, mutex] of this.mutexes.entries()) {
|
|
93
|
+
if (!mutex.isLocked()) {
|
|
94
|
+
this.mutexes.delete(key);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Idempotency key entry
|
|
102
|
+
*/
|
|
103
|
+
interface IdempotencyEntry {
|
|
104
|
+
key: string;
|
|
105
|
+
result: any;
|
|
106
|
+
createdAt: number;
|
|
107
|
+
expiresAt: number;
|
|
108
|
+
status: 'pending' | 'completed' | 'failed';
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Idempotency Manager
|
|
113
|
+
*
|
|
114
|
+
* Ensures operations are idempotent by tracking operation keys and their results.
|
|
115
|
+
* Implements C6-style approval safety: prevents duplicate approvals/denials.
|
|
116
|
+
*/
|
|
117
|
+
export class IdempotencyManager {
|
|
118
|
+
private entries: Map<string, IdempotencyEntry> = new Map();
|
|
119
|
+
private defaultTTLMs: number;
|
|
120
|
+
private cleanupIntervalId?: ReturnType<typeof setInterval>;
|
|
121
|
+
|
|
122
|
+
constructor(defaultTTLMs = 5 * 60 * 1000) { // 5 minutes default
|
|
123
|
+
this.defaultTTLMs = defaultTTLMs;
|
|
124
|
+
|
|
125
|
+
// Periodic cleanup of expired entries
|
|
126
|
+
this.cleanupIntervalId = setInterval(() => {
|
|
127
|
+
this.cleanupExpired();
|
|
128
|
+
}, 60 * 1000); // Every minute
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Generate an idempotency key for an operation
|
|
133
|
+
*/
|
|
134
|
+
static generateKey(operation: string, ...args: (string | number | undefined)[]): string {
|
|
135
|
+
const parts = [operation, ...args.filter(a => a !== undefined)];
|
|
136
|
+
return parts.join(':');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Check if an operation with this key is already in progress or completed
|
|
141
|
+
*/
|
|
142
|
+
check(key: string): { exists: boolean; status?: 'pending' | 'completed' | 'failed'; result?: any } {
|
|
143
|
+
const entry = this.entries.get(key);
|
|
144
|
+
|
|
145
|
+
if (!entry) {
|
|
146
|
+
return { exists: false };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Check if expired
|
|
150
|
+
if (Date.now() > entry.expiresAt) {
|
|
151
|
+
this.entries.delete(key);
|
|
152
|
+
return { exists: false };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
exists: true,
|
|
157
|
+
status: entry.status,
|
|
158
|
+
result: entry.result,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Start tracking an operation (mark as pending)
|
|
164
|
+
* Returns false if operation is already in progress
|
|
165
|
+
*/
|
|
166
|
+
start(key: string, ttlMs?: number): boolean {
|
|
167
|
+
const existing = this.check(key);
|
|
168
|
+
|
|
169
|
+
if (existing.exists) {
|
|
170
|
+
// Already in progress or completed
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const now = Date.now();
|
|
175
|
+
this.entries.set(key, {
|
|
176
|
+
key,
|
|
177
|
+
result: undefined,
|
|
178
|
+
createdAt: now,
|
|
179
|
+
expiresAt: now + (ttlMs || this.defaultTTLMs),
|
|
180
|
+
status: 'pending',
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Mark an operation as completed with its result
|
|
188
|
+
*/
|
|
189
|
+
complete(key: string, result: any): void {
|
|
190
|
+
const entry = this.entries.get(key);
|
|
191
|
+
if (entry) {
|
|
192
|
+
entry.status = 'completed';
|
|
193
|
+
entry.result = result;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Mark an operation as failed
|
|
199
|
+
*/
|
|
200
|
+
fail(key: string, error?: any): void {
|
|
201
|
+
const entry = this.entries.get(key);
|
|
202
|
+
if (entry) {
|
|
203
|
+
entry.status = 'failed';
|
|
204
|
+
entry.result = error;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Remove an entry (for retry scenarios)
|
|
210
|
+
*/
|
|
211
|
+
remove(key: string): void {
|
|
212
|
+
this.entries.delete(key);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Execute an operation with idempotency guarantee
|
|
217
|
+
* If operation was already completed, returns cached result
|
|
218
|
+
* If operation is in progress, waits and returns result
|
|
219
|
+
*/
|
|
220
|
+
async execute<T>(
|
|
221
|
+
key: string,
|
|
222
|
+
operation: () => Promise<T>,
|
|
223
|
+
ttlMs?: number
|
|
224
|
+
): Promise<{ result: T; cached: boolean }> {
|
|
225
|
+
const existing = this.check(key);
|
|
226
|
+
|
|
227
|
+
if (existing.exists && existing.status === 'completed') {
|
|
228
|
+
return { result: existing.result as T, cached: true };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (existing.exists && existing.status === 'pending') {
|
|
232
|
+
// Wait for completion
|
|
233
|
+
const result = await this.waitForCompletion<T>(key);
|
|
234
|
+
return { result, cached: true };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Start new operation
|
|
238
|
+
if (!this.start(key, ttlMs)) {
|
|
239
|
+
// Race condition: another call started between check and start
|
|
240
|
+
const result = await this.waitForCompletion<T>(key);
|
|
241
|
+
return { result, cached: true };
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
try {
|
|
245
|
+
const result = await operation();
|
|
246
|
+
this.complete(key, result);
|
|
247
|
+
return { result, cached: false };
|
|
248
|
+
} catch (error) {
|
|
249
|
+
this.fail(key, error);
|
|
250
|
+
throw error;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Wait for a pending operation to complete
|
|
256
|
+
*/
|
|
257
|
+
private async waitForCompletion<T>(key: string, timeoutMs = 30000): Promise<T> {
|
|
258
|
+
const startTime = Date.now();
|
|
259
|
+
|
|
260
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
261
|
+
const entry = this.entries.get(key);
|
|
262
|
+
|
|
263
|
+
if (!entry || Date.now() > entry.expiresAt) {
|
|
264
|
+
throw new Error(`Operation ${key} expired or not found`);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (entry.status === 'completed') {
|
|
268
|
+
return entry.result as T;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (entry.status === 'failed') {
|
|
272
|
+
throw entry.result || new Error(`Operation ${key} failed`);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Wait a bit before checking again
|
|
276
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
throw new Error(`Timeout waiting for operation ${key}`);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Clean up expired entries
|
|
284
|
+
*/
|
|
285
|
+
private cleanupExpired(): void {
|
|
286
|
+
const now = Date.now();
|
|
287
|
+
for (const [key, entry] of this.entries.entries()) {
|
|
288
|
+
if (now > entry.expiresAt) {
|
|
289
|
+
this.entries.delete(key);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Get statistics about the idempotency cache
|
|
296
|
+
*/
|
|
297
|
+
getStats(): { total: number; pending: number; completed: number; failed: number } {
|
|
298
|
+
let pending = 0;
|
|
299
|
+
let completed = 0;
|
|
300
|
+
let failed = 0;
|
|
301
|
+
|
|
302
|
+
for (const entry of this.entries.values()) {
|
|
303
|
+
switch (entry.status) {
|
|
304
|
+
case 'pending': pending++; break;
|
|
305
|
+
case 'completed': completed++; break;
|
|
306
|
+
case 'failed': failed++; break;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return {
|
|
311
|
+
total: this.entries.size,
|
|
312
|
+
pending,
|
|
313
|
+
completed,
|
|
314
|
+
failed,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Clean up and stop background tasks
|
|
320
|
+
*/
|
|
321
|
+
destroy(): void {
|
|
322
|
+
if (this.cleanupIntervalId) {
|
|
323
|
+
clearInterval(this.cleanupIntervalId);
|
|
324
|
+
this.cleanupIntervalId = undefined;
|
|
325
|
+
}
|
|
326
|
+
this.entries.clear();
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Global instances for common use cases
|
|
331
|
+
export const pairingMutex = new NamedMutexManager();
|
|
332
|
+
export const approvalIdempotency = new IdempotencyManager(5 * 60 * 1000); // 5 min TTL
|
|
333
|
+
export const taskIdempotency = new IdempotencyManager(60 * 1000); // 1 min TTL for task creation
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security Module
|
|
3
|
+
*
|
|
4
|
+
* Exports all security-related components for the application.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
SecurityPolicyManager,
|
|
9
|
+
createPolicyManager,
|
|
10
|
+
isToolAllowedQuick,
|
|
11
|
+
type PolicyCheckResult,
|
|
12
|
+
type PolicyLayer,
|
|
13
|
+
type LayerDecision,
|
|
14
|
+
type PolicyContext,
|
|
15
|
+
} from './policy-manager';
|
|
16
|
+
|
|
17
|
+
export { AsyncMutex, IdempotencyManager } from './concurrency';
|