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
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Dropbox API helpers
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DROPBOX_CONTENT_BASE = exports.DROPBOX_API_BASE = void 0;
|
|
7
|
+
exports.dropboxRequest = dropboxRequest;
|
|
8
|
+
exports.dropboxContentUpload = dropboxContentUpload;
|
|
9
|
+
exports.testDropboxConnection = testDropboxConnection;
|
|
10
|
+
exports.DROPBOX_API_BASE = 'https://api.dropboxapi.com/2';
|
|
11
|
+
exports.DROPBOX_CONTENT_BASE = 'https://content.dropboxapi.com/2';
|
|
12
|
+
const DEFAULT_TIMEOUT_MS = 20000;
|
|
13
|
+
function parseJsonSafe(text) {
|
|
14
|
+
const trimmed = text.trim();
|
|
15
|
+
if (!trimmed)
|
|
16
|
+
return undefined;
|
|
17
|
+
try {
|
|
18
|
+
return JSON.parse(trimmed);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function formatDropboxError(status, data, fallback) {
|
|
25
|
+
const message = data?.error_summary ||
|
|
26
|
+
data?.error?.summary ||
|
|
27
|
+
data?.message ||
|
|
28
|
+
fallback ||
|
|
29
|
+
'Dropbox API error';
|
|
30
|
+
return `Dropbox API error ${status}: ${message}`;
|
|
31
|
+
}
|
|
32
|
+
async function dropboxRequest(settings, options) {
|
|
33
|
+
if (!settings.accessToken) {
|
|
34
|
+
throw new Error('Dropbox access token not configured. Add it in Settings > Integrations > Dropbox.');
|
|
35
|
+
}
|
|
36
|
+
const url = `${exports.DROPBOX_API_BASE}${options.path}`;
|
|
37
|
+
const headers = {
|
|
38
|
+
Authorization: `Bearer ${settings.accessToken}`,
|
|
39
|
+
'Content-Type': 'application/json',
|
|
40
|
+
};
|
|
41
|
+
const timeoutMs = options.timeoutMs ?? settings.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
42
|
+
const controller = new AbortController();
|
|
43
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
44
|
+
try {
|
|
45
|
+
const response = await fetch(url, {
|
|
46
|
+
method: options.method,
|
|
47
|
+
headers,
|
|
48
|
+
body: JSON.stringify(options.body || {}),
|
|
49
|
+
signal: controller.signal,
|
|
50
|
+
});
|
|
51
|
+
const rawText = typeof response.text === 'function' ? await response.text() : '';
|
|
52
|
+
const data = rawText ? parseJsonSafe(rawText) : undefined;
|
|
53
|
+
if (!response.ok) {
|
|
54
|
+
throw new Error(formatDropboxError(response.status, data, response.statusText));
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
status: response.status,
|
|
58
|
+
data: data ?? undefined,
|
|
59
|
+
raw: rawText || undefined,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
if (error?.name === 'AbortError') {
|
|
64
|
+
throw new Error('Dropbox API request timed out');
|
|
65
|
+
}
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
finally {
|
|
69
|
+
clearTimeout(timeout);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async function dropboxContentUpload(settings, opts) {
|
|
73
|
+
if (!settings.accessToken) {
|
|
74
|
+
throw new Error('Dropbox access token not configured. Add it in Settings > Integrations > Dropbox.');
|
|
75
|
+
}
|
|
76
|
+
const url = `${exports.DROPBOX_CONTENT_BASE}/files/upload`;
|
|
77
|
+
const headers = {
|
|
78
|
+
Authorization: `Bearer ${settings.accessToken}`,
|
|
79
|
+
'Content-Type': 'application/octet-stream',
|
|
80
|
+
'Dropbox-API-Arg': JSON.stringify({
|
|
81
|
+
path: opts.path,
|
|
82
|
+
mode: 'add',
|
|
83
|
+
autorename: true,
|
|
84
|
+
mute: false,
|
|
85
|
+
strict_conflict: false,
|
|
86
|
+
}),
|
|
87
|
+
};
|
|
88
|
+
const timeoutMs = opts.timeoutMs ?? settings.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
89
|
+
const controller = new AbortController();
|
|
90
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
91
|
+
try {
|
|
92
|
+
const response = await fetch(url, {
|
|
93
|
+
method: 'POST',
|
|
94
|
+
headers,
|
|
95
|
+
body: opts.data,
|
|
96
|
+
signal: controller.signal,
|
|
97
|
+
});
|
|
98
|
+
const rawText = typeof response.text === 'function' ? await response.text() : '';
|
|
99
|
+
const data = rawText ? parseJsonSafe(rawText) : undefined;
|
|
100
|
+
if (!response.ok) {
|
|
101
|
+
throw new Error(formatDropboxError(response.status, data, response.statusText));
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
status: response.status,
|
|
105
|
+
data: data ?? undefined,
|
|
106
|
+
raw: rawText || undefined,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
if (error?.name === 'AbortError') {
|
|
111
|
+
throw new Error('Dropbox upload request timed out');
|
|
112
|
+
}
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
clearTimeout(timeout);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function extractAccountInfo(data) {
|
|
120
|
+
if (!data || typeof data !== 'object')
|
|
121
|
+
return {};
|
|
122
|
+
const name = data?.name?.display_name || data?.name?.abbreviated_name || undefined;
|
|
123
|
+
const userId = data?.account_id || data?.id || undefined;
|
|
124
|
+
const email = data?.email || undefined;
|
|
125
|
+
return { name, userId, email };
|
|
126
|
+
}
|
|
127
|
+
async function testDropboxConnection(settings) {
|
|
128
|
+
try {
|
|
129
|
+
const result = await dropboxRequest(settings, { method: 'POST', path: '/users/get_current_account' });
|
|
130
|
+
const extracted = extractAccountInfo(result.data);
|
|
131
|
+
return {
|
|
132
|
+
success: true,
|
|
133
|
+
name: extracted.name,
|
|
134
|
+
userId: extracted.userId,
|
|
135
|
+
email: extracted.email,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
return {
|
|
140
|
+
success: false,
|
|
141
|
+
error: error?.message || 'Failed to connect to Dropbox',
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -149,6 +149,25 @@ async function migrateEnvToSettings() {
|
|
|
149
149
|
migratedKeys.push('OpenRouter API Key');
|
|
150
150
|
llmChanged = true;
|
|
151
151
|
}
|
|
152
|
+
// Migrate Groq API key
|
|
153
|
+
if (env.GROQ_API_KEY && !llmSettings.groq?.apiKey) {
|
|
154
|
+
llmSettings.groq = { ...llmSettings.groq, apiKey: env.GROQ_API_KEY };
|
|
155
|
+
migratedKeys.push('Groq API Key');
|
|
156
|
+
llmChanged = true;
|
|
157
|
+
}
|
|
158
|
+
// Migrate xAI API key
|
|
159
|
+
if (env.XAI_API_KEY && !llmSettings.xai?.apiKey) {
|
|
160
|
+
llmSettings.xai = { ...llmSettings.xai, apiKey: env.XAI_API_KEY };
|
|
161
|
+
migratedKeys.push('xAI API Key');
|
|
162
|
+
llmChanged = true;
|
|
163
|
+
}
|
|
164
|
+
// Migrate Kimi API key (Moonshot)
|
|
165
|
+
const kimiApiKey = env.KIMI_API_KEY || env.MOONSHOT_API_KEY;
|
|
166
|
+
if (kimiApiKey && !llmSettings.kimi?.apiKey) {
|
|
167
|
+
llmSettings.kimi = { ...llmSettings.kimi, apiKey: kimiApiKey };
|
|
168
|
+
migratedKeys.push('Kimi API Key');
|
|
169
|
+
llmChanged = true;
|
|
170
|
+
}
|
|
152
171
|
// Migrate Ollama settings
|
|
153
172
|
if (env.OLLAMA_BASE_URL && !llmSettings.ollama?.baseUrl) {
|
|
154
173
|
llmSettings.ollama = {
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Google Drive API helpers
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.GOOGLE_DRIVE_UPLOAD_BASE = exports.GOOGLE_DRIVE_API_BASE = void 0;
|
|
7
|
+
exports.googleDriveRequest = googleDriveRequest;
|
|
8
|
+
exports.googleDriveUpload = googleDriveUpload;
|
|
9
|
+
exports.testGoogleDriveConnection = testGoogleDriveConnection;
|
|
10
|
+
exports.GOOGLE_DRIVE_API_BASE = 'https://www.googleapis.com/drive/v3';
|
|
11
|
+
exports.GOOGLE_DRIVE_UPLOAD_BASE = 'https://www.googleapis.com/upload/drive/v3';
|
|
12
|
+
const DEFAULT_TIMEOUT_MS = 20000;
|
|
13
|
+
function parseJsonSafe(text) {
|
|
14
|
+
const trimmed = text.trim();
|
|
15
|
+
if (!trimmed)
|
|
16
|
+
return undefined;
|
|
17
|
+
try {
|
|
18
|
+
return JSON.parse(trimmed);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function formatDriveError(status, data, fallback) {
|
|
25
|
+
const message = data?.error?.message ||
|
|
26
|
+
data?.message ||
|
|
27
|
+
fallback ||
|
|
28
|
+
'Google Drive API error';
|
|
29
|
+
return `Google Drive API error ${status}: ${message}`;
|
|
30
|
+
}
|
|
31
|
+
async function googleDriveRequest(settings, options) {
|
|
32
|
+
if (!settings.accessToken) {
|
|
33
|
+
throw new Error('Google Drive access token not configured. Add it in Settings > Integrations > Google Drive.');
|
|
34
|
+
}
|
|
35
|
+
const params = new URLSearchParams();
|
|
36
|
+
if (options.query) {
|
|
37
|
+
for (const [key, value] of Object.entries(options.query)) {
|
|
38
|
+
if (value === undefined || value === null)
|
|
39
|
+
continue;
|
|
40
|
+
params.set(key, String(value));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const queryString = params.toString();
|
|
44
|
+
const url = `${exports.GOOGLE_DRIVE_API_BASE}${options.path}${queryString ? `?${queryString}` : ''}`;
|
|
45
|
+
const headers = {
|
|
46
|
+
Authorization: `Bearer ${settings.accessToken}`,
|
|
47
|
+
};
|
|
48
|
+
if (options.method !== 'GET' && options.method !== 'DELETE') {
|
|
49
|
+
headers['Content-Type'] = 'application/json';
|
|
50
|
+
}
|
|
51
|
+
const timeoutMs = options.timeoutMs ?? settings.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
52
|
+
const controller = new AbortController();
|
|
53
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
54
|
+
try {
|
|
55
|
+
const response = await fetch(url, {
|
|
56
|
+
method: options.method,
|
|
57
|
+
headers,
|
|
58
|
+
body: options.body ? JSON.stringify(options.body) : undefined,
|
|
59
|
+
signal: controller.signal,
|
|
60
|
+
});
|
|
61
|
+
const rawText = typeof response.text === 'function' ? await response.text() : '';
|
|
62
|
+
const data = rawText ? parseJsonSafe(rawText) : undefined;
|
|
63
|
+
if (!response.ok) {
|
|
64
|
+
throw new Error(formatDriveError(response.status, data, response.statusText));
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
status: response.status,
|
|
68
|
+
data: data ?? undefined,
|
|
69
|
+
raw: rawText || undefined,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
if (error?.name === 'AbortError') {
|
|
74
|
+
throw new Error('Google Drive API request timed out');
|
|
75
|
+
}
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
clearTimeout(timeout);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async function googleDriveUpload(settings, fileId, data, contentType) {
|
|
83
|
+
if (!settings.accessToken) {
|
|
84
|
+
throw new Error('Google Drive access token not configured. Add it in Settings > Integrations > Google Drive.');
|
|
85
|
+
}
|
|
86
|
+
const url = `${exports.GOOGLE_DRIVE_UPLOAD_BASE}/files/${fileId}?uploadType=media`;
|
|
87
|
+
const headers = {
|
|
88
|
+
Authorization: `Bearer ${settings.accessToken}`,
|
|
89
|
+
'Content-Type': contentType,
|
|
90
|
+
};
|
|
91
|
+
const timeoutMs = settings.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
92
|
+
const controller = new AbortController();
|
|
93
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
94
|
+
try {
|
|
95
|
+
const response = await fetch(url, {
|
|
96
|
+
method: 'PATCH',
|
|
97
|
+
headers,
|
|
98
|
+
body: data,
|
|
99
|
+
signal: controller.signal,
|
|
100
|
+
});
|
|
101
|
+
const rawText = typeof response.text === 'function' ? await response.text() : '';
|
|
102
|
+
const dataJson = rawText ? parseJsonSafe(rawText) : undefined;
|
|
103
|
+
if (!response.ok) {
|
|
104
|
+
throw new Error(formatDriveError(response.status, dataJson, response.statusText));
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
status: response.status,
|
|
108
|
+
data: dataJson ?? undefined,
|
|
109
|
+
raw: rawText || undefined,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
if (error?.name === 'AbortError') {
|
|
114
|
+
throw new Error('Google Drive upload request timed out');
|
|
115
|
+
}
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
clearTimeout(timeout);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function extractUserInfo(data) {
|
|
123
|
+
if (!data || typeof data !== 'object')
|
|
124
|
+
return {};
|
|
125
|
+
const user = data.user || data;
|
|
126
|
+
const name = user.displayName || user.name || undefined;
|
|
127
|
+
const userId = user.permissionId || user.userId || user.id || undefined;
|
|
128
|
+
const email = user.emailAddress || user.email || undefined;
|
|
129
|
+
return { name, userId, email };
|
|
130
|
+
}
|
|
131
|
+
async function testGoogleDriveConnection(settings) {
|
|
132
|
+
try {
|
|
133
|
+
const result = await googleDriveRequest(settings, {
|
|
134
|
+
method: 'GET',
|
|
135
|
+
path: '/about',
|
|
136
|
+
query: { fields: 'user' },
|
|
137
|
+
});
|
|
138
|
+
const extracted = extractUserInfo(result.data);
|
|
139
|
+
return {
|
|
140
|
+
success: true,
|
|
141
|
+
name: extracted.name,
|
|
142
|
+
userId: extracted.userId,
|
|
143
|
+
email: extracted.email,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
return {
|
|
148
|
+
success: false,
|
|
149
|
+
error: error?.message || 'Failed to connect to Google Drive',
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Notion API helpers
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DEFAULT_NOTION_VERSION = exports.NOTION_API_BASE = void 0;
|
|
7
|
+
exports.notionRequest = notionRequest;
|
|
8
|
+
exports.testNotionConnection = testNotionConnection;
|
|
9
|
+
exports.NOTION_API_BASE = 'https://api.notion.com/v1';
|
|
10
|
+
exports.DEFAULT_NOTION_VERSION = '2025-09-03';
|
|
11
|
+
const DEFAULT_TIMEOUT_MS = 20000;
|
|
12
|
+
function getNotionVersion(settings) {
|
|
13
|
+
return settings.notionVersion || exports.DEFAULT_NOTION_VERSION;
|
|
14
|
+
}
|
|
15
|
+
function parseJsonSafe(text) {
|
|
16
|
+
const trimmed = text.trim();
|
|
17
|
+
if (!trimmed)
|
|
18
|
+
return undefined;
|
|
19
|
+
try {
|
|
20
|
+
return JSON.parse(trimmed);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function formatNotionError(status, data, fallback) {
|
|
27
|
+
const message = data?.message ||
|
|
28
|
+
data?.error ||
|
|
29
|
+
data?.details ||
|
|
30
|
+
fallback ||
|
|
31
|
+
'Notion API error';
|
|
32
|
+
return `Notion API error ${status}: ${message}`;
|
|
33
|
+
}
|
|
34
|
+
async function notionRequest(settings, options) {
|
|
35
|
+
if (!settings.apiKey) {
|
|
36
|
+
throw new Error('Notion API key not configured. Add it in Settings > Integrations > Notion.');
|
|
37
|
+
}
|
|
38
|
+
const url = `${exports.NOTION_API_BASE}${options.path}`;
|
|
39
|
+
const headers = {
|
|
40
|
+
Authorization: `Bearer ${settings.apiKey}`,
|
|
41
|
+
'Notion-Version': getNotionVersion(settings),
|
|
42
|
+
};
|
|
43
|
+
if (options.method !== 'GET' && options.method !== 'DELETE') {
|
|
44
|
+
headers['Content-Type'] = 'application/json';
|
|
45
|
+
}
|
|
46
|
+
const timeoutMs = options.timeoutMs ?? settings.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
47
|
+
const controller = new AbortController();
|
|
48
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
49
|
+
try {
|
|
50
|
+
const response = await fetch(url, {
|
|
51
|
+
method: options.method,
|
|
52
|
+
headers,
|
|
53
|
+
body: options.body ? JSON.stringify(options.body) : undefined,
|
|
54
|
+
signal: controller.signal,
|
|
55
|
+
});
|
|
56
|
+
const rawText = typeof response.text === 'function' ? await response.text() : '';
|
|
57
|
+
const data = rawText ? parseJsonSafe(rawText) : undefined;
|
|
58
|
+
if (!response.ok) {
|
|
59
|
+
throw new Error(formatNotionError(response.status, data, response.statusText));
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
status: response.status,
|
|
63
|
+
data: data ?? undefined,
|
|
64
|
+
raw: rawText || undefined,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
if (error?.name === 'AbortError') {
|
|
69
|
+
throw new Error('Notion API request timed out');
|
|
70
|
+
}
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
clearTimeout(timeout);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function extractUserInfo(data) {
|
|
78
|
+
if (!data || typeof data !== 'object')
|
|
79
|
+
return {};
|
|
80
|
+
const name = data.name ||
|
|
81
|
+
data?.bot?.owner?.user?.name ||
|
|
82
|
+
data?.person?.name ||
|
|
83
|
+
undefined;
|
|
84
|
+
const userId = data.id || data.user_id || undefined;
|
|
85
|
+
return { name, userId };
|
|
86
|
+
}
|
|
87
|
+
async function testNotionConnection(settings) {
|
|
88
|
+
try {
|
|
89
|
+
const result = await notionRequest(settings, { method: 'GET', path: '/users/me' });
|
|
90
|
+
const extracted = extractUserInfo(result.data);
|
|
91
|
+
return {
|
|
92
|
+
success: true,
|
|
93
|
+
name: extracted.name,
|
|
94
|
+
userId: extracted.userId,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
return {
|
|
99
|
+
success: false,
|
|
100
|
+
error: error?.message || 'Failed to connect to Notion',
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* OneDrive API helpers (Microsoft Graph)
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ONEDRIVE_API_BASE = void 0;
|
|
7
|
+
exports.onedriveRequest = onedriveRequest;
|
|
8
|
+
exports.testOneDriveConnection = testOneDriveConnection;
|
|
9
|
+
exports.ONEDRIVE_API_BASE = 'https://graph.microsoft.com/v1.0';
|
|
10
|
+
const DEFAULT_TIMEOUT_MS = 20000;
|
|
11
|
+
function parseJsonSafe(text) {
|
|
12
|
+
const trimmed = text.trim();
|
|
13
|
+
if (!trimmed)
|
|
14
|
+
return undefined;
|
|
15
|
+
try {
|
|
16
|
+
return JSON.parse(trimmed);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function formatGraphError(status, data, fallback) {
|
|
23
|
+
const message = data?.error?.message ||
|
|
24
|
+
data?.message ||
|
|
25
|
+
fallback ||
|
|
26
|
+
'Microsoft Graph error';
|
|
27
|
+
return `Microsoft Graph error ${status}: ${message}`;
|
|
28
|
+
}
|
|
29
|
+
async function onedriveRequest(settings, options) {
|
|
30
|
+
if (!settings.accessToken) {
|
|
31
|
+
throw new Error('OneDrive access token not configured. Add it in Settings > Integrations > OneDrive.');
|
|
32
|
+
}
|
|
33
|
+
const params = new URLSearchParams();
|
|
34
|
+
if (options.query) {
|
|
35
|
+
for (const [key, value] of Object.entries(options.query)) {
|
|
36
|
+
if (value === undefined || value === null)
|
|
37
|
+
continue;
|
|
38
|
+
params.set(key, String(value));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const queryString = params.toString();
|
|
42
|
+
const url = `${exports.ONEDRIVE_API_BASE}${options.path}${queryString ? `?${queryString}` : ''}`;
|
|
43
|
+
const headers = {
|
|
44
|
+
Authorization: `Bearer ${settings.accessToken}`,
|
|
45
|
+
...(options.headers || {}),
|
|
46
|
+
};
|
|
47
|
+
const isBinaryBody = options.body instanceof Uint8Array || options.body instanceof ArrayBuffer || Buffer.isBuffer(options.body);
|
|
48
|
+
if (options.body && !isBinaryBody && options.method !== 'GET' && options.method !== 'DELETE') {
|
|
49
|
+
headers['Content-Type'] = headers['Content-Type'] || 'application/json';
|
|
50
|
+
}
|
|
51
|
+
const timeoutMs = options.timeoutMs ?? settings.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
52
|
+
const controller = new AbortController();
|
|
53
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
54
|
+
try {
|
|
55
|
+
const response = await fetch(url, {
|
|
56
|
+
method: options.method,
|
|
57
|
+
headers,
|
|
58
|
+
body: options.body
|
|
59
|
+
? isBinaryBody
|
|
60
|
+
? options.body
|
|
61
|
+
: JSON.stringify(options.body)
|
|
62
|
+
: undefined,
|
|
63
|
+
signal: controller.signal,
|
|
64
|
+
});
|
|
65
|
+
const rawText = typeof response.text === 'function' ? await response.text() : '';
|
|
66
|
+
const data = rawText ? parseJsonSafe(rawText) : undefined;
|
|
67
|
+
if (!response.ok) {
|
|
68
|
+
throw new Error(formatGraphError(response.status, data, response.statusText));
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
status: response.status,
|
|
72
|
+
data: data ?? undefined,
|
|
73
|
+
raw: rawText || undefined,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
if (error?.name === 'AbortError') {
|
|
78
|
+
throw new Error('OneDrive API request timed out');
|
|
79
|
+
}
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
clearTimeout(timeout);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function extractDriveOwner(data) {
|
|
87
|
+
if (!data || typeof data !== 'object')
|
|
88
|
+
return {};
|
|
89
|
+
const name = data?.owner?.user?.displayName ||
|
|
90
|
+
data?.owner?.user?.id ||
|
|
91
|
+
undefined;
|
|
92
|
+
const userId = data?.owner?.user?.id || undefined;
|
|
93
|
+
const driveId = data?.id || undefined;
|
|
94
|
+
return { name, userId, driveId };
|
|
95
|
+
}
|
|
96
|
+
async function testOneDriveConnection(settings) {
|
|
97
|
+
try {
|
|
98
|
+
const result = await onedriveRequest(settings, { method: 'GET', path: '/me/drive' });
|
|
99
|
+
const extracted = extractDriveOwner(result.data);
|
|
100
|
+
return {
|
|
101
|
+
success: true,
|
|
102
|
+
name: extracted.name,
|
|
103
|
+
userId: extracted.userId,
|
|
104
|
+
driveId: extracted.driveId,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
return {
|
|
109
|
+
success: false,
|
|
110
|
+
error: error?.message || 'Failed to connect to OneDrive',
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* SharePoint API helpers (Microsoft Graph)
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SHAREPOINT_API_BASE = void 0;
|
|
7
|
+
exports.sharepointRequest = sharepointRequest;
|
|
8
|
+
exports.testSharePointConnection = testSharePointConnection;
|
|
9
|
+
exports.SHAREPOINT_API_BASE = 'https://graph.microsoft.com/v1.0';
|
|
10
|
+
const DEFAULT_TIMEOUT_MS = 20000;
|
|
11
|
+
function parseJsonSafe(text) {
|
|
12
|
+
const trimmed = text.trim();
|
|
13
|
+
if (!trimmed)
|
|
14
|
+
return undefined;
|
|
15
|
+
try {
|
|
16
|
+
return JSON.parse(trimmed);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function formatGraphError(status, data, fallback) {
|
|
23
|
+
const message = data?.error?.message ||
|
|
24
|
+
data?.message ||
|
|
25
|
+
fallback ||
|
|
26
|
+
'Microsoft Graph error';
|
|
27
|
+
return `Microsoft Graph error ${status}: ${message}`;
|
|
28
|
+
}
|
|
29
|
+
async function sharepointRequest(settings, options) {
|
|
30
|
+
if (!settings.accessToken) {
|
|
31
|
+
throw new Error('SharePoint access token not configured. Add it in Settings > Integrations > SharePoint.');
|
|
32
|
+
}
|
|
33
|
+
const params = new URLSearchParams();
|
|
34
|
+
if (options.query) {
|
|
35
|
+
for (const [key, value] of Object.entries(options.query)) {
|
|
36
|
+
if (value === undefined || value === null)
|
|
37
|
+
continue;
|
|
38
|
+
params.set(key, String(value));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const queryString = params.toString();
|
|
42
|
+
const url = `${exports.SHAREPOINT_API_BASE}${options.path}${queryString ? `?${queryString}` : ''}`;
|
|
43
|
+
const headers = {
|
|
44
|
+
Authorization: `Bearer ${settings.accessToken}`,
|
|
45
|
+
...(options.headers || {}),
|
|
46
|
+
};
|
|
47
|
+
const isBinaryBody = options.body instanceof Uint8Array || options.body instanceof ArrayBuffer || Buffer.isBuffer(options.body);
|
|
48
|
+
if (options.body && !isBinaryBody && options.method !== 'GET' && options.method !== 'DELETE') {
|
|
49
|
+
headers['Content-Type'] = headers['Content-Type'] || 'application/json';
|
|
50
|
+
}
|
|
51
|
+
const timeoutMs = options.timeoutMs ?? settings.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
52
|
+
const controller = new AbortController();
|
|
53
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
54
|
+
try {
|
|
55
|
+
const response = await fetch(url, {
|
|
56
|
+
method: options.method,
|
|
57
|
+
headers,
|
|
58
|
+
body: options.body
|
|
59
|
+
? isBinaryBody
|
|
60
|
+
? options.body
|
|
61
|
+
: JSON.stringify(options.body)
|
|
62
|
+
: undefined,
|
|
63
|
+
signal: controller.signal,
|
|
64
|
+
});
|
|
65
|
+
const rawText = typeof response.text === 'function' ? await response.text() : '';
|
|
66
|
+
const data = rawText ? parseJsonSafe(rawText) : undefined;
|
|
67
|
+
if (!response.ok) {
|
|
68
|
+
throw new Error(formatGraphError(response.status, data, response.statusText));
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
status: response.status,
|
|
72
|
+
data: data ?? undefined,
|
|
73
|
+
raw: rawText || undefined,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
if (error?.name === 'AbortError') {
|
|
78
|
+
throw new Error('SharePoint API request timed out');
|
|
79
|
+
}
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
clearTimeout(timeout);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function extractUserInfo(data) {
|
|
87
|
+
if (!data || typeof data !== 'object')
|
|
88
|
+
return {};
|
|
89
|
+
const name = data.displayName || data.name || undefined;
|
|
90
|
+
const userId = data.id || undefined;
|
|
91
|
+
return { name, userId };
|
|
92
|
+
}
|
|
93
|
+
async function testSharePointConnection(settings) {
|
|
94
|
+
try {
|
|
95
|
+
const result = await sharepointRequest(settings, { method: 'GET', path: '/me' });
|
|
96
|
+
const extracted = extractUserInfo(result.data);
|
|
97
|
+
return {
|
|
98
|
+
success: true,
|
|
99
|
+
name: extracted.name,
|
|
100
|
+
userId: extracted.userId,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
return {
|
|
105
|
+
success: false,
|
|
106
|
+
error: error?.message || 'Failed to connect to SharePoint',
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|