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,895 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* TrayManager - macOS Menu Bar App Integration
|
|
4
|
+
*
|
|
5
|
+
* Provides a native menu bar icon with:
|
|
6
|
+
* - Status indicator (connected/disconnected channels)
|
|
7
|
+
* - Quick actions menu (new task, workspaces, settings)
|
|
8
|
+
* - Show/hide main window on click
|
|
9
|
+
* - Gateway status monitoring
|
|
10
|
+
*
|
|
11
|
+
* Settings are stored encrypted in the database using SecureSettingsRepository.
|
|
12
|
+
*/
|
|
13
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
+
}
|
|
19
|
+
Object.defineProperty(o, k2, desc);
|
|
20
|
+
}) : (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
o[k2] = m[k];
|
|
23
|
+
}));
|
|
24
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
25
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
26
|
+
}) : function(o, v) {
|
|
27
|
+
o["default"] = v;
|
|
28
|
+
});
|
|
29
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
30
|
+
var ownKeys = function(o) {
|
|
31
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
32
|
+
var ar = [];
|
|
33
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
34
|
+
return ar;
|
|
35
|
+
};
|
|
36
|
+
return ownKeys(o);
|
|
37
|
+
};
|
|
38
|
+
return function (mod) {
|
|
39
|
+
if (mod && mod.__esModule) return mod;
|
|
40
|
+
var result = {};
|
|
41
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
42
|
+
__setModuleDefault(result, mod);
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
})();
|
|
46
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
exports.trayManager = exports.TrayManager = void 0;
|
|
48
|
+
const electron_1 = require("electron");
|
|
49
|
+
const path = __importStar(require("path"));
|
|
50
|
+
const os = __importStar(require("os"));
|
|
51
|
+
const fs = __importStar(require("fs"));
|
|
52
|
+
const repositories_1 = require("../database/repositories");
|
|
53
|
+
const QuickInputWindow_1 = require("./QuickInputWindow");
|
|
54
|
+
const types_1 = require("../../shared/types");
|
|
55
|
+
const SecureSettingsRepository_1 = require("../database/SecureSettingsRepository");
|
|
56
|
+
const LEGACY_SETTINGS_FILE = 'tray-settings.json';
|
|
57
|
+
const DEFAULT_SETTINGS = {
|
|
58
|
+
enabled: true,
|
|
59
|
+
showDockIcon: true,
|
|
60
|
+
startMinimized: false,
|
|
61
|
+
closeToTray: true,
|
|
62
|
+
showNotifications: true,
|
|
63
|
+
};
|
|
64
|
+
class TrayManager {
|
|
65
|
+
static getInstance() {
|
|
66
|
+
if (!TrayManager.instance) {
|
|
67
|
+
TrayManager.instance = new TrayManager();
|
|
68
|
+
}
|
|
69
|
+
return TrayManager.instance;
|
|
70
|
+
}
|
|
71
|
+
constructor() {
|
|
72
|
+
this.tray = null;
|
|
73
|
+
this.mainWindow = null;
|
|
74
|
+
this.gateway = null;
|
|
75
|
+
this.dbManager = null;
|
|
76
|
+
this.agentDaemon = null;
|
|
77
|
+
this.taskRepo = null;
|
|
78
|
+
this.workspaceRepo = null;
|
|
79
|
+
this.settings = DEFAULT_SETTINGS;
|
|
80
|
+
this.connectedChannels = 0;
|
|
81
|
+
this.activeTaskCount = 0;
|
|
82
|
+
this.quickInputWindow = null;
|
|
83
|
+
this.currentQuickTaskId = null;
|
|
84
|
+
this.quickTaskAccumulatedResponse = '';
|
|
85
|
+
this.currentStepInfo = '';
|
|
86
|
+
const userDataPath = electron_1.app.getPath('userData');
|
|
87
|
+
this.legacySettingsPath = path.join(userDataPath, LEGACY_SETTINGS_FILE);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Initialize the tray manager
|
|
91
|
+
*/
|
|
92
|
+
async initialize(mainWindow, gateway, dbManager, agentDaemon, options = {}) {
|
|
93
|
+
this.mainWindow = mainWindow;
|
|
94
|
+
this.gateway = gateway;
|
|
95
|
+
this.dbManager = dbManager;
|
|
96
|
+
this.agentDaemon = agentDaemon || null;
|
|
97
|
+
// Initialize repositories
|
|
98
|
+
const db = dbManager.getDatabase();
|
|
99
|
+
this.taskRepo = new repositories_1.TaskRepository(db);
|
|
100
|
+
this.workspaceRepo = new repositories_1.WorkspaceRepository(db);
|
|
101
|
+
// Load settings
|
|
102
|
+
this.loadSettings();
|
|
103
|
+
// Apply options overrides
|
|
104
|
+
if (options.showDockIcon !== undefined) {
|
|
105
|
+
this.settings.showDockIcon = options.showDockIcon;
|
|
106
|
+
}
|
|
107
|
+
if (options.startMinimized !== undefined) {
|
|
108
|
+
this.settings.startMinimized = options.startMinimized;
|
|
109
|
+
}
|
|
110
|
+
if (options.closeToTray !== undefined) {
|
|
111
|
+
this.settings.closeToTray = options.closeToTray;
|
|
112
|
+
}
|
|
113
|
+
// Create tray if enabled
|
|
114
|
+
if (this.settings.enabled) {
|
|
115
|
+
this.createTray();
|
|
116
|
+
}
|
|
117
|
+
// Apply dock icon setting (macOS only)
|
|
118
|
+
this.applyDockIconSetting();
|
|
119
|
+
// Handle start minimized
|
|
120
|
+
if (this.settings.startMinimized && this.mainWindow) {
|
|
121
|
+
this.mainWindow.hide();
|
|
122
|
+
}
|
|
123
|
+
// Set up window close behavior
|
|
124
|
+
this.setupCloseToTray();
|
|
125
|
+
// Update status periodically
|
|
126
|
+
this.startStatusUpdates();
|
|
127
|
+
// Set up task event listening for quick input responses
|
|
128
|
+
this.setupTaskEventListener();
|
|
129
|
+
// Initialize quick input window
|
|
130
|
+
this.quickInputWindow = new QuickInputWindow_1.QuickInputWindow();
|
|
131
|
+
this.quickInputWindow.setOnSubmit((task, workspaceId) => {
|
|
132
|
+
this.handleQuickTaskSubmit(task, workspaceId);
|
|
133
|
+
});
|
|
134
|
+
this.quickInputWindow.setOnOpenMain(() => {
|
|
135
|
+
this.showMainWindow();
|
|
136
|
+
this.quickInputWindow?.hide();
|
|
137
|
+
});
|
|
138
|
+
// Register global shortcut for quick input (Cmd+Shift+Space)
|
|
139
|
+
this.registerGlobalShortcut();
|
|
140
|
+
console.log('[TrayManager] Initialized');
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Set up listener for task events to stream to quick input
|
|
144
|
+
*/
|
|
145
|
+
setupTaskEventListener() {
|
|
146
|
+
if (!this.agentDaemon)
|
|
147
|
+
return;
|
|
148
|
+
// Listen for assistant messages (the main text response)
|
|
149
|
+
this.agentDaemon.on('assistant_message', (event) => {
|
|
150
|
+
if (event.taskId !== this.currentQuickTaskId)
|
|
151
|
+
return;
|
|
152
|
+
const message = event.message || '';
|
|
153
|
+
if (message) {
|
|
154
|
+
// Append to accumulated response (assistant may send multiple messages)
|
|
155
|
+
if (this.quickTaskAccumulatedResponse) {
|
|
156
|
+
this.quickTaskAccumulatedResponse += '\n\n' + message;
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
this.quickTaskAccumulatedResponse = message;
|
|
160
|
+
}
|
|
161
|
+
this.quickInputWindow?.updateResponse(this.formatResponseWithQuestion(this.quickTaskAccumulatedResponse), false);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
// Listen for progress updates
|
|
165
|
+
this.agentDaemon.on('progress_update', (event) => {
|
|
166
|
+
if (event.taskId !== this.currentQuickTaskId)
|
|
167
|
+
return;
|
|
168
|
+
// Only show progress if we don't have response content yet
|
|
169
|
+
if (!this.quickTaskAccumulatedResponse && event.message) {
|
|
170
|
+
this.quickInputWindow?.updateResponse(`<p style="color: rgba(255,255,255,0.6);">${event.message}</p>`, false);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
// Listen for task completion
|
|
174
|
+
this.agentDaemon.on('task_completed', (event) => {
|
|
175
|
+
if (event.taskId !== this.currentQuickTaskId)
|
|
176
|
+
return;
|
|
177
|
+
// Show the accumulated response as complete (without step prefix)
|
|
178
|
+
const finalContent = this.quickTaskAccumulatedResponse || event.result || event.message || 'Task completed successfully';
|
|
179
|
+
this.quickInputWindow?.updateResponse(this.formatResponseWithQuestion(finalContent), true);
|
|
180
|
+
this.currentQuickTaskId = null;
|
|
181
|
+
this.quickTaskAccumulatedResponse = '';
|
|
182
|
+
this.currentStepInfo = '';
|
|
183
|
+
});
|
|
184
|
+
// Listen for errors
|
|
185
|
+
this.agentDaemon.on('error', (event) => {
|
|
186
|
+
if (event.taskId !== this.currentQuickTaskId)
|
|
187
|
+
return;
|
|
188
|
+
const question = this.quickInputWindow?.getCurrentQuestion() || '';
|
|
189
|
+
const questionHtml = question ? `<div class="user-question"><strong>You:</strong> ${question.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')}</div>` : '';
|
|
190
|
+
this.quickInputWindow?.updateResponse(`${questionHtml}<div class="error-message">Error: ${event.message || 'An error occurred'}</div>`, true);
|
|
191
|
+
this.currentQuickTaskId = null;
|
|
192
|
+
this.quickTaskAccumulatedResponse = '';
|
|
193
|
+
this.currentStepInfo = '';
|
|
194
|
+
});
|
|
195
|
+
// Listen for step started (show what step is being executed)
|
|
196
|
+
this.agentDaemon.on('step_started', (event) => {
|
|
197
|
+
if (event.taskId !== this.currentQuickTaskId)
|
|
198
|
+
return;
|
|
199
|
+
// Show step info above the response
|
|
200
|
+
if (event.step?.description) {
|
|
201
|
+
const stepInfo = `**Step ${event.step.id}:** ${event.step.description}\n\n`;
|
|
202
|
+
// Prepend step info (it will be replaced by next step)
|
|
203
|
+
this.currentStepInfo = stepInfo;
|
|
204
|
+
this.quickInputWindow?.updateResponse(this.formatResponseWithQuestion(this.currentStepInfo + this.quickTaskAccumulatedResponse), false);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
// Listen for plan created (show what the agent is going to do)
|
|
208
|
+
this.agentDaemon.on('plan_created', (event) => {
|
|
209
|
+
if (event.taskId !== this.currentQuickTaskId)
|
|
210
|
+
return;
|
|
211
|
+
if (event.plan?.steps && event.plan.steps.length > 0) {
|
|
212
|
+
const planSummary = event.plan.steps.map((s, i) => `${i + 1}. ${s.description}`).join('\n');
|
|
213
|
+
this.quickTaskAccumulatedResponse = `**Plan:**\n${planSummary}\n\n`;
|
|
214
|
+
this.quickInputWindow?.updateResponse(this.formatResponseWithQuestion(this.quickTaskAccumulatedResponse), false);
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Format response text for HTML display
|
|
220
|
+
*/
|
|
221
|
+
formatResponseForDisplay(text) {
|
|
222
|
+
// Basic markdown-like formatting
|
|
223
|
+
return text
|
|
224
|
+
// Escape HTML
|
|
225
|
+
.replace(/&/g, '&')
|
|
226
|
+
.replace(/</g, '<')
|
|
227
|
+
.replace(/>/g, '>')
|
|
228
|
+
// Bold
|
|
229
|
+
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
|
|
230
|
+
// Code blocks
|
|
231
|
+
.replace(/```(\w*)\n?([\s\S]*?)```/g, '<pre><code>$2</code></pre>')
|
|
232
|
+
// Inline code
|
|
233
|
+
.replace(/`([^`]+)`/g, '<code>$1</code>')
|
|
234
|
+
// Line breaks
|
|
235
|
+
.replace(/\n/g, '<br>');
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Format response with user's question prepended
|
|
239
|
+
*/
|
|
240
|
+
formatResponseWithQuestion(text) {
|
|
241
|
+
const question = this.quickInputWindow?.getCurrentQuestion() || '';
|
|
242
|
+
const formattedResponse = this.formatResponseForDisplay(text);
|
|
243
|
+
if (question) {
|
|
244
|
+
const escapedQuestion = question
|
|
245
|
+
.replace(/&/g, '&')
|
|
246
|
+
.replace(/</g, '<')
|
|
247
|
+
.replace(/>/g, '>');
|
|
248
|
+
return `<div class="user-question"><strong>You:</strong> ${escapedQuestion}</div>${formattedResponse}`;
|
|
249
|
+
}
|
|
250
|
+
return formattedResponse;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Get or create the temp workspace
|
|
254
|
+
*/
|
|
255
|
+
async getOrCreateTempWorkspace() {
|
|
256
|
+
if (!this.dbManager)
|
|
257
|
+
throw new Error('Database not available');
|
|
258
|
+
const db = this.dbManager.getDatabase();
|
|
259
|
+
// Check if temp workspace exists
|
|
260
|
+
const existing = this.workspaceRepo?.findById(types_1.TEMP_WORKSPACE_ID);
|
|
261
|
+
if (existing) {
|
|
262
|
+
const updatedPermissions = {
|
|
263
|
+
...existing.permissions,
|
|
264
|
+
read: true,
|
|
265
|
+
write: true,
|
|
266
|
+
delete: true,
|
|
267
|
+
network: true,
|
|
268
|
+
shell: existing.permissions.shell ?? false,
|
|
269
|
+
unrestrictedFileAccess: true,
|
|
270
|
+
};
|
|
271
|
+
if (!existing.permissions.unrestrictedFileAccess) {
|
|
272
|
+
this.workspaceRepo?.updatePermissions(existing.id, updatedPermissions);
|
|
273
|
+
}
|
|
274
|
+
// Verify directory exists
|
|
275
|
+
if (fs.existsSync(existing.path)) {
|
|
276
|
+
return { ...existing, permissions: updatedPermissions, isTemp: true };
|
|
277
|
+
}
|
|
278
|
+
// Directory deleted, remove and recreate
|
|
279
|
+
this.workspaceRepo?.delete(types_1.TEMP_WORKSPACE_ID);
|
|
280
|
+
}
|
|
281
|
+
// Create temp directory
|
|
282
|
+
const tempDir = path.join(os.tmpdir(), 'cowork-os-temp');
|
|
283
|
+
if (!fs.existsSync(tempDir)) {
|
|
284
|
+
fs.mkdirSync(tempDir, { recursive: true });
|
|
285
|
+
}
|
|
286
|
+
// Create workspace record
|
|
287
|
+
const tempWorkspace = {
|
|
288
|
+
id: types_1.TEMP_WORKSPACE_ID,
|
|
289
|
+
name: types_1.TEMP_WORKSPACE_NAME,
|
|
290
|
+
path: tempDir,
|
|
291
|
+
createdAt: Date.now(),
|
|
292
|
+
permissions: {
|
|
293
|
+
read: true,
|
|
294
|
+
write: true,
|
|
295
|
+
delete: true,
|
|
296
|
+
network: true,
|
|
297
|
+
shell: false,
|
|
298
|
+
unrestrictedFileAccess: true,
|
|
299
|
+
},
|
|
300
|
+
isTemp: true,
|
|
301
|
+
};
|
|
302
|
+
const stmt = db.prepare(`
|
|
303
|
+
INSERT OR REPLACE INTO workspaces (id, name, path, created_at, permissions)
|
|
304
|
+
VALUES (?, ?, ?, ?, ?)
|
|
305
|
+
`);
|
|
306
|
+
stmt.run(tempWorkspace.id, tempWorkspace.name, tempWorkspace.path, tempWorkspace.createdAt, JSON.stringify(tempWorkspace.permissions));
|
|
307
|
+
return tempWorkspace;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Handle quick task submission - create and run task
|
|
311
|
+
*/
|
|
312
|
+
async handleQuickTaskSubmit(prompt, workspaceId) {
|
|
313
|
+
if (!this.taskRepo || !this.workspaceRepo || !this.agentDaemon) {
|
|
314
|
+
// Fall back to sending to main window
|
|
315
|
+
console.log('[TrayManager] Agent daemon not available, falling back to main window');
|
|
316
|
+
this.showMainWindow();
|
|
317
|
+
this.mainWindow?.webContents.send('tray:quick-task', { task: prompt, workspaceId });
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
// Show loading state and reset accumulated response
|
|
321
|
+
this.quickInputWindow?.showLoading();
|
|
322
|
+
this.quickTaskAccumulatedResponse = '';
|
|
323
|
+
this.currentStepInfo = '';
|
|
324
|
+
try {
|
|
325
|
+
// Get or select workspace
|
|
326
|
+
let wsId = workspaceId;
|
|
327
|
+
if (!wsId) {
|
|
328
|
+
// Get the first non-temp workspace, or use temp workspace as fallback
|
|
329
|
+
const workspaces = this.workspaceRepo.findAll().filter(w => w.id !== types_1.TEMP_WORKSPACE_ID);
|
|
330
|
+
if (workspaces.length > 0) {
|
|
331
|
+
wsId = workspaces[0].id;
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
// No user workspaces, use temp workspace
|
|
335
|
+
const tempWorkspace = await this.getOrCreateTempWorkspace();
|
|
336
|
+
wsId = tempWorkspace.id;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
// Create task
|
|
340
|
+
const task = this.taskRepo.create({
|
|
341
|
+
title: prompt.slice(0, 50) + (prompt.length > 50 ? '...' : ''),
|
|
342
|
+
prompt,
|
|
343
|
+
workspaceId: wsId,
|
|
344
|
+
status: 'queued',
|
|
345
|
+
});
|
|
346
|
+
this.currentQuickTaskId = task.id;
|
|
347
|
+
// Start task execution
|
|
348
|
+
await this.agentDaemon.startTask(task);
|
|
349
|
+
// Also notify main window so it updates the task list
|
|
350
|
+
this.mainWindow?.webContents.send('tray:task-created', { taskId: task.id });
|
|
351
|
+
}
|
|
352
|
+
catch (error) {
|
|
353
|
+
console.error('[TrayManager] Failed to create quick task:', error);
|
|
354
|
+
const question = this.quickInputWindow?.getCurrentQuestion() || '';
|
|
355
|
+
const questionHtml = question ? `<div class="user-question"><strong>You:</strong> ${question.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')}</div>` : '';
|
|
356
|
+
this.quickInputWindow?.updateResponse(`${questionHtml}<div class="error-message">Failed to create task: ${error instanceof Error ? error.message : 'Unknown error'}</div>`, true);
|
|
357
|
+
this.currentQuickTaskId = null;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Show the quick input window
|
|
362
|
+
*/
|
|
363
|
+
showQuickInput() {
|
|
364
|
+
this.quickInputWindow?.show();
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Toggle the quick input window
|
|
368
|
+
*/
|
|
369
|
+
toggleQuickInput() {
|
|
370
|
+
this.quickInputWindow?.toggle();
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Register global keyboard shortcut for quick input
|
|
374
|
+
*/
|
|
375
|
+
registerGlobalShortcut() {
|
|
376
|
+
try {
|
|
377
|
+
// Unregister first in case it's already registered
|
|
378
|
+
electron_1.globalShortcut.unregister('CommandOrControl+Shift+Space');
|
|
379
|
+
const registered = electron_1.globalShortcut.register('CommandOrControl+Shift+Space', () => {
|
|
380
|
+
this.showQuickInput();
|
|
381
|
+
});
|
|
382
|
+
if (registered) {
|
|
383
|
+
console.log('[TrayManager] Global shortcut registered: Cmd+Shift+Space');
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
console.warn('[TrayManager] Failed to register global shortcut - may be in use by another app');
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
catch (error) {
|
|
390
|
+
console.error('[TrayManager] Error registering global shortcut:', error);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Unregister global keyboard shortcut
|
|
395
|
+
*/
|
|
396
|
+
unregisterGlobalShortcut() {
|
|
397
|
+
try {
|
|
398
|
+
electron_1.globalShortcut.unregister('CommandOrControl+Shift+Space');
|
|
399
|
+
console.log('[TrayManager] Global shortcut unregistered');
|
|
400
|
+
}
|
|
401
|
+
catch (error) {
|
|
402
|
+
console.error('[TrayManager] Error unregistering global shortcut:', error);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Create the system tray icon
|
|
407
|
+
*/
|
|
408
|
+
createTray() {
|
|
409
|
+
if (this.tray) {
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
try {
|
|
413
|
+
// Create tray icon (use template image for macOS)
|
|
414
|
+
const icon = this.getTrayIcon('idle');
|
|
415
|
+
this.tray = new electron_1.Tray(icon);
|
|
416
|
+
this.tray.setToolTip('CoWork OS');
|
|
417
|
+
// Build and set context menu
|
|
418
|
+
this.updateContextMenu();
|
|
419
|
+
// Handle click events - always show context menu on click
|
|
420
|
+
this.tray.on('click', () => {
|
|
421
|
+
this.tray?.popUpContextMenu();
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
catch (error) {
|
|
425
|
+
console.error('[TrayManager] Failed to create tray:', error);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Get or create tray icon
|
|
430
|
+
*/
|
|
431
|
+
getTrayIcon(state) {
|
|
432
|
+
// Try to load from file first
|
|
433
|
+
const iconPath = this.getIconPath(state === 'active' ? 'trayActiveTemplate' : 'trayTemplate');
|
|
434
|
+
const fs = require('fs');
|
|
435
|
+
if (fs.existsSync(iconPath)) {
|
|
436
|
+
const icon = electron_1.nativeImage.createFromPath(iconPath);
|
|
437
|
+
if (process.platform === 'darwin') {
|
|
438
|
+
icon.setTemplateImage(true);
|
|
439
|
+
}
|
|
440
|
+
return icon;
|
|
441
|
+
}
|
|
442
|
+
// Create programmatic icon if file doesn't exist
|
|
443
|
+
return this.createProgrammaticIcon(state);
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Create a programmatic tray icon using raw RGBA bitmap
|
|
447
|
+
* More reliable than SVG data URLs for Electron tray icons
|
|
448
|
+
*/
|
|
449
|
+
createProgrammaticIcon(state) {
|
|
450
|
+
// Standard macOS menu bar icon size (16x16 for 1x, 32x32 for 2x retina)
|
|
451
|
+
const size = 16;
|
|
452
|
+
const scale = 2; // Create at 2x for retina
|
|
453
|
+
const actualSize = size * scale;
|
|
454
|
+
// Create RGBA buffer (4 bytes per pixel)
|
|
455
|
+
const buffer = Buffer.alloc(actualSize * actualSize * 4);
|
|
456
|
+
// Get color based on state
|
|
457
|
+
const [r, g, b] = state === 'error' ? [255, 59, 48] : // Red
|
|
458
|
+
state === 'active' ? [0, 122, 255] : // Blue
|
|
459
|
+
[255, 255, 255]; // White
|
|
460
|
+
// Draw a simple filled circle
|
|
461
|
+
const centerX = actualSize / 2;
|
|
462
|
+
const centerY = actualSize / 2;
|
|
463
|
+
const outerRadius = actualSize / 2 - 2;
|
|
464
|
+
const innerRadius = outerRadius - 4;
|
|
465
|
+
for (let y = 0; y < actualSize; y++) {
|
|
466
|
+
for (let x = 0; x < actualSize; x++) {
|
|
467
|
+
const dx = x - centerX;
|
|
468
|
+
const dy = y - centerY;
|
|
469
|
+
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
470
|
+
const idx = (y * actualSize + x) * 4;
|
|
471
|
+
// Draw ring (between inner and outer radius)
|
|
472
|
+
if (distance <= outerRadius && distance >= innerRadius) {
|
|
473
|
+
// Anti-aliasing at edges
|
|
474
|
+
let alpha = 255;
|
|
475
|
+
if (distance > outerRadius - 1) {
|
|
476
|
+
alpha = Math.round(255 * (outerRadius - distance));
|
|
477
|
+
}
|
|
478
|
+
else if (distance < innerRadius + 1) {
|
|
479
|
+
alpha = Math.round(255 * (distance - innerRadius));
|
|
480
|
+
}
|
|
481
|
+
alpha = Math.max(0, Math.min(255, alpha));
|
|
482
|
+
buffer[idx] = r;
|
|
483
|
+
buffer[idx + 1] = g;
|
|
484
|
+
buffer[idx + 2] = b;
|
|
485
|
+
buffer[idx + 3] = alpha;
|
|
486
|
+
}
|
|
487
|
+
else {
|
|
488
|
+
// Transparent
|
|
489
|
+
buffer[idx] = 0;
|
|
490
|
+
buffer[idx + 1] = 0;
|
|
491
|
+
buffer[idx + 2] = 0;
|
|
492
|
+
buffer[idx + 3] = 0;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
return electron_1.nativeImage.createFromBuffer(buffer, {
|
|
497
|
+
width: actualSize,
|
|
498
|
+
height: actualSize,
|
|
499
|
+
scaleFactor: scale,
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Get the path to a tray icon
|
|
504
|
+
*/
|
|
505
|
+
getIconPath(name) {
|
|
506
|
+
const isDev = process.env.NODE_ENV === 'development';
|
|
507
|
+
const basePath = isDev
|
|
508
|
+
? path.join(__dirname, '../../../assets/tray')
|
|
509
|
+
: path.join(process.resourcesPath, 'assets/tray');
|
|
510
|
+
// Use PNG for cross-platform compatibility
|
|
511
|
+
const extension = process.platform === 'darwin' ? 'png' : 'png';
|
|
512
|
+
return path.join(basePath, `${name}.${extension}`);
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Update the tray context menu
|
|
516
|
+
*/
|
|
517
|
+
updateContextMenu() {
|
|
518
|
+
if (!this.tray)
|
|
519
|
+
return;
|
|
520
|
+
const statusText = this.getStatusText();
|
|
521
|
+
const workspaces = this.getWorkspaces();
|
|
522
|
+
const menuTemplate = [
|
|
523
|
+
// Status section
|
|
524
|
+
{
|
|
525
|
+
label: statusText,
|
|
526
|
+
enabled: false,
|
|
527
|
+
icon: this.getStatusIcon(),
|
|
528
|
+
},
|
|
529
|
+
{ type: 'separator' },
|
|
530
|
+
// Quick actions
|
|
531
|
+
{
|
|
532
|
+
label: 'Quick Task...',
|
|
533
|
+
accelerator: 'CmdOrCtrl+Shift+Space',
|
|
534
|
+
click: () => {
|
|
535
|
+
this.showQuickInput();
|
|
536
|
+
},
|
|
537
|
+
},
|
|
538
|
+
{
|
|
539
|
+
label: 'New Task...',
|
|
540
|
+
accelerator: 'CmdOrCtrl+N',
|
|
541
|
+
click: () => {
|
|
542
|
+
this.showMainWindow();
|
|
543
|
+
this.mainWindow?.webContents.send('tray:new-task');
|
|
544
|
+
},
|
|
545
|
+
},
|
|
546
|
+
{ type: 'separator' },
|
|
547
|
+
// Workspaces submenu
|
|
548
|
+
{
|
|
549
|
+
label: 'Workspaces',
|
|
550
|
+
submenu: workspaces.length > 0
|
|
551
|
+
? workspaces.map((ws) => ({
|
|
552
|
+
label: ws.name,
|
|
553
|
+
click: () => {
|
|
554
|
+
this.showMainWindow();
|
|
555
|
+
this.mainWindow?.webContents.send('tray:select-workspace', ws.id);
|
|
556
|
+
},
|
|
557
|
+
}))
|
|
558
|
+
: [{ label: 'No workspaces', enabled: false }],
|
|
559
|
+
},
|
|
560
|
+
// Channels submenu
|
|
561
|
+
{
|
|
562
|
+
label: 'Channels',
|
|
563
|
+
submenu: this.buildChannelsSubmenu(),
|
|
564
|
+
},
|
|
565
|
+
{ type: 'separator' },
|
|
566
|
+
// Window controls
|
|
567
|
+
{
|
|
568
|
+
label: this.mainWindow?.isVisible() ? 'Hide Window' : 'Show Window',
|
|
569
|
+
accelerator: 'CmdOrCtrl+H',
|
|
570
|
+
click: () => this.toggleMainWindow(),
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
label: 'Settings...',
|
|
574
|
+
accelerator: 'CmdOrCtrl+,',
|
|
575
|
+
click: () => {
|
|
576
|
+
this.showMainWindow();
|
|
577
|
+
this.mainWindow?.webContents.send('tray:open-settings');
|
|
578
|
+
},
|
|
579
|
+
},
|
|
580
|
+
{ type: 'separator' },
|
|
581
|
+
// App controls
|
|
582
|
+
{
|
|
583
|
+
label: 'About CoWork OS',
|
|
584
|
+
click: () => {
|
|
585
|
+
this.showMainWindow();
|
|
586
|
+
this.mainWindow?.webContents.send('tray:open-about');
|
|
587
|
+
},
|
|
588
|
+
},
|
|
589
|
+
{
|
|
590
|
+
label: 'Check for Updates...',
|
|
591
|
+
click: () => {
|
|
592
|
+
this.showMainWindow();
|
|
593
|
+
this.mainWindow?.webContents.send('tray:check-updates');
|
|
594
|
+
},
|
|
595
|
+
},
|
|
596
|
+
{ type: 'separator' },
|
|
597
|
+
{
|
|
598
|
+
label: 'Quit CoWork OS',
|
|
599
|
+
accelerator: 'CmdOrCtrl+Q',
|
|
600
|
+
click: () => {
|
|
601
|
+
// Force quit (bypass close-to-tray)
|
|
602
|
+
this.settings.closeToTray = false;
|
|
603
|
+
electron_1.app.quit();
|
|
604
|
+
},
|
|
605
|
+
},
|
|
606
|
+
];
|
|
607
|
+
const contextMenu = electron_1.Menu.buildFromTemplate(menuTemplate);
|
|
608
|
+
this.tray.setContextMenu(contextMenu);
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Build the channels submenu
|
|
612
|
+
*/
|
|
613
|
+
buildChannelsSubmenu() {
|
|
614
|
+
const channels = this.gateway?.getChannels() || [];
|
|
615
|
+
if (channels.length === 0) {
|
|
616
|
+
return [{ label: 'No channels configured', enabled: false }];
|
|
617
|
+
}
|
|
618
|
+
return channels.map((channel) => {
|
|
619
|
+
const statusIcon = channel.status === 'connected' ? '🟢' :
|
|
620
|
+
channel.status === 'connecting' ? '🟡' :
|
|
621
|
+
channel.status === 'error' ? '🔴' : '⚪';
|
|
622
|
+
return {
|
|
623
|
+
label: `${statusIcon} ${channel.name} (${channel.type})`,
|
|
624
|
+
enabled: false,
|
|
625
|
+
};
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
/**
|
|
629
|
+
* Get status text for the menu
|
|
630
|
+
*/
|
|
631
|
+
getStatusText() {
|
|
632
|
+
const channels = this.gateway?.getChannels() || [];
|
|
633
|
+
this.connectedChannels = channels.filter((c) => c.status === 'connected').length;
|
|
634
|
+
if (this.activeTaskCount > 0) {
|
|
635
|
+
return `Working on ${this.activeTaskCount} task${this.activeTaskCount > 1 ? 's' : ''}`;
|
|
636
|
+
}
|
|
637
|
+
if (this.connectedChannels > 0) {
|
|
638
|
+
return `${this.connectedChannels} channel${this.connectedChannels > 1 ? 's' : ''} connected`;
|
|
639
|
+
}
|
|
640
|
+
return 'Ready';
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Get status icon for the menu
|
|
644
|
+
*/
|
|
645
|
+
getStatusIcon() {
|
|
646
|
+
// Return undefined for now - icons in menu items can be complex
|
|
647
|
+
return undefined;
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* Get workspaces from database (excluding temp workspace)
|
|
651
|
+
*/
|
|
652
|
+
getWorkspaces() {
|
|
653
|
+
if (!this.dbManager)
|
|
654
|
+
return [];
|
|
655
|
+
try {
|
|
656
|
+
const db = this.dbManager.getDatabase();
|
|
657
|
+
const stmt = db.prepare('SELECT id, name, path FROM workspaces WHERE id != ? ORDER BY name');
|
|
658
|
+
return stmt.all(types_1.TEMP_WORKSPACE_ID);
|
|
659
|
+
}
|
|
660
|
+
catch (error) {
|
|
661
|
+
console.error('[TrayManager] Failed to get workspaces:', error);
|
|
662
|
+
return [];
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Toggle main window visibility
|
|
667
|
+
*/
|
|
668
|
+
toggleMainWindow() {
|
|
669
|
+
if (!this.mainWindow)
|
|
670
|
+
return;
|
|
671
|
+
if (this.mainWindow.isVisible()) {
|
|
672
|
+
this.mainWindow.hide();
|
|
673
|
+
}
|
|
674
|
+
else {
|
|
675
|
+
this.showMainWindow();
|
|
676
|
+
}
|
|
677
|
+
// Update menu to reflect new state
|
|
678
|
+
this.updateContextMenu();
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Show and focus the main window
|
|
682
|
+
*/
|
|
683
|
+
showMainWindow() {
|
|
684
|
+
if (!this.mainWindow)
|
|
685
|
+
return;
|
|
686
|
+
this.mainWindow.show();
|
|
687
|
+
this.mainWindow.focus();
|
|
688
|
+
// On macOS, also bring app to foreground
|
|
689
|
+
if (process.platform === 'darwin') {
|
|
690
|
+
electron_1.app.dock?.show();
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Set up close-to-tray behavior
|
|
695
|
+
*/
|
|
696
|
+
setupCloseToTray() {
|
|
697
|
+
if (!this.mainWindow)
|
|
698
|
+
return;
|
|
699
|
+
this.mainWindow.on('close', (event) => {
|
|
700
|
+
if (this.settings.closeToTray && this.tray) {
|
|
701
|
+
event.preventDefault();
|
|
702
|
+
this.mainWindow?.hide();
|
|
703
|
+
// On macOS, hide from dock when minimized to tray
|
|
704
|
+
if (process.platform === 'darwin' && !this.settings.showDockIcon) {
|
|
705
|
+
electron_1.app.dock?.hide();
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Apply dock icon visibility setting (macOS only)
|
|
712
|
+
*/
|
|
713
|
+
applyDockIconSetting() {
|
|
714
|
+
if (process.platform !== 'darwin')
|
|
715
|
+
return;
|
|
716
|
+
if (this.settings.showDockIcon) {
|
|
717
|
+
electron_1.app.dock?.show();
|
|
718
|
+
}
|
|
719
|
+
else {
|
|
720
|
+
electron_1.app.dock?.hide();
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Start periodic status updates
|
|
725
|
+
*/
|
|
726
|
+
startStatusUpdates() {
|
|
727
|
+
// Update every 5 seconds
|
|
728
|
+
setInterval(() => {
|
|
729
|
+
this.updateContextMenu();
|
|
730
|
+
this.updateTrayIcon();
|
|
731
|
+
}, 5000);
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* Update tray icon based on status
|
|
735
|
+
*/
|
|
736
|
+
updateTrayIcon() {
|
|
737
|
+
if (!this.tray)
|
|
738
|
+
return;
|
|
739
|
+
// Determine icon state based on app status
|
|
740
|
+
const state = this.activeTaskCount > 0 ? 'active' : 'idle';
|
|
741
|
+
const icon = this.getTrayIcon(state);
|
|
742
|
+
this.tray.setImage(icon);
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Update active task count
|
|
746
|
+
*/
|
|
747
|
+
setActiveTaskCount(count) {
|
|
748
|
+
this.activeTaskCount = count;
|
|
749
|
+
this.updateContextMenu();
|
|
750
|
+
this.updateTrayIcon();
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* Migrate settings from legacy JSON file to encrypted database
|
|
754
|
+
*/
|
|
755
|
+
migrateFromLegacyFile() {
|
|
756
|
+
if (TrayManager.migrationCompleted)
|
|
757
|
+
return;
|
|
758
|
+
try {
|
|
759
|
+
if (!SecureSettingsRepository_1.SecureSettingsRepository.isInitialized()) {
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
const repository = SecureSettingsRepository_1.SecureSettingsRepository.getInstance();
|
|
763
|
+
// Check if already migrated
|
|
764
|
+
if (repository.exists('tray')) {
|
|
765
|
+
TrayManager.migrationCompleted = true;
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
// Check if legacy file exists
|
|
769
|
+
if (!fs.existsSync(this.legacySettingsPath)) {
|
|
770
|
+
TrayManager.migrationCompleted = true;
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
console.log('[TrayManager] Migrating settings from legacy JSON file to encrypted database...');
|
|
774
|
+
// Create backup before migration
|
|
775
|
+
const backupPath = this.legacySettingsPath + '.migration-backup';
|
|
776
|
+
fs.copyFileSync(this.legacySettingsPath, backupPath);
|
|
777
|
+
try {
|
|
778
|
+
const data = fs.readFileSync(this.legacySettingsPath, 'utf-8');
|
|
779
|
+
const parsed = JSON.parse(data);
|
|
780
|
+
const merged = { ...DEFAULT_SETTINGS, ...parsed };
|
|
781
|
+
repository.save('tray', merged);
|
|
782
|
+
console.log('[TrayManager] Settings migrated to encrypted database');
|
|
783
|
+
// Migration successful - delete backup and original
|
|
784
|
+
fs.unlinkSync(backupPath);
|
|
785
|
+
fs.unlinkSync(this.legacySettingsPath);
|
|
786
|
+
console.log('[TrayManager] Migration complete, cleaned up legacy files');
|
|
787
|
+
TrayManager.migrationCompleted = true;
|
|
788
|
+
}
|
|
789
|
+
catch (migrationError) {
|
|
790
|
+
console.error('[TrayManager] Migration failed, backup preserved at:', backupPath);
|
|
791
|
+
throw migrationError;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
catch (error) {
|
|
795
|
+
console.error('[TrayManager] Migration failed:', error);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* Load settings from encrypted database
|
|
800
|
+
*/
|
|
801
|
+
loadSettings() {
|
|
802
|
+
// Migrate from legacy file if needed
|
|
803
|
+
this.migrateFromLegacyFile();
|
|
804
|
+
try {
|
|
805
|
+
if (SecureSettingsRepository_1.SecureSettingsRepository.isInitialized()) {
|
|
806
|
+
const repository = SecureSettingsRepository_1.SecureSettingsRepository.getInstance();
|
|
807
|
+
const stored = repository.load('tray');
|
|
808
|
+
if (stored) {
|
|
809
|
+
this.settings = { ...DEFAULT_SETTINGS, ...stored };
|
|
810
|
+
console.log('[TrayManager] Loaded settings from encrypted database');
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
catch (error) {
|
|
816
|
+
console.error('[TrayManager] Failed to load settings:', error);
|
|
817
|
+
}
|
|
818
|
+
// Fall back to defaults
|
|
819
|
+
this.settings = { ...DEFAULT_SETTINGS };
|
|
820
|
+
}
|
|
821
|
+
/**
|
|
822
|
+
* Save settings to encrypted database
|
|
823
|
+
*/
|
|
824
|
+
saveSettings(settings) {
|
|
825
|
+
this.settings = { ...this.settings, ...settings };
|
|
826
|
+
try {
|
|
827
|
+
if (SecureSettingsRepository_1.SecureSettingsRepository.isInitialized()) {
|
|
828
|
+
const repository = SecureSettingsRepository_1.SecureSettingsRepository.getInstance();
|
|
829
|
+
repository.save('tray', this.settings);
|
|
830
|
+
console.log('[TrayManager] Settings saved to encrypted database');
|
|
831
|
+
}
|
|
832
|
+
else {
|
|
833
|
+
console.warn('[TrayManager] SecureSettingsRepository not initialized, settings not persisted');
|
|
834
|
+
}
|
|
835
|
+
// Apply settings immediately
|
|
836
|
+
this.applyDockIconSetting();
|
|
837
|
+
// Recreate tray if enabled status changed
|
|
838
|
+
if (settings.enabled !== undefined) {
|
|
839
|
+
if (settings.enabled && !this.tray) {
|
|
840
|
+
this.createTray();
|
|
841
|
+
}
|
|
842
|
+
else if (!settings.enabled && this.tray) {
|
|
843
|
+
this.destroy();
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
catch (error) {
|
|
848
|
+
console.error('[TrayManager] Failed to save settings:', error);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* Get current settings
|
|
853
|
+
*/
|
|
854
|
+
getSettings() {
|
|
855
|
+
return { ...this.settings };
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Show a notification from the tray
|
|
859
|
+
*/
|
|
860
|
+
showNotification(title, body) {
|
|
861
|
+
if (!this.settings.showNotifications)
|
|
862
|
+
return;
|
|
863
|
+
const { Notification } = require('electron');
|
|
864
|
+
if (Notification.isSupported()) {
|
|
865
|
+
const notification = new Notification({
|
|
866
|
+
title,
|
|
867
|
+
body,
|
|
868
|
+
silent: false,
|
|
869
|
+
});
|
|
870
|
+
notification.on('click', () => {
|
|
871
|
+
this.showMainWindow();
|
|
872
|
+
});
|
|
873
|
+
notification.show();
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* Destroy the tray
|
|
878
|
+
*/
|
|
879
|
+
destroy() {
|
|
880
|
+
// Unregister global shortcut
|
|
881
|
+
this.unregisterGlobalShortcut();
|
|
882
|
+
if (this.quickInputWindow) {
|
|
883
|
+
this.quickInputWindow.destroy();
|
|
884
|
+
this.quickInputWindow = null;
|
|
885
|
+
}
|
|
886
|
+
if (this.tray) {
|
|
887
|
+
this.tray.destroy();
|
|
888
|
+
this.tray = null;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
exports.TrayManager = TrayManager;
|
|
893
|
+
TrayManager.instance = null;
|
|
894
|
+
TrayManager.migrationCompleted = false;
|
|
895
|
+
exports.trayManager = TrayManager.getInstance();
|