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,60 @@
1
+ /**
2
+ * Notion Settings Manager
3
+ *
4
+ * Stores Notion integration settings in encrypted database.
5
+ */
6
+
7
+ import { SecureSettingsRepository } from '../database/SecureSettingsRepository';
8
+ import { NotionSettingsData } from '../../shared/types';
9
+ import { DEFAULT_NOTION_VERSION } from '../utils/notion-api';
10
+
11
+ const DEFAULT_SETTINGS: NotionSettingsData = {
12
+ enabled: false,
13
+ notionVersion: DEFAULT_NOTION_VERSION,
14
+ timeoutMs: 20000,
15
+ };
16
+
17
+ export class NotionSettingsManager {
18
+ private static cachedSettings: NotionSettingsData | null = null;
19
+
20
+ static loadSettings(): NotionSettingsData {
21
+ if (this.cachedSettings) {
22
+ return this.cachedSettings;
23
+ }
24
+
25
+ let settings: NotionSettingsData = { ...DEFAULT_SETTINGS };
26
+
27
+ try {
28
+ if (SecureSettingsRepository.isInitialized()) {
29
+ const repository = SecureSettingsRepository.getInstance();
30
+ const stored = repository.load<NotionSettingsData>('notion');
31
+ if (stored) {
32
+ settings = { ...DEFAULT_SETTINGS, ...stored };
33
+ }
34
+ }
35
+ } catch (error) {
36
+ console.error('[NotionSettingsManager] Failed to load settings:', error);
37
+ }
38
+
39
+ this.cachedSettings = settings;
40
+ return settings;
41
+ }
42
+
43
+ static saveSettings(settings: NotionSettingsData): void {
44
+ try {
45
+ if (!SecureSettingsRepository.isInitialized()) {
46
+ throw new Error('SecureSettingsRepository not initialized');
47
+ }
48
+ const repository = SecureSettingsRepository.getInstance();
49
+ repository.save('notion', settings);
50
+ this.cachedSettings = settings;
51
+ console.log('[NotionSettingsManager] Settings saved');
52
+ } catch (error) {
53
+ console.error('[NotionSettingsManager] Failed to save settings:', error);
54
+ }
55
+ }
56
+
57
+ static clearCache(): void {
58
+ this.cachedSettings = null;
59
+ }
60
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * OneDrive Settings Manager
3
+ *
4
+ * Stores OneDrive integration settings in encrypted database.
5
+ */
6
+
7
+ import { SecureSettingsRepository } from '../database/SecureSettingsRepository';
8
+ import { OneDriveSettingsData } from '../../shared/types';
9
+
10
+ const DEFAULT_SETTINGS: OneDriveSettingsData = {
11
+ enabled: false,
12
+ timeoutMs: 20000,
13
+ };
14
+
15
+ export class OneDriveSettingsManager {
16
+ private static cachedSettings: OneDriveSettingsData | null = null;
17
+
18
+ static loadSettings(): OneDriveSettingsData {
19
+ if (this.cachedSettings) {
20
+ return this.cachedSettings;
21
+ }
22
+
23
+ let settings: OneDriveSettingsData = { ...DEFAULT_SETTINGS };
24
+
25
+ try {
26
+ if (SecureSettingsRepository.isInitialized()) {
27
+ const repository = SecureSettingsRepository.getInstance();
28
+ const stored = repository.load<OneDriveSettingsData>('onedrive');
29
+ if (stored) {
30
+ settings = { ...DEFAULT_SETTINGS, ...stored };
31
+ }
32
+ }
33
+ } catch (error) {
34
+ console.error('[OneDriveSettingsManager] Failed to load settings:', error);
35
+ }
36
+
37
+ this.cachedSettings = settings;
38
+ return settings;
39
+ }
40
+
41
+ static saveSettings(settings: OneDriveSettingsData): void {
42
+ try {
43
+ if (!SecureSettingsRepository.isInitialized()) {
44
+ throw new Error('SecureSettingsRepository not initialized');
45
+ }
46
+ const repository = SecureSettingsRepository.getInstance();
47
+ repository.save('onedrive', settings);
48
+ this.cachedSettings = settings;
49
+ console.log('[OneDriveSettingsManager] Settings saved');
50
+ } catch (error) {
51
+ console.error('[OneDriveSettingsManager] Failed to save settings:', error);
52
+ }
53
+ }
54
+
55
+ static clearCache(): void {
56
+ this.cachedSettings = null;
57
+ }
58
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * SharePoint Settings Manager
3
+ *
4
+ * Stores SharePoint integration settings in encrypted database.
5
+ */
6
+
7
+ import { SecureSettingsRepository } from '../database/SecureSettingsRepository';
8
+ import { SharePointSettingsData } from '../../shared/types';
9
+
10
+ const DEFAULT_SETTINGS: SharePointSettingsData = {
11
+ enabled: false,
12
+ timeoutMs: 20000,
13
+ };
14
+
15
+ export class SharePointSettingsManager {
16
+ private static cachedSettings: SharePointSettingsData | null = null;
17
+
18
+ static loadSettings(): SharePointSettingsData {
19
+ if (this.cachedSettings) {
20
+ return this.cachedSettings;
21
+ }
22
+
23
+ let settings: SharePointSettingsData = { ...DEFAULT_SETTINGS };
24
+
25
+ try {
26
+ if (SecureSettingsRepository.isInitialized()) {
27
+ const repository = SecureSettingsRepository.getInstance();
28
+ const stored = repository.load<SharePointSettingsData>('sharepoint');
29
+ if (stored) {
30
+ settings = { ...DEFAULT_SETTINGS, ...stored };
31
+ }
32
+ }
33
+ } catch (error) {
34
+ console.error('[SharePointSettingsManager] Failed to load settings:', error);
35
+ }
36
+
37
+ this.cachedSettings = settings;
38
+ return settings;
39
+ }
40
+
41
+ static saveSettings(settings: SharePointSettingsData): void {
42
+ try {
43
+ if (!SecureSettingsRepository.isInitialized()) {
44
+ throw new Error('SecureSettingsRepository not initialized');
45
+ }
46
+ const repository = SecureSettingsRepository.getInstance();
47
+ repository.save('sharepoint', settings);
48
+ this.cachedSettings = settings;
49
+ console.log('[SharePointSettingsManager] Settings saved');
50
+ } catch (error) {
51
+ console.error('[SharePointSettingsManager] Failed to save settings:', error);
52
+ }
53
+ }
54
+
55
+ static clearCache(): void {
56
+ this.cachedSettings = null;
57
+ }
58
+ }
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Box API helpers
3
+ */
4
+
5
+ import { BoxConnectionTestResult, BoxSettingsData } from '../../shared/types';
6
+
7
+ export const BOX_API_BASE = 'https://api.box.com/2.0';
8
+ export const BOX_UPLOAD_BASE = 'https://upload.box.com/api/2.0';
9
+ const DEFAULT_TIMEOUT_MS = 20000;
10
+
11
+ function parseJsonSafe(text: string): any | undefined {
12
+ const trimmed = text.trim();
13
+ if (!trimmed) return undefined;
14
+ try {
15
+ return JSON.parse(trimmed);
16
+ } catch {
17
+ return undefined;
18
+ }
19
+ }
20
+
21
+ function formatBoxError(status: number, data: any, fallback?: string): string {
22
+ const message =
23
+ data?.message ||
24
+ data?.error?.message ||
25
+ data?.error_description ||
26
+ fallback ||
27
+ 'Box API error';
28
+ return `Box API error ${status}: ${message}`;
29
+ }
30
+
31
+ export interface BoxRequestOptions {
32
+ method: 'GET' | 'POST' | 'PUT' | 'DELETE';
33
+ path: string;
34
+ query?: Record<string, string | number | boolean | undefined>;
35
+ body?: Record<string, any>;
36
+ timeoutMs?: number;
37
+ }
38
+
39
+ export interface BoxRequestResult {
40
+ status: number;
41
+ data?: any;
42
+ raw?: string;
43
+ }
44
+
45
+ export async function boxRequest(
46
+ settings: BoxSettingsData,
47
+ options: BoxRequestOptions
48
+ ): Promise<BoxRequestResult> {
49
+ if (!settings.accessToken) {
50
+ throw new Error('Box access token not configured. Add it in Settings > Integrations > Box.');
51
+ }
52
+
53
+ const params = new URLSearchParams();
54
+ if (options.query) {
55
+ for (const [key, value] of Object.entries(options.query)) {
56
+ if (value === undefined || value === null) continue;
57
+ params.set(key, String(value));
58
+ }
59
+ }
60
+ const queryString = params.toString();
61
+ const url = `${BOX_API_BASE}${options.path}${queryString ? `?${queryString}` : ''}`;
62
+
63
+ const headers: Record<string, string> = {
64
+ Authorization: `Bearer ${settings.accessToken}`,
65
+ };
66
+
67
+ if (options.method !== 'GET' && options.method !== 'DELETE') {
68
+ headers['Content-Type'] = 'application/json';
69
+ }
70
+
71
+ const timeoutMs = options.timeoutMs ?? settings.timeoutMs ?? DEFAULT_TIMEOUT_MS;
72
+ const controller = new AbortController();
73
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
74
+
75
+ try {
76
+ const response = await fetch(url, {
77
+ method: options.method,
78
+ headers,
79
+ body: options.body ? JSON.stringify(options.body) : undefined,
80
+ signal: controller.signal,
81
+ });
82
+
83
+ const rawText = typeof response.text === 'function' ? await response.text() : '';
84
+ const data = rawText ? parseJsonSafe(rawText) : undefined;
85
+
86
+ if (!response.ok) {
87
+ throw new Error(formatBoxError(response.status, data, response.statusText));
88
+ }
89
+
90
+ return {
91
+ status: response.status,
92
+ data: data ?? undefined,
93
+ raw: rawText || undefined,
94
+ };
95
+ } catch (error: any) {
96
+ if (error?.name === 'AbortError') {
97
+ throw new Error('Box API request timed out');
98
+ }
99
+ throw error;
100
+ } finally {
101
+ clearTimeout(timeout);
102
+ }
103
+ }
104
+
105
+ export async function boxUploadFile(
106
+ settings: BoxSettingsData,
107
+ opts: { fileName: string; parentId: string; data: Uint8Array; timeoutMs?: number }
108
+ ): Promise<BoxRequestResult> {
109
+ if (!settings.accessToken) {
110
+ throw new Error('Box access token not configured. Add it in Settings > Integrations > Box.');
111
+ }
112
+
113
+ if (typeof FormData === 'undefined') {
114
+ throw new Error('FormData not available in this environment');
115
+ }
116
+
117
+ const form = new FormData();
118
+ form.append('attributes', JSON.stringify({ name: opts.fileName, parent: { id: opts.parentId } }));
119
+ // Create a copy with a regular ArrayBuffer to satisfy BlobPart type requirements
120
+ const fileData = new Uint8Array(opts.data);
121
+ form.append('file', new Blob([fileData]), opts.fileName);
122
+
123
+ const url = `${BOX_UPLOAD_BASE}/files/content`;
124
+ const headers: Record<string, string> = {
125
+ Authorization: `Bearer ${settings.accessToken}`,
126
+ };
127
+
128
+ const timeoutMs = opts.timeoutMs ?? settings.timeoutMs ?? DEFAULT_TIMEOUT_MS;
129
+ const controller = new AbortController();
130
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
131
+
132
+ try {
133
+ const response = await fetch(url, {
134
+ method: 'POST',
135
+ headers,
136
+ body: form,
137
+ signal: controller.signal,
138
+ });
139
+
140
+ const rawText = typeof response.text === 'function' ? await response.text() : '';
141
+ const data = rawText ? parseJsonSafe(rawText) : undefined;
142
+
143
+ if (!response.ok) {
144
+ throw new Error(formatBoxError(response.status, data, response.statusText));
145
+ }
146
+
147
+ return {
148
+ status: response.status,
149
+ data: data ?? undefined,
150
+ raw: rawText || undefined,
151
+ };
152
+ } catch (error: any) {
153
+ if (error?.name === 'AbortError') {
154
+ throw new Error('Box upload request timed out');
155
+ }
156
+ throw error;
157
+ } finally {
158
+ clearTimeout(timeout);
159
+ }
160
+ }
161
+
162
+ function extractUserInfo(data: any): { name?: string; userId?: string } {
163
+ if (!data || typeof data !== 'object') return {};
164
+ const name = data.name || data.login || undefined;
165
+ const userId = data.id || data.user_id || undefined;
166
+ return { name, userId };
167
+ }
168
+
169
+ export async function testBoxConnection(settings: BoxSettingsData): Promise<BoxConnectionTestResult> {
170
+ try {
171
+ const result = await boxRequest(settings, { method: 'GET', path: '/users/me' });
172
+ const extracted = extractUserInfo(result.data);
173
+ return {
174
+ success: true,
175
+ name: extracted.name,
176
+ userId: extracted.userId,
177
+ };
178
+ } catch (error: any) {
179
+ return {
180
+ success: false,
181
+ error: error?.message || 'Failed to connect to Box',
182
+ };
183
+ }
184
+ }
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Dropbox API helpers
3
+ */
4
+
5
+ import { DropboxConnectionTestResult, DropboxSettingsData } from '../../shared/types';
6
+
7
+ export const DROPBOX_API_BASE = 'https://api.dropboxapi.com/2';
8
+ export const DROPBOX_CONTENT_BASE = 'https://content.dropboxapi.com/2';
9
+ const DEFAULT_TIMEOUT_MS = 20000;
10
+
11
+ function parseJsonSafe(text: string): any | undefined {
12
+ const trimmed = text.trim();
13
+ if (!trimmed) return undefined;
14
+ try {
15
+ return JSON.parse(trimmed);
16
+ } catch {
17
+ return undefined;
18
+ }
19
+ }
20
+
21
+ function formatDropboxError(status: number, data: any, fallback?: string): string {
22
+ const message =
23
+ data?.error_summary ||
24
+ data?.error?.summary ||
25
+ data?.message ||
26
+ fallback ||
27
+ 'Dropbox API error';
28
+ return `Dropbox API error ${status}: ${message}`;
29
+ }
30
+
31
+ export interface DropboxRequestOptions {
32
+ method: 'POST';
33
+ path: string;
34
+ body?: Record<string, any>;
35
+ timeoutMs?: number;
36
+ }
37
+
38
+ export interface DropboxRequestResult {
39
+ status: number;
40
+ data?: any;
41
+ raw?: string;
42
+ }
43
+
44
+ export async function dropboxRequest(
45
+ settings: DropboxSettingsData,
46
+ options: DropboxRequestOptions
47
+ ): Promise<DropboxRequestResult> {
48
+ if (!settings.accessToken) {
49
+ throw new Error('Dropbox access token not configured. Add it in Settings > Integrations > Dropbox.');
50
+ }
51
+
52
+ const url = `${DROPBOX_API_BASE}${options.path}`;
53
+ const headers: Record<string, string> = {
54
+ Authorization: `Bearer ${settings.accessToken}`,
55
+ 'Content-Type': 'application/json',
56
+ };
57
+
58
+ const timeoutMs = options.timeoutMs ?? settings.timeoutMs ?? DEFAULT_TIMEOUT_MS;
59
+ const controller = new AbortController();
60
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
61
+
62
+ try {
63
+ const response = await fetch(url, {
64
+ method: options.method,
65
+ headers,
66
+ body: JSON.stringify(options.body || {}),
67
+ signal: controller.signal,
68
+ });
69
+
70
+ const rawText = typeof response.text === 'function' ? await response.text() : '';
71
+ const data = rawText ? parseJsonSafe(rawText) : undefined;
72
+
73
+ if (!response.ok) {
74
+ throw new Error(formatDropboxError(response.status, data, response.statusText));
75
+ }
76
+
77
+ return {
78
+ status: response.status,
79
+ data: data ?? undefined,
80
+ raw: rawText || undefined,
81
+ };
82
+ } catch (error: any) {
83
+ if (error?.name === 'AbortError') {
84
+ throw new Error('Dropbox API request timed out');
85
+ }
86
+ throw error;
87
+ } finally {
88
+ clearTimeout(timeout);
89
+ }
90
+ }
91
+
92
+ export async function dropboxContentUpload(
93
+ settings: DropboxSettingsData,
94
+ opts: { path: string; data: Uint8Array; timeoutMs?: number }
95
+ ): Promise<DropboxRequestResult> {
96
+ if (!settings.accessToken) {
97
+ throw new Error('Dropbox access token not configured. Add it in Settings > Integrations > Dropbox.');
98
+ }
99
+
100
+ const url = `${DROPBOX_CONTENT_BASE}/files/upload`;
101
+ const headers: Record<string, string> = {
102
+ Authorization: `Bearer ${settings.accessToken}`,
103
+ 'Content-Type': 'application/octet-stream',
104
+ 'Dropbox-API-Arg': JSON.stringify({
105
+ path: opts.path,
106
+ mode: 'add',
107
+ autorename: true,
108
+ mute: false,
109
+ strict_conflict: false,
110
+ }),
111
+ };
112
+
113
+ const timeoutMs = opts.timeoutMs ?? settings.timeoutMs ?? DEFAULT_TIMEOUT_MS;
114
+ const controller = new AbortController();
115
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
116
+
117
+ try {
118
+ const response = await fetch(url, {
119
+ method: 'POST',
120
+ headers,
121
+ body: opts.data,
122
+ signal: controller.signal,
123
+ });
124
+
125
+ const rawText = typeof response.text === 'function' ? await response.text() : '';
126
+ const data = rawText ? parseJsonSafe(rawText) : undefined;
127
+
128
+ if (!response.ok) {
129
+ throw new Error(formatDropboxError(response.status, data, response.statusText));
130
+ }
131
+
132
+ return {
133
+ status: response.status,
134
+ data: data ?? undefined,
135
+ raw: rawText || undefined,
136
+ };
137
+ } catch (error: any) {
138
+ if (error?.name === 'AbortError') {
139
+ throw new Error('Dropbox upload request timed out');
140
+ }
141
+ throw error;
142
+ } finally {
143
+ clearTimeout(timeout);
144
+ }
145
+ }
146
+
147
+ function extractAccountInfo(data: any): { name?: string; userId?: string; email?: string } {
148
+ if (!data || typeof data !== 'object') return {};
149
+ const name = data?.name?.display_name || data?.name?.abbreviated_name || undefined;
150
+ const userId = data?.account_id || data?.id || undefined;
151
+ const email = data?.email || undefined;
152
+ return { name, userId, email };
153
+ }
154
+
155
+ export async function testDropboxConnection(settings: DropboxSettingsData): Promise<DropboxConnectionTestResult> {
156
+ try {
157
+ const result = await dropboxRequest(settings, { method: 'POST', path: '/users/get_current_account' });
158
+ const extracted = extractAccountInfo(result.data);
159
+ return {
160
+ success: true,
161
+ name: extracted.name,
162
+ userId: extracted.userId,
163
+ email: extracted.email,
164
+ };
165
+ } catch (error: any) {
166
+ return {
167
+ success: false,
168
+ error: error?.message || 'Failed to connect to Dropbox',
169
+ };
170
+ }
171
+ }
@@ -137,6 +137,28 @@ export async function migrateEnvToSettings(): Promise<MigrationResult> {
137
137
  llmChanged = true;
138
138
  }
139
139
 
140
+ // Migrate Groq API key
141
+ if (env.GROQ_API_KEY && !llmSettings.groq?.apiKey) {
142
+ llmSettings.groq = { ...llmSettings.groq, apiKey: env.GROQ_API_KEY };
143
+ migratedKeys.push('Groq API Key');
144
+ llmChanged = true;
145
+ }
146
+
147
+ // Migrate xAI API key
148
+ if (env.XAI_API_KEY && !llmSettings.xai?.apiKey) {
149
+ llmSettings.xai = { ...llmSettings.xai, apiKey: env.XAI_API_KEY };
150
+ migratedKeys.push('xAI API Key');
151
+ llmChanged = true;
152
+ }
153
+
154
+ // Migrate Kimi API key (Moonshot)
155
+ const kimiApiKey = env.KIMI_API_KEY || env.MOONSHOT_API_KEY;
156
+ if (kimiApiKey && !llmSettings.kimi?.apiKey) {
157
+ llmSettings.kimi = { ...llmSettings.kimi, apiKey: kimiApiKey };
158
+ migratedKeys.push('Kimi API Key');
159
+ llmChanged = true;
160
+ }
161
+
140
162
  // Migrate Ollama settings
141
163
  if (env.OLLAMA_BASE_URL && !llmSettings.ollama?.baseUrl) {
142
164
  llmSettings.ollama = {
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Gmail API helpers
3
+ */
4
+
5
+ import { GoogleWorkspaceSettingsData } from '../../shared/types';
6
+ import { getGoogleWorkspaceAccessToken, refreshGoogleWorkspaceAccessToken } from './google-workspace-auth';
7
+
8
+ export const GMAIL_API_BASE = 'https://gmail.googleapis.com/gmail/v1';
9
+ const DEFAULT_TIMEOUT_MS = 20000;
10
+
11
+ function parseJsonSafe(text: string): any | undefined {
12
+ const trimmed = text.trim();
13
+ if (!trimmed) return undefined;
14
+ try {
15
+ return JSON.parse(trimmed);
16
+ } catch {
17
+ return undefined;
18
+ }
19
+ }
20
+
21
+ function formatGmailError(status: number, data: any, fallback?: string): string {
22
+ const message =
23
+ data?.error?.message ||
24
+ data?.message ||
25
+ fallback ||
26
+ 'Gmail API error';
27
+ return `Gmail API error ${status}: ${message}`;
28
+ }
29
+
30
+ export interface GmailRequestOptions {
31
+ method: 'GET' | 'POST' | 'PATCH' | 'DELETE';
32
+ path: string;
33
+ query?: Record<string, string | number | boolean | string[] | undefined>;
34
+ body?: any;
35
+ timeoutMs?: number;
36
+ }
37
+
38
+ export interface GmailRequestResult {
39
+ status: number;
40
+ data?: any;
41
+ raw?: string;
42
+ }
43
+
44
+ export async function gmailRequest(
45
+ settings: GoogleWorkspaceSettingsData,
46
+ options: GmailRequestOptions
47
+ ): Promise<GmailRequestResult> {
48
+ const params = new URLSearchParams();
49
+ if (options.query) {
50
+ for (const [key, value] of Object.entries(options.query)) {
51
+ if (value === undefined || value === null) continue;
52
+ if (Array.isArray(value)) {
53
+ value.forEach((item) => {
54
+ if (item !== undefined && item !== null) params.append(key, String(item));
55
+ });
56
+ continue;
57
+ }
58
+ params.set(key, String(value));
59
+ }
60
+ }
61
+ const queryString = params.toString();
62
+ const url = `${GMAIL_API_BASE}${options.path}${queryString ? `?${queryString}` : ''}`;
63
+
64
+ const timeoutMs = options.timeoutMs ?? settings.timeoutMs ?? DEFAULT_TIMEOUT_MS;
65
+
66
+ const requestOnce = async (accessToken: string): Promise<GmailRequestResult> => {
67
+ const headers: Record<string, string> = {
68
+ Authorization: `Bearer ${accessToken}`,
69
+ };
70
+
71
+ if (options.method !== 'GET' && options.method !== 'DELETE') {
72
+ headers['Content-Type'] = 'application/json';
73
+ }
74
+
75
+ const controller = new AbortController();
76
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
77
+
78
+ try {
79
+ const response = await fetch(url, {
80
+ method: options.method,
81
+ headers,
82
+ body: options.body ? JSON.stringify(options.body) : undefined,
83
+ signal: controller.signal,
84
+ });
85
+
86
+ const rawText = typeof response.text === 'function' ? await response.text() : '';
87
+ const data = rawText ? parseJsonSafe(rawText) : undefined;
88
+
89
+ if (!response.ok) {
90
+ throw Object.assign(new Error(formatGmailError(response.status, data, response.statusText)), {
91
+ status: response.status,
92
+ data,
93
+ });
94
+ }
95
+
96
+ return {
97
+ status: response.status,
98
+ data: data ?? undefined,
99
+ raw: rawText || undefined,
100
+ };
101
+ } catch (error: any) {
102
+ if (error?.name === 'AbortError') {
103
+ throw new Error('Gmail API request timed out');
104
+ }
105
+ throw error;
106
+ } finally {
107
+ clearTimeout(timeout);
108
+ }
109
+ };
110
+
111
+ try {
112
+ const accessToken = await getGoogleWorkspaceAccessToken(settings);
113
+ return await requestOnce(accessToken);
114
+ } catch (error: any) {
115
+ if (error?.status === 401 && settings.refreshToken) {
116
+ const refreshedToken = await refreshGoogleWorkspaceAccessToken(settings);
117
+ return await requestOnce(refreshedToken);
118
+ }
119
+ throw error;
120
+ }
121
+ }