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,971 @@
|
|
|
1
|
+
import { app, safeStorage } from 'electron';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { getModels as getPiAiModels } from '@mariozechner/pi-ai';
|
|
5
|
+
import {
|
|
6
|
+
LLMProvider,
|
|
7
|
+
LLMProviderConfig,
|
|
8
|
+
LLMProviderType,
|
|
9
|
+
MODELS,
|
|
10
|
+
ModelKey,
|
|
11
|
+
DEFAULT_MODEL,
|
|
12
|
+
} from './types';
|
|
13
|
+
import { AnthropicProvider } from './anthropic-provider';
|
|
14
|
+
import { BedrockProvider } from './bedrock-provider';
|
|
15
|
+
import { OllamaProvider } from './ollama-provider';
|
|
16
|
+
import { GeminiProvider } from './gemini-provider';
|
|
17
|
+
import { OpenRouterProvider } from './openrouter-provider';
|
|
18
|
+
import { OpenAIProvider } from './openai-provider';
|
|
19
|
+
import { SecureSettingsRepository } from '../../database/SecureSettingsRepository';
|
|
20
|
+
|
|
21
|
+
const LEGACY_SETTINGS_FILE = 'llm-settings.json';
|
|
22
|
+
const MASKED_VALUE = '***configured***';
|
|
23
|
+
const ENCRYPTED_PREFIX = 'encrypted:';
|
|
24
|
+
|
|
25
|
+
// ============ Legacy Encryption Functions (for migration only) ============
|
|
26
|
+
// These functions are only used to decrypt settings from legacy JSON files
|
|
27
|
+
// during migration to the encrypted database. New settings use full-object
|
|
28
|
+
// encryption via SecureSettingsRepository.
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @deprecated Used only for migration from legacy JSON files
|
|
32
|
+
* Encrypt a secret using OS keychain via safeStorage
|
|
33
|
+
*/
|
|
34
|
+
function encryptSecret(value?: string): string | undefined {
|
|
35
|
+
if (!value || !value.trim()) return undefined;
|
|
36
|
+
const trimmed = value.trim();
|
|
37
|
+
if (trimmed === MASKED_VALUE) return undefined;
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
if (safeStorage.isEncryptionAvailable()) {
|
|
41
|
+
const encrypted = safeStorage.encryptString(trimmed);
|
|
42
|
+
return ENCRYPTED_PREFIX + encrypted.toString('base64');
|
|
43
|
+
}
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.warn('Failed to encrypt secret, storing masked:', error);
|
|
46
|
+
}
|
|
47
|
+
// Fallback to masked value if encryption fails
|
|
48
|
+
return MASKED_VALUE;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @deprecated Used only for migration from legacy JSON files
|
|
53
|
+
* Decrypt a secret that was encrypted with safeStorage
|
|
54
|
+
*/
|
|
55
|
+
function decryptSecret(value?: string): string | undefined {
|
|
56
|
+
if (!value) return undefined;
|
|
57
|
+
if (value === MASKED_VALUE) return undefined;
|
|
58
|
+
|
|
59
|
+
if (value.startsWith(ENCRYPTED_PREFIX)) {
|
|
60
|
+
try {
|
|
61
|
+
const isAvailable = safeStorage.isEncryptionAvailable();
|
|
62
|
+
if (isAvailable) {
|
|
63
|
+
const encrypted = Buffer.from(value.slice(ENCRYPTED_PREFIX.length), 'base64');
|
|
64
|
+
const decrypted = safeStorage.decryptString(encrypted);
|
|
65
|
+
return decrypted;
|
|
66
|
+
} else {
|
|
67
|
+
console.error('[LLM Settings] safeStorage encryption not available - cannot decrypt secrets');
|
|
68
|
+
console.error('[LLM Settings] You may need to re-enter your API credentials in Settings');
|
|
69
|
+
}
|
|
70
|
+
} catch (error: any) {
|
|
71
|
+
// This can happen after app updates when the code signature changes
|
|
72
|
+
// The macOS Keychain ties encryption to the app's signature
|
|
73
|
+
console.error('[LLM Settings] Failed to decrypt secret - this can happen after app updates');
|
|
74
|
+
console.error('[LLM Settings] Error:', error.message || error);
|
|
75
|
+
console.error('[LLM Settings] Please re-enter your API credentials in Settings');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// If not encrypted and not masked, return as-is (for backwards compatibility)
|
|
80
|
+
if (value !== MASKED_VALUE && !value.startsWith(ENCRYPTED_PREFIX)) {
|
|
81
|
+
return value.trim() || undefined;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Normalize a secret value, filtering out masked/encrypted values
|
|
89
|
+
*/
|
|
90
|
+
function normalizeSecret(value?: string): string | undefined {
|
|
91
|
+
if (!value) return undefined;
|
|
92
|
+
const trimmed = value.trim();
|
|
93
|
+
if (!trimmed || trimmed === MASKED_VALUE || trimmed.startsWith(ENCRYPTED_PREFIX)) return undefined;
|
|
94
|
+
return trimmed;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @deprecated Used only for migration from legacy JSON files
|
|
99
|
+
* Decrypt all secrets in legacy settings
|
|
100
|
+
*/
|
|
101
|
+
function sanitizeSettings(settings: LLMSettings): LLMSettings {
|
|
102
|
+
const sanitized: LLMSettings = { ...settings };
|
|
103
|
+
|
|
104
|
+
// Decrypt secrets when loading from disk
|
|
105
|
+
if (sanitized.anthropic) {
|
|
106
|
+
sanitized.anthropic = {
|
|
107
|
+
...sanitized.anthropic,
|
|
108
|
+
apiKey: decryptSecret(sanitized.anthropic.apiKey),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (sanitized.bedrock) {
|
|
113
|
+
sanitized.bedrock = {
|
|
114
|
+
...sanitized.bedrock,
|
|
115
|
+
secretAccessKey: decryptSecret(sanitized.bedrock.secretAccessKey),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (sanitized.ollama) {
|
|
120
|
+
sanitized.ollama = {
|
|
121
|
+
...sanitized.ollama,
|
|
122
|
+
apiKey: decryptSecret(sanitized.ollama.apiKey),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (sanitized.gemini) {
|
|
127
|
+
sanitized.gemini = {
|
|
128
|
+
...sanitized.gemini,
|
|
129
|
+
apiKey: decryptSecret(sanitized.gemini.apiKey),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (sanitized.openrouter) {
|
|
134
|
+
sanitized.openrouter = {
|
|
135
|
+
...sanitized.openrouter,
|
|
136
|
+
apiKey: decryptSecret(sanitized.openrouter.apiKey),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (sanitized.openai) {
|
|
141
|
+
const decryptedAccessToken = decryptSecret(sanitized.openai.accessToken);
|
|
142
|
+
const decryptedRefreshToken = decryptSecret(sanitized.openai.refreshToken);
|
|
143
|
+
|
|
144
|
+
// Log OAuth token status for debugging
|
|
145
|
+
if (sanitized.openai.authMethod === 'oauth') {
|
|
146
|
+
console.log('[LLM Settings] Loading OpenAI OAuth settings:');
|
|
147
|
+
console.log('[LLM Settings] authMethod:', sanitized.openai.authMethod);
|
|
148
|
+
console.log('[LLM Settings] hasAccessToken:', !!sanitized.openai.accessToken);
|
|
149
|
+
console.log('[LLM Settings] decryptedAccessToken:', !!decryptedAccessToken);
|
|
150
|
+
console.log('[LLM Settings] hasRefreshToken:', !!sanitized.openai.refreshToken);
|
|
151
|
+
console.log('[LLM Settings] decryptedRefreshToken:', !!decryptedRefreshToken);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
sanitized.openai = {
|
|
155
|
+
...sanitized.openai,
|
|
156
|
+
apiKey: decryptSecret(sanitized.openai.apiKey),
|
|
157
|
+
accessToken: decryptedAccessToken,
|
|
158
|
+
refreshToken: decryptedRefreshToken,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return sanitized;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Cached model info for dynamic providers
|
|
167
|
+
*/
|
|
168
|
+
export interface CachedModelInfo {
|
|
169
|
+
key: string;
|
|
170
|
+
displayName: string;
|
|
171
|
+
description: string;
|
|
172
|
+
// Additional fields for provider-specific info
|
|
173
|
+
contextLength?: number; // For OpenRouter models
|
|
174
|
+
size?: number; // For Ollama models (in bytes)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Stored settings for LLM provider
|
|
179
|
+
*/
|
|
180
|
+
export interface LLMSettings {
|
|
181
|
+
providerType: LLMProviderType;
|
|
182
|
+
modelKey: ModelKey | string; // String for custom Ollama model names
|
|
183
|
+
anthropic?: {
|
|
184
|
+
apiKey?: string;
|
|
185
|
+
};
|
|
186
|
+
bedrock?: {
|
|
187
|
+
region?: string;
|
|
188
|
+
accessKeyId?: string;
|
|
189
|
+
secretAccessKey?: string;
|
|
190
|
+
sessionToken?: string;
|
|
191
|
+
profile?: string;
|
|
192
|
+
useDefaultCredentials?: boolean;
|
|
193
|
+
model?: string;
|
|
194
|
+
};
|
|
195
|
+
ollama?: {
|
|
196
|
+
baseUrl?: string;
|
|
197
|
+
model?: string;
|
|
198
|
+
apiKey?: string; // Optional, for remote Ollama servers
|
|
199
|
+
};
|
|
200
|
+
gemini?: {
|
|
201
|
+
apiKey?: string;
|
|
202
|
+
model?: string;
|
|
203
|
+
};
|
|
204
|
+
openrouter?: {
|
|
205
|
+
apiKey?: string;
|
|
206
|
+
model?: string;
|
|
207
|
+
};
|
|
208
|
+
openai?: {
|
|
209
|
+
apiKey?: string;
|
|
210
|
+
model?: string;
|
|
211
|
+
// OAuth tokens (alternative to API key)
|
|
212
|
+
accessToken?: string;
|
|
213
|
+
refreshToken?: string;
|
|
214
|
+
tokenExpiresAt?: number;
|
|
215
|
+
authMethod?: 'api_key' | 'oauth';
|
|
216
|
+
};
|
|
217
|
+
// Cached models from API (populated when user refreshes)
|
|
218
|
+
cachedGeminiModels?: CachedModelInfo[];
|
|
219
|
+
cachedOpenRouterModels?: CachedModelInfo[];
|
|
220
|
+
cachedOllamaModels?: CachedModelInfo[];
|
|
221
|
+
cachedBedrockModels?: CachedModelInfo[];
|
|
222
|
+
cachedOpenAIModels?: CachedModelInfo[];
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const DEFAULT_SETTINGS: LLMSettings = {
|
|
226
|
+
providerType: 'anthropic',
|
|
227
|
+
modelKey: DEFAULT_MODEL,
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Factory for creating LLM providers
|
|
232
|
+
*/
|
|
233
|
+
export class LLMProviderFactory {
|
|
234
|
+
private static legacySettingsPath: string;
|
|
235
|
+
private static cachedSettings: LLMSettings | null = null;
|
|
236
|
+
private static migrationCompleted = false;
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Initialize the factory
|
|
240
|
+
*/
|
|
241
|
+
static initialize(): void {
|
|
242
|
+
const userDataPath = app.getPath('userData');
|
|
243
|
+
this.legacySettingsPath = path.join(userDataPath, LEGACY_SETTINGS_FILE);
|
|
244
|
+
|
|
245
|
+
// Migrate from legacy JSON file to encrypted database
|
|
246
|
+
this.migrateFromLegacyFile();
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Migrate settings from legacy JSON file to encrypted database
|
|
251
|
+
*/
|
|
252
|
+
private static migrateFromLegacyFile(): void {
|
|
253
|
+
if (this.migrationCompleted) return;
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
// Check if SecureSettingsRepository is initialized
|
|
257
|
+
if (!SecureSettingsRepository.isInitialized()) {
|
|
258
|
+
console.log('[LLMProviderFactory] SecureSettingsRepository not yet initialized, skipping migration');
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const repository = SecureSettingsRepository.getInstance();
|
|
263
|
+
|
|
264
|
+
// Check if already migrated to database
|
|
265
|
+
if (repository.exists('llm')) {
|
|
266
|
+
this.migrationCompleted = true;
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Check if legacy file exists
|
|
271
|
+
if (!fs.existsSync(this.legacySettingsPath)) {
|
|
272
|
+
console.log('[LLMProviderFactory] No legacy settings file found');
|
|
273
|
+
this.migrationCompleted = true;
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
console.log('[LLMProviderFactory] Migrating settings from legacy JSON file to encrypted database...');
|
|
278
|
+
|
|
279
|
+
// Create backup before migration
|
|
280
|
+
const backupPath = this.legacySettingsPath + '.migration-backup';
|
|
281
|
+
fs.copyFileSync(this.legacySettingsPath, backupPath);
|
|
282
|
+
|
|
283
|
+
try {
|
|
284
|
+
// Read and decrypt legacy settings
|
|
285
|
+
const data = fs.readFileSync(this.legacySettingsPath, 'utf-8');
|
|
286
|
+
const legacySettings = { ...DEFAULT_SETTINGS, ...JSON.parse(data) };
|
|
287
|
+
const decryptedSettings = sanitizeSettings(legacySettings);
|
|
288
|
+
|
|
289
|
+
// Save to encrypted database
|
|
290
|
+
repository.save('llm', decryptedSettings);
|
|
291
|
+
console.log('[LLMProviderFactory] Settings migrated to encrypted database');
|
|
292
|
+
|
|
293
|
+
// Migration successful - delete backup and original
|
|
294
|
+
fs.unlinkSync(backupPath);
|
|
295
|
+
fs.unlinkSync(this.legacySettingsPath);
|
|
296
|
+
console.log('[LLMProviderFactory] Migration complete, cleaned up legacy files');
|
|
297
|
+
|
|
298
|
+
this.migrationCompleted = true;
|
|
299
|
+
} catch (migrationError) {
|
|
300
|
+
console.error('[LLMProviderFactory] Migration failed, backup preserved at:', backupPath);
|
|
301
|
+
throw migrationError;
|
|
302
|
+
}
|
|
303
|
+
} catch (error) {
|
|
304
|
+
console.error('[LLMProviderFactory] Migration failed:', error);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Get the path to legacy settings file (for testing)
|
|
310
|
+
*/
|
|
311
|
+
static getSettingsPath(): string {
|
|
312
|
+
return this.legacySettingsPath;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Load settings from encrypted database
|
|
317
|
+
*/
|
|
318
|
+
static loadSettings(): LLMSettings {
|
|
319
|
+
if (this.cachedSettings) {
|
|
320
|
+
return this.cachedSettings;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
let settings: LLMSettings = { ...DEFAULT_SETTINGS };
|
|
324
|
+
let settingsExist = false;
|
|
325
|
+
|
|
326
|
+
try {
|
|
327
|
+
// Try to load from encrypted database
|
|
328
|
+
if (SecureSettingsRepository.isInitialized()) {
|
|
329
|
+
const repository = SecureSettingsRepository.getInstance();
|
|
330
|
+
const stored = repository.load<LLMSettings>('llm');
|
|
331
|
+
if (stored) {
|
|
332
|
+
settings = { ...DEFAULT_SETTINGS, ...stored };
|
|
333
|
+
settingsExist = true;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
} catch (error) {
|
|
337
|
+
console.error('[LLMProviderFactory] Failed to load settings from database:', error);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Auto-detect provider if no settings exist
|
|
341
|
+
if (!settingsExist) {
|
|
342
|
+
const detectedProvider = this.detectProviderFromSettings(settings);
|
|
343
|
+
if (detectedProvider) {
|
|
344
|
+
settings.providerType = detectedProvider;
|
|
345
|
+
console.log(`[LLMProviderFactory] Auto-detected LLM provider: ${detectedProvider}`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
this.cachedSettings = settings;
|
|
350
|
+
return settings;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Detect which provider to use based on saved settings
|
|
355
|
+
* Note: Environment variables are no longer used for security reasons.
|
|
356
|
+
* All configuration should be done through the Settings UI.
|
|
357
|
+
*/
|
|
358
|
+
private static detectProviderFromSettings(settings: LLMSettings): LLMProviderType | null {
|
|
359
|
+
// Check if any provider has credentials configured in settings
|
|
360
|
+
if (settings.anthropic?.apiKey) {
|
|
361
|
+
return 'anthropic';
|
|
362
|
+
}
|
|
363
|
+
if (settings.gemini?.apiKey) {
|
|
364
|
+
return 'gemini';
|
|
365
|
+
}
|
|
366
|
+
if (settings.openrouter?.apiKey) {
|
|
367
|
+
return 'openrouter';
|
|
368
|
+
}
|
|
369
|
+
if (settings.openai?.apiKey || settings.openai?.accessToken) {
|
|
370
|
+
return 'openai';
|
|
371
|
+
}
|
|
372
|
+
if (settings.bedrock?.accessKeyId || settings.bedrock?.profile) {
|
|
373
|
+
return 'bedrock';
|
|
374
|
+
}
|
|
375
|
+
if (settings.ollama?.baseUrl || settings.ollama?.model) {
|
|
376
|
+
return 'ollama';
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// No valid credentials detected - user needs to configure via Settings
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Save settings to encrypted database
|
|
385
|
+
*/
|
|
386
|
+
static saveSettings(settings: LLMSettings): void {
|
|
387
|
+
try {
|
|
388
|
+
if (!SecureSettingsRepository.isInitialized()) {
|
|
389
|
+
throw new Error('SecureSettingsRepository not initialized');
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const repository = SecureSettingsRepository.getInstance();
|
|
393
|
+
|
|
394
|
+
// Save entire settings object to encrypted database
|
|
395
|
+
// No need for per-field encryption - the entire object is encrypted
|
|
396
|
+
repository.save('llm', settings);
|
|
397
|
+
this.cachedSettings = settings;
|
|
398
|
+
|
|
399
|
+
console.log('[LLMProviderFactory] Settings saved to encrypted database');
|
|
400
|
+
} catch (error) {
|
|
401
|
+
console.error('[LLMProviderFactory] Failed to save settings:', error);
|
|
402
|
+
throw error;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Clear cached settings
|
|
408
|
+
*/
|
|
409
|
+
static clearCache(): void {
|
|
410
|
+
this.cachedSettings = null;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Create a provider based on current settings
|
|
415
|
+
* Note: All credentials must be configured via the Settings UI.
|
|
416
|
+
* Environment variables are no longer used for security reasons.
|
|
417
|
+
*/
|
|
418
|
+
static createProvider(overrideConfig?: Partial<LLMProviderConfig>): LLMProvider {
|
|
419
|
+
const settings = this.loadSettings();
|
|
420
|
+
const providerType = overrideConfig?.type || settings.providerType;
|
|
421
|
+
|
|
422
|
+
const config: LLMProviderConfig = {
|
|
423
|
+
type: providerType,
|
|
424
|
+
model: this.getModelId(settings.modelKey, providerType, settings.ollama?.model, settings.gemini?.model, settings.openrouter?.model, settings.openai?.model),
|
|
425
|
+
// Anthropic config - from settings only
|
|
426
|
+
anthropicApiKey: normalizeSecret(overrideConfig?.anthropicApiKey) || settings.anthropic?.apiKey,
|
|
427
|
+
// Bedrock config - from settings only
|
|
428
|
+
awsRegion: overrideConfig?.awsRegion || settings.bedrock?.region || 'us-east-1',
|
|
429
|
+
awsAccessKeyId: overrideConfig?.awsAccessKeyId || settings.bedrock?.accessKeyId,
|
|
430
|
+
awsSecretAccessKey: normalizeSecret(overrideConfig?.awsSecretAccessKey) || settings.bedrock?.secretAccessKey,
|
|
431
|
+
awsSessionToken: overrideConfig?.awsSessionToken || settings.bedrock?.sessionToken,
|
|
432
|
+
awsProfile: overrideConfig?.awsProfile || settings.bedrock?.profile,
|
|
433
|
+
// Ollama config - from settings only
|
|
434
|
+
ollamaBaseUrl: overrideConfig?.ollamaBaseUrl || settings.ollama?.baseUrl || 'http://localhost:11434',
|
|
435
|
+
ollamaApiKey: normalizeSecret(overrideConfig?.ollamaApiKey) || settings.ollama?.apiKey,
|
|
436
|
+
// Gemini config - from settings only
|
|
437
|
+
geminiApiKey: normalizeSecret(overrideConfig?.geminiApiKey) || settings.gemini?.apiKey,
|
|
438
|
+
// OpenRouter config - from settings only
|
|
439
|
+
openrouterApiKey: normalizeSecret(overrideConfig?.openrouterApiKey) || settings.openrouter?.apiKey,
|
|
440
|
+
// OpenAI config - from settings only
|
|
441
|
+
openaiApiKey: normalizeSecret(overrideConfig?.openaiApiKey) || settings.openai?.apiKey,
|
|
442
|
+
openaiAccessToken: normalizeSecret(overrideConfig?.openaiAccessToken) || settings.openai?.accessToken,
|
|
443
|
+
openaiRefreshToken: settings.openai?.refreshToken,
|
|
444
|
+
openaiTokenExpiresAt: settings.openai?.tokenExpiresAt,
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
return this.createProviderFromConfig(config);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Create a provider from explicit config
|
|
452
|
+
*/
|
|
453
|
+
static createProviderFromConfig(config: LLMProviderConfig): LLMProvider {
|
|
454
|
+
switch (config.type) {
|
|
455
|
+
case 'anthropic':
|
|
456
|
+
return new AnthropicProvider(config);
|
|
457
|
+
case 'bedrock':
|
|
458
|
+
return new BedrockProvider(config);
|
|
459
|
+
case 'ollama':
|
|
460
|
+
return new OllamaProvider(config);
|
|
461
|
+
case 'gemini':
|
|
462
|
+
return new GeminiProvider(config);
|
|
463
|
+
case 'openrouter':
|
|
464
|
+
return new OpenRouterProvider(config);
|
|
465
|
+
case 'openai':
|
|
466
|
+
return new OpenAIProvider(config);
|
|
467
|
+
default:
|
|
468
|
+
throw new Error(`Unknown provider type: ${config.type}`);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Get the model ID for a provider
|
|
474
|
+
*/
|
|
475
|
+
static getModelId(modelKey: ModelKey | string, providerType: LLMProviderType, ollamaModel?: string, geminiModel?: string, openrouterModel?: string, openaiModel?: string): string {
|
|
476
|
+
// For Ollama, use the specific Ollama model if provided
|
|
477
|
+
if (providerType === 'ollama') {
|
|
478
|
+
return ollamaModel || 'gpt-oss:20b';
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// For Gemini, use the specific Gemini model if provided or default
|
|
482
|
+
if (providerType === 'gemini') {
|
|
483
|
+
return geminiModel || 'gemini-2.0-flash';
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// For OpenRouter, use the specific model if provided or default
|
|
487
|
+
if (providerType === 'openrouter') {
|
|
488
|
+
return openrouterModel || 'anthropic/claude-3.5-sonnet';
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// For OpenAI, use the specific model if provided or default
|
|
492
|
+
if (providerType === 'openai') {
|
|
493
|
+
return openaiModel || 'gpt-4o-mini';
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// For other providers, look up in MODELS
|
|
497
|
+
const model = MODELS[modelKey as ModelKey];
|
|
498
|
+
if (!model) {
|
|
499
|
+
throw new Error(`Unknown model: ${modelKey}`);
|
|
500
|
+
}
|
|
501
|
+
return model[providerType as 'anthropic' | 'bedrock'];
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Get display name for a model
|
|
506
|
+
*/
|
|
507
|
+
static getModelDisplayName(modelKey: ModelKey): string {
|
|
508
|
+
return MODELS[modelKey]?.displayName || modelKey;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Get all available models
|
|
513
|
+
*/
|
|
514
|
+
static getAvailableModels(): Array<{ key: ModelKey; displayName: string }> {
|
|
515
|
+
return Object.entries(MODELS).map(([key, value]) => ({
|
|
516
|
+
key: key as ModelKey,
|
|
517
|
+
displayName: value.displayName,
|
|
518
|
+
}));
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Get available providers based on saved settings configuration
|
|
523
|
+
* Note: Environment variables are no longer checked for security reasons.
|
|
524
|
+
*/
|
|
525
|
+
static getAvailableProviders(): Array<{
|
|
526
|
+
type: LLMProviderType;
|
|
527
|
+
name: string;
|
|
528
|
+
configured: boolean;
|
|
529
|
+
}> {
|
|
530
|
+
const settings = this.loadSettings();
|
|
531
|
+
|
|
532
|
+
return [
|
|
533
|
+
{
|
|
534
|
+
type: 'anthropic' as LLMProviderType,
|
|
535
|
+
name: 'Anthropic API',
|
|
536
|
+
configured: !!settings.anthropic?.apiKey,
|
|
537
|
+
},
|
|
538
|
+
{
|
|
539
|
+
type: 'gemini' as LLMProviderType,
|
|
540
|
+
name: 'Google Gemini',
|
|
541
|
+
configured: !!settings.gemini?.apiKey,
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
type: 'openrouter' as LLMProviderType,
|
|
545
|
+
name: 'OpenRouter',
|
|
546
|
+
configured: !!settings.openrouter?.apiKey,
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
type: 'openai' as LLMProviderType,
|
|
550
|
+
name: 'OpenAI',
|
|
551
|
+
configured: !!(settings.openai?.apiKey || settings.openai?.accessToken),
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
type: 'bedrock' as LLMProviderType,
|
|
555
|
+
name: 'AWS Bedrock',
|
|
556
|
+
configured: !!(settings.bedrock?.accessKeyId || settings.bedrock?.profile),
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
type: 'ollama' as LLMProviderType,
|
|
560
|
+
name: 'Ollama (Local)',
|
|
561
|
+
configured: !!(settings.ollama?.baseUrl || settings.ollama?.model),
|
|
562
|
+
},
|
|
563
|
+
];
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Get current configuration status
|
|
568
|
+
*/
|
|
569
|
+
static getConfigStatus(): {
|
|
570
|
+
currentProvider: LLMProviderType;
|
|
571
|
+
currentModel: ModelKey | string;
|
|
572
|
+
providers: Array<{ type: LLMProviderType; name: string; configured: boolean }>;
|
|
573
|
+
models: Array<{ key: ModelKey; displayName: string }>;
|
|
574
|
+
} {
|
|
575
|
+
const settings = this.loadSettings();
|
|
576
|
+
return {
|
|
577
|
+
currentProvider: settings.providerType,
|
|
578
|
+
currentModel: settings.modelKey,
|
|
579
|
+
providers: this.getAvailableProviders(),
|
|
580
|
+
models: this.getAvailableModels(),
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Get the currently selected provider type
|
|
586
|
+
*/
|
|
587
|
+
static getSelectedProvider(): LLMProviderType {
|
|
588
|
+
const settings = this.loadSettings();
|
|
589
|
+
return settings.providerType;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Get the currently selected model key
|
|
594
|
+
*/
|
|
595
|
+
static getSelectedModel(): ModelKey | string {
|
|
596
|
+
const settings = this.loadSettings();
|
|
597
|
+
return settings.modelKey;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Get the current LLM settings
|
|
602
|
+
*/
|
|
603
|
+
static getSettings(): LLMSettings {
|
|
604
|
+
return this.loadSettings();
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Test a provider configuration
|
|
609
|
+
*/
|
|
610
|
+
static async testProvider(config: LLMProviderConfig): Promise<{ success: boolean; error?: string }> {
|
|
611
|
+
try {
|
|
612
|
+
const provider = this.createProviderFromConfig(config);
|
|
613
|
+
return await provider.testConnection();
|
|
614
|
+
} catch (error: any) {
|
|
615
|
+
return {
|
|
616
|
+
success: false,
|
|
617
|
+
error: error.message || 'Failed to create provider',
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* Fetch available Bedrock models from AWS
|
|
624
|
+
*/
|
|
625
|
+
static async getBedrockModels(config?: {
|
|
626
|
+
region?: string;
|
|
627
|
+
accessKeyId?: string;
|
|
628
|
+
secretAccessKey?: string;
|
|
629
|
+
profile?: string;
|
|
630
|
+
}): Promise<Array<{ id: string; name: string; provider: string; description: string }>> {
|
|
631
|
+
const settings = this.loadSettings();
|
|
632
|
+
const region = config?.region || settings.bedrock?.region || 'us-east-1';
|
|
633
|
+
const accessKeyId = config?.accessKeyId || settings.bedrock?.accessKeyId;
|
|
634
|
+
const secretAccessKey = config?.secretAccessKey || settings.bedrock?.secretAccessKey;
|
|
635
|
+
const profile = config?.profile || settings.bedrock?.profile;
|
|
636
|
+
|
|
637
|
+
// Default Claude models available on Bedrock
|
|
638
|
+
const defaultModels = Object.entries(MODELS).map(([key, value]) => ({
|
|
639
|
+
id: value.bedrock,
|
|
640
|
+
name: value.displayName,
|
|
641
|
+
provider: 'Anthropic',
|
|
642
|
+
description: key.includes('opus') ? 'Most capable for complex tasks' :
|
|
643
|
+
key.includes('sonnet') ? 'Balanced performance and speed' :
|
|
644
|
+
'Fast and efficient',
|
|
645
|
+
}));
|
|
646
|
+
|
|
647
|
+
try {
|
|
648
|
+
// Import BedrockClient for listing models (different from runtime client)
|
|
649
|
+
const { BedrockClient, ListFoundationModelsCommand } = await import('@aws-sdk/client-bedrock');
|
|
650
|
+
const { fromIni } = await import('@aws-sdk/credential-provider-ini');
|
|
651
|
+
|
|
652
|
+
const clientConfig: any = { region };
|
|
653
|
+
|
|
654
|
+
if (accessKeyId && secretAccessKey) {
|
|
655
|
+
clientConfig.credentials = {
|
|
656
|
+
accessKeyId,
|
|
657
|
+
secretAccessKey,
|
|
658
|
+
};
|
|
659
|
+
} else if (profile) {
|
|
660
|
+
clientConfig.credentials = fromIni({ profile });
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
const client = new BedrockClient(clientConfig);
|
|
664
|
+
const command = new ListFoundationModelsCommand({
|
|
665
|
+
byOutputModality: 'TEXT',
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
const response = await client.send(command);
|
|
669
|
+
const models = response.modelSummaries || [];
|
|
670
|
+
|
|
671
|
+
// Filter for Claude models and format the response
|
|
672
|
+
const claudeModels = models
|
|
673
|
+
.filter((m: any) => m.providerName === 'Anthropic' && m.modelId?.includes('claude'))
|
|
674
|
+
.map((m: any) => ({
|
|
675
|
+
id: m.modelId || '',
|
|
676
|
+
name: m.modelName || m.modelId || '',
|
|
677
|
+
provider: m.providerName || 'Anthropic',
|
|
678
|
+
description: m.modelId?.includes('opus') ? 'Most capable for complex tasks' :
|
|
679
|
+
m.modelId?.includes('sonnet') ? 'Balanced performance and speed' :
|
|
680
|
+
m.modelId?.includes('haiku') ? 'Fast and efficient' :
|
|
681
|
+
'Claude model',
|
|
682
|
+
}))
|
|
683
|
+
.filter((m: any) => m.id);
|
|
684
|
+
|
|
685
|
+
return claudeModels.length > 0 ? claudeModels : defaultModels;
|
|
686
|
+
} catch (error: any) {
|
|
687
|
+
console.error('Failed to fetch Bedrock models:', error);
|
|
688
|
+
// Return default models on error
|
|
689
|
+
return defaultModels;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* Fetch available Ollama models from the server
|
|
695
|
+
*/
|
|
696
|
+
static async getOllamaModels(baseUrl?: string): Promise<Array<{ name: string; size: number; modified: string }>> {
|
|
697
|
+
const settings = this.loadSettings();
|
|
698
|
+
const url = baseUrl || settings.ollama?.baseUrl || 'http://localhost:11434';
|
|
699
|
+
|
|
700
|
+
try {
|
|
701
|
+
console.log(`[ProviderFactory] Fetching Ollama models from ${url}...`);
|
|
702
|
+
const provider = new OllamaProvider({
|
|
703
|
+
type: 'ollama',
|
|
704
|
+
model: '',
|
|
705
|
+
ollamaBaseUrl: url,
|
|
706
|
+
ollamaApiKey: settings.ollama?.apiKey,
|
|
707
|
+
});
|
|
708
|
+
const models = await provider.getAvailableModels();
|
|
709
|
+
console.log(`[ProviderFactory] Fetched ${models.length} models from Ollama`);
|
|
710
|
+
return models;
|
|
711
|
+
} catch (error: any) {
|
|
712
|
+
console.error('Failed to fetch Ollama models:', error);
|
|
713
|
+
return [];
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
/**
|
|
718
|
+
* Fetch available Gemini models from the API
|
|
719
|
+
*/
|
|
720
|
+
static async getGeminiModels(apiKey?: string): Promise<Array<{ name: string; displayName: string; description: string }>> {
|
|
721
|
+
const settings = this.loadSettings();
|
|
722
|
+
// Normalize empty strings to undefined
|
|
723
|
+
const normalizedApiKey = apiKey?.trim() || undefined;
|
|
724
|
+
const settingsKey = settings.gemini?.apiKey;
|
|
725
|
+
const key = normalizedApiKey || settingsKey;
|
|
726
|
+
|
|
727
|
+
const defaultModels = [
|
|
728
|
+
{ name: 'gemini-2.5-pro-preview-05-06', displayName: 'Gemini 2.5 Pro', description: 'Most capable model for complex tasks' },
|
|
729
|
+
{ name: 'gemini-2.5-flash-preview-05-20', displayName: 'Gemini 2.5 Flash', description: 'Fast and efficient for most tasks' },
|
|
730
|
+
{ name: 'gemini-2.0-flash', displayName: 'Gemini 2.0 Flash', description: 'Balanced speed and capability' },
|
|
731
|
+
{ name: 'gemini-2.0-flash-lite', displayName: 'Gemini 2.0 Flash Lite', description: 'Fastest and most cost-effective' },
|
|
732
|
+
{ name: 'gemini-1.5-pro', displayName: 'Gemini 1.5 Pro', description: 'Previous generation pro model' },
|
|
733
|
+
{ name: 'gemini-1.5-flash', displayName: 'Gemini 1.5 Flash', description: 'Previous generation flash model' },
|
|
734
|
+
];
|
|
735
|
+
|
|
736
|
+
if (!key) {
|
|
737
|
+
// Return default models if no API key
|
|
738
|
+
return defaultModels;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
try {
|
|
742
|
+
const provider = new GeminiProvider({
|
|
743
|
+
type: 'gemini',
|
|
744
|
+
model: '',
|
|
745
|
+
geminiApiKey: key,
|
|
746
|
+
});
|
|
747
|
+
return await provider.getAvailableModels();
|
|
748
|
+
} catch (error: any) {
|
|
749
|
+
console.error('Failed to fetch Gemini models:', error);
|
|
750
|
+
// Return default models on error instead of empty array
|
|
751
|
+
return defaultModels;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* Fetch available OpenRouter models from the API
|
|
757
|
+
*/
|
|
758
|
+
static async getOpenRouterModels(apiKey?: string): Promise<Array<{ id: string; name: string; context_length: number }>> {
|
|
759
|
+
const settings = this.loadSettings();
|
|
760
|
+
// Normalize empty strings to undefined
|
|
761
|
+
const normalizedApiKey = apiKey?.trim() || undefined;
|
|
762
|
+
const key = normalizedApiKey || settings.openrouter?.apiKey;
|
|
763
|
+
|
|
764
|
+
const defaultModels = [
|
|
765
|
+
{ id: 'anthropic/claude-3.5-sonnet', name: 'Claude 3.5 Sonnet', context_length: 200000 },
|
|
766
|
+
{ id: 'anthropic/claude-3-opus', name: 'Claude 3 Opus', context_length: 200000 },
|
|
767
|
+
{ id: 'openai/gpt-4o', name: 'GPT-4o', context_length: 128000 },
|
|
768
|
+
{ id: 'openai/gpt-4o-mini', name: 'GPT-4o Mini', context_length: 128000 },
|
|
769
|
+
{ id: 'google/gemini-pro-1.5', name: 'Gemini Pro 1.5', context_length: 1000000 },
|
|
770
|
+
{ id: 'meta-llama/llama-3.1-405b-instruct', name: 'Llama 3.1 405B', context_length: 131072 },
|
|
771
|
+
];
|
|
772
|
+
|
|
773
|
+
if (!key) {
|
|
774
|
+
// Return default models if no API key
|
|
775
|
+
return defaultModels;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
try {
|
|
779
|
+
const provider = new OpenRouterProvider({
|
|
780
|
+
type: 'openrouter',
|
|
781
|
+
model: '',
|
|
782
|
+
openrouterApiKey: key,
|
|
783
|
+
});
|
|
784
|
+
return await provider.getAvailableModels();
|
|
785
|
+
} catch (error: any) {
|
|
786
|
+
console.error('Failed to fetch OpenRouter models:', error);
|
|
787
|
+
// Return default models on error instead of empty array
|
|
788
|
+
return defaultModels;
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
/**
|
|
793
|
+
* Fetch available OpenAI models
|
|
794
|
+
* For API key auth: uses the models.list API via OpenAI SDK
|
|
795
|
+
* For OAuth auth: uses pi-ai SDK's model list for openai-codex provider
|
|
796
|
+
*/
|
|
797
|
+
static async getOpenAIModels(apiKey?: string): Promise<Array<{ id: string; name: string; description: string }>> {
|
|
798
|
+
const settings = this.loadSettings();
|
|
799
|
+
// Normalize empty strings to undefined
|
|
800
|
+
const normalizedApiKey = apiKey?.trim() || undefined;
|
|
801
|
+
const key = normalizedApiKey || settings.openai?.apiKey;
|
|
802
|
+
// Check for OAuth access token if no API key
|
|
803
|
+
const accessToken = settings.openai?.accessToken;
|
|
804
|
+
const refreshToken = settings.openai?.refreshToken;
|
|
805
|
+
|
|
806
|
+
const defaultModels = [
|
|
807
|
+
{ id: 'gpt-4o', name: 'GPT-4o', description: 'Most capable model for complex tasks' },
|
|
808
|
+
{ id: 'gpt-4o-mini', name: 'GPT-4o Mini', description: 'Fast and affordable for most tasks' },
|
|
809
|
+
{ id: 'gpt-4-turbo', name: 'GPT-4 Turbo', description: 'Previous generation flagship' },
|
|
810
|
+
{ id: 'gpt-3.5-turbo', name: 'GPT-3.5 Turbo', description: 'Fast and cost-effective' },
|
|
811
|
+
{ id: 'o1', name: 'o1', description: 'Advanced reasoning model' },
|
|
812
|
+
{ id: 'o1-mini', name: 'o1 Mini', description: 'Fast reasoning model' },
|
|
813
|
+
];
|
|
814
|
+
|
|
815
|
+
// For OAuth users, use pi-ai SDK's model list directly
|
|
816
|
+
if (accessToken && refreshToken && !key) {
|
|
817
|
+
console.log('[OpenAI] Using OAuth - fetching models from pi-ai SDK...');
|
|
818
|
+
try {
|
|
819
|
+
const piAiModels = getPiAiModels('openai-codex');
|
|
820
|
+
const models = piAiModels.map((m) => ({
|
|
821
|
+
id: m.id,
|
|
822
|
+
name: m.name || this.formatOpenAIModelName(m.id),
|
|
823
|
+
description: this.getOpenAIModelDescription(m.id),
|
|
824
|
+
}));
|
|
825
|
+
|
|
826
|
+
// Sort by priority (ChatGPT internal models)
|
|
827
|
+
models.sort((a, b) => {
|
|
828
|
+
const priority = (id: string) => {
|
|
829
|
+
if (id.includes('5.1-codex-mini')) return 0;
|
|
830
|
+
if (id.includes('5.1-codex-max')) return 1;
|
|
831
|
+
if (id === 'gpt-5.1') return 2;
|
|
832
|
+
if (id.includes('5.2-codex')) return 3;
|
|
833
|
+
if (id === 'gpt-5.2') return 4;
|
|
834
|
+
return 5;
|
|
835
|
+
};
|
|
836
|
+
return priority(a.id) - priority(b.id);
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
console.log(`[OpenAI] Found ${models.length} models via pi-ai SDK`);
|
|
840
|
+
return models;
|
|
841
|
+
} catch (error) {
|
|
842
|
+
console.error('[OpenAI] Failed to get models from pi-ai SDK:', error);
|
|
843
|
+
// Return ChatGPT-specific defaults for OAuth users
|
|
844
|
+
return [
|
|
845
|
+
{ id: 'gpt-5.1-codex-mini', name: 'GPT-5.1 Codex Mini', description: 'Fast and efficient for most tasks' },
|
|
846
|
+
{ id: 'gpt-5.1-codex-max', name: 'GPT-5.1 Codex Max', description: 'Maximum capability for complex tasks' },
|
|
847
|
+
{ id: 'gpt-5.1', name: 'GPT-5.1', description: 'Balanced performance and capability' },
|
|
848
|
+
{ id: 'gpt-5.2-codex', name: 'GPT-5.2 Codex', description: 'Advanced reasoning model' },
|
|
849
|
+
{ id: 'gpt-5.2', name: 'GPT-5.2', description: 'Most advanced reasoning' },
|
|
850
|
+
];
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
if (!key) {
|
|
855
|
+
// Return default models if no authentication
|
|
856
|
+
return defaultModels;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
try {
|
|
860
|
+
// For API key, use the OpenAI provider
|
|
861
|
+
const provider = new OpenAIProvider({
|
|
862
|
+
type: 'openai',
|
|
863
|
+
model: '',
|
|
864
|
+
openaiApiKey: key,
|
|
865
|
+
});
|
|
866
|
+
return await provider.getAvailableModels();
|
|
867
|
+
} catch (error: any) {
|
|
868
|
+
console.error('Failed to fetch OpenAI models:', error);
|
|
869
|
+
// Return default models on error instead of empty array
|
|
870
|
+
return defaultModels;
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
/**
|
|
875
|
+
* Format OpenAI model ID to display name
|
|
876
|
+
*/
|
|
877
|
+
private static formatOpenAIModelName(modelId: string): string {
|
|
878
|
+
// Public API models
|
|
879
|
+
if (modelId === 'gpt-4o') return 'GPT-4o';
|
|
880
|
+
if (modelId === 'gpt-4o-mini') return 'GPT-4o Mini';
|
|
881
|
+
if (modelId.includes('gpt-4o-')) return `GPT-4o (${modelId.replace('gpt-4o-', '')})`;
|
|
882
|
+
if (modelId === 'gpt-4-turbo') return 'GPT-4 Turbo';
|
|
883
|
+
if (modelId === 'gpt-4') return 'GPT-4';
|
|
884
|
+
if (modelId === 'gpt-3.5-turbo') return 'GPT-3.5 Turbo';
|
|
885
|
+
if (modelId === 'o1') return 'o1';
|
|
886
|
+
if (modelId === 'o1-mini') return 'o1 Mini';
|
|
887
|
+
if (modelId === 'o1-preview') return 'o1 Preview';
|
|
888
|
+
if (modelId === 'o3-mini') return 'o3 Mini';
|
|
889
|
+
// ChatGPT internal models
|
|
890
|
+
if (modelId === 'gpt-5.1') return 'GPT-5.1';
|
|
891
|
+
if (modelId === 'gpt-5.1-codex-mini') return 'GPT-5.1 Codex Mini';
|
|
892
|
+
if (modelId === 'gpt-5.1-codex-max') return 'GPT-5.1 Codex Max';
|
|
893
|
+
if (modelId === 'gpt-5.2') return 'GPT-5.2';
|
|
894
|
+
if (modelId === 'gpt-5.2-codex') return 'GPT-5.2 Codex';
|
|
895
|
+
return modelId;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
/**
|
|
899
|
+
* Get OpenAI model description
|
|
900
|
+
*/
|
|
901
|
+
private static getOpenAIModelDescription(modelId: string): string {
|
|
902
|
+
// Public API models
|
|
903
|
+
if (modelId.includes('gpt-4o') && !modelId.includes('mini')) return 'Most capable model for complex tasks';
|
|
904
|
+
if (modelId.includes('gpt-4o-mini')) return 'Fast and affordable for most tasks';
|
|
905
|
+
if (modelId.includes('gpt-4-turbo')) return 'Previous generation flagship';
|
|
906
|
+
if (modelId.includes('gpt-4')) return 'High capability model';
|
|
907
|
+
if (modelId.includes('gpt-3.5')) return 'Fast and cost-effective';
|
|
908
|
+
if (modelId === 'o1' || modelId === 'o1-preview') return 'Advanced reasoning model';
|
|
909
|
+
if (modelId === 'o1-mini') return 'Fast reasoning model';
|
|
910
|
+
if (modelId.includes('o3')) return 'Next generation reasoning';
|
|
911
|
+
// ChatGPT internal models
|
|
912
|
+
if (modelId === 'gpt-5.1') return 'Balanced performance and capability';
|
|
913
|
+
if (modelId === 'gpt-5.1-codex-mini') return 'Fast and efficient for most tasks';
|
|
914
|
+
if (modelId === 'gpt-5.1-codex-max') return 'Maximum capability for complex tasks';
|
|
915
|
+
if (modelId === 'gpt-5.2') return 'Most advanced reasoning';
|
|
916
|
+
if (modelId === 'gpt-5.2-codex') return 'Advanced reasoning model';
|
|
917
|
+
return 'OpenAI model';
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
/**
|
|
921
|
+
* Save cached models for a provider
|
|
922
|
+
*/
|
|
923
|
+
static saveCachedModels(
|
|
924
|
+
providerType: 'gemini' | 'openrouter' | 'ollama' | 'bedrock' | 'openai',
|
|
925
|
+
models: CachedModelInfo[]
|
|
926
|
+
): void {
|
|
927
|
+
const settings = this.loadSettings();
|
|
928
|
+
|
|
929
|
+
switch (providerType) {
|
|
930
|
+
case 'gemini':
|
|
931
|
+
settings.cachedGeminiModels = models;
|
|
932
|
+
break;
|
|
933
|
+
case 'openrouter':
|
|
934
|
+
settings.cachedOpenRouterModels = models;
|
|
935
|
+
break;
|
|
936
|
+
case 'ollama':
|
|
937
|
+
settings.cachedOllamaModels = models;
|
|
938
|
+
break;
|
|
939
|
+
case 'bedrock':
|
|
940
|
+
settings.cachedBedrockModels = models;
|
|
941
|
+
break;
|
|
942
|
+
case 'openai':
|
|
943
|
+
settings.cachedOpenAIModels = models;
|
|
944
|
+
break;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
this.saveSettings(settings);
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
/**
|
|
951
|
+
* Get cached models for a provider
|
|
952
|
+
*/
|
|
953
|
+
static getCachedModels(providerType: 'gemini' | 'openrouter' | 'ollama' | 'bedrock' | 'openai'): CachedModelInfo[] | undefined {
|
|
954
|
+
const settings = this.loadSettings();
|
|
955
|
+
|
|
956
|
+
switch (providerType) {
|
|
957
|
+
case 'gemini':
|
|
958
|
+
return settings.cachedGeminiModels;
|
|
959
|
+
case 'openrouter':
|
|
960
|
+
return settings.cachedOpenRouterModels;
|
|
961
|
+
case 'ollama':
|
|
962
|
+
return settings.cachedOllamaModels;
|
|
963
|
+
case 'bedrock':
|
|
964
|
+
return settings.cachedBedrockModels;
|
|
965
|
+
case 'openai':
|
|
966
|
+
return settings.cachedOpenAIModels;
|
|
967
|
+
default:
|
|
968
|
+
return undefined;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
}
|