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,444 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* SecureSettingsRepository
|
|
4
|
+
*
|
|
5
|
+
* Stores all application settings in an encrypted format in the database.
|
|
6
|
+
* Uses Electron's safeStorage API which leverages the OS keychain:
|
|
7
|
+
* - macOS: Keychain
|
|
8
|
+
* - Windows: DPAPI (Data Protection API)
|
|
9
|
+
* - Linux: libsecret
|
|
10
|
+
*
|
|
11
|
+
* This ensures settings can ONLY be accessed by this app.
|
|
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.SecureSettingsRepository = void 0;
|
|
48
|
+
const electron_1 = require("electron");
|
|
49
|
+
const uuid_1 = require("uuid");
|
|
50
|
+
const crypto = __importStar(require("crypto"));
|
|
51
|
+
const fs = __importStar(require("fs"));
|
|
52
|
+
const path = __importStar(require("path"));
|
|
53
|
+
/** Machine ID file name - persisted for stable key derivation */
|
|
54
|
+
const MACHINE_ID_FILE = '.cowork-machine-id';
|
|
55
|
+
/**
|
|
56
|
+
* Repository for securely storing encrypted settings in the database
|
|
57
|
+
*/
|
|
58
|
+
class SecureSettingsRepository {
|
|
59
|
+
constructor(db) {
|
|
60
|
+
this.db = db;
|
|
61
|
+
this.machineId = null;
|
|
62
|
+
this.encryptionAvailable = electron_1.safeStorage.isEncryptionAvailable();
|
|
63
|
+
if (!this.encryptionAvailable) {
|
|
64
|
+
console.warn('[SecureSettingsRepository] OS encryption not available. Settings will be stored with app-level encryption only.');
|
|
65
|
+
}
|
|
66
|
+
// Initialize stable machine ID for fallback encryption
|
|
67
|
+
this.initializeMachineId();
|
|
68
|
+
// Set as singleton instance
|
|
69
|
+
SecureSettingsRepository.instance = this;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Initialize or load the stable machine ID
|
|
73
|
+
* This ID persists across hostname changes, making fallback encryption stable
|
|
74
|
+
*/
|
|
75
|
+
initializeMachineId() {
|
|
76
|
+
try {
|
|
77
|
+
const userDataPath = electron_1.app.getPath('userData');
|
|
78
|
+
const machineIdPath = path.join(userDataPath, MACHINE_ID_FILE);
|
|
79
|
+
if (fs.existsSync(machineIdPath)) {
|
|
80
|
+
this.machineId = fs.readFileSync(machineIdPath, 'utf-8').trim();
|
|
81
|
+
console.log('[SecureSettingsRepository] Loaded existing machine ID');
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
// Generate a new stable machine ID
|
|
85
|
+
this.machineId = (0, uuid_1.v4)();
|
|
86
|
+
// Write with restrictive permissions (owner read/write only)
|
|
87
|
+
fs.writeFileSync(machineIdPath, this.machineId, { mode: 0o600 });
|
|
88
|
+
console.log('[SecureSettingsRepository] Generated new machine ID');
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
console.warn('[SecureSettingsRepository] Failed to initialize machine ID, using fallback:', error);
|
|
93
|
+
// Fallback to old method if file operations fail
|
|
94
|
+
this.machineId = null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Get the singleton instance of SecureSettingsRepository.
|
|
99
|
+
* Must be called after the instance has been created.
|
|
100
|
+
*/
|
|
101
|
+
static getInstance() {
|
|
102
|
+
if (!SecureSettingsRepository.instance) {
|
|
103
|
+
throw new Error('SecureSettingsRepository has not been initialized. Initialize it in main.ts first.');
|
|
104
|
+
}
|
|
105
|
+
return SecureSettingsRepository.instance;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Check if the repository has been initialized
|
|
109
|
+
*/
|
|
110
|
+
static isInitialized() {
|
|
111
|
+
return SecureSettingsRepository.instance !== null;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Save settings for a category (creates or updates)
|
|
115
|
+
*/
|
|
116
|
+
save(category, settings) {
|
|
117
|
+
const now = Date.now();
|
|
118
|
+
const jsonData = JSON.stringify(settings);
|
|
119
|
+
const encryptedData = this.encrypt(jsonData);
|
|
120
|
+
const checksum = this.computeChecksum(jsonData);
|
|
121
|
+
const existing = this.findByCategory(category);
|
|
122
|
+
if (existing) {
|
|
123
|
+
// Update existing
|
|
124
|
+
const stmt = this.db.prepare(`
|
|
125
|
+
UPDATE secure_settings
|
|
126
|
+
SET encrypted_data = ?, checksum = ?, updated_at = ?
|
|
127
|
+
WHERE category = ?
|
|
128
|
+
`);
|
|
129
|
+
stmt.run(encryptedData, checksum, now, category);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
// Insert new
|
|
133
|
+
const stmt = this.db.prepare(`
|
|
134
|
+
INSERT INTO secure_settings (id, category, encrypted_data, checksum, created_at, updated_at)
|
|
135
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
136
|
+
`);
|
|
137
|
+
stmt.run((0, uuid_1.v4)(), category, encryptedData, checksum, now, now);
|
|
138
|
+
}
|
|
139
|
+
console.log(`[SecureSettingsRepository] Saved settings for category: ${category}`);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Load settings for a category
|
|
143
|
+
* Returns undefined if no settings exist or if decryption fails
|
|
144
|
+
*/
|
|
145
|
+
load(category) {
|
|
146
|
+
const result = this.loadWithStatus(category);
|
|
147
|
+
return result.data;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Load settings with detailed status information
|
|
151
|
+
* Use this when you need to distinguish between "not found" vs "corrupted" vs "decryption failed"
|
|
152
|
+
*/
|
|
153
|
+
loadWithStatus(category) {
|
|
154
|
+
const row = this.findByCategory(category);
|
|
155
|
+
if (!row) {
|
|
156
|
+
return { status: 'not_found' };
|
|
157
|
+
}
|
|
158
|
+
try {
|
|
159
|
+
const decrypted = this.decrypt(row.encrypted_data);
|
|
160
|
+
// Verify checksum to detect tampering
|
|
161
|
+
const checksum = this.computeChecksum(decrypted);
|
|
162
|
+
if (checksum !== row.checksum) {
|
|
163
|
+
console.error(`[SecureSettingsRepository] Checksum mismatch for category: ${category}. Data may be corrupted.`);
|
|
164
|
+
return {
|
|
165
|
+
status: 'checksum_mismatch',
|
|
166
|
+
error: 'Data integrity check failed. Settings may be corrupted.',
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
return {
|
|
170
|
+
status: 'success',
|
|
171
|
+
data: JSON.parse(decrypted),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
176
|
+
console.error(`[SecureSettingsRepository] Failed to decrypt settings for category: ${category}`, error);
|
|
177
|
+
// Detect specific failure modes
|
|
178
|
+
if (errorMessage.includes('OS encryption was used but is no longer available')) {
|
|
179
|
+
return {
|
|
180
|
+
status: 'os_encryption_unavailable',
|
|
181
|
+
error: 'Settings were encrypted with OS keychain which is no longer accessible. You may need to re-enter your credentials.',
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
status: 'decryption_failed',
|
|
186
|
+
error: errorMessage,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Check if settings can be decrypted (health check)
|
|
192
|
+
* Returns the status without exposing the actual data
|
|
193
|
+
*/
|
|
194
|
+
checkHealth(category) {
|
|
195
|
+
return this.loadWithStatus(category).status;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Delete settings for a category
|
|
199
|
+
*/
|
|
200
|
+
delete(category) {
|
|
201
|
+
const stmt = this.db.prepare('DELETE FROM secure_settings WHERE category = ?');
|
|
202
|
+
const result = stmt.run(category);
|
|
203
|
+
return result.changes > 0;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Check if settings exist for a category
|
|
207
|
+
*/
|
|
208
|
+
exists(category) {
|
|
209
|
+
const stmt = this.db.prepare('SELECT 1 FROM secure_settings WHERE category = ? LIMIT 1');
|
|
210
|
+
const row = stmt.get(category);
|
|
211
|
+
return row !== undefined;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Get all categories that have settings stored
|
|
215
|
+
*/
|
|
216
|
+
listCategories() {
|
|
217
|
+
const stmt = this.db.prepare('SELECT category FROM secure_settings ORDER BY category');
|
|
218
|
+
const rows = stmt.all();
|
|
219
|
+
return rows.map((r) => r.category);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Get metadata about stored settings (without decrypting)
|
|
223
|
+
*/
|
|
224
|
+
getMetadata(category) {
|
|
225
|
+
const stmt = this.db.prepare('SELECT created_at, updated_at FROM secure_settings WHERE category = ?');
|
|
226
|
+
const row = stmt.get(category);
|
|
227
|
+
return row ? { createdAt: row.created_at, updatedAt: row.updated_at } : undefined;
|
|
228
|
+
}
|
|
229
|
+
// ============ Backup & Recovery ============
|
|
230
|
+
/**
|
|
231
|
+
* Create an encrypted backup of all settings to a file
|
|
232
|
+
* The backup is encrypted with OS keychain when available
|
|
233
|
+
*/
|
|
234
|
+
createBackup(backupPath) {
|
|
235
|
+
try {
|
|
236
|
+
const categories = this.listCategories();
|
|
237
|
+
const backupData = {};
|
|
238
|
+
for (const category of categories) {
|
|
239
|
+
const result = this.loadWithStatus(category);
|
|
240
|
+
if (result.status === 'success' && result.data) {
|
|
241
|
+
backupData[category] = result.data;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
const jsonData = JSON.stringify({
|
|
245
|
+
version: 1,
|
|
246
|
+
timestamp: Date.now(),
|
|
247
|
+
categories: backupData,
|
|
248
|
+
});
|
|
249
|
+
// Encrypt the backup
|
|
250
|
+
const encryptedBackup = this.encrypt(jsonData);
|
|
251
|
+
fs.writeFileSync(backupPath, encryptedBackup, { mode: 0o600 });
|
|
252
|
+
console.log(`[SecureSettingsRepository] Created backup with ${categories.length} categories`);
|
|
253
|
+
return { success: true, categoriesBackedUp: categories };
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
257
|
+
console.error('[SecureSettingsRepository] Backup failed:', error);
|
|
258
|
+
return { success: false, categoriesBackedUp: [], error: errorMessage };
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Restore settings from an encrypted backup file
|
|
263
|
+
* @param backupPath Path to the backup file
|
|
264
|
+
* @param overwrite Whether to overwrite existing settings (default: false)
|
|
265
|
+
*/
|
|
266
|
+
restoreBackup(backupPath, overwrite = false) {
|
|
267
|
+
try {
|
|
268
|
+
if (!fs.existsSync(backupPath)) {
|
|
269
|
+
return { success: false, categoriesRestored: [], error: 'Backup file not found' };
|
|
270
|
+
}
|
|
271
|
+
const encryptedBackup = fs.readFileSync(backupPath, 'utf-8');
|
|
272
|
+
const jsonData = this.decrypt(encryptedBackup);
|
|
273
|
+
const backup = JSON.parse(jsonData);
|
|
274
|
+
if (!backup.version || !backup.categories) {
|
|
275
|
+
return { success: false, categoriesRestored: [], error: 'Invalid backup format' };
|
|
276
|
+
}
|
|
277
|
+
const categoriesRestored = [];
|
|
278
|
+
for (const [category, data] of Object.entries(backup.categories)) {
|
|
279
|
+
const existingStatus = this.checkHealth(category);
|
|
280
|
+
// Skip if exists and not overwriting
|
|
281
|
+
if (existingStatus === 'success' && !overwrite) {
|
|
282
|
+
console.log(`[SecureSettingsRepository] Skipping ${category} (exists, overwrite=false)`);
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
this.save(category, data);
|
|
286
|
+
categoriesRestored.push(category);
|
|
287
|
+
}
|
|
288
|
+
console.log(`[SecureSettingsRepository] Restored ${categoriesRestored.length} categories from backup`);
|
|
289
|
+
return { success: true, categoriesRestored };
|
|
290
|
+
}
|
|
291
|
+
catch (error) {
|
|
292
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
293
|
+
console.error('[SecureSettingsRepository] Restore failed:', error);
|
|
294
|
+
return { success: false, categoriesRestored: [], error: errorMessage };
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Delete corrupted settings for a category
|
|
299
|
+
* Use this when checkHealth returns 'checksum_mismatch' or 'decryption_failed'
|
|
300
|
+
* to allow the user to start fresh
|
|
301
|
+
*/
|
|
302
|
+
deleteCorrupted(category) {
|
|
303
|
+
const status = this.checkHealth(category);
|
|
304
|
+
if (status === 'success' || status === 'not_found') {
|
|
305
|
+
console.warn(`[SecureSettingsRepository] Category ${category} is not corrupted, not deleting`);
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
308
|
+
console.log(`[SecureSettingsRepository] Deleting corrupted settings for ${category} (status: ${status})`);
|
|
309
|
+
return this.delete(category);
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Re-encrypt all settings with current encryption method
|
|
313
|
+
* Useful after OS keychain becomes available or for migration
|
|
314
|
+
*/
|
|
315
|
+
reEncryptAll() {
|
|
316
|
+
const categories = this.listCategories();
|
|
317
|
+
const processed = [];
|
|
318
|
+
const errors = [];
|
|
319
|
+
for (const category of categories) {
|
|
320
|
+
try {
|
|
321
|
+
const result = this.loadWithStatus(category);
|
|
322
|
+
if (result.status === 'success' && result.data) {
|
|
323
|
+
// Re-save with current encryption method
|
|
324
|
+
this.save(category, result.data);
|
|
325
|
+
processed.push(category);
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
errors.push(`${category}: ${result.error || result.status}`);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
catch (error) {
|
|
332
|
+
errors.push(`${category}: ${error instanceof Error ? error.message : String(error)}`);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
console.log(`[SecureSettingsRepository] Re-encrypted ${processed.length}/${categories.length} categories`);
|
|
336
|
+
return { success: errors.length === 0, categoriesProcessed: processed, errors };
|
|
337
|
+
}
|
|
338
|
+
// ============ Private Methods ============
|
|
339
|
+
findByCategory(category) {
|
|
340
|
+
const stmt = this.db.prepare('SELECT * FROM secure_settings WHERE category = ?');
|
|
341
|
+
return stmt.get(category);
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Encrypt data using OS keychain (safeStorage) when available,
|
|
345
|
+
* otherwise use app-level encryption with a derived key
|
|
346
|
+
*/
|
|
347
|
+
encrypt(data) {
|
|
348
|
+
if (this.encryptionAvailable) {
|
|
349
|
+
// Use OS keychain encryption
|
|
350
|
+
const encryptedBuffer = electron_1.safeStorage.encryptString(data);
|
|
351
|
+
return 'os:' + encryptedBuffer.toString('base64');
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
// Fallback: Use app-level AES encryption
|
|
355
|
+
// The key is derived from a combination of app identity and machine-specific data
|
|
356
|
+
const key = this.deriveAppKey();
|
|
357
|
+
const iv = crypto.randomBytes(16);
|
|
358
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
|
|
359
|
+
let encrypted = cipher.update(data, 'utf8', 'base64');
|
|
360
|
+
encrypted += cipher.final('base64');
|
|
361
|
+
const authTag = cipher.getAuthTag();
|
|
362
|
+
// Format: app:<iv>:<authTag>:<encrypted>
|
|
363
|
+
return `app:${iv.toString('base64')}:${authTag.toString('base64')}:${encrypted}`;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Decrypt data using the appropriate method based on prefix
|
|
368
|
+
*/
|
|
369
|
+
decrypt(encryptedData) {
|
|
370
|
+
if (encryptedData.startsWith('os:')) {
|
|
371
|
+
// OS keychain decryption
|
|
372
|
+
if (!this.encryptionAvailable) {
|
|
373
|
+
throw new Error('OS encryption was used but is no longer available');
|
|
374
|
+
}
|
|
375
|
+
const base64Data = encryptedData.slice(3);
|
|
376
|
+
const encryptedBuffer = Buffer.from(base64Data, 'base64');
|
|
377
|
+
return electron_1.safeStorage.decryptString(encryptedBuffer);
|
|
378
|
+
}
|
|
379
|
+
else if (encryptedData.startsWith('app:')) {
|
|
380
|
+
// App-level AES decryption
|
|
381
|
+
const parts = encryptedData.slice(4).split(':');
|
|
382
|
+
if (parts.length !== 3) {
|
|
383
|
+
throw new Error('Invalid encrypted data format');
|
|
384
|
+
}
|
|
385
|
+
const [ivBase64, authTagBase64, encrypted] = parts;
|
|
386
|
+
const key = this.deriveAppKey();
|
|
387
|
+
const iv = Buffer.from(ivBase64, 'base64');
|
|
388
|
+
const authTag = Buffer.from(authTagBase64, 'base64');
|
|
389
|
+
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
|
|
390
|
+
decipher.setAuthTag(authTag);
|
|
391
|
+
let decrypted = decipher.update(encrypted, 'base64', 'utf8');
|
|
392
|
+
decrypted += decipher.final('utf8');
|
|
393
|
+
return decrypted;
|
|
394
|
+
}
|
|
395
|
+
else {
|
|
396
|
+
// Legacy unencrypted data (shouldn't happen, but handle gracefully)
|
|
397
|
+
console.warn('[SecureSettingsRepository] Found unencrypted data, returning as-is');
|
|
398
|
+
return encryptedData;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Derive an app-specific encryption key
|
|
403
|
+
* This key is deterministic per machine but not stored anywhere
|
|
404
|
+
*/
|
|
405
|
+
deriveAppKey() {
|
|
406
|
+
// Use a combination of:
|
|
407
|
+
// 1. App identifier (hardcoded, same for all installations)
|
|
408
|
+
// 2. Process info (changes per machine but is consistent)
|
|
409
|
+
const appSalt = 'cowork-os-secure-settings-v1';
|
|
410
|
+
const machineId = this.getMachineIdentifier();
|
|
411
|
+
// Derive a 256-bit key using PBKDF2
|
|
412
|
+
return crypto.pbkdf2Sync(appSalt, machineId, 100000, 32, 'sha512');
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Get a machine-specific identifier for key derivation
|
|
416
|
+
* Uses a stable persistent ID to survive hostname changes, user renames, etc.
|
|
417
|
+
*/
|
|
418
|
+
getMachineIdentifier() {
|
|
419
|
+
// Use stable machine ID if available (generated at first launch)
|
|
420
|
+
if (this.machineId) {
|
|
421
|
+
return this.machineId;
|
|
422
|
+
}
|
|
423
|
+
// Fallback: Combine system properties (less stable, for backwards compatibility)
|
|
424
|
+
// This path is only taken if machine ID file operations failed
|
|
425
|
+
const os = require('os');
|
|
426
|
+
const factors = [
|
|
427
|
+
os.hostname(),
|
|
428
|
+
os.platform(),
|
|
429
|
+
os.arch(),
|
|
430
|
+
os.homedir(),
|
|
431
|
+
process.env.USER || process.env.USERNAME || 'default-user',
|
|
432
|
+
];
|
|
433
|
+
console.warn('[SecureSettingsRepository] Using volatile machine identifier as fallback');
|
|
434
|
+
return factors.join(':');
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Compute a SHA-256 checksum for integrity verification
|
|
438
|
+
*/
|
|
439
|
+
computeChecksum(data) {
|
|
440
|
+
return crypto.createHash('sha256').update(data).digest('hex');
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
exports.SecureSettingsRepository = SecureSettingsRepository;
|
|
444
|
+
SecureSettingsRepository.instance = null;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TaskLabelRepository = void 0;
|
|
4
|
+
const uuid_1 = require("uuid");
|
|
5
|
+
const DEFAULT_LABEL_COLOR = '#6366f1';
|
|
6
|
+
/**
|
|
7
|
+
* Repository for managing task labels in the database
|
|
8
|
+
*/
|
|
9
|
+
class TaskLabelRepository {
|
|
10
|
+
constructor(db) {
|
|
11
|
+
this.db = db;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Create a new task label
|
|
15
|
+
*/
|
|
16
|
+
create(request) {
|
|
17
|
+
const now = Date.now();
|
|
18
|
+
const label = {
|
|
19
|
+
id: (0, uuid_1.v4)(),
|
|
20
|
+
workspaceId: request.workspaceId,
|
|
21
|
+
name: request.name,
|
|
22
|
+
color: request.color || DEFAULT_LABEL_COLOR,
|
|
23
|
+
createdAt: now,
|
|
24
|
+
};
|
|
25
|
+
const stmt = this.db.prepare(`
|
|
26
|
+
INSERT INTO task_labels (id, workspace_id, name, color, created_at)
|
|
27
|
+
VALUES (?, ?, ?, ?, ?)
|
|
28
|
+
`);
|
|
29
|
+
stmt.run(label.id, label.workspaceId, label.name, label.color, label.createdAt);
|
|
30
|
+
return label;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Find a task label by ID
|
|
34
|
+
*/
|
|
35
|
+
findById(id) {
|
|
36
|
+
const stmt = this.db.prepare('SELECT * FROM task_labels WHERE id = ?');
|
|
37
|
+
const row = stmt.get(id);
|
|
38
|
+
return row ? this.mapRowToLabel(row) : undefined;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Find a task label by name within a workspace
|
|
42
|
+
*/
|
|
43
|
+
findByName(workspaceId, name) {
|
|
44
|
+
const stmt = this.db.prepare('SELECT * FROM task_labels WHERE workspace_id = ? AND name = ?');
|
|
45
|
+
const row = stmt.get(workspaceId, name);
|
|
46
|
+
return row ? this.mapRowToLabel(row) : undefined;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* List all labels for a workspace
|
|
50
|
+
*/
|
|
51
|
+
list(query) {
|
|
52
|
+
const stmt = this.db.prepare('SELECT * FROM task_labels WHERE workspace_id = ? ORDER BY name ASC');
|
|
53
|
+
const rows = stmt.all(query.workspaceId);
|
|
54
|
+
return rows.map((row) => this.mapRowToLabel(row));
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Update a task label
|
|
58
|
+
*/
|
|
59
|
+
update(id, request) {
|
|
60
|
+
const existing = this.findById(id);
|
|
61
|
+
if (!existing)
|
|
62
|
+
return undefined;
|
|
63
|
+
const updates = [];
|
|
64
|
+
const params = [];
|
|
65
|
+
if (request.name !== undefined) {
|
|
66
|
+
updates.push('name = ?');
|
|
67
|
+
params.push(request.name);
|
|
68
|
+
}
|
|
69
|
+
if (request.color !== undefined) {
|
|
70
|
+
updates.push('color = ?');
|
|
71
|
+
params.push(request.color);
|
|
72
|
+
}
|
|
73
|
+
if (updates.length === 0)
|
|
74
|
+
return existing;
|
|
75
|
+
params.push(id);
|
|
76
|
+
const stmt = this.db.prepare(`UPDATE task_labels SET ${updates.join(', ')} WHERE id = ?`);
|
|
77
|
+
stmt.run(...params);
|
|
78
|
+
return this.findById(id);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Delete a task label
|
|
82
|
+
*/
|
|
83
|
+
delete(id) {
|
|
84
|
+
const stmt = this.db.prepare('DELETE FROM task_labels WHERE id = ?');
|
|
85
|
+
const result = stmt.run(id);
|
|
86
|
+
return result.changes > 0;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Delete all labels for a workspace
|
|
90
|
+
*/
|
|
91
|
+
deleteByWorkspace(workspaceId) {
|
|
92
|
+
const stmt = this.db.prepare('DELETE FROM task_labels WHERE workspace_id = ?');
|
|
93
|
+
const result = stmt.run(workspaceId);
|
|
94
|
+
return result.changes;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Get multiple labels by IDs
|
|
98
|
+
*/
|
|
99
|
+
getByIds(ids) {
|
|
100
|
+
if (ids.length === 0)
|
|
101
|
+
return [];
|
|
102
|
+
const placeholders = ids.map(() => '?').join(', ');
|
|
103
|
+
const stmt = this.db.prepare(`SELECT * FROM task_labels WHERE id IN (${placeholders})`);
|
|
104
|
+
const rows = stmt.all(...ids);
|
|
105
|
+
return rows.map((row) => this.mapRowToLabel(row));
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Map database row to TaskLabel object
|
|
109
|
+
*/
|
|
110
|
+
mapRowToLabel(row) {
|
|
111
|
+
return {
|
|
112
|
+
id: row.id,
|
|
113
|
+
workspaceId: row.workspace_id,
|
|
114
|
+
name: row.name,
|
|
115
|
+
color: row.color,
|
|
116
|
+
createdAt: row.created_at,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
exports.TaskLabelRepository = TaskLabelRepository;
|