cowork-os 0.3.21 → 0.3.25

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.
Files changed (252) hide show
  1. package/README.md +372 -10
  2. package/connectors/README.md +20 -0
  3. package/connectors/asana-mcp/README.md +24 -0
  4. package/connectors/asana-mcp/dist/index.js +427 -0
  5. package/connectors/asana-mcp/package.json +15 -0
  6. package/connectors/asana-mcp/src/index.ts +553 -0
  7. package/connectors/asana-mcp/tsconfig.json +13 -0
  8. package/connectors/hubspot-mcp/README.md +35 -0
  9. package/connectors/hubspot-mcp/dist/index.js +454 -0
  10. package/connectors/hubspot-mcp/package.json +15 -0
  11. package/connectors/hubspot-mcp/src/index.ts +562 -0
  12. package/connectors/hubspot-mcp/tsconfig.json +13 -0
  13. package/connectors/jira-mcp/README.md +49 -0
  14. package/connectors/jira-mcp/dist/index.js +588 -0
  15. package/connectors/jira-mcp/package.json +15 -0
  16. package/connectors/jira-mcp/src/index.ts +711 -0
  17. package/connectors/jira-mcp/tsconfig.json +13 -0
  18. package/connectors/linear-mcp/README.md +22 -0
  19. package/connectors/linear-mcp/dist/index.js +402 -0
  20. package/connectors/linear-mcp/package.json +15 -0
  21. package/connectors/linear-mcp/src/index.ts +522 -0
  22. package/connectors/linear-mcp/tsconfig.json +13 -0
  23. package/connectors/okta-mcp/README.md +24 -0
  24. package/connectors/okta-mcp/dist/index.js +411 -0
  25. package/connectors/okta-mcp/package.json +15 -0
  26. package/connectors/okta-mcp/src/index.ts +520 -0
  27. package/connectors/okta-mcp/tsconfig.json +13 -0
  28. package/connectors/salesforce-mcp/README.md +47 -0
  29. package/connectors/salesforce-mcp/dist/index.js +584 -0
  30. package/connectors/salesforce-mcp/package.json +15 -0
  31. package/connectors/salesforce-mcp/src/index.ts +722 -0
  32. package/connectors/salesforce-mcp/tsconfig.json +13 -0
  33. package/connectors/servicenow-mcp/README.md +26 -0
  34. package/connectors/servicenow-mcp/dist/index.js +400 -0
  35. package/connectors/servicenow-mcp/package.json +15 -0
  36. package/connectors/servicenow-mcp/src/index.ts +500 -0
  37. package/connectors/servicenow-mcp/tsconfig.json +13 -0
  38. package/connectors/templates/mcp-connector/README.md +31 -0
  39. package/connectors/templates/mcp-connector/package.json +15 -0
  40. package/connectors/templates/mcp-connector/src/index.ts +330 -0
  41. package/connectors/templates/mcp-connector/tsconfig.json +13 -0
  42. package/connectors/zendesk-mcp/README.md +40 -0
  43. package/connectors/zendesk-mcp/dist/index.js +431 -0
  44. package/connectors/zendesk-mcp/package.json +15 -0
  45. package/connectors/zendesk-mcp/src/index.ts +543 -0
  46. package/connectors/zendesk-mcp/tsconfig.json +13 -0
  47. package/dist/electron/electron/agent/custom-skill-loader.js +31 -1
  48. package/dist/electron/electron/agent/daemon.js +189 -13
  49. package/dist/electron/electron/agent/executor.js +895 -78
  50. package/dist/electron/electron/agent/llm/anthropic-compatible-provider.js +177 -0
  51. package/dist/electron/electron/agent/llm/azure-openai-provider.js +328 -0
  52. package/dist/electron/electron/agent/llm/bedrock-provider.js +49 -9
  53. package/dist/electron/electron/agent/llm/github-copilot-provider.js +97 -0
  54. package/dist/electron/electron/agent/llm/groq-provider.js +33 -0
  55. package/dist/electron/electron/agent/llm/index.js +13 -1
  56. package/dist/electron/electron/agent/llm/kimi-provider.js +33 -0
  57. package/dist/electron/electron/agent/llm/openai-compatible-provider.js +116 -0
  58. package/dist/electron/electron/agent/llm/openai-compatible.js +111 -0
  59. package/dist/electron/electron/agent/llm/openai-oauth.js +2 -1
  60. package/dist/electron/electron/agent/llm/openrouter-provider.js +1 -1
  61. package/dist/electron/electron/agent/llm/provider-factory.js +350 -4
  62. package/dist/electron/electron/agent/llm/types.js +66 -1
  63. package/dist/electron/electron/agent/llm/xai-provider.js +33 -0
  64. package/dist/electron/electron/agent/search/provider-factory.js +38 -2
  65. package/dist/electron/electron/agent/tools/box-tools.js +231 -0
  66. package/dist/electron/electron/agent/tools/builtin-settings.js +28 -0
  67. package/dist/electron/electron/agent/tools/dropbox-tools.js +237 -0
  68. package/dist/electron/electron/agent/tools/file-tools.js +66 -3
  69. package/dist/electron/electron/agent/tools/google-drive-tools.js +227 -0
  70. package/dist/electron/electron/agent/tools/grep-tools.js +90 -10
  71. package/dist/electron/electron/agent/tools/image-tools.js +11 -1
  72. package/dist/electron/electron/agent/tools/notion-tools.js +312 -0
  73. package/dist/electron/electron/agent/tools/onedrive-tools.js +217 -0
  74. package/dist/electron/electron/agent/tools/registry.js +548 -10
  75. package/dist/electron/electron/agent/tools/search-tools.js +28 -10
  76. package/dist/electron/electron/agent/tools/sharepoint-tools.js +243 -0
  77. package/dist/electron/electron/agent/tools/shell-tools.js +12 -3
  78. package/dist/electron/electron/agent/tools/x-tools.js +1 -1
  79. package/dist/electron/electron/agents/agent-dispatch.js +63 -0
  80. package/dist/electron/electron/database/repositories.js +19 -5
  81. package/dist/electron/electron/database/schema.js +8 -0
  82. package/dist/electron/electron/gateway/channels/whatsapp.js +55 -0
  83. package/dist/electron/electron/gateway/index.js +75 -1
  84. package/dist/electron/electron/gateway/router.js +209 -154
  85. package/dist/electron/electron/ipc/canvas-handlers.js +5 -0
  86. package/dist/electron/electron/ipc/handlers.js +763 -267
  87. package/dist/electron/electron/main.js +63 -0
  88. package/dist/electron/electron/mcp/oauth/connector-oauth.js +333 -0
  89. package/dist/electron/electron/mcp/registry/MCPRegistryManager.js +503 -154
  90. package/dist/electron/electron/memory/MemoryService.js +2 -1
  91. package/dist/electron/electron/preload.js +78 -1
  92. package/dist/electron/electron/settings/appearance-manager.js +18 -1
  93. package/dist/electron/electron/settings/box-manager.js +54 -0
  94. package/dist/electron/electron/settings/dropbox-manager.js +54 -0
  95. package/dist/electron/electron/settings/google-drive-manager.js +54 -0
  96. package/dist/electron/electron/settings/notion-manager.js +56 -0
  97. package/dist/electron/electron/settings/onedrive-manager.js +54 -0
  98. package/dist/electron/electron/settings/sharepoint-manager.js +54 -0
  99. package/dist/electron/electron/utils/box-api.js +153 -0
  100. package/dist/electron/electron/utils/dropbox-api.js +144 -0
  101. package/dist/electron/electron/utils/env-migration.js +19 -0
  102. package/dist/electron/electron/utils/google-drive-api.js +152 -0
  103. package/dist/electron/electron/utils/notion-api.js +103 -0
  104. package/dist/electron/electron/utils/onedrive-api.js +113 -0
  105. package/dist/electron/electron/utils/sharepoint-api.js +109 -0
  106. package/dist/electron/electron/utils/validation.js +98 -3
  107. package/dist/electron/electron/utils/x-cli.js +1 -1
  108. package/dist/electron/shared/channelMessages.js +284 -3
  109. package/dist/electron/shared/llm-provider-catalog.js +198 -0
  110. package/dist/electron/shared/types.js +90 -1
  111. package/package.json +14 -3
  112. package/resources/skills/nano-banana-pro.json +4 -4
  113. package/resources/skills/openai-image-gen.json +3 -3
  114. package/resources/skills/scripts/gen.py +163 -0
  115. package/resources/skills/scripts/generate_image.py +91 -0
  116. package/src/electron/agent/custom-skill-loader.ts +34 -1
  117. package/src/electron/agent/daemon.ts +210 -14
  118. package/src/electron/agent/executor.ts +1124 -85
  119. package/src/electron/agent/llm/anthropic-compatible-provider.ts +214 -0
  120. package/src/electron/agent/llm/azure-openai-provider.ts +388 -0
  121. package/src/electron/agent/llm/bedrock-provider.ts +62 -9
  122. package/src/electron/agent/llm/github-copilot-provider.ts +117 -0
  123. package/src/electron/agent/llm/groq-provider.ts +39 -0
  124. package/src/electron/agent/llm/index.ts +6 -0
  125. package/src/electron/agent/llm/kimi-provider.ts +39 -0
  126. package/src/electron/agent/llm/openai-compatible-provider.ts +153 -0
  127. package/src/electron/agent/llm/openai-compatible.ts +133 -0
  128. package/src/electron/agent/llm/openai-oauth.ts +2 -1
  129. package/src/electron/agent/llm/openrouter-provider.ts +2 -1
  130. package/src/electron/agent/llm/provider-factory.ts +459 -6
  131. package/src/electron/agent/llm/types.ts +95 -1
  132. package/src/electron/agent/llm/xai-provider.ts +39 -0
  133. package/src/electron/agent/search/provider-factory.ts +43 -2
  134. package/src/electron/agent/tools/box-tools.ts +239 -0
  135. package/src/electron/agent/tools/builtin-settings.ts +36 -0
  136. package/src/electron/agent/tools/dropbox-tools.ts +237 -0
  137. package/src/electron/agent/tools/file-tools.ts +66 -3
  138. package/src/electron/agent/tools/gmail-tools.ts +240 -0
  139. package/src/electron/agent/tools/google-calendar-tools.ts +258 -0
  140. package/src/electron/agent/tools/google-drive-tools.ts +228 -0
  141. package/src/electron/agent/tools/grep-tools.ts +97 -12
  142. package/src/electron/agent/tools/image-tools.ts +11 -1
  143. package/src/electron/agent/tools/notion-tools.ts +330 -0
  144. package/src/electron/agent/tools/onedrive-tools.ts +217 -0
  145. package/src/electron/agent/tools/registry.ts +794 -10
  146. package/src/electron/agent/tools/search-tools.ts +29 -11
  147. package/src/electron/agent/tools/sharepoint-tools.ts +247 -0
  148. package/src/electron/agent/tools/shell-tools.ts +11 -3
  149. package/src/electron/agent/tools/x-tools.ts +1 -1
  150. package/src/electron/agents/agent-dispatch.ts +79 -0
  151. package/src/electron/database/SecureSettingsRepository.ts +7 -1
  152. package/src/electron/database/repositories.ts +58 -6
  153. package/src/electron/database/schema.ts +8 -0
  154. package/src/electron/gateway/channels/discord.ts +4 -0
  155. package/src/electron/gateway/channels/google-chat.ts +3 -0
  156. package/src/electron/gateway/channels/line.ts +3 -0
  157. package/src/electron/gateway/channels/matrix-client.ts +15 -0
  158. package/src/electron/gateway/channels/matrix.ts +31 -0
  159. package/src/electron/gateway/channels/mattermost.ts +3 -0
  160. package/src/electron/gateway/channels/signal.ts +3 -0
  161. package/src/electron/gateway/channels/slack.ts +9 -4
  162. package/src/electron/gateway/channels/teams.ts +4 -0
  163. package/src/electron/gateway/channels/telegram.ts +2 -0
  164. package/src/electron/gateway/channels/twitch.ts +2 -0
  165. package/src/electron/gateway/channels/types.ts +8 -0
  166. package/src/electron/gateway/channels/whatsapp.ts +66 -0
  167. package/src/electron/gateway/index.ts +95 -2
  168. package/src/electron/gateway/router.ts +231 -161
  169. package/src/electron/gateway/security.ts +21 -9
  170. package/src/electron/ipc/canvas-handlers.ts +10 -0
  171. package/src/electron/ipc/handlers.ts +848 -292
  172. package/src/electron/main.ts +35 -0
  173. package/src/electron/mcp/oauth/connector-oauth.ts +448 -0
  174. package/src/electron/mcp/registry/MCPRegistryManager.ts +343 -12
  175. package/src/electron/memory/MemoryService.ts +7 -1
  176. package/src/electron/preload.ts +200 -5
  177. package/src/electron/settings/appearance-manager.ts +20 -2
  178. package/src/electron/settings/box-manager.ts +58 -0
  179. package/src/electron/settings/dropbox-manager.ts +58 -0
  180. package/src/electron/settings/google-workspace-manager.ts +59 -0
  181. package/src/electron/settings/notion-manager.ts +60 -0
  182. package/src/electron/settings/onedrive-manager.ts +58 -0
  183. package/src/electron/settings/sharepoint-manager.ts +58 -0
  184. package/src/electron/utils/box-api.ts +184 -0
  185. package/src/electron/utils/dropbox-api.ts +171 -0
  186. package/src/electron/utils/env-migration.ts +22 -0
  187. package/src/electron/utils/gmail-api.ts +121 -0
  188. package/src/electron/utils/google-calendar-api.ts +115 -0
  189. package/src/electron/utils/google-workspace-api.ts +228 -0
  190. package/src/electron/utils/google-workspace-auth.ts +109 -0
  191. package/src/electron/utils/google-workspace-oauth.ts +232 -0
  192. package/src/electron/utils/notion-api.ts +126 -0
  193. package/src/electron/utils/onedrive-api.ts +137 -0
  194. package/src/electron/utils/sharepoint-api.ts +132 -0
  195. package/src/electron/utils/validation.ts +128 -1
  196. package/src/electron/utils/x-cli.ts +1 -1
  197. package/src/renderer/App.tsx +119 -8
  198. package/src/renderer/components/ActivityFeedItem.tsx +34 -17
  199. package/src/renderer/components/AgentWorkingStatePanel.tsx +7 -5
  200. package/src/renderer/components/AppearanceSettings.tsx +37 -2
  201. package/src/renderer/components/BlueBubblesSettings.tsx +18 -7
  202. package/src/renderer/components/BoxSettings.tsx +203 -0
  203. package/src/renderer/components/BrowserView.tsx +101 -0
  204. package/src/renderer/components/BuiltinToolsSettings.tsx +105 -0
  205. package/src/renderer/components/CanvasPreview.tsx +68 -1
  206. package/src/renderer/components/ConnectorEnvModal.tsx +116 -0
  207. package/src/renderer/components/ConnectorSetupModal.tsx +566 -0
  208. package/src/renderer/components/ConnectorsSettings.tsx +397 -0
  209. package/src/renderer/components/ControlPlaneSettings.tsx +2 -0
  210. package/src/renderer/components/DiscordSettings.tsx +18 -7
  211. package/src/renderer/components/DropboxSettings.tsx +202 -0
  212. package/src/renderer/components/EmailSettings.tsx +18 -7
  213. package/src/renderer/components/FileViewer.tsx +21 -13
  214. package/src/renderer/components/GoogleChatSettings.tsx +17 -7
  215. package/src/renderer/components/GoogleWorkspaceSettings.tsx +332 -0
  216. package/src/renderer/components/ImessageSettings.tsx +22 -11
  217. package/src/renderer/components/LineIcons.tsx +376 -0
  218. package/src/renderer/components/LineSettings.tsx +18 -7
  219. package/src/renderer/components/MCPSettings.tsx +56 -0
  220. package/src/renderer/components/MainContent.tsx +740 -76
  221. package/src/renderer/components/MatrixSettings.tsx +18 -7
  222. package/src/renderer/components/MattermostSettings.tsx +18 -7
  223. package/src/renderer/components/NodesSettings.tsx +58 -99
  224. package/src/renderer/components/NotificationPanel.tsx +25 -11
  225. package/src/renderer/components/NotionSettings.tsx +231 -0
  226. package/src/renderer/components/Onboarding/Onboarding.tsx +13 -1
  227. package/src/renderer/components/OnboardingModal.tsx +70 -1
  228. package/src/renderer/components/OneDriveSettings.tsx +212 -0
  229. package/src/renderer/components/RightPanel.tsx +141 -28
  230. package/src/renderer/components/ScheduledTasksSettings.tsx +10 -62
  231. package/src/renderer/components/SearchSettings.tsx +118 -114
  232. package/src/renderer/components/Settings.tsx +1425 -651
  233. package/src/renderer/components/SharePointSettings.tsx +224 -0
  234. package/src/renderer/components/Sidebar.tsx +94 -19
  235. package/src/renderer/components/SignalSettings.tsx +18 -7
  236. package/src/renderer/components/SkillHubBrowser.tsx +144 -185
  237. package/src/renderer/components/SlackSettings.tsx +18 -7
  238. package/src/renderer/components/TaskQuickActions.tsx +11 -6
  239. package/src/renderer/components/TaskTimeline.tsx +58 -26
  240. package/src/renderer/components/TeamsSettings.tsx +18 -7
  241. package/src/renderer/components/TelegramSettings.tsx +18 -7
  242. package/src/renderer/components/ThemeIcon.tsx +16 -0
  243. package/src/renderer/components/TwitchSettings.tsx +18 -7
  244. package/src/renderer/components/VoiceSettings.tsx +30 -74
  245. package/src/renderer/components/WhatsAppSettings.tsx +48 -37
  246. package/src/renderer/components/WorkingStateHistory.tsx +7 -5
  247. package/src/renderer/components/WorkspaceSelector.tsx +42 -13
  248. package/src/renderer/hooks/useOnboardingFlow.ts +21 -0
  249. package/src/renderer/styles/index.css +2333 -209
  250. package/src/shared/channelMessages.ts +367 -4
  251. package/src/shared/llm-provider-catalog.ts +217 -0
  252. package/src/shared/types.ts +251 -2
