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,337 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandbox Factory
|
|
3
|
+
*
|
|
4
|
+
* Provides a unified interface for sandbox implementations and factory
|
|
5
|
+
* function to create the appropriate sandbox based on platform and availability.
|
|
6
|
+
*
|
|
7
|
+
* Supports:
|
|
8
|
+
* - macOS sandbox-exec (native, preferred on macOS)
|
|
9
|
+
* - Docker containers (cross-platform)
|
|
10
|
+
* - No sandbox (fallback)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { Workspace } from '../../../shared/types';
|
|
14
|
+
import { MacOSSandbox } from './macos-sandbox';
|
|
15
|
+
import { DockerSandbox } from './docker-sandbox';
|
|
16
|
+
import { spawn } from 'child_process';
|
|
17
|
+
import { createSecureTempFile } from './security-utils';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Sandbox type enumeration
|
|
21
|
+
*/
|
|
22
|
+
export type SandboxType = 'macos' | 'docker' | 'none';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Sandbox execution options
|
|
26
|
+
*/
|
|
27
|
+
export interface SandboxOptions {
|
|
28
|
+
/** Working directory for command execution */
|
|
29
|
+
cwd?: string;
|
|
30
|
+
/** Command execution timeout in milliseconds */
|
|
31
|
+
timeout?: number;
|
|
32
|
+
/** Maximum output size in bytes */
|
|
33
|
+
maxOutputSize?: number;
|
|
34
|
+
/** Allow network access */
|
|
35
|
+
allowNetwork?: boolean;
|
|
36
|
+
/** Additional allowed paths for read access */
|
|
37
|
+
allowedReadPaths?: string[];
|
|
38
|
+
/** Additional allowed paths for write access */
|
|
39
|
+
allowedWritePaths?: string[];
|
|
40
|
+
/** Environment variables to pass through */
|
|
41
|
+
envPassthrough?: string[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Sandbox execution result
|
|
46
|
+
*/
|
|
47
|
+
export interface SandboxResult {
|
|
48
|
+
exitCode: number;
|
|
49
|
+
stdout: string;
|
|
50
|
+
stderr: string;
|
|
51
|
+
killed: boolean;
|
|
52
|
+
timedOut: boolean;
|
|
53
|
+
error?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Unified sandbox interface
|
|
58
|
+
* All sandbox implementations must implement this interface
|
|
59
|
+
*/
|
|
60
|
+
export interface ISandbox {
|
|
61
|
+
/** The type of sandbox implementation */
|
|
62
|
+
readonly type: SandboxType;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Initialize the sandbox environment
|
|
66
|
+
* Must be called before execute()
|
|
67
|
+
*/
|
|
68
|
+
initialize(): Promise<void>;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Execute a command in the sandbox
|
|
72
|
+
*/
|
|
73
|
+
execute(
|
|
74
|
+
command: string,
|
|
75
|
+
args?: string[],
|
|
76
|
+
options?: SandboxOptions
|
|
77
|
+
): Promise<SandboxResult>;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Execute code in the sandbox (Python or JavaScript)
|
|
81
|
+
*/
|
|
82
|
+
executeCode(
|
|
83
|
+
code: string,
|
|
84
|
+
language: 'python' | 'javascript'
|
|
85
|
+
): Promise<SandboxResult>;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Cleanup sandbox resources
|
|
89
|
+
*/
|
|
90
|
+
cleanup(): void;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* No-op sandbox implementation for when sandboxing is unavailable
|
|
95
|
+
* Still enforces timeouts and output limits, but no OS-level isolation
|
|
96
|
+
*/
|
|
97
|
+
export class NoSandbox implements ISandbox {
|
|
98
|
+
readonly type: SandboxType = 'none';
|
|
99
|
+
private workspace: Workspace;
|
|
100
|
+
|
|
101
|
+
constructor(workspace: Workspace) {
|
|
102
|
+
this.workspace = workspace;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async initialize(): Promise<void> {
|
|
106
|
+
// No initialization needed
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async execute(
|
|
110
|
+
command: string,
|
|
111
|
+
args: string[] = [],
|
|
112
|
+
options: SandboxOptions = {}
|
|
113
|
+
): Promise<SandboxResult> {
|
|
114
|
+
const timeout = options.timeout ?? 5 * 60 * 1000;
|
|
115
|
+
const maxOutputSize = options.maxOutputSize ?? 100 * 1024;
|
|
116
|
+
const cwd = options.cwd || this.workspace.path;
|
|
117
|
+
|
|
118
|
+
return new Promise((resolve) => {
|
|
119
|
+
let stdout = '';
|
|
120
|
+
let stderr = '';
|
|
121
|
+
let killed = false;
|
|
122
|
+
let timedOut = false;
|
|
123
|
+
|
|
124
|
+
const proc = spawn('/bin/sh', ['-c', `${command} ${args.join(' ')}`], {
|
|
125
|
+
cwd,
|
|
126
|
+
shell: true,
|
|
127
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const timeoutHandle = setTimeout(() => {
|
|
131
|
+
timedOut = true;
|
|
132
|
+
killed = true;
|
|
133
|
+
proc.kill('SIGKILL');
|
|
134
|
+
}, timeout);
|
|
135
|
+
|
|
136
|
+
proc.stdout?.on('data', (data: Buffer) => {
|
|
137
|
+
const chunk = data.toString();
|
|
138
|
+
if (stdout.length + chunk.length <= maxOutputSize) {
|
|
139
|
+
stdout += chunk;
|
|
140
|
+
} else if (stdout.length < maxOutputSize) {
|
|
141
|
+
stdout += chunk.slice(0, maxOutputSize - stdout.length);
|
|
142
|
+
stdout += '\n[Output truncated]';
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
proc.stderr?.on('data', (data: Buffer) => {
|
|
147
|
+
const chunk = data.toString();
|
|
148
|
+
if (stderr.length + chunk.length <= maxOutputSize) {
|
|
149
|
+
stderr += chunk;
|
|
150
|
+
} else if (stderr.length < maxOutputSize) {
|
|
151
|
+
stderr += chunk.slice(0, maxOutputSize - stderr.length);
|
|
152
|
+
stderr += '\n[Output truncated]';
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
proc.on('close', (code) => {
|
|
157
|
+
clearTimeout(timeoutHandle);
|
|
158
|
+
resolve({
|
|
159
|
+
exitCode: code ?? 1,
|
|
160
|
+
stdout,
|
|
161
|
+
stderr,
|
|
162
|
+
killed,
|
|
163
|
+
timedOut,
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
proc.on('error', (err) => {
|
|
168
|
+
clearTimeout(timeoutHandle);
|
|
169
|
+
resolve({
|
|
170
|
+
exitCode: 1,
|
|
171
|
+
stdout,
|
|
172
|
+
stderr: err.message,
|
|
173
|
+
killed,
|
|
174
|
+
timedOut,
|
|
175
|
+
error: err.message,
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async executeCode(
|
|
182
|
+
code: string,
|
|
183
|
+
language: 'python' | 'javascript'
|
|
184
|
+
): Promise<SandboxResult> {
|
|
185
|
+
const ext = language === 'python' ? '.py' : '.js';
|
|
186
|
+
const { filePath, cleanup } = createSecureTempFile(ext, code);
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
const interpreter = language === 'python' ? 'python3' : 'node';
|
|
190
|
+
return await this.execute(interpreter, [filePath], {
|
|
191
|
+
timeout: 60 * 1000,
|
|
192
|
+
allowNetwork: false,
|
|
193
|
+
});
|
|
194
|
+
} finally {
|
|
195
|
+
cleanup();
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
cleanup(): void {
|
|
200
|
+
// No cleanup needed
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Cached Docker availability status
|
|
206
|
+
*/
|
|
207
|
+
let dockerAvailable: boolean | null = null;
|
|
208
|
+
let dockerCheckPromise: Promise<boolean> | null = null;
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Check if Docker is available and running
|
|
212
|
+
*/
|
|
213
|
+
export async function isDockerAvailable(): Promise<boolean> {
|
|
214
|
+
// Return cached result if available
|
|
215
|
+
if (dockerAvailable !== null) {
|
|
216
|
+
return dockerAvailable;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Return existing promise if check is in progress
|
|
220
|
+
if (dockerCheckPromise) {
|
|
221
|
+
return dockerCheckPromise;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
dockerCheckPromise = new Promise((resolve) => {
|
|
225
|
+
const proc = spawn('docker', ['info'], {
|
|
226
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
let resolved = false;
|
|
230
|
+
|
|
231
|
+
const timeout = setTimeout(() => {
|
|
232
|
+
if (!resolved) {
|
|
233
|
+
resolved = true;
|
|
234
|
+
proc.kill();
|
|
235
|
+
dockerAvailable = false;
|
|
236
|
+
resolve(false);
|
|
237
|
+
}
|
|
238
|
+
}, 5000);
|
|
239
|
+
|
|
240
|
+
proc.on('close', (code) => {
|
|
241
|
+
clearTimeout(timeout);
|
|
242
|
+
if (!resolved) {
|
|
243
|
+
resolved = true;
|
|
244
|
+
dockerAvailable = code === 0;
|
|
245
|
+
resolve(dockerAvailable);
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
proc.on('error', () => {
|
|
250
|
+
clearTimeout(timeout);
|
|
251
|
+
if (!resolved) {
|
|
252
|
+
resolved = true;
|
|
253
|
+
dockerAvailable = false;
|
|
254
|
+
resolve(false);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
return dockerCheckPromise;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Detect the best available sandbox type for the current platform
|
|
264
|
+
*/
|
|
265
|
+
export async function detectAvailableSandbox(): Promise<SandboxType> {
|
|
266
|
+
// On macOS, prefer native sandbox-exec
|
|
267
|
+
if (process.platform === 'darwin') {
|
|
268
|
+
return 'macos';
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// On other platforms, check for Docker
|
|
272
|
+
if (await isDockerAvailable()) {
|
|
273
|
+
return 'docker';
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Fallback to no sandbox
|
|
277
|
+
return 'none';
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Create a sandbox instance for the given workspace
|
|
282
|
+
*
|
|
283
|
+
* @param workspace - The workspace to create a sandbox for
|
|
284
|
+
* @param preferredType - Optional preferred sandbox type (overrides auto-detection)
|
|
285
|
+
* @returns An initialized sandbox instance
|
|
286
|
+
*/
|
|
287
|
+
export async function createSandbox(
|
|
288
|
+
workspace: Workspace,
|
|
289
|
+
preferredType?: SandboxType | 'auto'
|
|
290
|
+
): Promise<ISandbox> {
|
|
291
|
+
let sandboxType: SandboxType;
|
|
292
|
+
|
|
293
|
+
if (preferredType && preferredType !== 'auto') {
|
|
294
|
+
// Validate the preferred type is available
|
|
295
|
+
if (preferredType === 'macos' && process.platform !== 'darwin') {
|
|
296
|
+
console.warn(
|
|
297
|
+
'macOS sandbox requested but not on macOS, falling back to auto-detect'
|
|
298
|
+
);
|
|
299
|
+
sandboxType = await detectAvailableSandbox();
|
|
300
|
+
} else if (preferredType === 'docker' && !(await isDockerAvailable())) {
|
|
301
|
+
console.warn(
|
|
302
|
+
'Docker sandbox requested but Docker not available, falling back to auto-detect'
|
|
303
|
+
);
|
|
304
|
+
sandboxType = await detectAvailableSandbox();
|
|
305
|
+
} else {
|
|
306
|
+
sandboxType = preferredType;
|
|
307
|
+
}
|
|
308
|
+
} else {
|
|
309
|
+
sandboxType = await detectAvailableSandbox();
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
let sandbox: ISandbox;
|
|
313
|
+
|
|
314
|
+
switch (sandboxType) {
|
|
315
|
+
case 'macos':
|
|
316
|
+
sandbox = new MacOSSandbox(workspace);
|
|
317
|
+
break;
|
|
318
|
+
case 'docker':
|
|
319
|
+
sandbox = new DockerSandbox(workspace);
|
|
320
|
+
break;
|
|
321
|
+
case 'none':
|
|
322
|
+
default:
|
|
323
|
+
sandbox = new NoSandbox(workspace);
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
await sandbox.initialize();
|
|
328
|
+
return sandbox;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Reset Docker availability cache (useful for testing or after Docker installation)
|
|
333
|
+
*/
|
|
334
|
+
export function resetDockerCache(): void {
|
|
335
|
+
dockerAvailable = null;
|
|
336
|
+
dockerCheckPromise = null;
|
|
337
|
+
}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security Utilities for Sandbox Operations
|
|
3
|
+
*
|
|
4
|
+
* Provides secure implementations for common operations:
|
|
5
|
+
* - Secure temp file creation (TOCTOU-safe)
|
|
6
|
+
* - Path validation with symlink resolution
|
|
7
|
+
* - String escaping for sandbox profiles
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import * as fs from 'fs';
|
|
11
|
+
import * as path from 'path';
|
|
12
|
+
import * as os from 'os';
|
|
13
|
+
import * as crypto from 'crypto';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Create a secure temporary file with random name
|
|
17
|
+
* Uses crypto.randomBytes to prevent predictable filenames (TOCTOU mitigation)
|
|
18
|
+
*
|
|
19
|
+
* @param extension - File extension (e.g., '.py', '.js')
|
|
20
|
+
* @param content - Content to write to the file
|
|
21
|
+
* @returns Object with file path and cleanup function
|
|
22
|
+
*/
|
|
23
|
+
export function createSecureTempFile(
|
|
24
|
+
extension: string,
|
|
25
|
+
content: string
|
|
26
|
+
): { filePath: string; cleanup: () => void } {
|
|
27
|
+
// Generate cryptographically random filename
|
|
28
|
+
const randomBytes = crypto.randomBytes(16).toString('hex');
|
|
29
|
+
const filename = `cowork_${randomBytes}${extension}`;
|
|
30
|
+
const tempDir = os.tmpdir();
|
|
31
|
+
|
|
32
|
+
// Validate temp directory exists and is writable
|
|
33
|
+
if (!fs.existsSync(tempDir)) {
|
|
34
|
+
throw new Error(`Temp directory does not exist: ${tempDir}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const filePath = path.join(tempDir, filename);
|
|
38
|
+
|
|
39
|
+
// Use O_CREAT | O_EXCL to atomically create file (fails if exists)
|
|
40
|
+
// This prevents TOCTOU race conditions
|
|
41
|
+
const fd = fs.openSync(filePath, fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY, 0o600);
|
|
42
|
+
try {
|
|
43
|
+
fs.writeSync(fd, content, 0, 'utf8');
|
|
44
|
+
} finally {
|
|
45
|
+
fs.closeSync(fd);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const cleanup = () => {
|
|
49
|
+
try {
|
|
50
|
+
if (fs.existsSync(filePath)) {
|
|
51
|
+
fs.unlinkSync(filePath);
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
// Ignore cleanup errors
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
return { filePath, cleanup };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Validate and resolve a path, following symlinks
|
|
63
|
+
* Returns null if path is outside allowed boundaries
|
|
64
|
+
*
|
|
65
|
+
* @param targetPath - Path to validate
|
|
66
|
+
* @param allowedBasePaths - List of allowed base paths
|
|
67
|
+
* @returns Resolved real path or null if invalid/outside boundaries
|
|
68
|
+
*/
|
|
69
|
+
export function validateAndResolvePath(
|
|
70
|
+
targetPath: string,
|
|
71
|
+
allowedBasePaths: string[]
|
|
72
|
+
): string | null {
|
|
73
|
+
// Reject paths with null bytes (path traversal attack vector)
|
|
74
|
+
if (targetPath.includes('\0')) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Normalize the path first
|
|
79
|
+
const normalizedTarget = path.normalize(targetPath);
|
|
80
|
+
|
|
81
|
+
// Check if path exists before attempting to resolve symlinks
|
|
82
|
+
if (!fs.existsSync(normalizedTarget)) {
|
|
83
|
+
// For non-existent paths, just validate against normalized path
|
|
84
|
+
for (const basePath of allowedBasePaths) {
|
|
85
|
+
const normalizedBase = path.resolve(basePath);
|
|
86
|
+
if (path.resolve(normalizedTarget).startsWith(normalizedBase + path.sep) ||
|
|
87
|
+
path.resolve(normalizedTarget) === normalizedBase) {
|
|
88
|
+
return path.resolve(normalizedTarget);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
// Resolve symlinks to get real path
|
|
96
|
+
const realPath = fs.realpathSync(normalizedTarget);
|
|
97
|
+
|
|
98
|
+
// Check if real path is within allowed boundaries
|
|
99
|
+
for (const basePath of allowedBasePaths) {
|
|
100
|
+
const realBasePath = fs.existsSync(basePath)
|
|
101
|
+
? fs.realpathSync(basePath)
|
|
102
|
+
: path.resolve(basePath);
|
|
103
|
+
|
|
104
|
+
if (realPath.startsWith(realBasePath + path.sep) || realPath === realBasePath) {
|
|
105
|
+
return realPath;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return null;
|
|
110
|
+
} catch {
|
|
111
|
+
// If we can't resolve the path, it's not valid
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Escape a string for use in macOS sandbox-exec profile
|
|
118
|
+
* Prevents sandbox profile injection attacks
|
|
119
|
+
*
|
|
120
|
+
* @param input - String to escape
|
|
121
|
+
* @returns Escaped string safe for sandbox profile
|
|
122
|
+
*/
|
|
123
|
+
export function escapeSandboxProfileString(input: string): string {
|
|
124
|
+
// Validate input doesn't contain null bytes
|
|
125
|
+
if (input.includes('\0')) {
|
|
126
|
+
throw new Error('Path contains null byte, which is not allowed');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Escape characters that have special meaning in sandbox profiles:
|
|
130
|
+
// - Backslash: escape character
|
|
131
|
+
// - Double quote: string delimiter
|
|
132
|
+
// - Parentheses: LISP-like syntax
|
|
133
|
+
// - Semicolon: comment start
|
|
134
|
+
return input
|
|
135
|
+
.replace(/\\/g, '\\\\') // Escape backslashes first
|
|
136
|
+
.replace(/"/g, '\\"') // Escape double quotes
|
|
137
|
+
.replace(/\(/g, '\\(') // Escape open paren
|
|
138
|
+
.replace(/\)/g, '\\)') // Escape close paren
|
|
139
|
+
.replace(/;/g, '\\;'); // Escape semicolons
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Validate a path is safe for sandbox profile inclusion
|
|
144
|
+
* Rejects paths with dangerous characters that could break profile syntax
|
|
145
|
+
*
|
|
146
|
+
* @param pathToValidate - Path to validate
|
|
147
|
+
* @returns true if safe, throws error if not
|
|
148
|
+
*/
|
|
149
|
+
export function validatePathForSandboxProfile(pathToValidate: string): boolean {
|
|
150
|
+
// Check for null bytes
|
|
151
|
+
if (pathToValidate.includes('\0')) {
|
|
152
|
+
throw new Error('Path contains null byte');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Check for newlines (could inject new profile lines)
|
|
156
|
+
if (pathToValidate.includes('\n') || pathToValidate.includes('\r')) {
|
|
157
|
+
throw new Error('Path contains newline characters');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Validate path is absolute and normalized
|
|
161
|
+
if (!path.isAbsolute(pathToValidate)) {
|
|
162
|
+
throw new Error('Path must be absolute');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Escape a value for Docker environment variable
|
|
170
|
+
* Prevents command injection through environment variables
|
|
171
|
+
*
|
|
172
|
+
* @param value - Environment variable value to escape
|
|
173
|
+
* @returns Escaped value safe for Docker -e flag
|
|
174
|
+
*/
|
|
175
|
+
export function escapeDockerEnvValue(value: string): string {
|
|
176
|
+
// Reject values with null bytes
|
|
177
|
+
if (value.includes('\0')) {
|
|
178
|
+
throw new Error('Environment value contains null byte');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// For Docker, we need to handle shell metacharacters
|
|
182
|
+
// The safest approach is to reject problematic characters
|
|
183
|
+
// or use Docker's --env-file feature for complex values
|
|
184
|
+
|
|
185
|
+
// Check for newlines which could inject additional arguments
|
|
186
|
+
if (value.includes('\n') || value.includes('\r')) {
|
|
187
|
+
throw new Error('Environment value contains newline characters');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return value;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Validate an environment variable name is safe
|
|
195
|
+
*
|
|
196
|
+
* @param name - Environment variable name
|
|
197
|
+
* @returns true if valid, throws if not
|
|
198
|
+
*/
|
|
199
|
+
export function validateEnvVarName(name: string): boolean {
|
|
200
|
+
// Standard env var naming: alphanumeric and underscore, starting with letter or underscore
|
|
201
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
|
|
202
|
+
throw new Error(`Invalid environment variable name: ${name}`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Blacklist dangerous environment variables that could affect sandboxed processes
|
|
206
|
+
const dangerousVars = [
|
|
207
|
+
'LD_PRELOAD',
|
|
208
|
+
'LD_LIBRARY_PATH',
|
|
209
|
+
'DYLD_INSERT_LIBRARIES',
|
|
210
|
+
'DYLD_LIBRARY_PATH',
|
|
211
|
+
'PYTHONPATH',
|
|
212
|
+
'NODE_OPTIONS',
|
|
213
|
+
'NODE_PATH',
|
|
214
|
+
'PERL5OPT',
|
|
215
|
+
'RUBYOPT',
|
|
216
|
+
];
|
|
217
|
+
|
|
218
|
+
if (dangerousVars.includes(name.toUpperCase())) {
|
|
219
|
+
throw new Error(`Dangerous environment variable not allowed: ${name}`);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Safe environment passthrough that validates and filters variables
|
|
227
|
+
*
|
|
228
|
+
* @param requestedVars - List of environment variable names to pass through
|
|
229
|
+
* @returns Record of safe environment variables
|
|
230
|
+
*/
|
|
231
|
+
export function buildSafeEnvironment(
|
|
232
|
+
requestedVars: string[]
|
|
233
|
+
): Record<string, string | undefined> {
|
|
234
|
+
const safeEnv: Record<string, string | undefined> = {};
|
|
235
|
+
|
|
236
|
+
for (const name of requestedVars) {
|
|
237
|
+
try {
|
|
238
|
+
validateEnvVarName(name);
|
|
239
|
+
const value = process.env[name];
|
|
240
|
+
if (value !== undefined) {
|
|
241
|
+
escapeDockerEnvValue(value); // Validates value is safe
|
|
242
|
+
safeEnv[name] = value;
|
|
243
|
+
}
|
|
244
|
+
} catch {
|
|
245
|
+
// Skip invalid or dangerous variables silently
|
|
246
|
+
console.warn(`[SecurityUtils] Skipping unsafe environment variable: ${name}`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return safeEnv;
|
|
251
|
+
}
|