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,714 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Canvas Manager
|
|
4
|
+
*
|
|
5
|
+
* Manages Live Canvas sessions - agent-driven visual workspaces that render
|
|
6
|
+
* HTML/CSS/JS content in dedicated Electron BrowserWindows.
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - Session lifecycle management (create, show, hide, close)
|
|
10
|
+
* - Content pushing with auto-reload via file watching
|
|
11
|
+
* - JavaScript execution in canvas context
|
|
12
|
+
* - Screenshot capture
|
|
13
|
+
* - A2UI (Agent-to-UI) action handling
|
|
14
|
+
*/
|
|
15
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
18
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
19
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
20
|
+
}
|
|
21
|
+
Object.defineProperty(o, k2, desc);
|
|
22
|
+
}) : (function(o, m, k, k2) {
|
|
23
|
+
if (k2 === undefined) k2 = k;
|
|
24
|
+
o[k2] = m[k];
|
|
25
|
+
}));
|
|
26
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
27
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
28
|
+
}) : function(o, v) {
|
|
29
|
+
o["default"] = v;
|
|
30
|
+
});
|
|
31
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
32
|
+
var ownKeys = function(o) {
|
|
33
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
34
|
+
var ar = [];
|
|
35
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
36
|
+
return ar;
|
|
37
|
+
};
|
|
38
|
+
return ownKeys(o);
|
|
39
|
+
};
|
|
40
|
+
return function (mod) {
|
|
41
|
+
if (mod && mod.__esModule) return mod;
|
|
42
|
+
var result = {};
|
|
43
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
44
|
+
__setModuleDefault(result, mod);
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
47
|
+
})();
|
|
48
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
49
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
50
|
+
};
|
|
51
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
52
|
+
exports.CanvasManager = void 0;
|
|
53
|
+
const electron_1 = require("electron");
|
|
54
|
+
const path = __importStar(require("path"));
|
|
55
|
+
const fs = __importStar(require("fs/promises"));
|
|
56
|
+
const fs_1 = require("fs");
|
|
57
|
+
const crypto_1 = require("crypto");
|
|
58
|
+
const chokidar_1 = __importDefault(require("chokidar"));
|
|
59
|
+
const canvas_store_1 = require("./canvas-store");
|
|
60
|
+
// Default HTML scaffold for new canvas sessions
|
|
61
|
+
const DEFAULT_HTML = `<!DOCTYPE html>
|
|
62
|
+
<html>
|
|
63
|
+
<head>
|
|
64
|
+
<meta charset="UTF-8">
|
|
65
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
66
|
+
<title>Live Canvas</title>
|
|
67
|
+
<style>
|
|
68
|
+
body {
|
|
69
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
70
|
+
margin: 0;
|
|
71
|
+
padding: 20px;
|
|
72
|
+
background: #0f1220;
|
|
73
|
+
color: #e7e9f2;
|
|
74
|
+
min-height: 100vh;
|
|
75
|
+
display: grid;
|
|
76
|
+
place-items: center;
|
|
77
|
+
}
|
|
78
|
+
.loading {
|
|
79
|
+
display: flex;
|
|
80
|
+
flex-direction: column;
|
|
81
|
+
align-items: center;
|
|
82
|
+
justify-content: center;
|
|
83
|
+
gap: 16px;
|
|
84
|
+
text-align: center;
|
|
85
|
+
}
|
|
86
|
+
.spinner {
|
|
87
|
+
width: 44px;
|
|
88
|
+
height: 44px;
|
|
89
|
+
border-radius: 50%;
|
|
90
|
+
border: 4px solid rgba(255, 255, 255, 0.15);
|
|
91
|
+
border-top-color: #52d1dc;
|
|
92
|
+
animation: spin 0.9s linear infinite;
|
|
93
|
+
box-shadow: 0 0 18px rgba(82, 209, 220, 0.35);
|
|
94
|
+
}
|
|
95
|
+
.message {
|
|
96
|
+
font-size: 1.05em;
|
|
97
|
+
color: #a3acc4;
|
|
98
|
+
letter-spacing: 0.2px;
|
|
99
|
+
}
|
|
100
|
+
@keyframes spin {
|
|
101
|
+
to { transform: rotate(360deg); }
|
|
102
|
+
}
|
|
103
|
+
</style>
|
|
104
|
+
</head>
|
|
105
|
+
<body>
|
|
106
|
+
<div class="loading">
|
|
107
|
+
<div class="spinner"></div>
|
|
108
|
+
<div class="message">Waiting for content...</div>
|
|
109
|
+
</div>
|
|
110
|
+
</body>
|
|
111
|
+
</html>`;
|
|
112
|
+
/**
|
|
113
|
+
* Canvas Manager Singleton
|
|
114
|
+
*/
|
|
115
|
+
class CanvasManager {
|
|
116
|
+
constructor() {
|
|
117
|
+
this.sessions = new Map();
|
|
118
|
+
this.windows = new Map();
|
|
119
|
+
this.watchers = new Map();
|
|
120
|
+
this.windowToSession = new Map();
|
|
121
|
+
this.mainWindow = null;
|
|
122
|
+
this.eventCallback = null;
|
|
123
|
+
this.a2uiCallback = null;
|
|
124
|
+
}
|
|
125
|
+
getSessionMode(session) {
|
|
126
|
+
return session.mode || 'html';
|
|
127
|
+
}
|
|
128
|
+
getCanvasUrl(sessionId) {
|
|
129
|
+
return `canvas://${sessionId}/index.html`;
|
|
130
|
+
}
|
|
131
|
+
normalizeUrl(rawUrl) {
|
|
132
|
+
const trimmed = rawUrl.trim();
|
|
133
|
+
if (!trimmed) {
|
|
134
|
+
throw new Error('URL cannot be empty');
|
|
135
|
+
}
|
|
136
|
+
const withScheme = /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
|
|
137
|
+
const parsed = new URL(withScheme);
|
|
138
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
139
|
+
throw new Error('Only http and https URLs are supported for canvas browsing');
|
|
140
|
+
}
|
|
141
|
+
return parsed.toString();
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Get the singleton instance
|
|
145
|
+
*/
|
|
146
|
+
static getInstance() {
|
|
147
|
+
if (!CanvasManager.instance) {
|
|
148
|
+
CanvasManager.instance = new CanvasManager();
|
|
149
|
+
}
|
|
150
|
+
return CanvasManager.instance;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Set the main window reference for event broadcasting
|
|
154
|
+
*/
|
|
155
|
+
setMainWindow(window) {
|
|
156
|
+
this.mainWindow = window;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Set callback for canvas events (used for IPC broadcasting)
|
|
160
|
+
*/
|
|
161
|
+
setEventCallback(callback) {
|
|
162
|
+
this.eventCallback = callback;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Set callback for A2UI actions
|
|
166
|
+
*/
|
|
167
|
+
setA2UICallback(callback) {
|
|
168
|
+
this.a2uiCallback = callback;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Restore sessions from disk storage
|
|
172
|
+
* Called on app startup to reload persisted sessions
|
|
173
|
+
*/
|
|
174
|
+
async restoreSessions() {
|
|
175
|
+
try {
|
|
176
|
+
const store = await (0, canvas_store_1.loadCanvasStore)();
|
|
177
|
+
for (const session of store.sessions) {
|
|
178
|
+
// Only restore active sessions with valid directories
|
|
179
|
+
if (session.status === 'active' && (0, fs_1.existsSync)(session.sessionDir)) {
|
|
180
|
+
this.sessions.set(session.id, session);
|
|
181
|
+
console.log(`[CanvasManager] Restored session ${session.id} for task ${session.taskId}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
console.log(`[CanvasManager] Restored ${this.sessions.size} sessions from disk`);
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
console.error('[CanvasManager] Failed to restore sessions:', error);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Persist all sessions to disk
|
|
192
|
+
*/
|
|
193
|
+
async persistSessions() {
|
|
194
|
+
try {
|
|
195
|
+
const sessions = Array.from(this.sessions.values());
|
|
196
|
+
await (0, canvas_store_1.saveCanvasStore)({ version: 1, sessions });
|
|
197
|
+
console.log(`[CanvasManager] Persisted ${sessions.length} sessions to disk`);
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
console.error('[CanvasManager] Failed to persist sessions:', error);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Create a new canvas session
|
|
205
|
+
*/
|
|
206
|
+
async createSession(taskId, workspaceId, title, options) {
|
|
207
|
+
const sessionId = (0, crypto_1.randomUUID)();
|
|
208
|
+
const sessionDir = path.join(electron_1.app.getPath('userData'), 'canvas', sessionId);
|
|
209
|
+
// Create session directory
|
|
210
|
+
await fs.mkdir(sessionDir, { recursive: true });
|
|
211
|
+
// Write default HTML scaffold
|
|
212
|
+
await fs.writeFile(path.join(sessionDir, 'index.html'), DEFAULT_HTML, 'utf-8');
|
|
213
|
+
const normalizedUrl = options?.url ? this.normalizeUrl(options.url) : undefined;
|
|
214
|
+
const normalizedMode = options?.mode === 'browser' && normalizedUrl ? 'browser' : 'html';
|
|
215
|
+
const session = {
|
|
216
|
+
id: sessionId,
|
|
217
|
+
taskId,
|
|
218
|
+
workspaceId,
|
|
219
|
+
sessionDir,
|
|
220
|
+
mode: normalizedMode,
|
|
221
|
+
url: normalizedMode === 'browser' ? normalizedUrl : undefined,
|
|
222
|
+
status: 'active',
|
|
223
|
+
title: title || `Canvas ${new Date().toLocaleTimeString()}`,
|
|
224
|
+
createdAt: Date.now(),
|
|
225
|
+
lastUpdatedAt: Date.now(),
|
|
226
|
+
};
|
|
227
|
+
this.sessions.set(sessionId, session);
|
|
228
|
+
// Persist sessions to disk
|
|
229
|
+
await this.persistSessions();
|
|
230
|
+
// Emit event
|
|
231
|
+
this.emitEvent({
|
|
232
|
+
type: 'session_created',
|
|
233
|
+
sessionId,
|
|
234
|
+
taskId,
|
|
235
|
+
timestamp: Date.now(),
|
|
236
|
+
session,
|
|
237
|
+
});
|
|
238
|
+
console.log(`[CanvasManager] Created session ${sessionId} for task ${taskId}`);
|
|
239
|
+
return session;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Get a session by ID
|
|
243
|
+
*/
|
|
244
|
+
getSession(sessionId) {
|
|
245
|
+
return this.sessions.get(sessionId);
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Get session ID from a BrowserWindow
|
|
249
|
+
*/
|
|
250
|
+
getSessionFromWindow(window) {
|
|
251
|
+
return this.windowToSession.get(window.id);
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* List all sessions for a task
|
|
255
|
+
*/
|
|
256
|
+
listSessionsForTask(taskId) {
|
|
257
|
+
return Array.from(this.sessions.values()).filter((s) => s.taskId === taskId);
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* List all active sessions
|
|
261
|
+
*/
|
|
262
|
+
listAllSessions() {
|
|
263
|
+
return Array.from(this.sessions.values());
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Push content to a canvas session
|
|
267
|
+
*/
|
|
268
|
+
async pushContent(sessionId, content, filename = 'index.html') {
|
|
269
|
+
const session = this.sessions.get(sessionId);
|
|
270
|
+
if (!session) {
|
|
271
|
+
const existingSessions = Array.from(this.sessions.keys());
|
|
272
|
+
console.error(`[CanvasManager] Session not found: "${sessionId}"`);
|
|
273
|
+
console.error(`[CanvasManager] Existing sessions: ${existingSessions.length > 0 ? existingSessions.join(', ') : 'none'}`);
|
|
274
|
+
throw new Error(`Canvas session not found: "${sessionId}". Available sessions: ${existingSessions.join(', ') || 'none'}`);
|
|
275
|
+
}
|
|
276
|
+
const wasBrowser = this.getSessionMode(session) === 'browser';
|
|
277
|
+
// Sanitize filename to prevent path traversal
|
|
278
|
+
const safeFilename = path.basename(filename);
|
|
279
|
+
const filePath = path.join(session.sessionDir, safeFilename);
|
|
280
|
+
await fs.writeFile(filePath, content, 'utf-8');
|
|
281
|
+
// Switch back to HTML mode when content is pushed
|
|
282
|
+
session.mode = 'html';
|
|
283
|
+
session.url = undefined;
|
|
284
|
+
// Update session timestamp
|
|
285
|
+
session.lastUpdatedAt = Date.now();
|
|
286
|
+
// Persist sessions to disk (in background, don't await to avoid blocking)
|
|
287
|
+
this.persistSessions().catch(err => console.error('[CanvasManager] Failed to persist after push:', err));
|
|
288
|
+
// Ensure a hidden window exists for snapshots (NOT shown to user)
|
|
289
|
+
// The window will only be shown when user explicitly requests via showCanvas()
|
|
290
|
+
const window = await this.ensureWindowForSnapshots(sessionId);
|
|
291
|
+
// If the session previously loaded a remote URL, navigate back to canvas content
|
|
292
|
+
if (wasBrowser && window && !window.isDestroyed()) {
|
|
293
|
+
await window.loadURL(this.getCanvasUrl(sessionId));
|
|
294
|
+
}
|
|
295
|
+
// Ensure watcher is running for HTML mode
|
|
296
|
+
this.startWatcher(sessionId, session.sessionDir, window);
|
|
297
|
+
// Emit event
|
|
298
|
+
this.emitEvent({
|
|
299
|
+
type: 'content_pushed',
|
|
300
|
+
sessionId,
|
|
301
|
+
taskId: session.taskId,
|
|
302
|
+
timestamp: Date.now(),
|
|
303
|
+
});
|
|
304
|
+
this.emitEvent({
|
|
305
|
+
type: 'session_updated',
|
|
306
|
+
sessionId,
|
|
307
|
+
taskId: session.taskId,
|
|
308
|
+
timestamp: Date.now(),
|
|
309
|
+
session,
|
|
310
|
+
});
|
|
311
|
+
console.log(`[CanvasManager] Pushed ${safeFilename} to session ${sessionId}`);
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Open a remote URL inside the canvas window (browser mode)
|
|
315
|
+
*/
|
|
316
|
+
async openUrl(sessionId, rawUrl, options) {
|
|
317
|
+
const session = this.sessions.get(sessionId);
|
|
318
|
+
if (!session) {
|
|
319
|
+
const existingSessions = Array.from(this.sessions.keys());
|
|
320
|
+
console.error(`[CanvasManager] Session not found: "${sessionId}"`);
|
|
321
|
+
console.error(`[CanvasManager] Existing sessions: ${existingSessions.length > 0 ? existingSessions.join(', ') : 'none'}`);
|
|
322
|
+
throw new Error(`Canvas session not found: "${sessionId}". Available sessions: ${existingSessions.join(', ') || 'none'}`);
|
|
323
|
+
}
|
|
324
|
+
const normalizedUrl = this.normalizeUrl(rawUrl);
|
|
325
|
+
session.mode = 'browser';
|
|
326
|
+
session.url = normalizedUrl;
|
|
327
|
+
session.lastUpdatedAt = Date.now();
|
|
328
|
+
this.persistSessions().catch(err => console.error('[CanvasManager] Failed to persist after openUrl:', err));
|
|
329
|
+
const window = await this.ensureWindowForSnapshots(sessionId);
|
|
330
|
+
if (window && !window.isDestroyed()) {
|
|
331
|
+
this.stopWatcher(sessionId);
|
|
332
|
+
if (window.webContents.getURL() !== normalizedUrl) {
|
|
333
|
+
await window.loadURL(normalizedUrl);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
this.emitEvent({
|
|
337
|
+
type: 'session_updated',
|
|
338
|
+
sessionId,
|
|
339
|
+
taskId: session.taskId,
|
|
340
|
+
timestamp: Date.now(),
|
|
341
|
+
session,
|
|
342
|
+
});
|
|
343
|
+
if (options?.show) {
|
|
344
|
+
await this.showCanvas(sessionId);
|
|
345
|
+
}
|
|
346
|
+
console.log(`[CanvasManager] Opened URL in session ${sessionId}: ${normalizedUrl}`);
|
|
347
|
+
return normalizedUrl;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Ensure a window exists for taking snapshots (hidden by default)
|
|
351
|
+
* This creates a hidden window that can be used for previews without
|
|
352
|
+
* showing a separate window to the user
|
|
353
|
+
*/
|
|
354
|
+
async ensureWindowForSnapshots(sessionId) {
|
|
355
|
+
const session = this.sessions.get(sessionId);
|
|
356
|
+
if (!session) {
|
|
357
|
+
throw new Error('Canvas session not found');
|
|
358
|
+
}
|
|
359
|
+
let window = this.windows.get(sessionId);
|
|
360
|
+
if (!window || window.isDestroyed()) {
|
|
361
|
+
// Calculate initial position - to the right of main window or right side of screen
|
|
362
|
+
let initialX;
|
|
363
|
+
let initialY;
|
|
364
|
+
let initialHeight = 700;
|
|
365
|
+
if (this.mainWindow && !this.mainWindow.isDestroyed()) {
|
|
366
|
+
const mainBounds = this.mainWindow.getBounds();
|
|
367
|
+
initialX = mainBounds.x + mainBounds.width + 20;
|
|
368
|
+
initialY = mainBounds.y;
|
|
369
|
+
initialHeight = mainBounds.height;
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
// Fallback: position on right side of primary display
|
|
373
|
+
const primaryDisplay = electron_1.screen.getPrimaryDisplay();
|
|
374
|
+
const { width: screenWidth, height: screenHeight } = primaryDisplay.workAreaSize;
|
|
375
|
+
initialX = screenWidth - 920; // 900 width + 20 margin
|
|
376
|
+
initialY = 50;
|
|
377
|
+
initialHeight = screenHeight - 100;
|
|
378
|
+
}
|
|
379
|
+
// Create new HIDDEN window for snapshots
|
|
380
|
+
// NOT a child window - will be positioned to the side when shown
|
|
381
|
+
window = new electron_1.BrowserWindow({
|
|
382
|
+
x: initialX,
|
|
383
|
+
y: initialY,
|
|
384
|
+
width: 900,
|
|
385
|
+
height: initialHeight,
|
|
386
|
+
title: session.title || 'Live Canvas',
|
|
387
|
+
show: false, // Start hidden - only show when user explicitly requests
|
|
388
|
+
// No parent - independent window that won't overlap main app
|
|
389
|
+
webPreferences: {
|
|
390
|
+
preload: path.join(__dirname, 'canvas-preload.js'),
|
|
391
|
+
contextIsolation: true,
|
|
392
|
+
nodeIntegration: false,
|
|
393
|
+
sandbox: false,
|
|
394
|
+
},
|
|
395
|
+
backgroundColor: '#1a1a2e',
|
|
396
|
+
});
|
|
397
|
+
this.windows.set(sessionId, window);
|
|
398
|
+
this.windowToSession.set(window.id, sessionId);
|
|
399
|
+
// Handle window close
|
|
400
|
+
window.on('closed', () => {
|
|
401
|
+
this.windows.delete(sessionId);
|
|
402
|
+
this.windowToSession.delete(window.id);
|
|
403
|
+
this.stopWatcher(sessionId);
|
|
404
|
+
});
|
|
405
|
+
const mode = this.getSessionMode(session);
|
|
406
|
+
let targetUrl = this.getCanvasUrl(sessionId);
|
|
407
|
+
if (mode === 'browser' && session.url) {
|
|
408
|
+
try {
|
|
409
|
+
targetUrl = this.normalizeUrl(session.url);
|
|
410
|
+
}
|
|
411
|
+
catch (error) {
|
|
412
|
+
console.warn('[CanvasManager] Invalid stored URL, falling back to canvas content:', error);
|
|
413
|
+
session.mode = 'html';
|
|
414
|
+
session.url = undefined;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
// Load the canvas URL (or remote URL for browser mode)
|
|
418
|
+
await window.loadURL(targetUrl);
|
|
419
|
+
// Start file watcher for auto-reload only in HTML mode
|
|
420
|
+
if (mode === 'html') {
|
|
421
|
+
this.startWatcher(sessionId, session.sessionDir, window);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
// Ensure watcher state is correct for existing windows
|
|
425
|
+
if (window && !window.isDestroyed()) {
|
|
426
|
+
const mode = this.getSessionMode(session);
|
|
427
|
+
if (mode === 'html') {
|
|
428
|
+
this.startWatcher(sessionId, session.sessionDir, window);
|
|
429
|
+
}
|
|
430
|
+
else {
|
|
431
|
+
this.stopWatcher(sessionId);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
return window;
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Show the canvas window (opens it visibly to the user)
|
|
438
|
+
*/
|
|
439
|
+
async showCanvas(sessionId) {
|
|
440
|
+
// Ensure window exists (may be hidden)
|
|
441
|
+
const window = await this.ensureWindowForSnapshots(sessionId);
|
|
442
|
+
let bounds;
|
|
443
|
+
if (this.mainWindow && !this.mainWindow.isDestroyed()) {
|
|
444
|
+
const mainBounds = this.mainWindow.getBounds();
|
|
445
|
+
console.log(`[CanvasManager] Main window bounds:`, mainBounds);
|
|
446
|
+
// Position canvas window completely to the RIGHT of the main window
|
|
447
|
+
// This ensures it never overlaps with the main app
|
|
448
|
+
bounds = {
|
|
449
|
+
x: mainBounds.x + mainBounds.width + 20, // 20px gap to the right
|
|
450
|
+
y: mainBounds.y,
|
|
451
|
+
width: 900,
|
|
452
|
+
height: mainBounds.height,
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
else {
|
|
456
|
+
console.log(`[CanvasManager] WARNING: mainWindow not available, using fallback position`);
|
|
457
|
+
// Fallback: position on right side of primary display
|
|
458
|
+
const primaryDisplay = electron_1.screen.getPrimaryDisplay();
|
|
459
|
+
const { width: screenWidth, height: screenHeight } = primaryDisplay.workAreaSize;
|
|
460
|
+
bounds = {
|
|
461
|
+
x: screenWidth - 920, // 900 width + 20 margin
|
|
462
|
+
y: 50,
|
|
463
|
+
width: 900,
|
|
464
|
+
height: screenHeight - 100,
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
console.log(`[CanvasManager] Setting canvas window bounds:`, bounds);
|
|
468
|
+
// Always set position first to ensure correct placement
|
|
469
|
+
window.setPosition(bounds.x, bounds.y);
|
|
470
|
+
window.setSize(bounds.width, bounds.height);
|
|
471
|
+
// Show and focus the window so keyboard input works for interactive browsing
|
|
472
|
+
if (!window.isVisible()) {
|
|
473
|
+
window.show();
|
|
474
|
+
}
|
|
475
|
+
window.focus();
|
|
476
|
+
// Ensure bounds are applied after show (some systems need this)
|
|
477
|
+
window.setBounds(bounds);
|
|
478
|
+
this.emitEvent({
|
|
479
|
+
type: 'window_opened',
|
|
480
|
+
sessionId,
|
|
481
|
+
taskId: this.sessions.get(sessionId).taskId,
|
|
482
|
+
timestamp: Date.now(),
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Hide the canvas window
|
|
487
|
+
*/
|
|
488
|
+
hideCanvas(sessionId) {
|
|
489
|
+
const window = this.windows.get(sessionId);
|
|
490
|
+
if (window && !window.isDestroyed()) {
|
|
491
|
+
window.hide();
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Close a canvas session
|
|
496
|
+
*/
|
|
497
|
+
async closeSession(sessionId) {
|
|
498
|
+
const session = this.sessions.get(sessionId);
|
|
499
|
+
if (!session) {
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
// Close window if open
|
|
503
|
+
const window = this.windows.get(sessionId);
|
|
504
|
+
if (window && !window.isDestroyed()) {
|
|
505
|
+
window.close();
|
|
506
|
+
}
|
|
507
|
+
// Stop watcher
|
|
508
|
+
this.stopWatcher(sessionId);
|
|
509
|
+
// Update session status
|
|
510
|
+
session.status = 'closed';
|
|
511
|
+
// Persist sessions to disk (removes closed sessions)
|
|
512
|
+
await this.persistSessions();
|
|
513
|
+
// Emit event
|
|
514
|
+
this.emitEvent({
|
|
515
|
+
type: 'session_closed',
|
|
516
|
+
sessionId,
|
|
517
|
+
taskId: session.taskId,
|
|
518
|
+
timestamp: Date.now(),
|
|
519
|
+
});
|
|
520
|
+
console.log(`[CanvasManager] Closed session ${sessionId}`);
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Execute JavaScript in the canvas context
|
|
524
|
+
*/
|
|
525
|
+
async evalScript(sessionId, script) {
|
|
526
|
+
// Ensure window exists (create hidden one if needed)
|
|
527
|
+
const window = await this.ensureWindowForSnapshots(sessionId);
|
|
528
|
+
if (!window || window.isDestroyed()) {
|
|
529
|
+
throw new Error('Canvas window could not be created');
|
|
530
|
+
}
|
|
531
|
+
return window.webContents.executeJavaScript(script);
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Take a screenshot of the canvas
|
|
535
|
+
*/
|
|
536
|
+
async takeSnapshot(sessionId) {
|
|
537
|
+
// Ensure window exists (create hidden one if needed)
|
|
538
|
+
const window = await this.ensureWindowForSnapshots(sessionId);
|
|
539
|
+
if (!window || window.isDestroyed()) {
|
|
540
|
+
throw new Error('Canvas window could not be created');
|
|
541
|
+
}
|
|
542
|
+
const image = await window.webContents.capturePage();
|
|
543
|
+
const size = image.getSize();
|
|
544
|
+
return {
|
|
545
|
+
sessionId,
|
|
546
|
+
imageBase64: image.toPNG().toString('base64'),
|
|
547
|
+
width: size.width,
|
|
548
|
+
height: size.height,
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Export canvas content as a standalone HTML file
|
|
553
|
+
* Returns the HTML content with all assets inlined if possible
|
|
554
|
+
*/
|
|
555
|
+
async exportAsHTML(sessionId) {
|
|
556
|
+
const session = this.sessions.get(sessionId);
|
|
557
|
+
if (!session) {
|
|
558
|
+
throw new Error(`Canvas session not found: ${sessionId}`);
|
|
559
|
+
}
|
|
560
|
+
const htmlPath = path.join(session.sessionDir, 'index.html');
|
|
561
|
+
if (!(0, fs_1.existsSync)(htmlPath)) {
|
|
562
|
+
throw new Error('Canvas index.html not found');
|
|
563
|
+
}
|
|
564
|
+
const content = await fs.readFile(htmlPath, 'utf-8');
|
|
565
|
+
const filename = `canvas-${session.title?.replace(/[^a-z0-9]/gi, '-') || sessionId.slice(0, 8)}.html`;
|
|
566
|
+
console.log(`[CanvasManager] Exported HTML for session ${sessionId}`);
|
|
567
|
+
return { content, filename };
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Export all canvas files to a target directory
|
|
571
|
+
*/
|
|
572
|
+
async exportToFolder(sessionId, targetDir) {
|
|
573
|
+
const session = this.sessions.get(sessionId);
|
|
574
|
+
if (!session) {
|
|
575
|
+
throw new Error(`Canvas session not found: ${sessionId}`);
|
|
576
|
+
}
|
|
577
|
+
if (!(0, fs_1.existsSync)(session.sessionDir)) {
|
|
578
|
+
throw new Error('Canvas session directory not found');
|
|
579
|
+
}
|
|
580
|
+
// Create target directory if it doesn't exist
|
|
581
|
+
await fs.mkdir(targetDir, { recursive: true });
|
|
582
|
+
// Get all files in the session directory
|
|
583
|
+
const files = (0, fs_1.readdirSync)(session.sessionDir);
|
|
584
|
+
const copiedFiles = [];
|
|
585
|
+
for (const file of files) {
|
|
586
|
+
const srcPath = path.join(session.sessionDir, file);
|
|
587
|
+
const destPath = path.join(targetDir, file);
|
|
588
|
+
await fs.copyFile(srcPath, destPath);
|
|
589
|
+
copiedFiles.push(file);
|
|
590
|
+
}
|
|
591
|
+
console.log(`[CanvasManager] Exported ${copiedFiles.length} files for session ${sessionId} to ${targetDir}`);
|
|
592
|
+
return { files: copiedFiles, targetDir };
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Open canvas content in the default system browser
|
|
596
|
+
*/
|
|
597
|
+
async openInBrowser(sessionId) {
|
|
598
|
+
const session = this.sessions.get(sessionId);
|
|
599
|
+
if (!session) {
|
|
600
|
+
throw new Error(`Canvas session not found: ${sessionId}`);
|
|
601
|
+
}
|
|
602
|
+
if (this.getSessionMode(session) === 'browser' && session.url) {
|
|
603
|
+
await electron_1.shell.openExternal(session.url);
|
|
604
|
+
console.log(`[CanvasManager] Opened session ${sessionId} in browser: ${session.url}`);
|
|
605
|
+
return { success: true, path: session.url };
|
|
606
|
+
}
|
|
607
|
+
const htmlPath = path.join(session.sessionDir, 'index.html');
|
|
608
|
+
if (!(0, fs_1.existsSync)(htmlPath)) {
|
|
609
|
+
throw new Error('Canvas index.html not found');
|
|
610
|
+
}
|
|
611
|
+
// Open in default browser
|
|
612
|
+
await electron_1.shell.openPath(htmlPath);
|
|
613
|
+
console.log(`[CanvasManager] Opened session ${sessionId} in browser: ${htmlPath}`);
|
|
614
|
+
return { success: true, path: htmlPath };
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Get the session directory path for external access
|
|
618
|
+
*/
|
|
619
|
+
getSessionDir(sessionId) {
|
|
620
|
+
const session = this.sessions.get(sessionId);
|
|
621
|
+
return session?.sessionDir || null;
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Handle A2UI action from canvas window
|
|
625
|
+
*/
|
|
626
|
+
handleA2UIAction(windowId, action) {
|
|
627
|
+
const sessionId = this.windowToSession.get(windowId);
|
|
628
|
+
if (!sessionId)
|
|
629
|
+
return;
|
|
630
|
+
const session = this.sessions.get(sessionId);
|
|
631
|
+
if (!session)
|
|
632
|
+
return;
|
|
633
|
+
const a2uiAction = {
|
|
634
|
+
actionName: action.actionName,
|
|
635
|
+
sessionId,
|
|
636
|
+
componentId: action.componentId,
|
|
637
|
+
context: action.context,
|
|
638
|
+
timestamp: Date.now(),
|
|
639
|
+
};
|
|
640
|
+
// Emit event for UI
|
|
641
|
+
this.emitEvent({
|
|
642
|
+
type: 'a2ui_action',
|
|
643
|
+
sessionId,
|
|
644
|
+
taskId: session.taskId,
|
|
645
|
+
timestamp: Date.now(),
|
|
646
|
+
action: a2uiAction,
|
|
647
|
+
});
|
|
648
|
+
// Call A2UI callback if set
|
|
649
|
+
if (this.a2uiCallback) {
|
|
650
|
+
this.a2uiCallback(a2uiAction);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* Start file watcher for a session
|
|
655
|
+
*/
|
|
656
|
+
startWatcher(sessionId, sessionDir, window) {
|
|
657
|
+
if (this.watchers.has(sessionId)) {
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
const watcher = chokidar_1.default.watch(sessionDir, {
|
|
661
|
+
ignoreInitial: true,
|
|
662
|
+
awaitWriteFinish: {
|
|
663
|
+
stabilityThreshold: 100,
|
|
664
|
+
pollInterval: 50,
|
|
665
|
+
},
|
|
666
|
+
});
|
|
667
|
+
watcher.on('change', () => {
|
|
668
|
+
if (!window.isDestroyed()) {
|
|
669
|
+
window.webContents.reload();
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
this.watchers.set(sessionId, watcher);
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Stop file watcher for a session
|
|
676
|
+
*/
|
|
677
|
+
stopWatcher(sessionId) {
|
|
678
|
+
const watcher = this.watchers.get(sessionId);
|
|
679
|
+
if (watcher) {
|
|
680
|
+
watcher.close();
|
|
681
|
+
this.watchers.delete(sessionId);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* Emit a canvas event
|
|
686
|
+
*/
|
|
687
|
+
emitEvent(event) {
|
|
688
|
+
// Call event callback
|
|
689
|
+
if (this.eventCallback) {
|
|
690
|
+
this.eventCallback(event);
|
|
691
|
+
}
|
|
692
|
+
// Broadcast to main window
|
|
693
|
+
if (this.mainWindow && !this.mainWindow.isDestroyed()) {
|
|
694
|
+
this.mainWindow.webContents.send('canvas:event', event);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* Cleanup all sessions and resources
|
|
699
|
+
*/
|
|
700
|
+
async cleanup() {
|
|
701
|
+
// Close all windows
|
|
702
|
+
for (const [sessionId, window] of this.windows) {
|
|
703
|
+
if (!window.isDestroyed()) {
|
|
704
|
+
window.close();
|
|
705
|
+
}
|
|
706
|
+
this.stopWatcher(sessionId);
|
|
707
|
+
}
|
|
708
|
+
this.sessions.clear();
|
|
709
|
+
this.windows.clear();
|
|
710
|
+
this.windowToSession.clear();
|
|
711
|
+
console.log('[CanvasManager] Cleanup complete');
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
exports.CanvasManager = CanvasManager;
|