@@ -0,0 +1,566 @@
1
+ import { useMemo, useState } from 'react';
2
+
3
+ export type ConnectorProvider = 'salesforce' | 'jira' | 'hubspot' | 'zendesk';
4
+
5
+ interface ConnectorSetupModalProps {
6
+ provider: ConnectorProvider;
7
+ serverId: string;
8
+ serverName: string;
9
+ initialEnv?: Record<string, string>;
10
+ onClose: () => void;
11
+ onSaved: () => void;
12
+ }
13
+
14
+ interface JiraResource {
15
+ id: string;
16
+ name: string;
17
+ url: string;
18
+ scopes?: string[];
19
+ }
20
+
21
+ export function ConnectorSetupModal({
22
+ provider,
23
+ serverId,
24
+ serverName,
25
+ initialEnv = {},
26
+ onClose,
27
+ onSaved,
28
+ }: ConnectorSetupModalProps) {
29
+ const [mode, setMode] = useState<'oauth' | 'manual'>('oauth');
30
+ const [saving, setSaving] = useState(false);
31
+ const [oauthBusy, setOauthBusy] = useState(false);
32
+ const [oauthError, setOauthError] = useState<string | null>(null);
33
+
34
+ // Salesforce fields
35
+ const [sfClientId, setSfClientId] = useState(initialEnv.SALESFORCE_CLIENT_ID || '');
36
+ const [sfClientSecret, setSfClientSecret] = useState(initialEnv.SALESFORCE_CLIENT_SECRET || '');
37
+ const [sfLoginUrl, setSfLoginUrl] = useState(initialEnv.SALESFORCE_LOGIN_URL || 'https://login.salesforce.com');
38
+ const [sfScopes, setSfScopes] = useState('api refresh_token');
39
+ const [sfInstanceUrl, setSfInstanceUrl] = useState(initialEnv.SALESFORCE_INSTANCE_URL || '');
40
+ const [sfAccessToken, setSfAccessToken] = useState(initialEnv.SALESFORCE_ACCESS_TOKEN || '');
41
+
42
+ // Jira fields
43
+ const [jiraClientId, setJiraClientId] = useState(initialEnv.JIRA_CLIENT_ID || '');
44
+ const [jiraClientSecret, setJiraClientSecret] = useState(initialEnv.JIRA_CLIENT_SECRET || '');
45
+ const [jiraScopes, setJiraScopes] = useState('read:jira-user read:jira-work write:jira-work offline_access');
46
+ const [jiraBaseUrl, setJiraBaseUrl] = useState(initialEnv.JIRA_BASE_URL || '');
47
+ const [jiraEmail, setJiraEmail] = useState(initialEnv.JIRA_EMAIL || '');
48
+ const [jiraApiToken, setJiraApiToken] = useState(initialEnv.JIRA_API_TOKEN || '');
49
+ const [jiraResources, setJiraResources] = useState<JiraResource[]>([]);
50
+ const [selectedJiraResourceId, setSelectedJiraResourceId] = useState('');
51
+ const [jiraOauthTokens, setJiraOauthTokens] = useState<{ accessToken: string; refreshToken?: string } | null>(null);
52
+
53
+ // HubSpot fields
54
+ const [hubspotClientId, setHubspotClientId] = useState(initialEnv.HUBSPOT_CLIENT_ID || '');
55
+ const [hubspotClientSecret, setHubspotClientSecret] = useState(initialEnv.HUBSPOT_CLIENT_SECRET || '');
56
+ const [hubspotScopes, setHubspotScopes] = useState(
57
+ 'crm.objects.contacts.read crm.objects.contacts.write crm.objects.companies.read crm.objects.companies.write crm.objects.deals.read crm.objects.deals.write'
58
+ );
59
+ const [hubspotAccessToken, setHubspotAccessToken] = useState(initialEnv.HUBSPOT_ACCESS_TOKEN || '');
60
+
61
+ // Zendesk fields
62
+ const [zendeskSubdomain, setZendeskSubdomain] = useState(initialEnv.ZENDESK_SUBDOMAIN || '');
63
+ const [zendeskClientId, setZendeskClientId] = useState(initialEnv.ZENDESK_CLIENT_ID || '');
64
+ const [zendeskClientSecret, setZendeskClientSecret] = useState(initialEnv.ZENDESK_CLIENT_SECRET || '');
65
+ const [zendeskScopes, setZendeskScopes] = useState('read write');
66
+ const [zendeskEmail, setZendeskEmail] = useState(initialEnv.ZENDESK_EMAIL || '');
67
+ const [zendeskApiToken, setZendeskApiToken] = useState(initialEnv.ZENDESK_API_TOKEN || '');
68
+ const [zendeskAccessToken, setZendeskAccessToken] = useState(initialEnv.ZENDESK_ACCESS_TOKEN || '');
69
+
70
+ const isSalesforce = provider === 'salesforce';
71
+ const isJira = provider === 'jira';
72
+ const isHubSpot = provider === 'hubspot';
73
+ const isZendesk = provider === 'zendesk';
74
+
75
+ const selectedJiraResource = useMemo(() => {
76
+ if (!selectedJiraResourceId) return null;
77
+ return jiraResources.find((resource) => resource.id === selectedJiraResourceId) || null;
78
+ }, [jiraResources, selectedJiraResourceId]);
79
+
80
+ const parseScopes = (value: string) =>
81
+ value
82
+ .split(/\s+/)
83
+ .map((s) => s.trim())
84
+ .filter(Boolean);
85
+
86
+ const sanitizeEnv = (env: Record<string, string | undefined>): Record<string, string> => {
87
+ const merged: Record<string, string> = { ...initialEnv };
88
+ Object.entries(env).forEach(([key, value]) => {
89
+ if (value === undefined || value === '') {
90
+ delete merged[key];
91
+ return;
92
+ }
93
+ merged[key] = value;
94
+ });
95
+ return merged;
96
+ };
97
+
98
+ const reconnectServer = async () => {
99
+ try {
100
+ await window.electronAPI.disconnectMCPServer(serverId);
101
+ } catch {
102
+ // ignore
103
+ }
104
+ await window.electronAPI.connectMCPServer(serverId);
105
+ };
106
+
107
+ const saveEnv = async (env: Record<string, string | undefined>) => {
108
+ setSaving(true);
109
+ try {
110
+ const merged = sanitizeEnv(env);
111
+ await window.electronAPI.updateMCPServer(serverId, { env: merged });
112
+ await reconnectServer();
113
+ onSaved();
114
+ onClose();
115
+ } catch (error: any) {
116
+ setOauthError(error.message || 'Failed to save credentials');
117
+ } finally {
118
+ setSaving(false);
119
+ }
120
+ };
121
+
122
+ const handleSalesforceOAuth = async () => {
123
+ setOauthBusy(true);
124
+ setOauthError(null);
125
+ try {
126
+ const result = await window.electronAPI.startConnectorOAuth({
127
+ provider: 'salesforce',
128
+ clientId: sfClientId,
129
+ clientSecret: sfClientSecret,
130
+ scopes: parseScopes(sfScopes),
131
+ loginUrl: sfLoginUrl,
132
+ });
133
+
134
+ await saveEnv({
135
+ SALESFORCE_ACCESS_TOKEN: result.accessToken,
136
+ SALESFORCE_REFRESH_TOKEN: result.refreshToken || '',
137
+ SALESFORCE_INSTANCE_URL: result.instanceUrl || sfInstanceUrl,
138
+ SALESFORCE_CLIENT_ID: sfClientId,
139
+ SALESFORCE_CLIENT_SECRET: sfClientSecret,
140
+ SALESFORCE_LOGIN_URL: sfLoginUrl,
141
+ });
142
+ } catch (error: any) {
143
+ setOauthError(error.message || 'Salesforce OAuth failed');
144
+ } finally {
145
+ setOauthBusy(false);
146
+ }
147
+ };
148
+
149
+ const handleJiraOAuth = async () => {
150
+ setOauthBusy(true);
151
+ setOauthError(null);
152
+ try {
153
+ const result = await window.electronAPI.startConnectorOAuth({
154
+ provider: 'jira',
155
+ clientId: jiraClientId,
156
+ clientSecret: jiraClientSecret,
157
+ scopes: parseScopes(jiraScopes),
158
+ });
159
+
160
+ const resources = result.resources || [];
161
+ setJiraResources(resources);
162
+ if (resources.length === 0) {
163
+ setOauthError('No Jira sites were returned for this account.');
164
+ return;
165
+ }
166
+ if (resources.length === 1) {
167
+ setSelectedJiraResourceId(resources[0].id);
168
+ }
169
+ setJiraOauthTokens({ accessToken: result.accessToken, refreshToken: result.refreshToken });
170
+ } catch (error: any) {
171
+ setOauthError(error.message || 'Jira OAuth failed');
172
+ } finally {
173
+ setOauthBusy(false);
174
+ }
175
+ };
176
+
177
+ const handleHubSpotOAuth = async () => {
178
+ setOauthBusy(true);
179
+ setOauthError(null);
180
+ try {
181
+ const result = await window.electronAPI.startConnectorOAuth({
182
+ provider: 'hubspot',
183
+ clientId: hubspotClientId,
184
+ clientSecret: hubspotClientSecret,
185
+ scopes: parseScopes(hubspotScopes),
186
+ });
187
+
188
+ await saveEnv({
189
+ HUBSPOT_ACCESS_TOKEN: result.accessToken,
190
+ HUBSPOT_REFRESH_TOKEN: result.refreshToken || '',
191
+ HUBSPOT_CLIENT_ID: hubspotClientId,
192
+ HUBSPOT_CLIENT_SECRET: hubspotClientSecret,
193
+ });
194
+ } catch (error: any) {
195
+ setOauthError(error.message || 'HubSpot OAuth failed');
196
+ } finally {
197
+ setOauthBusy(false);
198
+ }
199
+ };
200
+
201
+ const handleZendeskOAuth = async () => {
202
+ setOauthBusy(true);
203
+ setOauthError(null);
204
+ try {
205
+ const result = await window.electronAPI.startConnectorOAuth({
206
+ provider: 'zendesk',
207
+ clientId: zendeskClientId,
208
+ clientSecret: zendeskClientSecret,
209
+ scopes: parseScopes(zendeskScopes),
210
+ subdomain: zendeskSubdomain,
211
+ });
212
+
213
+ await saveEnv({
214
+ ZENDESK_SUBDOMAIN: zendeskSubdomain,
215
+ ZENDESK_ACCESS_TOKEN: result.accessToken,
216
+ ZENDESK_REFRESH_TOKEN: result.refreshToken || '',
217
+ ZENDESK_CLIENT_ID: zendeskClientId,
218
+ ZENDESK_CLIENT_SECRET: zendeskClientSecret,
219
+ ZENDESK_EMAIL: '',
220
+ ZENDESK_API_TOKEN: '',
221
+ });
222
+ } catch (error: any) {
223
+ setOauthError(error.message || 'Zendesk OAuth failed');
224
+ } finally {
225
+ setOauthBusy(false);
226
+ }
227
+ };
228
+
229
+ const handleManualSave = async () => {
230
+ if (isSalesforce) {
231
+ await saveEnv({
232
+ SALESFORCE_INSTANCE_URL: sfInstanceUrl,
233
+ SALESFORCE_ACCESS_TOKEN: sfAccessToken,
234
+ SALESFORCE_REFRESH_TOKEN: '',
235
+ });
236
+ } else if (isJira) {
237
+ await saveEnv({
238
+ JIRA_BASE_URL: jiraBaseUrl,
239
+ JIRA_EMAIL: jiraEmail,
240
+ JIRA_API_TOKEN: jiraApiToken,
241
+ JIRA_ACCESS_TOKEN: '',
242
+ JIRA_REFRESH_TOKEN: '',
243
+ JIRA_CLIENT_ID: '',
244
+ JIRA_CLIENT_SECRET: '',
245
+ });
246
+ } else if (isHubSpot) {
247
+ await saveEnv({
248
+ HUBSPOT_ACCESS_TOKEN: hubspotAccessToken,
249
+ HUBSPOT_REFRESH_TOKEN: '',
250
+ });
251
+ } else if (isZendesk) {
252
+ await saveEnv({
253
+ ZENDESK_SUBDOMAIN: zendeskSubdomain,
254
+ ZENDESK_EMAIL: zendeskEmail,
255
+ ZENDESK_API_TOKEN: zendeskApiToken,
256
+ ZENDESK_ACCESS_TOKEN: zendeskAccessToken,
257
+ ZENDESK_REFRESH_TOKEN: '',
258
+ ZENDESK_CLIENT_ID: '',
259
+ ZENDESK_CLIENT_SECRET: '',
260
+ });
261
+ }
262
+ };
263
+
264
+ const handleJiraOauthSave = async () => {
265
+ if (!jiraOauthTokens || !selectedJiraResource) {
266
+ setOauthError('Select a Jira site before saving.');
267
+ return;
268
+ }
269
+ const cloudBase = `https://api.atlassian.com/ex/jira/${selectedJiraResource.id}`;
270
+ await saveEnv({
271
+ JIRA_BASE_URL: cloudBase,
272
+ JIRA_ACCESS_TOKEN: jiraOauthTokens.accessToken,
273
+ JIRA_REFRESH_TOKEN: jiraOauthTokens.refreshToken || '',
274
+ JIRA_CLIENT_ID: jiraClientId,
275
+ JIRA_CLIENT_SECRET: jiraClientSecret,
276
+ JIRA_EMAIL: '',
277
+ JIRA_API_TOKEN: '',
278
+ });
279
+ };
280
+
281
+ return (
282
+ <div className="mcp-modal-overlay" onClick={onClose}>
283
+ <div className="mcp-modal connector-setup-modal" onClick={(e) => e.stopPropagation()}>
284
+ <div className="mcp-modal-header">
285
+ <div className="registry-details-title">
286
+ <h3>{serverName} Setup</h3>
287
+ </div>
288
+ <button className="mcp-modal-close" onClick={onClose}>
289
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
290
+ <path d="M18 6L6 18M6 6l12 12" />
291
+ </svg>
292
+ </button>
293
+ </div>
294
+ <div className="mcp-modal-content">
295
+ <div className="settings-field">
296
+ <label>Setup Method</label>
297
+ <div className="connector-mode-toggle">
298
+ <button
299
+ className={`button-small ${mode === 'oauth' ? 'button-primary' : 'button-secondary'}`}
300
+ onClick={() => setMode('oauth')}
301
+ >
302
+ OAuth
303
+ </button>
304
+ <button
305
+ className={`button-small ${mode === 'manual' ? 'button-primary' : 'button-secondary'}`}
306
+ onClick={() => setMode('manual')}
307
+ >
308
+ Manual Token
309
+ </button>
310
+ </div>
311
+ </div>
312
+
313
+ {mode === 'oauth' && isSalesforce && (
314
+ <>
315
+ <div className="settings-field">
316
+ <label>Client ID</label>
317
+ <input className="settings-input" value={sfClientId} onChange={(e) => setSfClientId(e.target.value)} />
318
+ </div>
319
+ <div className="settings-field">
320
+ <label>Client Secret</label>
321
+ <input className="settings-input" type="password" value={sfClientSecret} onChange={(e) => setSfClientSecret(e.target.value)} />
322
+ </div>
323
+ <div className="settings-field">
324
+ <label>Login URL</label>
325
+ <input className="settings-input" value={sfLoginUrl} onChange={(e) => setSfLoginUrl(e.target.value)} />
326
+ <p className="settings-hint">Use https://test.salesforce.com for sandbox orgs.</p>
327
+ </div>
328
+ <div className="settings-field">
329
+ <label>Scopes</label>
330
+ <input className="settings-input" value={sfScopes} onChange={(e) => setSfScopes(e.target.value)} />
331
+ </div>
332
+ <p className="settings-hint">Redirect URI: http://127.0.0.1:18765/oauth/callback</p>
333
+ <div className="connector-setup-actions">
334
+ <button
335
+ className="button-primary"
336
+ onClick={handleSalesforceOAuth}
337
+ disabled={oauthBusy || !sfClientId || !sfClientSecret}
338
+ >
339
+ {oauthBusy ? 'Authorizing...' : 'Authorize Salesforce'}
340
+ </button>
341
+ </div>
342
+ </>
343
+ )}
344
+
345
+ {mode === 'oauth' && isJira && (
346
+ <>
347
+ <div className="settings-field">
348
+ <label>Client ID</label>
349
+ <input className="settings-input" value={jiraClientId} onChange={(e) => setJiraClientId(e.target.value)} />
350
+ </div>
351
+ <div className="settings-field">
352
+ <label>Client Secret</label>
353
+ <input className="settings-input" type="password" value={jiraClientSecret} onChange={(e) => setJiraClientSecret(e.target.value)} />
354
+ </div>
355
+ <div className="settings-field">
356
+ <label>Scopes</label>
357
+ <input className="settings-input" value={jiraScopes} onChange={(e) => setJiraScopes(e.target.value)} />
358
+ </div>
359
+ <p className="settings-hint">Redirect URI: http://127.0.0.1:18765/oauth/callback</p>
360
+ <div className="connector-setup-actions">
361
+ <button
362
+ className="button-primary"
363
+ onClick={handleJiraOAuth}
364
+ disabled={oauthBusy || !jiraClientId || !jiraClientSecret}
365
+ >
366
+ {oauthBusy ? 'Authorizing...' : 'Authorize Jira'}
367
+ </button>
368
+ </div>
369
+
370
+ {jiraResources.length > 0 && (
371
+ <div className="settings-field">
372
+ <label>Select Jira Site</label>
373
+ <select
374
+ className="settings-input"
375
+ value={selectedJiraResourceId}
376
+ onChange={(e) => setSelectedJiraResourceId(e.target.value)}
377
+ >
378
+ <option value="">Choose a site</option>
379
+ {jiraResources.map((resource) => (
380
+ <option key={resource.id} value={resource.id}>
381
+ {resource.name} ({resource.url})
382
+ </option>
383
+ ))}
384
+ </select>
385
+ </div>
386
+ )}
387
+
388
+ {jiraOauthTokens && (
389
+ <div className="connector-setup-actions">
390
+ <button
391
+ className="button-primary"
392
+ onClick={handleJiraOauthSave}
393
+ disabled={!selectedJiraResourceId || saving}
394
+ >
395
+ {saving ? 'Saving...' : 'Save Jira Connection'}
396
+ </button>
397
+ </div>
398
+ )}
399
+ </>
400
+ )}
401
+
402
+ {mode === 'oauth' && isHubSpot && (
403
+ <>
404
+ <div className="settings-field">
405
+ <label>Client ID</label>
406
+ <input className="settings-input" value={hubspotClientId} onChange={(e) => setHubspotClientId(e.target.value)} />
407
+ </div>
408
+ <div className="settings-field">
409
+ <label>Client Secret</label>
410
+ <input className="settings-input" type="password" value={hubspotClientSecret} onChange={(e) => setHubspotClientSecret(e.target.value)} />
411
+ </div>
412
+ <div className="settings-field">
413
+ <label>Scopes</label>
414
+ <input className="settings-input" value={hubspotScopes} onChange={(e) => setHubspotScopes(e.target.value)} />
415
+ </div>
416
+ <p className="settings-hint">Redirect URI: http://127.0.0.1:18765/oauth/callback</p>
417
+ <div className="connector-setup-actions">
418
+ <button
419
+ className="button-primary"
420
+ onClick={handleHubSpotOAuth}
421
+ disabled={oauthBusy || !hubspotClientId || !hubspotClientSecret}
422
+ >
423
+ {oauthBusy ? 'Authorizing...' : 'Authorize HubSpot'}
424
+ </button>
425
+ </div>
426
+ </>
427
+ )}
428
+
429
+ {mode === 'oauth' && isZendesk && (
430
+ <>
431
+ <div className="settings-field">
432
+ <label>Subdomain</label>
433
+ <input className="settings-input" value={zendeskSubdomain} onChange={(e) => setZendeskSubdomain(e.target.value)} placeholder="your-company" />
434
+ </div>
435
+ <div className="settings-field">
436
+ <label>Client ID</label>
437
+ <input className="settings-input" value={zendeskClientId} onChange={(e) => setZendeskClientId(e.target.value)} />
438
+ </div>
439
+ <div className="settings-field">
440
+ <label>Client Secret</label>
441
+ <input className="settings-input" type="password" value={zendeskClientSecret} onChange={(e) => setZendeskClientSecret(e.target.value)} />
442
+ </div>
443
+ <div className="settings-field">
444
+ <label>Scopes</label>
445
+ <input className="settings-input" value={zendeskScopes} onChange={(e) => setZendeskScopes(e.target.value)} />
446
+ </div>
447
+ <p className="settings-hint">Redirect URI: http://127.0.0.1:18765/oauth/callback</p>
448
+ <div className="connector-setup-actions">
449
+ <button
450
+ className="button-primary"
451
+ onClick={handleZendeskOAuth}
452
+ disabled={oauthBusy || !zendeskClientId || !zendeskClientSecret || !zendeskSubdomain}
453
+ >
454
+ {oauthBusy ? 'Authorizing...' : 'Authorize Zendesk'}
455
+ </button>
456
+ </div>
457
+ </>
458
+ )}
459
+
460
+ {mode === 'manual' && isSalesforce && (
461
+ <>
462
+ <div className="settings-field">
463
+ <label>Instance URL</label>
464
+ <input className="settings-input" value={sfInstanceUrl} onChange={(e) => setSfInstanceUrl(e.target.value)} />
465
+ </div>
466
+ <div className="settings-field">
467
+ <label>Access Token</label>
468
+ <textarea className="settings-textarea" rows={3} value={sfAccessToken} onChange={(e) => setSfAccessToken(e.target.value)} />
469
+ </div>
470
+ <div className="connector-setup-actions">
471
+ <button
472
+ className="button-primary"
473
+ onClick={handleManualSave}
474
+ disabled={!sfInstanceUrl || !sfAccessToken || saving}
475
+ >
476
+ {saving ? 'Saving...' : 'Save Salesforce Credentials'}
477
+ </button>
478
+ </div>
479
+ </>
480
+ )}
481
+
482
+ {mode === 'manual' && isJira && (
483
+ <>
484
+ <div className="settings-field">
485
+ <label>Base URL</label>
486
+ <input className="settings-input" value={jiraBaseUrl} onChange={(e) => setJiraBaseUrl(e.target.value)} />
487
+ </div>
488
+ <div className="settings-field">
489
+ <label>Email</label>
490
+ <input className="settings-input" value={jiraEmail} onChange={(e) => setJiraEmail(e.target.value)} />
491
+ </div>
492
+ <div className="settings-field">
493
+ <label>API Token</label>
494
+ <textarea className="settings-textarea" rows={3} value={jiraApiToken} onChange={(e) => setJiraApiToken(e.target.value)} />
495
+ </div>
496
+ <div className="connector-setup-actions">
497
+ <button
498
+ className="button-primary"
499
+ onClick={handleManualSave}
500
+ disabled={!jiraBaseUrl || !jiraEmail || !jiraApiToken || saving}
501
+ >
502
+ {saving ? 'Saving...' : 'Save Jira Credentials'}
503
+ </button>
504
+ </div>
505
+ </>
506
+ )}
507
+
508
+ {mode === 'manual' && isHubSpot && (
509
+ <>
510
+ <div className="settings-field">
511
+ <label>Access Token</label>
512
+ <textarea className="settings-textarea" rows={3} value={hubspotAccessToken} onChange={(e) => setHubspotAccessToken(e.target.value)} />
513
+ </div>
514
+ <div className="connector-setup-actions">
515
+ <button
516
+ className="button-primary"
517
+ onClick={handleManualSave}
518
+ disabled={!hubspotAccessToken || saving}
519
+ >
520
+ {saving ? 'Saving...' : 'Save HubSpot Credentials'}
521
+ </button>
522
+ </div>
523
+ </>
524
+ )}
525
+
526
+ {mode === 'manual' && isZendesk && (
527
+ <>
528
+ <div className="settings-field">
529
+ <label>Subdomain</label>
530
+ <input className="settings-input" value={zendeskSubdomain} onChange={(e) => setZendeskSubdomain(e.target.value)} placeholder="your-company" />
531
+ </div>
532
+ <div className="settings-field">
533
+ <label>Email</label>
534
+ <input className="settings-input" value={zendeskEmail} onChange={(e) => setZendeskEmail(e.target.value)} />
535
+ </div>
536
+ <div className="settings-field">
537
+ <label>API Token</label>
538
+ <textarea className="settings-textarea" rows={3} value={zendeskApiToken} onChange={(e) => setZendeskApiToken(e.target.value)} />
539
+ </div>
540
+ <div className="settings-field">
541
+ <label>Access Token (optional)</label>
542
+ <textarea className="settings-textarea" rows={3} value={zendeskAccessToken} onChange={(e) => setZendeskAccessToken(e.target.value)} />
543
+ </div>
544
+ <div className="connector-setup-actions">
545
+ <button
546
+ className="button-primary"
547
+ onClick={handleManualSave}
548
+ disabled={!zendeskSubdomain || !zendeskEmail || !zendeskApiToken || saving}
549
+ >
550
+ {saving ? 'Saving...' : 'Save Zendesk Credentials'}
551
+ </button>
552
+ </div>
553
+ </>
554
+ )}
555
+
556
+ {oauthError && (
557
+ <div className="mcp-server-error">
558
+ <span className="mcp-error-icon">⚠</span>
559
+ {oauthError}
560
+ </div>
561
+ )}
562
+ </div>
563
+ </div>
564
+ </div>
565
+ );
566
+ }