cowork-os 0.3.21 → 0.3.23
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/README.md +293 -6
- package/connectors/README.md +20 -0
- package/connectors/asana-mcp/README.md +24 -0
- package/connectors/asana-mcp/dist/index.js +427 -0
- package/connectors/asana-mcp/package.json +15 -0
- package/connectors/asana-mcp/src/index.ts +553 -0
- package/connectors/asana-mcp/tsconfig.json +13 -0
- package/connectors/hubspot-mcp/README.md +35 -0
- package/connectors/hubspot-mcp/dist/index.js +454 -0
- package/connectors/hubspot-mcp/package.json +15 -0
- package/connectors/hubspot-mcp/src/index.ts +562 -0
- package/connectors/hubspot-mcp/tsconfig.json +13 -0
- package/connectors/jira-mcp/README.md +49 -0
- package/connectors/jira-mcp/dist/index.js +588 -0
- package/connectors/jira-mcp/package.json +15 -0
- package/connectors/jira-mcp/src/index.ts +711 -0
- package/connectors/jira-mcp/tsconfig.json +13 -0
- package/connectors/linear-mcp/README.md +22 -0
- package/connectors/linear-mcp/dist/index.js +402 -0
- package/connectors/linear-mcp/package.json +15 -0
- package/connectors/linear-mcp/src/index.ts +522 -0
- package/connectors/linear-mcp/tsconfig.json +13 -0
- package/connectors/okta-mcp/README.md +24 -0
- package/connectors/okta-mcp/dist/index.js +411 -0
- package/connectors/okta-mcp/package.json +15 -0
- package/connectors/okta-mcp/src/index.ts +520 -0
- package/connectors/okta-mcp/tsconfig.json +13 -0
- package/connectors/salesforce-mcp/README.md +47 -0
- package/connectors/salesforce-mcp/dist/index.js +584 -0
- package/connectors/salesforce-mcp/package.json +15 -0
- package/connectors/salesforce-mcp/src/index.ts +722 -0
- package/connectors/salesforce-mcp/tsconfig.json +13 -0
- package/connectors/servicenow-mcp/README.md +26 -0
- package/connectors/servicenow-mcp/dist/index.js +400 -0
- package/connectors/servicenow-mcp/package.json +15 -0
- package/connectors/servicenow-mcp/src/index.ts +500 -0
- package/connectors/servicenow-mcp/tsconfig.json +13 -0
- package/connectors/templates/mcp-connector/README.md +31 -0
- package/connectors/templates/mcp-connector/package.json +15 -0
- package/connectors/templates/mcp-connector/src/index.ts +330 -0
- package/connectors/templates/mcp-connector/tsconfig.json +13 -0
- package/connectors/zendesk-mcp/README.md +40 -0
- package/connectors/zendesk-mcp/dist/index.js +431 -0
- package/connectors/zendesk-mcp/package.json +15 -0
- package/connectors/zendesk-mcp/src/index.ts +543 -0
- package/connectors/zendesk-mcp/tsconfig.json +13 -0
- package/dist/electron/electron/agent/daemon.js +25 -0
- package/dist/electron/electron/agent/executor.js +181 -26
- package/dist/electron/electron/agent/llm/anthropic-compatible-provider.js +177 -0
- package/dist/electron/electron/agent/llm/github-copilot-provider.js +97 -0
- package/dist/electron/electron/agent/llm/groq-provider.js +33 -0
- package/dist/electron/electron/agent/llm/index.js +11 -1
- package/dist/electron/electron/agent/llm/kimi-provider.js +33 -0
- package/dist/electron/electron/agent/llm/openai-compatible-provider.js +116 -0
- package/dist/electron/electron/agent/llm/openai-compatible.js +111 -0
- package/dist/electron/electron/agent/llm/openai-oauth.js +2 -1
- package/dist/electron/electron/agent/llm/openrouter-provider.js +1 -1
- package/dist/electron/electron/agent/llm/provider-factory.js +318 -4
- package/dist/electron/electron/agent/llm/types.js +66 -1
- package/dist/electron/electron/agent/llm/xai-provider.js +33 -0
- package/dist/electron/electron/agent/tools/box-tools.js +231 -0
- package/dist/electron/electron/agent/tools/builtin-settings.js +28 -0
- package/dist/electron/electron/agent/tools/dropbox-tools.js +237 -0
- package/dist/electron/electron/agent/tools/google-drive-tools.js +227 -0
- package/dist/electron/electron/agent/tools/notion-tools.js +312 -0
- package/dist/electron/electron/agent/tools/onedrive-tools.js +217 -0
- package/dist/electron/electron/agent/tools/registry.js +541 -0
- package/dist/electron/electron/agent/tools/sharepoint-tools.js +243 -0
- package/dist/electron/electron/agent/tools/shell-tools.js +12 -3
- package/dist/electron/electron/agent/tools/x-tools.js +1 -1
- package/dist/electron/electron/gateway/index.js +1 -0
- package/dist/electron/electron/gateway/router.js +123 -143
- package/dist/electron/electron/ipc/canvas-handlers.js +5 -0
- package/dist/electron/electron/ipc/handlers.js +627 -158
- package/dist/electron/electron/main.js +63 -0
- package/dist/electron/electron/mcp/oauth/connector-oauth.js +333 -0
- package/dist/electron/electron/mcp/registry/MCPRegistryManager.js +503 -154
- package/dist/electron/electron/memory/MemoryService.js +1 -1
- package/dist/electron/electron/preload.js +74 -1
- package/dist/electron/electron/settings/box-manager.js +54 -0
- package/dist/electron/electron/settings/dropbox-manager.js +54 -0
- package/dist/electron/electron/settings/google-drive-manager.js +54 -0
- package/dist/electron/electron/settings/notion-manager.js +56 -0
- package/dist/electron/electron/settings/onedrive-manager.js +54 -0
- package/dist/electron/electron/settings/sharepoint-manager.js +54 -0
- package/dist/electron/electron/utils/box-api.js +153 -0
- package/dist/electron/electron/utils/dropbox-api.js +144 -0
- package/dist/electron/electron/utils/env-migration.js +19 -0
- package/dist/electron/electron/utils/google-drive-api.js +152 -0
- package/dist/electron/electron/utils/notion-api.js +103 -0
- package/dist/electron/electron/utils/onedrive-api.js +113 -0
- package/dist/electron/electron/utils/sharepoint-api.js +109 -0
- package/dist/electron/electron/utils/validation.js +82 -3
- package/dist/electron/electron/utils/x-cli.js +1 -1
- package/dist/electron/shared/channelMessages.js +284 -3
- package/dist/electron/shared/llm-provider-catalog.js +198 -0
- package/dist/electron/shared/types.js +88 -1
- package/package.json +12 -2
- package/src/electron/agent/executor.ts +205 -28
- package/src/electron/agent/llm/anthropic-compatible-provider.ts +214 -0
- package/src/electron/agent/llm/github-copilot-provider.ts +117 -0
- package/src/electron/agent/llm/groq-provider.ts +39 -0
- package/src/electron/agent/llm/index.ts +5 -0
- package/src/electron/agent/llm/kimi-provider.ts +39 -0
- package/src/electron/agent/llm/openai-compatible-provider.ts +153 -0
- package/src/electron/agent/llm/openai-compatible.ts +133 -0
- package/src/electron/agent/llm/openai-oauth.ts +2 -1
- package/src/electron/agent/llm/openrouter-provider.ts +2 -1
- package/src/electron/agent/llm/provider-factory.ts +414 -6
- package/src/electron/agent/llm/types.ts +90 -1
- package/src/electron/agent/llm/xai-provider.ts +39 -0
- package/src/electron/agent/tools/box-tools.ts +239 -0
- package/src/electron/agent/tools/builtin-settings.ts +34 -0
- package/src/electron/agent/tools/dropbox-tools.ts +237 -0
- package/src/electron/agent/tools/google-drive-tools.ts +228 -0
- package/src/electron/agent/tools/notion-tools.ts +330 -0
- package/src/electron/agent/tools/onedrive-tools.ts +217 -0
- package/src/electron/agent/tools/registry.ts +565 -0
- package/src/electron/agent/tools/sharepoint-tools.ts +247 -0
- package/src/electron/agent/tools/shell-tools.ts +11 -3
- package/src/electron/agent/tools/x-tools.ts +1 -1
- package/src/electron/database/SecureSettingsRepository.ts +7 -1
- package/src/electron/gateway/index.ts +1 -0
- package/src/electron/gateway/router.ts +134 -149
- package/src/electron/ipc/canvas-handlers.ts +10 -0
- package/src/electron/ipc/handlers.ts +673 -153
- package/src/electron/main.ts +35 -0
- package/src/electron/mcp/oauth/connector-oauth.ts +448 -0
- package/src/electron/mcp/registry/MCPRegistryManager.ts +343 -12
- package/src/electron/memory/MemoryService.ts +5 -1
- package/src/electron/preload.ts +167 -4
- package/src/electron/settings/box-manager.ts +58 -0
- package/src/electron/settings/dropbox-manager.ts +58 -0
- package/src/electron/settings/google-drive-manager.ts +58 -0
- package/src/electron/settings/notion-manager.ts +60 -0
- package/src/electron/settings/onedrive-manager.ts +58 -0
- package/src/electron/settings/sharepoint-manager.ts +58 -0
- package/src/electron/utils/box-api.ts +184 -0
- package/src/electron/utils/dropbox-api.ts +171 -0
- package/src/electron/utils/env-migration.ts +22 -0
- package/src/electron/utils/google-drive-api.ts +183 -0
- package/src/electron/utils/notion-api.ts +126 -0
- package/src/electron/utils/onedrive-api.ts +137 -0
- package/src/electron/utils/sharepoint-api.ts +132 -0
- package/src/electron/utils/validation.ts +102 -1
- package/src/electron/utils/x-cli.ts +1 -1
- package/src/renderer/App.tsx +20 -2
- package/src/renderer/components/BoxSettings.tsx +203 -0
- package/src/renderer/components/BrowserView.tsx +101 -0
- package/src/renderer/components/BuiltinToolsSettings.tsx +105 -0
- package/src/renderer/components/CanvasPreview.tsx +68 -1
- package/src/renderer/components/ConnectorEnvModal.tsx +116 -0
- package/src/renderer/components/ConnectorSetupModal.tsx +566 -0
- package/src/renderer/components/ConnectorsSettings.tsx +397 -0
- package/src/renderer/components/DropboxSettings.tsx +202 -0
- package/src/renderer/components/GoogleDriveSettings.tsx +201 -0
- package/src/renderer/components/MCPSettings.tsx +56 -0
- package/src/renderer/components/MainContent.tsx +270 -34
- package/src/renderer/components/NotionSettings.tsx +231 -0
- package/src/renderer/components/Onboarding/Onboarding.tsx +13 -1
- package/src/renderer/components/OnboardingModal.tsx +70 -1
- package/src/renderer/components/OneDriveSettings.tsx +212 -0
- package/src/renderer/components/Settings.tsx +611 -8
- package/src/renderer/components/SharePointSettings.tsx +224 -0
- package/src/renderer/components/Sidebar.tsx +25 -9
- package/src/renderer/hooks/useOnboardingFlow.ts +21 -0
- package/src/renderer/styles/index.css +438 -25
- package/src/shared/channelMessages.ts +367 -4
- package/src/shared/llm-provider-catalog.ts +217 -0
- package/src/shared/types.ts +226 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useState, useEffect, useRef } from 'react';
|
|
2
|
-
import { LLMSettingsData, ThemeMode, AccentColor } from '../../shared/types';
|
|
2
|
+
import { LLMSettingsData, ThemeMode, AccentColor, type LLMProviderType, type CustomProviderConfig } from '../../shared/types';
|
|
3
|
+
import { CUSTOM_PROVIDER_MAP } from '../../shared/llm-provider-catalog';
|
|
3
4
|
import { TelegramSettings } from './TelegramSettings';
|
|
4
5
|
import { DiscordSettings } from './DiscordSettings';
|
|
5
6
|
import { SlackSettings } from './SlackSettings';
|
|
@@ -15,6 +16,12 @@ import { EmailSettings } from './EmailSettings';
|
|
|
15
16
|
import { TeamsSettings } from './TeamsSettings';
|
|
16
17
|
import { GoogleChatSettings } from './GoogleChatSettings';
|
|
17
18
|
import { XSettings } from './XSettings';
|
|
19
|
+
import { NotionSettings } from './NotionSettings';
|
|
20
|
+
import { BoxSettings } from './BoxSettings';
|
|
21
|
+
import { OneDriveSettings } from './OneDriveSettings';
|
|
22
|
+
import { GoogleDriveSettings } from './GoogleDriveSettings';
|
|
23
|
+
import { DropboxSettings } from './DropboxSettings';
|
|
24
|
+
import { SharePointSettings } from './SharePointSettings';
|
|
18
25
|
import { SearchSettings } from './SearchSettings';
|
|
19
26
|
import { UpdateSettings } from './UpdateSettings';
|
|
20
27
|
import { GuardrailSettings } from './GuardrailSettings';
|
|
@@ -23,6 +30,7 @@ import { QueueSettings } from './QueueSettings';
|
|
|
23
30
|
import { SkillsSettings } from './SkillsSettings';
|
|
24
31
|
import { SkillHubBrowser } from './SkillHubBrowser';
|
|
25
32
|
import { MCPSettings } from './MCPSettings';
|
|
33
|
+
import { ConnectorsSettings } from './ConnectorsSettings';
|
|
26
34
|
import { BuiltinToolsSettings } from './BuiltinToolsSettings';
|
|
27
35
|
import { TraySettings } from './TraySettings';
|
|
28
36
|
import { ScheduledTasksSettings } from './ScheduledTasksSettings';
|
|
@@ -34,10 +42,13 @@ import { ExtensionsSettings } from './ExtensionsSettings';
|
|
|
34
42
|
import { VoiceSettings } from './VoiceSettings';
|
|
35
43
|
import { MissionControlPanel } from './MissionControlPanel';
|
|
36
44
|
|
|
37
|
-
type SettingsTab = 'appearance' | 'personality' | 'missioncontrol' | 'tray' | 'voice' | 'llm' | 'search' | 'telegram' | 'slack' | 'whatsapp' | 'teams' | 'morechannels' | 'updates' | 'guardrails' | 'queue' | 'skills' | 'skillhub' | 'mcp' | 'tools' | 'scheduled' | 'hooks' | 'controlplane' | 'nodes' | 'extensions';
|
|
45
|
+
type SettingsTab = 'appearance' | 'personality' | 'missioncontrol' | 'tray' | 'voice' | 'llm' | 'search' | 'telegram' | 'slack' | 'whatsapp' | 'teams' | 'x' | 'morechannels' | 'integrations' | 'updates' | 'guardrails' | 'queue' | 'skills' | 'skillhub' | 'connectors' | 'mcp' | 'tools' | 'scheduled' | 'hooks' | 'controlplane' | 'nodes' | 'extensions';
|
|
38
46
|
|
|
39
47
|
// Secondary channels shown inside "More Channels" tab
|
|
40
|
-
type SecondaryChannel = 'discord' | 'imessage' | 'signal' | 'mattermost' | 'matrix' | 'twitch' | 'line' | 'bluebubbles' | 'email' | 'googlechat'
|
|
48
|
+
type SecondaryChannel = 'discord' | 'imessage' | 'signal' | 'mattermost' | 'matrix' | 'twitch' | 'line' | 'bluebubbles' | 'email' | 'googlechat';
|
|
49
|
+
|
|
50
|
+
// App integrations shown inside "Integrations" tab
|
|
51
|
+
type IntegrationChannel = 'notion' | 'box' | 'onedrive' | 'googledrive' | 'dropbox' | 'sharepoint';
|
|
41
52
|
|
|
42
53
|
interface SettingsProps {
|
|
43
54
|
onBack: () => void;
|
|
@@ -246,12 +257,15 @@ const sidebarItems: Array<{ tab: SettingsTab; label: string; icon: React.ReactNo
|
|
|
246
257
|
{ tab: 'telegram', label: 'Telegram', icon: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z" /></svg> },
|
|
247
258
|
{ tab: 'slack', label: 'Slack', icon: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M14.5 10c-.83 0-1.5-.67-1.5-1.5v-5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5v5c0 .83-.67 1.5-1.5 1.5z" /><path d="M20.5 10H19V8.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5-.67 1.5-1.5 1.5z" /><path d="M9.5 14c.83 0 1.5.67 1.5 1.5v5c0 .83-.67 1.5-1.5 1.5S8 21.33 8 20.5v-5c0-.83.67-1.5 1.5-1.5z" /><path d="M3.5 14H5v1.5c0 .83-.67 1.5-1.5 1.5S2 16.33 2 15.5 2.67 14 3.5 14z" /><path d="M14 14.5c0-.83.67-1.5 1.5-1.5h5c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5h-5c-.83 0-1.5-.67-1.5-1.5z" /><path d="M15.5 19H14v1.5c0 .83.67 1.5 1.5 1.5s1.5-.67 1.5-1.5-.67-1.5-1.5-1.5z" /><path d="M10 9.5C10 8.67 9.33 8 8.5 8h-5C2.67 8 2 8.67 2 9.5S2.67 11 3.5 11h5c.83 0 1.5-.67 1.5-1.5z" /><path d="M8.5 5H10V3.5C10 2.67 9.33 2 8.5 2S7 2.67 7 3.5 7.67 5 8.5 5z" /></svg> },
|
|
248
259
|
{ tab: 'teams', label: 'Teams', icon: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" /><circle cx="9" cy="7" r="4" /><path d="M23 21v-2a4 4 0 0 0-3-3.87" /><path d="M16 3.13a4 4 0 0 1 0 7.75" /></svg> },
|
|
260
|
+
{ tab: 'x', label: 'X (Twitter)', icon: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M5 4l14 16" /><path d="M19 4L5 20" /></svg> },
|
|
249
261
|
{ tab: 'morechannels', label: 'More Channels', icon: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="12" cy="12" r="1" /><circle cx="19" cy="12" r="1" /><circle cx="5" cy="12" r="1" /></svg> },
|
|
262
|
+
{ tab: 'integrations', label: 'Integrations', icon: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M12 2v4" /><path d="M12 18v4" /><path d="M4.93 4.93l2.83 2.83" /><path d="M16.24 16.24l2.83 2.83" /><path d="M2 12h4" /><path d="M18 12h4" /><path d="M4.93 19.07l2.83-2.83" /><path d="M16.24 7.76l2.83-2.83" /><circle cx="12" cy="12" r="3" /></svg> },
|
|
250
263
|
{ tab: 'guardrails', label: 'Guardrails', icon: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" /></svg> },
|
|
251
264
|
{ tab: 'queue', label: 'Task Queue', icon: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="3" y="4" width="18" height="4" rx="1" /><rect x="3" y="10" width="18" height="4" rx="1" /><rect x="3" y="16" width="18" height="4" rx="1" /></svg> },
|
|
252
265
|
{ tab: 'skills', label: 'Custom Skills', icon: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z" /></svg> },
|
|
253
266
|
{ tab: 'skillhub', label: 'SkillHub', icon: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="12" cy="12" r="10" /><path d="M12 16v-4" /><path d="M12 8h.01" /><path d="M8 12h8" /></svg> },
|
|
254
267
|
{ tab: 'scheduled', label: 'Scheduled Tasks', icon: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="12" cy="12" r="10" /><polyline points="12 6 12 12 16 14" /></svg> },
|
|
268
|
+
{ tab: 'connectors', label: 'Connectors', icon: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="3" y="4" width="7" height="7" rx="1" /><rect x="14" y="4" width="7" height="7" rx="1" /><rect x="3" y="13" width="7" height="7" rx="1" /><rect x="14" y="13" width="7" height="7" rx="1" /></svg> },
|
|
255
269
|
{ tab: 'mcp', label: 'MCP Servers', icon: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="2" y="3" width="20" height="14" rx="2" /><path d="M8 21h8" /><path d="M12 17v4" /><path d="M7 8h2M15 8h2" /><path d="M9 12h6" /></svg> },
|
|
256
270
|
{ tab: 'tools', label: 'Built-in Tools', icon: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" /></svg> },
|
|
257
271
|
{ tab: 'hooks', label: 'Webhooks', icon: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" /><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" /></svg> },
|
|
@@ -273,12 +287,22 @@ const secondaryChannelItems: Array<{ key: SecondaryChannel; label: string; icon:
|
|
|
273
287
|
{ key: 'matrix', label: 'Matrix', icon: <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="2" y="2" width="20" height="20" rx="2" /><path d="M7 7h10M7 12h10M7 17h10" /></svg> },
|
|
274
288
|
{ key: 'twitch', label: 'Twitch', icon: <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M21 2H3v16h5v4l4-4h5l4-4V2zM11 11V7M16 11V7" /></svg> },
|
|
275
289
|
{ key: 'bluebubbles', label: 'BlueBubbles', icon: <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="12" cy="12" r="10" /><path d="M8 14s1.5 2 4 2 4-2 4-2" /><line x1="9" y1="9" x2="9.01" y2="9" /><line x1="15" y1="9" x2="15.01" y2="9" /></svg> },
|
|
276
|
-
|
|
290
|
+
];
|
|
291
|
+
|
|
292
|
+
// App integrations configuration for "Integrations" tab
|
|
293
|
+
const integrationItems: Array<{ key: IntegrationChannel; label: string; icon: React.ReactNode }> = [
|
|
294
|
+
{ key: 'notion', label: 'Notion', icon: <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="4" y="3" width="16" height="18" rx="2" /><path d="M8 7h8M8 11h8M8 15h6" /></svg> },
|
|
295
|
+
{ key: 'sharepoint', label: 'SharePoint', icon: <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="3" y="4" width="18" height="16" rx="2" /><path d="M7 8h10M7 12h6" /></svg> },
|
|
296
|
+
{ key: 'onedrive', label: 'OneDrive', icon: <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M7 18h10a4 4 0 0 0 0-8 5 5 0 0 0-9.7-1.6A4 4 0 0 0 7 18z" /></svg> },
|
|
297
|
+
{ key: 'googledrive', label: 'Google Drive', icon: <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M4 7l4-4h8l4 4-8 14H8L4 7z" /></svg> },
|
|
298
|
+
{ key: 'box', label: 'Box', icon: <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="3" y="3" width="18" height="18" rx="3" /><path d="M7 8h10M7 12h10M7 16h6" /></svg> },
|
|
299
|
+
{ key: 'dropbox', label: 'Dropbox', icon: <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M7 5l5 3-5 3-5-3 5-3zm10 0l5 3-5 3-5-3 5-3zM7 13l5 3-5 3-5-3 5-3zm10 0l5 3-5 3-5-3 5-3z" /></svg> },
|
|
277
300
|
];
|
|
278
301
|
|
|
279
302
|
export function Settings({ onBack, onSettingsChanged, themeMode, accentColor, onThemeChange, onAccentChange, initialTab = 'appearance', onShowOnboarding, onboardingCompletedAt }: SettingsProps) {
|
|
280
303
|
const [activeTab, setActiveTab] = useState<SettingsTab>(initialTab);
|
|
281
304
|
const [activeSecondaryChannel, setActiveSecondaryChannel] = useState<SecondaryChannel>('discord');
|
|
305
|
+
const [activeIntegration, setActiveIntegration] = useState<IntegrationChannel>('notion');
|
|
282
306
|
const [sidebarSearch, setSidebarSearch] = useState('');
|
|
283
307
|
const [settings, setSettings] = useState<LLMSettingsData>({
|
|
284
308
|
providerType: 'anthropic',
|
|
@@ -314,6 +338,7 @@ export function Settings({ onBack, onSettingsChanged, themeMode, accentColor, on
|
|
|
314
338
|
|
|
315
339
|
// OpenRouter state
|
|
316
340
|
const [openrouterApiKey, setOpenrouterApiKey] = useState('');
|
|
341
|
+
const [openrouterBaseUrl, setOpenrouterBaseUrl] = useState('');
|
|
317
342
|
const [openrouterModel, setOpenrouterModel] = useState('anthropic/claude-3.5-sonnet');
|
|
318
343
|
const [openrouterModels, setOpenrouterModels] = useState<Array<{ id: string; name: string; context_length: number }>>([]);
|
|
319
344
|
const [loadingOpenRouterModels, setLoadingOpenRouterModels] = useState(false);
|
|
@@ -327,6 +352,30 @@ export function Settings({ onBack, onSettingsChanged, themeMode, accentColor, on
|
|
|
327
352
|
const [openaiOAuthConnected, setOpenaiOAuthConnected] = useState(false);
|
|
328
353
|
const [openaiOAuthLoading, setOpenaiOAuthLoading] = useState(false);
|
|
329
354
|
|
|
355
|
+
// Groq state
|
|
356
|
+
const [groqApiKey, setGroqApiKey] = useState('');
|
|
357
|
+
const [groqBaseUrl, setGroqBaseUrl] = useState('');
|
|
358
|
+
const [groqModel, setGroqModel] = useState('llama-3.1-8b-instant');
|
|
359
|
+
const [groqModels, setGroqModels] = useState<Array<{ id: string; name: string }>>([]);
|
|
360
|
+
const [loadingGroqModels, setLoadingGroqModels] = useState(false);
|
|
361
|
+
|
|
362
|
+
// xAI state
|
|
363
|
+
const [xaiApiKey, setXaiApiKey] = useState('');
|
|
364
|
+
const [xaiBaseUrl, setXaiBaseUrl] = useState('');
|
|
365
|
+
const [xaiModel, setXaiModel] = useState('grok-4-fast-non-reasoning');
|
|
366
|
+
const [xaiModels, setXaiModels] = useState<Array<{ id: string; name: string }>>([]);
|
|
367
|
+
const [loadingXaiModels, setLoadingXaiModels] = useState(false);
|
|
368
|
+
|
|
369
|
+
// Kimi state
|
|
370
|
+
const [kimiApiKey, setKimiApiKey] = useState('');
|
|
371
|
+
const [kimiBaseUrl, setKimiBaseUrl] = useState('');
|
|
372
|
+
const [kimiModel, setKimiModel] = useState('kimi-k2.5');
|
|
373
|
+
const [kimiModels, setKimiModels] = useState<Array<{ id: string; name: string }>>([]);
|
|
374
|
+
const [loadingKimiModels, setLoadingKimiModels] = useState(false);
|
|
375
|
+
|
|
376
|
+
// Custom provider state
|
|
377
|
+
const [customProviders, setCustomProviders] = useState<Record<string, CustomProviderConfig>>({});
|
|
378
|
+
|
|
330
379
|
// Bedrock state
|
|
331
380
|
const [bedrockModel, setBedrockModel] = useState('');
|
|
332
381
|
const [bedrockModels, setBedrockModels] = useState<Array<{ id: string; name: string; description: string }>>([]);
|
|
@@ -336,6 +385,37 @@ export function Settings({ onBack, onSettingsChanged, themeMode, accentColor, on
|
|
|
336
385
|
loadConfigStatus();
|
|
337
386
|
}, []);
|
|
338
387
|
|
|
388
|
+
const resolveCustomProviderId = (providerType: LLMProviderType) =>
|
|
389
|
+
providerType === 'kimi-coding' ? 'kimi-code' : providerType;
|
|
390
|
+
|
|
391
|
+
const updateCustomProvider = (providerType: LLMProviderType, updates: Partial<CustomProviderConfig>) => {
|
|
392
|
+
const resolvedType = resolveCustomProviderId(providerType);
|
|
393
|
+
setCustomProviders((prev) => ({
|
|
394
|
+
...prev,
|
|
395
|
+
[resolvedType]: {
|
|
396
|
+
...(prev[resolvedType] || {}),
|
|
397
|
+
...updates,
|
|
398
|
+
},
|
|
399
|
+
}));
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
const sanitizeCustomProviders = (providers: Record<string, CustomProviderConfig>) => {
|
|
403
|
+
const sanitized: Record<string, CustomProviderConfig> = {};
|
|
404
|
+
Object.entries(providers).forEach(([key, value]) => {
|
|
405
|
+
const apiKey = value.apiKey?.trim();
|
|
406
|
+
const model = value.model?.trim();
|
|
407
|
+
const baseUrl = value.baseUrl?.trim();
|
|
408
|
+
if (apiKey || model || baseUrl) {
|
|
409
|
+
sanitized[key] = {
|
|
410
|
+
...(apiKey ? { apiKey } : {}),
|
|
411
|
+
...(model ? { model } : {}),
|
|
412
|
+
...(baseUrl ? { baseUrl } : {}),
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
return Object.keys(sanitized).length > 0 ? sanitized : undefined;
|
|
417
|
+
};
|
|
418
|
+
|
|
339
419
|
const loadConfigStatus = async () => {
|
|
340
420
|
try {
|
|
341
421
|
setLoading(true);
|
|
@@ -349,6 +429,18 @@ export function Settings({ onBack, onSettingsChanged, themeMode, accentColor, on
|
|
|
349
429
|
// Load full settings separately for bedrock config
|
|
350
430
|
const loadedSettings = await window.electronAPI.getLLMSettings();
|
|
351
431
|
setSettings(loadedSettings);
|
|
432
|
+
if (loadedSettings.customProviders) {
|
|
433
|
+
const normalized = { ...loadedSettings.customProviders };
|
|
434
|
+
if (normalized['kimi-coding'] && !normalized['kimi-code']) {
|
|
435
|
+
normalized['kimi-code'] = normalized['kimi-coding'];
|
|
436
|
+
}
|
|
437
|
+
if (normalized['kimi-coding']) {
|
|
438
|
+
delete normalized['kimi-coding'];
|
|
439
|
+
}
|
|
440
|
+
setCustomProviders(normalized);
|
|
441
|
+
} else {
|
|
442
|
+
setCustomProviders({});
|
|
443
|
+
}
|
|
352
444
|
|
|
353
445
|
// Set form state from loaded settings
|
|
354
446
|
if (loadedSettings.bedrock?.region) {
|
|
@@ -387,6 +479,9 @@ export function Settings({ onBack, onSettingsChanged, themeMode, accentColor, on
|
|
|
387
479
|
if (loadedSettings.openrouter?.apiKey) {
|
|
388
480
|
setOpenrouterApiKey(loadedSettings.openrouter.apiKey);
|
|
389
481
|
}
|
|
482
|
+
if (loadedSettings.openrouter?.baseUrl) {
|
|
483
|
+
setOpenrouterBaseUrl(loadedSettings.openrouter.baseUrl);
|
|
484
|
+
}
|
|
390
485
|
if (loadedSettings.openrouter?.model) {
|
|
391
486
|
setOpenrouterModel(loadedSettings.openrouter.model);
|
|
392
487
|
}
|
|
@@ -419,6 +514,39 @@ export function Settings({ onBack, onSettingsChanged, themeMode, accentColor, on
|
|
|
419
514
|
setOpenaiAuthMethod('oauth');
|
|
420
515
|
}
|
|
421
516
|
|
|
517
|
+
// Set Groq form state
|
|
518
|
+
if (loadedSettings.groq?.apiKey) {
|
|
519
|
+
setGroqApiKey(loadedSettings.groq.apiKey);
|
|
520
|
+
}
|
|
521
|
+
if (loadedSettings.groq?.baseUrl) {
|
|
522
|
+
setGroqBaseUrl(loadedSettings.groq.baseUrl);
|
|
523
|
+
}
|
|
524
|
+
if (loadedSettings.groq?.model) {
|
|
525
|
+
setGroqModel(loadedSettings.groq.model);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Set xAI form state
|
|
529
|
+
if (loadedSettings.xai?.apiKey) {
|
|
530
|
+
setXaiApiKey(loadedSettings.xai.apiKey);
|
|
531
|
+
}
|
|
532
|
+
if (loadedSettings.xai?.baseUrl) {
|
|
533
|
+
setXaiBaseUrl(loadedSettings.xai.baseUrl);
|
|
534
|
+
}
|
|
535
|
+
if (loadedSettings.xai?.model) {
|
|
536
|
+
setXaiModel(loadedSettings.xai.model);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// Set Kimi form state
|
|
540
|
+
if (loadedSettings.kimi?.apiKey) {
|
|
541
|
+
setKimiApiKey(loadedSettings.kimi.apiKey);
|
|
542
|
+
}
|
|
543
|
+
if (loadedSettings.kimi?.baseUrl) {
|
|
544
|
+
setKimiBaseUrl(loadedSettings.kimi.baseUrl);
|
|
545
|
+
}
|
|
546
|
+
if (loadedSettings.kimi?.model) {
|
|
547
|
+
setKimiModel(loadedSettings.kimi.model);
|
|
548
|
+
}
|
|
549
|
+
|
|
422
550
|
// Set Bedrock form state (access key and secret key are set earlier)
|
|
423
551
|
if (loadedSettings.bedrock?.accessKeyId) {
|
|
424
552
|
setAwsAccessKeyId(loadedSettings.bedrock.accessKeyId);
|
|
@@ -514,7 +642,7 @@ export function Settings({ onBack, onSettingsChanged, themeMode, accentColor, on
|
|
|
514
642
|
const loadOpenRouterModels = async (apiKey?: string) => {
|
|
515
643
|
try {
|
|
516
644
|
setLoadingOpenRouterModels(true);
|
|
517
|
-
const models = await window.electronAPI.getOpenRouterModels(apiKey || openrouterApiKey);
|
|
645
|
+
const models = await window.electronAPI.getOpenRouterModels(apiKey || openrouterApiKey, openrouterBaseUrl || undefined);
|
|
518
646
|
setOpenrouterModels(models || []);
|
|
519
647
|
// If we got models and current model isn't in the list, select the first one
|
|
520
648
|
if (models && models.length > 0 && !models.some(m => m.id === openrouterModel)) {
|
|
@@ -549,6 +677,57 @@ export function Settings({ onBack, onSettingsChanged, themeMode, accentColor, on
|
|
|
549
677
|
}
|
|
550
678
|
};
|
|
551
679
|
|
|
680
|
+
const loadGroqModels = async (apiKey?: string) => {
|
|
681
|
+
try {
|
|
682
|
+
setLoadingGroqModels(true);
|
|
683
|
+
const models = await window.electronAPI.getGroqModels(apiKey || groqApiKey, groqBaseUrl || undefined);
|
|
684
|
+
setGroqModels(models || []);
|
|
685
|
+
if (models && models.length > 0 && !models.some(m => m.id === groqModel)) {
|
|
686
|
+
setGroqModel(models[0].id);
|
|
687
|
+
}
|
|
688
|
+
onSettingsChanged?.();
|
|
689
|
+
} catch (error) {
|
|
690
|
+
console.error('Failed to load Groq models:', error);
|
|
691
|
+
setGroqModels([]);
|
|
692
|
+
} finally {
|
|
693
|
+
setLoadingGroqModels(false);
|
|
694
|
+
}
|
|
695
|
+
};
|
|
696
|
+
|
|
697
|
+
const loadXAIModels = async (apiKey?: string) => {
|
|
698
|
+
try {
|
|
699
|
+
setLoadingXaiModels(true);
|
|
700
|
+
const models = await window.electronAPI.getXAIModels(apiKey || xaiApiKey, xaiBaseUrl || undefined);
|
|
701
|
+
setXaiModels(models || []);
|
|
702
|
+
if (models && models.length > 0 && !models.some(m => m.id === xaiModel)) {
|
|
703
|
+
setXaiModel(models[0].id);
|
|
704
|
+
}
|
|
705
|
+
onSettingsChanged?.();
|
|
706
|
+
} catch (error) {
|
|
707
|
+
console.error('Failed to load xAI models:', error);
|
|
708
|
+
setXaiModels([]);
|
|
709
|
+
} finally {
|
|
710
|
+
setLoadingXaiModels(false);
|
|
711
|
+
}
|
|
712
|
+
};
|
|
713
|
+
|
|
714
|
+
const loadKimiModels = async (apiKey?: string) => {
|
|
715
|
+
try {
|
|
716
|
+
setLoadingKimiModels(true);
|
|
717
|
+
const models = await window.electronAPI.getKimiModels(apiKey || kimiApiKey, kimiBaseUrl || undefined);
|
|
718
|
+
setKimiModels(models || []);
|
|
719
|
+
if (models && models.length > 0 && !models.some(m => m.id === kimiModel)) {
|
|
720
|
+
setKimiModel(models[0].id);
|
|
721
|
+
}
|
|
722
|
+
onSettingsChanged?.();
|
|
723
|
+
} catch (error) {
|
|
724
|
+
console.error('Failed to load Kimi models:', error);
|
|
725
|
+
setKimiModels([]);
|
|
726
|
+
} finally {
|
|
727
|
+
setLoadingKimiModels(false);
|
|
728
|
+
}
|
|
729
|
+
};
|
|
730
|
+
|
|
552
731
|
const handleOpenAIOAuthLogin = async () => {
|
|
553
732
|
try {
|
|
554
733
|
setOpenaiOAuthLoading(true);
|
|
@@ -613,6 +792,21 @@ export function Settings({ onBack, onSettingsChanged, themeMode, accentColor, on
|
|
|
613
792
|
setSaving(true);
|
|
614
793
|
setTestResult(null);
|
|
615
794
|
|
|
795
|
+
const sanitizedCustomProviders = sanitizeCustomProviders(customProviders) || {};
|
|
796
|
+
const resolvedProviderTypeForSave = resolveCustomProviderId(settings.providerType as LLMProviderType);
|
|
797
|
+
const selectedCustomEntry = CUSTOM_PROVIDER_MAP.get(resolvedProviderTypeForSave);
|
|
798
|
+
if (selectedCustomEntry) {
|
|
799
|
+
const existing = sanitizedCustomProviders[resolvedProviderTypeForSave] || {};
|
|
800
|
+
const withDefaults: CustomProviderConfig = { ...existing };
|
|
801
|
+
if (!withDefaults.model && selectedCustomEntry.defaultModel) {
|
|
802
|
+
withDefaults.model = selectedCustomEntry.defaultModel;
|
|
803
|
+
}
|
|
804
|
+
if (!withDefaults.baseUrl && selectedCustomEntry.baseUrl) {
|
|
805
|
+
withDefaults.baseUrl = selectedCustomEntry.baseUrl;
|
|
806
|
+
}
|
|
807
|
+
sanitizedCustomProviders[resolvedProviderTypeForSave] = withDefaults;
|
|
808
|
+
}
|
|
809
|
+
|
|
616
810
|
// Always save settings for ALL providers to preserve API keys and model selections
|
|
617
811
|
// when switching between providers
|
|
618
812
|
const settingsToSave: LLMSettingsData = {
|
|
@@ -648,6 +842,7 @@ export function Settings({ onBack, onSettingsChanged, themeMode, accentColor, on
|
|
|
648
842
|
openrouter: {
|
|
649
843
|
apiKey: openrouterApiKey || undefined,
|
|
650
844
|
model: openrouterModel || undefined,
|
|
845
|
+
baseUrl: openrouterBaseUrl || undefined,
|
|
651
846
|
},
|
|
652
847
|
// Always include openai settings
|
|
653
848
|
openai: {
|
|
@@ -655,6 +850,25 @@ export function Settings({ onBack, onSettingsChanged, themeMode, accentColor, on
|
|
|
655
850
|
model: openaiModel || undefined,
|
|
656
851
|
authMethod: openaiAuthMethod,
|
|
657
852
|
},
|
|
853
|
+
// Always include Groq settings
|
|
854
|
+
groq: {
|
|
855
|
+
apiKey: groqApiKey || undefined,
|
|
856
|
+
model: groqModel || undefined,
|
|
857
|
+
baseUrl: groqBaseUrl || undefined,
|
|
858
|
+
},
|
|
859
|
+
// Always include xAI settings
|
|
860
|
+
xai: {
|
|
861
|
+
apiKey: xaiApiKey || undefined,
|
|
862
|
+
model: xaiModel || undefined,
|
|
863
|
+
baseUrl: xaiBaseUrl || undefined,
|
|
864
|
+
},
|
|
865
|
+
// Always include Kimi settings
|
|
866
|
+
kimi: {
|
|
867
|
+
apiKey: kimiApiKey || undefined,
|
|
868
|
+
model: kimiModel || undefined,
|
|
869
|
+
baseUrl: kimiBaseUrl || undefined,
|
|
870
|
+
},
|
|
871
|
+
customProviders: Object.keys(sanitizedCustomProviders).length > 0 ? sanitizedCustomProviders : undefined,
|
|
658
872
|
};
|
|
659
873
|
|
|
660
874
|
await window.electronAPI.saveLLMSettings(settingsToSave);
|
|
@@ -672,6 +886,8 @@ export function Settings({ onBack, onSettingsChanged, themeMode, accentColor, on
|
|
|
672
886
|
setTesting(true);
|
|
673
887
|
setTestResult(null);
|
|
674
888
|
|
|
889
|
+
const sanitizedCustomProviders = sanitizeCustomProviders(customProviders) || {};
|
|
890
|
+
|
|
675
891
|
const testConfig = {
|
|
676
892
|
providerType: settings.providerType,
|
|
677
893
|
modelKey: settings.modelKey,
|
|
@@ -699,6 +915,7 @@ export function Settings({ onBack, onSettingsChanged, themeMode, accentColor, on
|
|
|
699
915
|
openrouter: settings.providerType === 'openrouter' ? {
|
|
700
916
|
apiKey: openrouterApiKey || undefined,
|
|
701
917
|
model: openrouterModel || undefined,
|
|
918
|
+
baseUrl: openrouterBaseUrl || undefined,
|
|
702
919
|
} : undefined,
|
|
703
920
|
openai: settings.providerType === 'openai' ? {
|
|
704
921
|
apiKey: openaiAuthMethod === 'api_key' ? (openaiApiKey || undefined) : undefined,
|
|
@@ -706,6 +923,22 @@ export function Settings({ onBack, onSettingsChanged, themeMode, accentColor, on
|
|
|
706
923
|
authMethod: openaiAuthMethod,
|
|
707
924
|
// OAuth tokens are handled by the backend from stored settings
|
|
708
925
|
} : undefined,
|
|
926
|
+
groq: settings.providerType === 'groq' ? {
|
|
927
|
+
apiKey: groqApiKey || undefined,
|
|
928
|
+
model: groqModel || undefined,
|
|
929
|
+
baseUrl: groqBaseUrl || undefined,
|
|
930
|
+
} : undefined,
|
|
931
|
+
xai: settings.providerType === 'xai' ? {
|
|
932
|
+
apiKey: xaiApiKey || undefined,
|
|
933
|
+
model: xaiModel || undefined,
|
|
934
|
+
baseUrl: xaiBaseUrl || undefined,
|
|
935
|
+
} : undefined,
|
|
936
|
+
kimi: settings.providerType === 'kimi' ? {
|
|
937
|
+
apiKey: kimiApiKey || undefined,
|
|
938
|
+
model: kimiModel || undefined,
|
|
939
|
+
baseUrl: kimiBaseUrl || undefined,
|
|
940
|
+
} : undefined,
|
|
941
|
+
customProviders: Object.keys(sanitizedCustomProviders).length > 0 ? sanitizedCustomProviders : undefined,
|
|
709
942
|
};
|
|
710
943
|
|
|
711
944
|
const result = await window.electronAPI.testLLMProvider(testConfig);
|
|
@@ -717,6 +950,10 @@ export function Settings({ onBack, onSettingsChanged, themeMode, accentColor, on
|
|
|
717
950
|
}
|
|
718
951
|
};
|
|
719
952
|
|
|
953
|
+
const resolvedProviderType = resolveCustomProviderId(settings.providerType as LLMProviderType);
|
|
954
|
+
const selectedCustomProvider = CUSTOM_PROVIDER_MAP.get(resolvedProviderType);
|
|
955
|
+
const selectedCustomConfig = selectedCustomProvider ? (customProviders[resolvedProviderType] || {}) : {};
|
|
956
|
+
|
|
720
957
|
return (
|
|
721
958
|
<div className="settings-page">
|
|
722
959
|
<div className="settings-page-header">
|
|
@@ -814,11 +1051,13 @@ export function Settings({ onBack, onSettingsChanged, themeMode, accentColor, on
|
|
|
814
1051
|
<WhatsAppSettings />
|
|
815
1052
|
) : activeTab === 'teams' ? (
|
|
816
1053
|
<TeamsSettings />
|
|
1054
|
+
) : activeTab === 'x' ? (
|
|
1055
|
+
<XSettings />
|
|
817
1056
|
) : activeTab === 'morechannels' ? (
|
|
818
1057
|
<div className="more-channels-panel">
|
|
819
1058
|
<div className="more-channels-header">
|
|
820
1059
|
<h2>More Channels</h2>
|
|
821
|
-
<p className="settings-description">Configure additional messaging platforms
|
|
1060
|
+
<p className="settings-description">Configure additional messaging platforms</p>
|
|
822
1061
|
</div>
|
|
823
1062
|
<div className="more-channels-tabs">
|
|
824
1063
|
{secondaryChannelItems.map(item => (
|
|
@@ -843,7 +1082,33 @@ export function Settings({ onBack, onSettingsChanged, themeMode, accentColor, on
|
|
|
843
1082
|
{activeSecondaryChannel === 'bluebubbles' && <BlueBubblesSettings />}
|
|
844
1083
|
{activeSecondaryChannel === 'email' && <EmailSettings />}
|
|
845
1084
|
{activeSecondaryChannel === 'googlechat' && <GoogleChatSettings />}
|
|
846
|
-
|
|
1085
|
+
</div>
|
|
1086
|
+
</div>
|
|
1087
|
+
) : activeTab === 'integrations' ? (
|
|
1088
|
+
<div className="integrations-panel">
|
|
1089
|
+
<div className="integrations-header">
|
|
1090
|
+
<h2>Integrations</h2>
|
|
1091
|
+
<p className="settings-description">Connect productivity and storage tools for the agent</p>
|
|
1092
|
+
</div>
|
|
1093
|
+
<div className="integrations-tabs">
|
|
1094
|
+
{integrationItems.map(item => (
|
|
1095
|
+
<button
|
|
1096
|
+
key={item.key}
|
|
1097
|
+
className={`integrations-tab ${activeIntegration === item.key ? 'active' : ''}`}
|
|
1098
|
+
onClick={() => setActiveIntegration(item.key)}
|
|
1099
|
+
>
|
|
1100
|
+
{item.icon}
|
|
1101
|
+
<span>{item.label}</span>
|
|
1102
|
+
</button>
|
|
1103
|
+
))}
|
|
1104
|
+
</div>
|
|
1105
|
+
<div className="integrations-content">
|
|
1106
|
+
{activeIntegration === 'notion' && <NotionSettings />}
|
|
1107
|
+
{activeIntegration === 'box' && <BoxSettings />}
|
|
1108
|
+
{activeIntegration === 'onedrive' && <OneDriveSettings />}
|
|
1109
|
+
{activeIntegration === 'googledrive' && <GoogleDriveSettings />}
|
|
1110
|
+
{activeIntegration === 'dropbox' && <DropboxSettings />}
|
|
1111
|
+
{activeIntegration === 'sharepoint' && <SharePointSettings />}
|
|
847
1112
|
</div>
|
|
848
1113
|
</div>
|
|
849
1114
|
) : activeTab === 'search' ? (
|
|
@@ -860,6 +1125,8 @@ export function Settings({ onBack, onSettingsChanged, themeMode, accentColor, on
|
|
|
860
1125
|
<SkillHubBrowser />
|
|
861
1126
|
) : activeTab === 'scheduled' ? (
|
|
862
1127
|
<ScheduledTasksSettings />
|
|
1128
|
+
) : activeTab === 'connectors' ? (
|
|
1129
|
+
<ConnectorsSettings />
|
|
863
1130
|
) : activeTab === 'mcp' ? (
|
|
864
1131
|
<MCPSettings />
|
|
865
1132
|
) : activeTab === 'tools' ? (
|
|
@@ -890,6 +1157,13 @@ export function Settings({ onBack, onSettingsChanged, themeMode, accentColor, on
|
|
|
890
1157
|
const isGemini = provider.type === 'gemini';
|
|
891
1158
|
const isOpenRouter = provider.type === 'openrouter';
|
|
892
1159
|
const isOpenAI = provider.type === 'openai';
|
|
1160
|
+
const isGroq = provider.type === 'groq';
|
|
1161
|
+
const isXAI = provider.type === 'xai';
|
|
1162
|
+
const isKimi = provider.type === 'kimi';
|
|
1163
|
+
const providerType = provider.type as LLMProviderType;
|
|
1164
|
+
const resolvedCustomType = resolveCustomProviderId(providerType);
|
|
1165
|
+
const customEntry = CUSTOM_PROVIDER_MAP.get(resolvedCustomType);
|
|
1166
|
+
const isCustom = !!customEntry;
|
|
893
1167
|
|
|
894
1168
|
return (
|
|
895
1169
|
<label
|
|
@@ -902,7 +1176,20 @@ export function Settings({ onBack, onSettingsChanged, themeMode, accentColor, on
|
|
|
902
1176
|
value={provider.type}
|
|
903
1177
|
checked={settings.providerType === provider.type}
|
|
904
1178
|
onChange={() => {
|
|
905
|
-
setSettings({ ...settings, providerType:
|
|
1179
|
+
setSettings({ ...settings, providerType: providerType });
|
|
1180
|
+
if (customEntry) {
|
|
1181
|
+
setCustomProviders((prev) => {
|
|
1182
|
+
const existing = prev[resolvedCustomType] || {};
|
|
1183
|
+
const updated: CustomProviderConfig = { ...existing };
|
|
1184
|
+
if (!updated.model && customEntry.defaultModel) {
|
|
1185
|
+
updated.model = customEntry.defaultModel;
|
|
1186
|
+
}
|
|
1187
|
+
if (!updated.baseUrl && customEntry.baseUrl) {
|
|
1188
|
+
updated.baseUrl = customEntry.baseUrl;
|
|
1189
|
+
}
|
|
1190
|
+
return { ...prev, [resolvedCustomType]: updated };
|
|
1191
|
+
});
|
|
1192
|
+
}
|
|
906
1193
|
// Load models when selecting provider
|
|
907
1194
|
if (provider.type === 'ollama') {
|
|
908
1195
|
loadOllamaModels();
|
|
@@ -912,6 +1199,12 @@ export function Settings({ onBack, onSettingsChanged, themeMode, accentColor, on
|
|
|
912
1199
|
loadOpenRouterModels();
|
|
913
1200
|
} else if (provider.type === 'openai') {
|
|
914
1201
|
loadOpenAIModels();
|
|
1202
|
+
} else if (provider.type === 'groq') {
|
|
1203
|
+
loadGroqModels();
|
|
1204
|
+
} else if (provider.type === 'xai') {
|
|
1205
|
+
loadXAIModels();
|
|
1206
|
+
} else if (provider.type === 'kimi') {
|
|
1207
|
+
loadKimiModels();
|
|
915
1208
|
}
|
|
916
1209
|
}}
|
|
917
1210
|
/>
|
|
@@ -952,6 +1245,30 @@ export function Settings({ onBack, onSettingsChanged, themeMode, accentColor, on
|
|
|
952
1245
|
{isOpenAI && !provider.configured && (
|
|
953
1246
|
<>Sign in with ChatGPT or enter API key</>
|
|
954
1247
|
)}
|
|
1248
|
+
{isGroq && provider.configured && (
|
|
1249
|
+
<>API key configured</>
|
|
1250
|
+
)}
|
|
1251
|
+
{isGroq && !provider.configured && (
|
|
1252
|
+
<>Enter your Groq API key below</>
|
|
1253
|
+
)}
|
|
1254
|
+
{isXAI && provider.configured && (
|
|
1255
|
+
<>API key configured</>
|
|
1256
|
+
)}
|
|
1257
|
+
{isXAI && !provider.configured && (
|
|
1258
|
+
<>Enter your xAI API key below</>
|
|
1259
|
+
)}
|
|
1260
|
+
{isKimi && provider.configured && (
|
|
1261
|
+
<>API key configured</>
|
|
1262
|
+
)}
|
|
1263
|
+
{isKimi && !provider.configured && (
|
|
1264
|
+
<>Enter your Kimi API key below</>
|
|
1265
|
+
)}
|
|
1266
|
+
{isCustom && provider.configured && (
|
|
1267
|
+
<>API key configured</>
|
|
1268
|
+
)}
|
|
1269
|
+
{isCustom && !provider.configured && (
|
|
1270
|
+
<>{customEntry?.description || `Configure ${provider.name} below`}</>
|
|
1271
|
+
)}
|
|
955
1272
|
{isBedrock && provider.configured && (
|
|
956
1273
|
<>AWS credentials configured</>
|
|
957
1274
|
)}
|
|
@@ -1093,6 +1410,20 @@ export function Settings({ onBack, onSettingsChanged, themeMode, accentColor, on
|
|
|
1093
1410
|
</div>
|
|
1094
1411
|
</div>
|
|
1095
1412
|
|
|
1413
|
+
<div className="settings-section">
|
|
1414
|
+
<h3>Base URL</h3>
|
|
1415
|
+
<p className="settings-description">
|
|
1416
|
+
Optional override for the OpenRouter API endpoint.
|
|
1417
|
+
</p>
|
|
1418
|
+
<input
|
|
1419
|
+
type="text"
|
|
1420
|
+
className="settings-input"
|
|
1421
|
+
placeholder="https://openrouter.ai/api/v1"
|
|
1422
|
+
value={openrouterBaseUrl}
|
|
1423
|
+
onChange={(e) => setOpenrouterBaseUrl(e.target.value)}
|
|
1424
|
+
/>
|
|
1425
|
+
</div>
|
|
1426
|
+
|
|
1096
1427
|
<div className="settings-section">
|
|
1097
1428
|
<h3>Model</h3>
|
|
1098
1429
|
<p className="settings-description">
|
|
@@ -1280,6 +1611,278 @@ export function Settings({ onBack, onSettingsChanged, themeMode, accentColor, on
|
|
|
1280
1611
|
</>
|
|
1281
1612
|
)}
|
|
1282
1613
|
|
|
1614
|
+
{settings.providerType === 'groq' && (
|
|
1615
|
+
<>
|
|
1616
|
+
<div className="settings-section">
|
|
1617
|
+
<h3>Groq API Key</h3>
|
|
1618
|
+
<p className="settings-description">
|
|
1619
|
+
Enter your API key from{' '}
|
|
1620
|
+
<a href="https://console.groq.com/keys" target="_blank" rel="noopener noreferrer">
|
|
1621
|
+
Groq Console
|
|
1622
|
+
</a>
|
|
1623
|
+
</p>
|
|
1624
|
+
<div className="settings-input-group">
|
|
1625
|
+
<input
|
|
1626
|
+
type="password"
|
|
1627
|
+
className="settings-input"
|
|
1628
|
+
placeholder="gsk_..."
|
|
1629
|
+
value={groqApiKey}
|
|
1630
|
+
onChange={(e) => setGroqApiKey(e.target.value)}
|
|
1631
|
+
/>
|
|
1632
|
+
<button
|
|
1633
|
+
className="button-small button-secondary"
|
|
1634
|
+
onClick={() => loadGroqModels(groqApiKey)}
|
|
1635
|
+
disabled={loadingGroqModels}
|
|
1636
|
+
>
|
|
1637
|
+
{loadingGroqModels ? 'Loading...' : 'Refresh Models'}
|
|
1638
|
+
</button>
|
|
1639
|
+
</div>
|
|
1640
|
+
</div>
|
|
1641
|
+
|
|
1642
|
+
<div className="settings-section">
|
|
1643
|
+
<h3>Base URL</h3>
|
|
1644
|
+
<p className="settings-description">
|
|
1645
|
+
Optional override for the Groq API endpoint.
|
|
1646
|
+
</p>
|
|
1647
|
+
<input
|
|
1648
|
+
type="text"
|
|
1649
|
+
className="settings-input"
|
|
1650
|
+
placeholder="https://api.groq.com/openai/v1"
|
|
1651
|
+
value={groqBaseUrl}
|
|
1652
|
+
onChange={(e) => setGroqBaseUrl(e.target.value)}
|
|
1653
|
+
/>
|
|
1654
|
+
</div>
|
|
1655
|
+
|
|
1656
|
+
<div className="settings-section">
|
|
1657
|
+
<h3>Model</h3>
|
|
1658
|
+
<p className="settings-description">
|
|
1659
|
+
Select a Groq model. Enter your API key and click "Refresh Models" to load available models.
|
|
1660
|
+
</p>
|
|
1661
|
+
{groqModels.length > 0 ? (
|
|
1662
|
+
<SearchableSelect
|
|
1663
|
+
options={groqModels.map(model => ({
|
|
1664
|
+
value: model.id,
|
|
1665
|
+
label: model.name,
|
|
1666
|
+
}))}
|
|
1667
|
+
value={groqModel}
|
|
1668
|
+
onChange={setGroqModel}
|
|
1669
|
+
placeholder="Select a model..."
|
|
1670
|
+
/>
|
|
1671
|
+
) : (
|
|
1672
|
+
<input
|
|
1673
|
+
type="text"
|
|
1674
|
+
className="settings-input"
|
|
1675
|
+
placeholder="llama-3.1-8b-instant"
|
|
1676
|
+
value={groqModel}
|
|
1677
|
+
onChange={(e) => setGroqModel(e.target.value)}
|
|
1678
|
+
/>
|
|
1679
|
+
)}
|
|
1680
|
+
</div>
|
|
1681
|
+
</>
|
|
1682
|
+
)}
|
|
1683
|
+
|
|
1684
|
+
{settings.providerType === 'xai' && (
|
|
1685
|
+
<>
|
|
1686
|
+
<div className="settings-section">
|
|
1687
|
+
<h3>xAI API Key</h3>
|
|
1688
|
+
<p className="settings-description">
|
|
1689
|
+
Enter your API key from{' '}
|
|
1690
|
+
<a href="https://console.x.ai/" target="_blank" rel="noopener noreferrer">
|
|
1691
|
+
xAI Console
|
|
1692
|
+
</a>
|
|
1693
|
+
</p>
|
|
1694
|
+
<div className="settings-input-group">
|
|
1695
|
+
<input
|
|
1696
|
+
type="password"
|
|
1697
|
+
className="settings-input"
|
|
1698
|
+
placeholder="xai-..."
|
|
1699
|
+
value={xaiApiKey}
|
|
1700
|
+
onChange={(e) => setXaiApiKey(e.target.value)}
|
|
1701
|
+
/>
|
|
1702
|
+
<button
|
|
1703
|
+
className="button-small button-secondary"
|
|
1704
|
+
onClick={() => loadXAIModels(xaiApiKey)}
|
|
1705
|
+
disabled={loadingXaiModels}
|
|
1706
|
+
>
|
|
1707
|
+
{loadingXaiModels ? 'Loading...' : 'Refresh Models'}
|
|
1708
|
+
</button>
|
|
1709
|
+
</div>
|
|
1710
|
+
</div>
|
|
1711
|
+
|
|
1712
|
+
<div className="settings-section">
|
|
1713
|
+
<h3>Base URL</h3>
|
|
1714
|
+
<p className="settings-description">
|
|
1715
|
+
Optional override for the xAI API endpoint.
|
|
1716
|
+
</p>
|
|
1717
|
+
<input
|
|
1718
|
+
type="text"
|
|
1719
|
+
className="settings-input"
|
|
1720
|
+
placeholder="https://api.x.ai/v1"
|
|
1721
|
+
value={xaiBaseUrl}
|
|
1722
|
+
onChange={(e) => setXaiBaseUrl(e.target.value)}
|
|
1723
|
+
/>
|
|
1724
|
+
</div>
|
|
1725
|
+
|
|
1726
|
+
<div className="settings-section">
|
|
1727
|
+
<h3>Model</h3>
|
|
1728
|
+
<p className="settings-description">
|
|
1729
|
+
Select a Grok model. Enter your API key and click "Refresh Models" to load available models.
|
|
1730
|
+
</p>
|
|
1731
|
+
{xaiModels.length > 0 ? (
|
|
1732
|
+
<SearchableSelect
|
|
1733
|
+
options={xaiModels.map(model => ({
|
|
1734
|
+
value: model.id,
|
|
1735
|
+
label: model.name,
|
|
1736
|
+
}))}
|
|
1737
|
+
value={xaiModel}
|
|
1738
|
+
onChange={setXaiModel}
|
|
1739
|
+
placeholder="Select a model..."
|
|
1740
|
+
/>
|
|
1741
|
+
) : (
|
|
1742
|
+
<input
|
|
1743
|
+
type="text"
|
|
1744
|
+
className="settings-input"
|
|
1745
|
+
placeholder="grok-4-fast-non-reasoning"
|
|
1746
|
+
value={xaiModel}
|
|
1747
|
+
onChange={(e) => setXaiModel(e.target.value)}
|
|
1748
|
+
/>
|
|
1749
|
+
)}
|
|
1750
|
+
</div>
|
|
1751
|
+
</>
|
|
1752
|
+
)}
|
|
1753
|
+
|
|
1754
|
+
{settings.providerType === 'kimi' && (
|
|
1755
|
+
<>
|
|
1756
|
+
<div className="settings-section">
|
|
1757
|
+
<h3>Kimi API Key</h3>
|
|
1758
|
+
<p className="settings-description">
|
|
1759
|
+
Enter your API key from{' '}
|
|
1760
|
+
<a href="https://platform.moonshot.ai/" target="_blank" rel="noopener noreferrer">
|
|
1761
|
+
Moonshot Platform
|
|
1762
|
+
</a>
|
|
1763
|
+
</p>
|
|
1764
|
+
<div className="settings-input-group">
|
|
1765
|
+
<input
|
|
1766
|
+
type="password"
|
|
1767
|
+
className="settings-input"
|
|
1768
|
+
placeholder="sk-..."
|
|
1769
|
+
value={kimiApiKey}
|
|
1770
|
+
onChange={(e) => setKimiApiKey(e.target.value)}
|
|
1771
|
+
/>
|
|
1772
|
+
<button
|
|
1773
|
+
className="button-small button-secondary"
|
|
1774
|
+
onClick={() => loadKimiModels(kimiApiKey)}
|
|
1775
|
+
disabled={loadingKimiModels}
|
|
1776
|
+
>
|
|
1777
|
+
{loadingKimiModels ? 'Loading...' : 'Refresh Models'}
|
|
1778
|
+
</button>
|
|
1779
|
+
</div>
|
|
1780
|
+
</div>
|
|
1781
|
+
|
|
1782
|
+
<div className="settings-section">
|
|
1783
|
+
<h3>Base URL</h3>
|
|
1784
|
+
<p className="settings-description">
|
|
1785
|
+
Optional override for the Kimi API endpoint.
|
|
1786
|
+
</p>
|
|
1787
|
+
<input
|
|
1788
|
+
type="text"
|
|
1789
|
+
className="settings-input"
|
|
1790
|
+
placeholder="https://api.moonshot.ai/v1"
|
|
1791
|
+
value={kimiBaseUrl}
|
|
1792
|
+
onChange={(e) => setKimiBaseUrl(e.target.value)}
|
|
1793
|
+
/>
|
|
1794
|
+
</div>
|
|
1795
|
+
|
|
1796
|
+
<div className="settings-section">
|
|
1797
|
+
<h3>Model</h3>
|
|
1798
|
+
<p className="settings-description">
|
|
1799
|
+
Select a Kimi model. Enter your API key and click "Refresh Models" to load available models.
|
|
1800
|
+
</p>
|
|
1801
|
+
{kimiModels.length > 0 ? (
|
|
1802
|
+
<SearchableSelect
|
|
1803
|
+
options={kimiModels.map(model => ({
|
|
1804
|
+
value: model.id,
|
|
1805
|
+
label: model.name,
|
|
1806
|
+
}))}
|
|
1807
|
+
value={kimiModel}
|
|
1808
|
+
onChange={setKimiModel}
|
|
1809
|
+
placeholder="Select a model..."
|
|
1810
|
+
/>
|
|
1811
|
+
) : (
|
|
1812
|
+
<input
|
|
1813
|
+
type="text"
|
|
1814
|
+
className="settings-input"
|
|
1815
|
+
placeholder="kimi-k2.5"
|
|
1816
|
+
value={kimiModel}
|
|
1817
|
+
onChange={(e) => setKimiModel(e.target.value)}
|
|
1818
|
+
/>
|
|
1819
|
+
)}
|
|
1820
|
+
</div>
|
|
1821
|
+
</>
|
|
1822
|
+
)}
|
|
1823
|
+
|
|
1824
|
+
{selectedCustomProvider && (
|
|
1825
|
+
<>
|
|
1826
|
+
<div className="settings-section">
|
|
1827
|
+
<h3>{selectedCustomProvider.apiKeyLabel}</h3>
|
|
1828
|
+
{selectedCustomProvider.apiKeyUrl ? (
|
|
1829
|
+
<p className="settings-description">
|
|
1830
|
+
Enter your API key from{' '}
|
|
1831
|
+
<a href={selectedCustomProvider.apiKeyUrl} target="_blank" rel="noopener noreferrer">
|
|
1832
|
+
{selectedCustomProvider.name}
|
|
1833
|
+
</a>
|
|
1834
|
+
</p>
|
|
1835
|
+
) : selectedCustomProvider.description ? (
|
|
1836
|
+
<p className="settings-description">
|
|
1837
|
+
{selectedCustomProvider.description}
|
|
1838
|
+
</p>
|
|
1839
|
+
) : null}
|
|
1840
|
+
<input
|
|
1841
|
+
type="password"
|
|
1842
|
+
className="settings-input"
|
|
1843
|
+
placeholder={selectedCustomProvider.apiKeyPlaceholder || 'sk-...'}
|
|
1844
|
+
value={selectedCustomConfig.apiKey || ''}
|
|
1845
|
+
onChange={(e) => updateCustomProvider(resolvedProviderType, { apiKey: e.target.value })}
|
|
1846
|
+
/>
|
|
1847
|
+
{selectedCustomProvider.apiKeyOptional && (
|
|
1848
|
+
<p className="settings-hint">API key is optional for this provider.</p>
|
|
1849
|
+
)}
|
|
1850
|
+
</div>
|
|
1851
|
+
|
|
1852
|
+
{(selectedCustomProvider.requiresBaseUrl || selectedCustomProvider.baseUrl) && (
|
|
1853
|
+
<div className="settings-section">
|
|
1854
|
+
<h3>Base URL</h3>
|
|
1855
|
+
<p className="settings-description">
|
|
1856
|
+
{selectedCustomProvider.requiresBaseUrl
|
|
1857
|
+
? 'Base URL is required for this provider.'
|
|
1858
|
+
: 'Override the default base URL if needed.'}
|
|
1859
|
+
</p>
|
|
1860
|
+
<input
|
|
1861
|
+
type="text"
|
|
1862
|
+
className="settings-input"
|
|
1863
|
+
placeholder={selectedCustomProvider.baseUrl || 'https://...'}
|
|
1864
|
+
value={selectedCustomConfig.baseUrl || ''}
|
|
1865
|
+
onChange={(e) => updateCustomProvider(resolvedProviderType, { baseUrl: e.target.value })}
|
|
1866
|
+
/>
|
|
1867
|
+
</div>
|
|
1868
|
+
)}
|
|
1869
|
+
|
|
1870
|
+
<div className="settings-section">
|
|
1871
|
+
<h3>Model</h3>
|
|
1872
|
+
<p className="settings-description">
|
|
1873
|
+
Enter the model ID to use for {selectedCustomProvider.name}.
|
|
1874
|
+
</p>
|
|
1875
|
+
<input
|
|
1876
|
+
type="text"
|
|
1877
|
+
className="settings-input"
|
|
1878
|
+
placeholder={selectedCustomProvider.defaultModel || 'model-id'}
|
|
1879
|
+
value={selectedCustomConfig.model || ''}
|
|
1880
|
+
onChange={(e) => updateCustomProvider(resolvedProviderType, { model: e.target.value })}
|
|
1881
|
+
/>
|
|
1882
|
+
</div>
|
|
1883
|
+
</>
|
|
1884
|
+
)}
|
|
1885
|
+
|
|
1283
1886
|
{settings.providerType === 'bedrock' && (
|
|
1284
1887
|
<>
|
|
1285
1888
|
<div className="settings-section">
|