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
|
@@ -3,6 +3,7 @@ import * as path from 'path';
|
|
|
3
3
|
import * as fs from 'fs/promises';
|
|
4
4
|
import * as fsSync from 'fs';
|
|
5
5
|
import mammoth from 'mammoth';
|
|
6
|
+
import mime from 'mime-types';
|
|
6
7
|
|
|
7
8
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
8
9
|
const pdfParseModule = require('pdf-parse');
|
|
@@ -28,10 +29,23 @@ import { MentionRepository } from '../agents/MentionRepository';
|
|
|
28
29
|
import { TaskLabelRepository } from '../database/TaskLabelRepository';
|
|
29
30
|
import { WorkingStateRepository } from '../agents/WorkingStateRepository';
|
|
30
31
|
import { ContextPolicyManager } from '../gateway/context-policy';
|
|
31
|
-
import { IPC_CHANNELS, LLMSettingsData, AddChannelRequest, UpdateChannelRequest, SecurityMode, UpdateInfo, TEMP_WORKSPACE_ID, TEMP_WORKSPACE_NAME, Workspace, AgentRole, Task, BoardColumn, XSettingsData } from '../../shared/types';
|
|
32
|
+
import { IPC_CHANNELS, LLMSettingsData, AddChannelRequest, UpdateChannelRequest, SecurityMode, UpdateInfo, TEMP_WORKSPACE_ID, TEMP_WORKSPACE_NAME, Workspace, AgentRole, Task, BoardColumn, XSettingsData, NotionSettingsData, BoxSettingsData, OneDriveSettingsData, GoogleDriveSettingsData, DropboxSettingsData, SharePointSettingsData } from '../../shared/types';
|
|
33
|
+
import { CUSTOM_PROVIDER_MAP, CUSTOM_PROVIDER_IDS } from '../../shared/llm-provider-catalog';
|
|
32
34
|
import * as os from 'os';
|
|
33
35
|
import { AgentDaemon } from '../agent/daemon';
|
|
34
|
-
import {
|
|
36
|
+
import {
|
|
37
|
+
LLMProviderFactory,
|
|
38
|
+
LLMProviderConfig,
|
|
39
|
+
ModelKey,
|
|
40
|
+
MODELS,
|
|
41
|
+
GEMINI_MODELS,
|
|
42
|
+
OPENROUTER_MODELS,
|
|
43
|
+
OLLAMA_MODELS,
|
|
44
|
+
GROQ_MODELS,
|
|
45
|
+
XAI_MODELS,
|
|
46
|
+
KIMI_MODELS,
|
|
47
|
+
OpenAIOAuth,
|
|
48
|
+
} from '../agent/llm';
|
|
35
49
|
import { SearchProviderFactory, SearchSettings, SearchProviderType } from '../agent/search';
|
|
36
50
|
import { ChannelGateway } from '../gateway';
|
|
37
51
|
import { updateManager } from '../updater';
|
|
@@ -42,10 +56,17 @@ import {
|
|
|
42
56
|
TaskCreateSchema,
|
|
43
57
|
TaskRenameSchema,
|
|
44
58
|
TaskMessageSchema,
|
|
59
|
+
FileImportSchema,
|
|
45
60
|
ApprovalResponseSchema,
|
|
46
61
|
LLMSettingsSchema,
|
|
47
62
|
SearchSettingsSchema,
|
|
48
63
|
XSettingsSchema,
|
|
64
|
+
NotionSettingsSchema,
|
|
65
|
+
BoxSettingsSchema,
|
|
66
|
+
OneDriveSettingsSchema,
|
|
67
|
+
GoogleDriveSettingsSchema,
|
|
68
|
+
DropboxSettingsSchema,
|
|
69
|
+
SharePointSettingsSchema,
|
|
49
70
|
AddChannelSchema,
|
|
50
71
|
UpdateChannelSchema,
|
|
51
72
|
GrantAccessSchema,
|
|
@@ -54,10 +75,24 @@ import {
|
|
|
54
75
|
GuardrailSettingsSchema,
|
|
55
76
|
UUIDSchema,
|
|
56
77
|
StringIdSchema,
|
|
78
|
+
MCPConnectorOAuthSchema,
|
|
57
79
|
} from '../utils/validation';
|
|
58
80
|
import { GuardrailManager } from '../guardrails/guardrail-manager';
|
|
59
81
|
import { AppearanceManager } from '../settings/appearance-manager';
|
|
60
82
|
import { PersonalityManager } from '../settings/personality-manager';
|
|
83
|
+
import { NotionSettingsManager } from '../settings/notion-manager';
|
|
84
|
+
import { testNotionConnection } from '../utils/notion-api';
|
|
85
|
+
import { BoxSettingsManager } from '../settings/box-manager';
|
|
86
|
+
import { OneDriveSettingsManager } from '../settings/onedrive-manager';
|
|
87
|
+
import { GoogleDriveSettingsManager } from '../settings/google-drive-manager';
|
|
88
|
+
import { DropboxSettingsManager } from '../settings/dropbox-manager';
|
|
89
|
+
import { SharePointSettingsManager } from '../settings/sharepoint-manager';
|
|
90
|
+
import { testBoxConnection } from '../utils/box-api';
|
|
91
|
+
import { testOneDriveConnection } from '../utils/onedrive-api';
|
|
92
|
+
import { testGoogleDriveConnection } from '../utils/google-drive-api';
|
|
93
|
+
import { testDropboxConnection } from '../utils/dropbox-api';
|
|
94
|
+
import { testSharePointConnection } from '../utils/sharepoint-api';
|
|
95
|
+
import { startConnectorOAuth } from '../mcp/oauth/connector-oauth';
|
|
61
96
|
|
|
62
97
|
const normalizeMentionToken = (value: string): string =>
|
|
63
98
|
value.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
@@ -124,7 +159,9 @@ const scoreAgentForTask = (role: AgentRole, text: string) => {
|
|
|
124
159
|
return score;
|
|
125
160
|
};
|
|
126
161
|
|
|
127
|
-
const
|
|
162
|
+
const MAX_AUTO_AGENTS = 4;
|
|
163
|
+
|
|
164
|
+
const selectBestAgentsForTask = (text: string, roles: AgentRole[], maxAgents = MAX_AUTO_AGENTS) => {
|
|
128
165
|
if (roles.length === 0) return roles;
|
|
129
166
|
const scored = roles
|
|
130
167
|
.map((role) => ({ role, score: scoreAgentForTask(role, text) }))
|
|
@@ -139,19 +176,19 @@ const selectBestAgentsForTask = (text: string, roles: AgentRole[]) => {
|
|
|
139
176
|
const threshold = Math.max(1, maxScore - 2);
|
|
140
177
|
const selected = withScore
|
|
141
178
|
.filter((entry) => entry.score >= threshold)
|
|
142
|
-
.slice(0,
|
|
179
|
+
.slice(0, maxAgents)
|
|
143
180
|
.map((entry) => entry.role);
|
|
144
|
-
return selected.length > 0 ? selected : withScore.slice(0,
|
|
181
|
+
return selected.length > 0 ? selected : withScore.slice(0, maxAgents).map((entry) => entry.role);
|
|
145
182
|
}
|
|
146
183
|
|
|
147
184
|
const leads = roles
|
|
148
185
|
.filter((role) => role.autonomyLevel === 'lead')
|
|
149
186
|
.sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0));
|
|
150
187
|
if (leads.length > 0) {
|
|
151
|
-
return leads.slice(0,
|
|
188
|
+
return leads.slice(0, maxAgents);
|
|
152
189
|
}
|
|
153
190
|
|
|
154
|
-
return roles.slice(0, Math.min(
|
|
191
|
+
return roles.slice(0, Math.min(maxAgents, roles.length));
|
|
155
192
|
};
|
|
156
193
|
|
|
157
194
|
const extractMentionedRoles = (
|
|
@@ -159,10 +196,9 @@ const extractMentionedRoles = (
|
|
|
159
196
|
roles: AgentRole[]
|
|
160
197
|
) => {
|
|
161
198
|
const normalizedText = text.toLowerCase();
|
|
162
|
-
const useSmartSelection = /\B@everybody\b/.test(normalizedText)
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}
|
|
199
|
+
const useSmartSelection = /\B@everybody\b/.test(normalizedText) ||
|
|
200
|
+
/\B@all\b/.test(normalizedText) ||
|
|
201
|
+
/\B@everyone\b/.test(normalizedText);
|
|
166
202
|
|
|
167
203
|
const index = buildAgentMentionIndex(roles);
|
|
168
204
|
const matches = new Map<string, AgentRole>();
|
|
@@ -180,11 +216,15 @@ const extractMentionedRoles = (
|
|
|
180
216
|
|
|
181
217
|
if (matches.size > 0) {
|
|
182
218
|
if (useSmartSelection) {
|
|
183
|
-
const selected = selectBestAgentsForTask(text, roles);
|
|
184
219
|
const merged = new Map<string, AgentRole>();
|
|
185
|
-
selected.forEach((role) => merged.set(role.id, role));
|
|
186
220
|
matches.forEach((role) => merged.set(role.id, role));
|
|
187
|
-
|
|
221
|
+
const selected = selectBestAgentsForTask(text, roles, MAX_AUTO_AGENTS);
|
|
222
|
+
selected.forEach((role) => {
|
|
223
|
+
if (merged.size < MAX_AUTO_AGENTS) {
|
|
224
|
+
merged.set(role.id, role);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
return Array.from(merged.values()).slice(0, MAX_AUTO_AGENTS);
|
|
188
228
|
}
|
|
189
229
|
return Array.from(matches.values());
|
|
190
230
|
}
|
|
@@ -200,7 +240,7 @@ const extractMentionedRoles = (
|
|
|
200
240
|
});
|
|
201
241
|
|
|
202
242
|
if (useSmartSelection) {
|
|
203
|
-
return selectBestAgentsForTask(text, roles);
|
|
243
|
+
return selectBestAgentsForTask(text, roles, MAX_AUTO_AGENTS);
|
|
204
244
|
}
|
|
205
245
|
|
|
206
246
|
return Array.from(matches.values());
|
|
@@ -303,6 +343,8 @@ import { getVoiceService } from '../voice/VoiceService';
|
|
|
303
343
|
|
|
304
344
|
// Global notification service instance
|
|
305
345
|
let notificationService: NotificationService | null = null;
|
|
346
|
+
const resolveCustomProviderId = (providerType: string) =>
|
|
347
|
+
providerType === 'kimi-coding' ? 'kimi-code' : providerType;
|
|
306
348
|
|
|
307
349
|
/**
|
|
308
350
|
* Get the notification service instance
|
|
@@ -329,6 +371,9 @@ rateLimiter.configure(IPC_CHANNELS.LLM_GET_OLLAMA_MODELS, RATE_LIMIT_CONFIGS.sta
|
|
|
329
371
|
rateLimiter.configure(IPC_CHANNELS.LLM_GET_GEMINI_MODELS, RATE_LIMIT_CONFIGS.standard);
|
|
330
372
|
rateLimiter.configure(IPC_CHANNELS.LLM_GET_OPENROUTER_MODELS, RATE_LIMIT_CONFIGS.standard);
|
|
331
373
|
rateLimiter.configure(IPC_CHANNELS.LLM_GET_BEDROCK_MODELS, RATE_LIMIT_CONFIGS.standard);
|
|
374
|
+
rateLimiter.configure(IPC_CHANNELS.LLM_GET_GROQ_MODELS, RATE_LIMIT_CONFIGS.standard);
|
|
375
|
+
rateLimiter.configure(IPC_CHANNELS.LLM_GET_XAI_MODELS, RATE_LIMIT_CONFIGS.standard);
|
|
376
|
+
rateLimiter.configure(IPC_CHANNELS.LLM_GET_KIMI_MODELS, RATE_LIMIT_CONFIGS.standard);
|
|
332
377
|
rateLimiter.configure(IPC_CHANNELS.SEARCH_SAVE_SETTINGS, RATE_LIMIT_CONFIGS.limited);
|
|
333
378
|
rateLimiter.configure(IPC_CHANNELS.SEARCH_TEST_PROVIDER, RATE_LIMIT_CONFIGS.expensive);
|
|
334
379
|
rateLimiter.configure(IPC_CHANNELS.GATEWAY_ADD_CHANNEL, RATE_LIMIT_CONFIGS.limited);
|
|
@@ -624,6 +669,92 @@ export async function setupIpcHandlers(
|
|
|
624
669
|
}
|
|
625
670
|
});
|
|
626
671
|
|
|
672
|
+
// File import handler - copy selected files into the workspace for attachment use
|
|
673
|
+
ipcMain.handle('file:importToWorkspace', async (_, data: { workspaceId: string; files: string[] }) => {
|
|
674
|
+
const validated = validateInput(FileImportSchema, data, 'file import');
|
|
675
|
+
const workspace = workspaceRepo.findById(validated.workspaceId);
|
|
676
|
+
|
|
677
|
+
if (!workspace) {
|
|
678
|
+
throw new Error(`Workspace not found: ${validated.workspaceId}`);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
if (!workspace.permissions.write) {
|
|
682
|
+
throw new Error('Write permission not granted for workspace');
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
const sanitizeFileName = (fileName: string): string => {
|
|
686
|
+
const sanitized = fileName.replace(/[<>:"/\\|?*\x00-\x1F]/g, '_').trim();
|
|
687
|
+
return sanitized.length > 0 ? sanitized : 'file';
|
|
688
|
+
};
|
|
689
|
+
|
|
690
|
+
const ensureUniqueName = (dir: string, baseName: string, usedNames: Set<string>): string => {
|
|
691
|
+
const ext = path.extname(baseName);
|
|
692
|
+
const stem = path.basename(baseName, ext);
|
|
693
|
+
let candidate = baseName;
|
|
694
|
+
let counter = 1;
|
|
695
|
+
while (usedNames.has(candidate) || fsSync.existsSync(path.join(dir, candidate))) {
|
|
696
|
+
candidate = `${stem}-${counter}${ext}`;
|
|
697
|
+
counter += 1;
|
|
698
|
+
}
|
|
699
|
+
usedNames.add(candidate);
|
|
700
|
+
return candidate;
|
|
701
|
+
};
|
|
702
|
+
|
|
703
|
+
let uploadRoot: string | null = null;
|
|
704
|
+
const usedNames = new Set<string>();
|
|
705
|
+
|
|
706
|
+
const ensureUploadRoot = async (): Promise<string> => {
|
|
707
|
+
if (uploadRoot) return uploadRoot;
|
|
708
|
+
uploadRoot = path.join(workspace.path, '.cowork', 'uploads', `${Date.now()}`);
|
|
709
|
+
await fs.mkdir(uploadRoot, { recursive: true });
|
|
710
|
+
return uploadRoot;
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
const results: Array<{ relativePath: string; fileName: string; size: number; mimeType?: string }> = [];
|
|
714
|
+
|
|
715
|
+
for (const filePath of validated.files) {
|
|
716
|
+
const absolutePath = path.isAbsolute(filePath) ? filePath : path.resolve(filePath);
|
|
717
|
+
const stats = await fs.stat(absolutePath);
|
|
718
|
+
|
|
719
|
+
if (!stats.isFile()) {
|
|
720
|
+
throw new Error(`Not a file: ${filePath}`);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
const sizeCheck = GuardrailManager.isFileSizeExceeded(stats.size);
|
|
724
|
+
if (sizeCheck.exceeded) {
|
|
725
|
+
throw new Error(`File "${path.basename(filePath)}" is ${sizeCheck.sizeMB.toFixed(1)}MB and exceeds the ${sizeCheck.limitMB}MB limit.`);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
const mimeType = (mime.lookup(absolutePath) || undefined) as string | undefined;
|
|
729
|
+
|
|
730
|
+
if (isPathWithinWorkspace(absolutePath, workspace.path)) {
|
|
731
|
+
results.push({
|
|
732
|
+
relativePath: path.relative(workspace.path, absolutePath),
|
|
733
|
+
fileName: path.basename(absolutePath),
|
|
734
|
+
size: stats.size,
|
|
735
|
+
mimeType,
|
|
736
|
+
});
|
|
737
|
+
continue;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
const safeName = sanitizeFileName(path.basename(absolutePath));
|
|
741
|
+
const targetRoot = await ensureUploadRoot();
|
|
742
|
+
const uniqueName = ensureUniqueName(targetRoot, safeName, usedNames);
|
|
743
|
+
const destination = path.join(targetRoot, uniqueName);
|
|
744
|
+
|
|
745
|
+
await fs.copyFile(absolutePath, destination);
|
|
746
|
+
|
|
747
|
+
results.push({
|
|
748
|
+
relativePath: path.relative(workspace.path, destination),
|
|
749
|
+
fileName: uniqueName,
|
|
750
|
+
size: stats.size,
|
|
751
|
+
mimeType,
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
return results;
|
|
756
|
+
});
|
|
757
|
+
|
|
627
758
|
// Workspace handlers
|
|
628
759
|
ipcMain.handle(IPC_CHANNELS.WORKSPACE_CREATE, async (_, data) => {
|
|
629
760
|
const validated = validateInput(WorkspaceCreateSchema, data, 'workspace');
|
|
@@ -702,7 +833,7 @@ export async function setupIpcHandlers(
|
|
|
702
833
|
try {
|
|
703
834
|
const activeRoles = agentRoleRepo.findAll(false).filter((role) => role.isActive);
|
|
704
835
|
const mentionedRoles = extractMentionedRoles(`${title}\n${prompt}`, activeRoles);
|
|
705
|
-
const dispatchRoles = mentionedRoles
|
|
836
|
+
const dispatchRoles = mentionedRoles;
|
|
706
837
|
|
|
707
838
|
if (dispatchRoles.length > 0) {
|
|
708
839
|
const taskUpdate: Partial<Task> = {
|
|
@@ -1040,12 +1171,19 @@ export async function setupIpcHandlers(
|
|
|
1040
1171
|
gemini: validated.gemini,
|
|
1041
1172
|
openrouter: validated.openrouter,
|
|
1042
1173
|
openai: openaiSettings,
|
|
1174
|
+
groq: validated.groq,
|
|
1175
|
+
xai: validated.xai,
|
|
1176
|
+
kimi: validated.kimi,
|
|
1177
|
+
customProviders: validated.customProviders ?? existingSettings.customProviders,
|
|
1043
1178
|
// Preserve cached models from existing settings
|
|
1044
1179
|
cachedGeminiModels: existingSettings.cachedGeminiModels,
|
|
1045
1180
|
cachedOpenRouterModels: existingSettings.cachedOpenRouterModels,
|
|
1046
1181
|
cachedOllamaModels: existingSettings.cachedOllamaModels,
|
|
1047
1182
|
cachedBedrockModels: existingSettings.cachedBedrockModels,
|
|
1048
1183
|
cachedOpenAIModels: existingSettings.cachedOpenAIModels,
|
|
1184
|
+
cachedGroqModels: existingSettings.cachedGroqModels,
|
|
1185
|
+
cachedXaiModels: existingSettings.cachedXaiModels,
|
|
1186
|
+
cachedKimiModels: existingSettings.cachedKimiModels,
|
|
1049
1187
|
});
|
|
1050
1188
|
// Clear cache so next task uses new settings
|
|
1051
1189
|
LLMProviderFactory.clearCache();
|
|
@@ -1062,6 +1200,8 @@ export async function setupIpcHandlers(
|
|
|
1062
1200
|
openaiAccessToken = settings.openai?.accessToken;
|
|
1063
1201
|
openaiRefreshToken = settings.openai?.refreshToken;
|
|
1064
1202
|
}
|
|
1203
|
+
const resolvedProviderType = resolveCustomProviderId(config.providerType);
|
|
1204
|
+
const customProviderConfig = config.customProviders?.[resolvedProviderType] || config.customProviders?.[config.providerType];
|
|
1065
1205
|
const providerConfig: LLMProviderConfig = {
|
|
1066
1206
|
type: config.providerType,
|
|
1067
1207
|
model: LLMProviderFactory.getModelId(
|
|
@@ -1070,7 +1210,11 @@ export async function setupIpcHandlers(
|
|
|
1070
1210
|
config.ollama?.model,
|
|
1071
1211
|
config.gemini?.model,
|
|
1072
1212
|
config.openrouter?.model,
|
|
1073
|
-
config.openai?.model
|
|
1213
|
+
config.openai?.model,
|
|
1214
|
+
config.groq?.model,
|
|
1215
|
+
config.xai?.model,
|
|
1216
|
+
config.kimi?.model,
|
|
1217
|
+
config.customProviders
|
|
1074
1218
|
),
|
|
1075
1219
|
anthropicApiKey: config.anthropic?.apiKey,
|
|
1076
1220
|
awsRegion: config.bedrock?.region,
|
|
@@ -1082,9 +1226,18 @@ export async function setupIpcHandlers(
|
|
|
1082
1226
|
ollamaApiKey: config.ollama?.apiKey,
|
|
1083
1227
|
geminiApiKey: config.gemini?.apiKey,
|
|
1084
1228
|
openrouterApiKey: config.openrouter?.apiKey,
|
|
1229
|
+
openrouterBaseUrl: config.openrouter?.baseUrl,
|
|
1085
1230
|
openaiApiKey: config.openai?.apiKey,
|
|
1086
1231
|
openaiAccessToken: openaiAccessToken,
|
|
1087
1232
|
openaiRefreshToken: openaiRefreshToken,
|
|
1233
|
+
groqApiKey: config.groq?.apiKey,
|
|
1234
|
+
groqBaseUrl: config.groq?.baseUrl,
|
|
1235
|
+
xaiApiKey: config.xai?.apiKey,
|
|
1236
|
+
xaiBaseUrl: config.xai?.baseUrl,
|
|
1237
|
+
kimiApiKey: config.kimi?.apiKey,
|
|
1238
|
+
kimiBaseUrl: config.kimi?.baseUrl,
|
|
1239
|
+
providerApiKey: customProviderConfig?.apiKey,
|
|
1240
|
+
providerBaseUrl: customProviderConfig?.baseUrl,
|
|
1088
1241
|
};
|
|
1089
1242
|
return LLMProviderFactory.testProvider(providerConfig);
|
|
1090
1243
|
});
|
|
@@ -1106,134 +1259,214 @@ export async function setupIpcHandlers(
|
|
|
1106
1259
|
// Get models based on the current provider type
|
|
1107
1260
|
let models: Array<{ key: string; displayName: string; description: string }> = [];
|
|
1108
1261
|
let currentModel = settings.modelKey;
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
models = cachedGemini;
|
|
1130
|
-
} else {
|
|
1131
|
-
// Fall back to static models
|
|
1132
|
-
models = Object.values(GEMINI_MODELS).map((value) => ({
|
|
1133
|
-
key: value.id,
|
|
1262
|
+
const resolvedProviderType = resolveCustomProviderId(settings.providerType);
|
|
1263
|
+
const customEntry = CUSTOM_PROVIDER_MAP.get(resolvedProviderType as any);
|
|
1264
|
+
|
|
1265
|
+
if (customEntry) {
|
|
1266
|
+
const customConfig = settings.customProviders?.[resolvedProviderType] || settings.customProviders?.[settings.providerType];
|
|
1267
|
+
currentModel = customConfig?.model || customEntry.defaultModel;
|
|
1268
|
+
models = [
|
|
1269
|
+
{
|
|
1270
|
+
key: currentModel,
|
|
1271
|
+
displayName: currentModel,
|
|
1272
|
+
description: customEntry.description || `${customEntry.name} model`,
|
|
1273
|
+
},
|
|
1274
|
+
];
|
|
1275
|
+
} else {
|
|
1276
|
+
switch (settings.providerType) {
|
|
1277
|
+
case 'anthropic':
|
|
1278
|
+
case 'bedrock':
|
|
1279
|
+
// Use Anthropic/Bedrock models from MODELS
|
|
1280
|
+
models = Object.entries(MODELS).map(([key, value]) => ({
|
|
1281
|
+
key,
|
|
1134
1282
|
displayName: value.displayName,
|
|
1135
|
-
description:
|
|
1283
|
+
description: key.includes('opus') ? 'Most capable for complex work' :
|
|
1284
|
+
key.includes('sonnet') ? 'Balanced performance and speed' :
|
|
1285
|
+
'Fast and efficient',
|
|
1136
1286
|
}));
|
|
1287
|
+
break;
|
|
1288
|
+
|
|
1289
|
+
case 'gemini': {
|
|
1290
|
+
// For Gemini, use the specific model from settings (full model ID)
|
|
1291
|
+
currentModel = settings.gemini?.model || 'gemini-2.0-flash';
|
|
1292
|
+
// Use cached models if available, otherwise fall back to static list
|
|
1293
|
+
const cachedGemini = LLMProviderFactory.getCachedModels('gemini');
|
|
1294
|
+
if (cachedGemini && cachedGemini.length > 0) {
|
|
1295
|
+
models = cachedGemini;
|
|
1296
|
+
} else {
|
|
1297
|
+
// Fall back to static models
|
|
1298
|
+
models = Object.values(GEMINI_MODELS).map((value) => ({
|
|
1299
|
+
key: value.id,
|
|
1300
|
+
displayName: value.displayName,
|
|
1301
|
+
description: value.description,
|
|
1302
|
+
}));
|
|
1303
|
+
}
|
|
1304
|
+
// Ensure the currently selected model is in the list
|
|
1305
|
+
if (currentModel && !models.some(m => m.key === currentModel)) {
|
|
1306
|
+
models.unshift({
|
|
1307
|
+
key: currentModel,
|
|
1308
|
+
displayName: currentModel,
|
|
1309
|
+
description: 'Selected model',
|
|
1310
|
+
});
|
|
1311
|
+
}
|
|
1312
|
+
break;
|
|
1137
1313
|
}
|
|
1138
|
-
// Ensure the currently selected model is in the list
|
|
1139
|
-
if (currentModel && !models.some(m => m.key === currentModel)) {
|
|
1140
|
-
models.unshift({
|
|
1141
|
-
key: currentModel,
|
|
1142
|
-
displayName: currentModel,
|
|
1143
|
-
description: 'Selected model',
|
|
1144
|
-
});
|
|
1145
|
-
}
|
|
1146
|
-
break;
|
|
1147
|
-
}
|
|
1148
1314
|
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1315
|
+
case 'openrouter': {
|
|
1316
|
+
// For OpenRouter, use the specific model from settings (full model ID)
|
|
1317
|
+
currentModel = settings.openrouter?.model || 'anthropic/claude-3.5-sonnet';
|
|
1318
|
+
// Use cached models if available, otherwise fall back to static list
|
|
1319
|
+
const cachedOpenRouter = LLMProviderFactory.getCachedModels('openrouter');
|
|
1320
|
+
if (cachedOpenRouter && cachedOpenRouter.length > 0) {
|
|
1321
|
+
models = cachedOpenRouter;
|
|
1322
|
+
} else {
|
|
1323
|
+
// Fall back to static models
|
|
1324
|
+
models = Object.values(OPENROUTER_MODELS).map((value) => ({
|
|
1325
|
+
key: value.id,
|
|
1326
|
+
displayName: value.displayName,
|
|
1327
|
+
description: value.description,
|
|
1328
|
+
}));
|
|
1329
|
+
}
|
|
1330
|
+
// Ensure the currently selected model is in the list
|
|
1331
|
+
if (currentModel && !models.some(m => m.key === currentModel)) {
|
|
1332
|
+
models.unshift({
|
|
1333
|
+
key: currentModel,
|
|
1334
|
+
displayName: currentModel,
|
|
1335
|
+
description: 'Selected model',
|
|
1336
|
+
});
|
|
1337
|
+
}
|
|
1338
|
+
break;
|
|
1163
1339
|
}
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1340
|
+
|
|
1341
|
+
case 'ollama': {
|
|
1342
|
+
// For Ollama, use the specific model from settings
|
|
1343
|
+
currentModel = settings.ollama?.model || 'llama3.2';
|
|
1344
|
+
// Use cached models if available, otherwise fall back to static list
|
|
1345
|
+
const cachedOllama = LLMProviderFactory.getCachedModels('ollama');
|
|
1346
|
+
if (cachedOllama && cachedOllama.length > 0) {
|
|
1347
|
+
models = cachedOllama;
|
|
1348
|
+
} else {
|
|
1349
|
+
// Fall back to static models
|
|
1350
|
+
models = Object.entries(OLLAMA_MODELS).map(([key, value]) => ({
|
|
1351
|
+
key,
|
|
1352
|
+
displayName: value.displayName,
|
|
1353
|
+
description: `${value.size} parameter model`,
|
|
1354
|
+
}));
|
|
1355
|
+
}
|
|
1356
|
+
// Ensure the currently selected model is in the list
|
|
1357
|
+
if (currentModel && !models.some(m => m.key === currentModel)) {
|
|
1358
|
+
models.unshift({
|
|
1359
|
+
key: currentModel,
|
|
1360
|
+
displayName: currentModel,
|
|
1361
|
+
description: 'Selected model',
|
|
1362
|
+
});
|
|
1363
|
+
}
|
|
1364
|
+
break;
|
|
1171
1365
|
}
|
|
1172
|
-
break;
|
|
1173
|
-
}
|
|
1174
1366
|
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1367
|
+
case 'openai': {
|
|
1368
|
+
// For OpenAI, use the specific model from settings
|
|
1369
|
+
currentModel = settings.openai?.model || 'gpt-4o-mini';
|
|
1370
|
+
// Use cached models if available, otherwise fall back to static list
|
|
1371
|
+
const cachedOpenAI = LLMProviderFactory.getCachedModels('openai');
|
|
1372
|
+
if (cachedOpenAI && cachedOpenAI.length > 0) {
|
|
1373
|
+
models = cachedOpenAI;
|
|
1374
|
+
} else {
|
|
1375
|
+
// Fall back to static models
|
|
1376
|
+
models = [
|
|
1377
|
+
{ key: 'gpt-4o', displayName: 'GPT-4o', description: 'Most capable model for complex tasks' },
|
|
1378
|
+
{ key: 'gpt-4o-mini', displayName: 'GPT-4o Mini', description: 'Fast and affordable for most tasks' },
|
|
1379
|
+
{ key: 'gpt-4-turbo', displayName: 'GPT-4 Turbo', description: 'Previous generation flagship' },
|
|
1380
|
+
{ key: 'gpt-3.5-turbo', displayName: 'GPT-3.5 Turbo', description: 'Fast and cost-effective' },
|
|
1381
|
+
{ key: 'o1', displayName: 'o1', description: 'Advanced reasoning model' },
|
|
1382
|
+
{ key: 'o1-mini', displayName: 'o1 Mini', description: 'Fast reasoning model' },
|
|
1383
|
+
];
|
|
1384
|
+
}
|
|
1385
|
+
// Ensure the currently selected model is in the list
|
|
1386
|
+
if (currentModel && !models.some(m => m.key === currentModel)) {
|
|
1387
|
+
models.unshift({
|
|
1388
|
+
key: currentModel,
|
|
1389
|
+
displayName: currentModel,
|
|
1390
|
+
description: 'Selected model',
|
|
1391
|
+
});
|
|
1392
|
+
}
|
|
1393
|
+
break;
|
|
1189
1394
|
}
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
}
|
|
1395
|
+
|
|
1396
|
+
case 'groq': {
|
|
1397
|
+
currentModel = settings.groq?.model || 'llama-3.1-8b-instant';
|
|
1398
|
+
const cachedGroq = LLMProviderFactory.getCachedModels('groq');
|
|
1399
|
+
if (cachedGroq && cachedGroq.length > 0) {
|
|
1400
|
+
models = cachedGroq;
|
|
1401
|
+
} else {
|
|
1402
|
+
models = Object.values(GROQ_MODELS).map((value) => ({
|
|
1403
|
+
key: value.id,
|
|
1404
|
+
displayName: value.displayName,
|
|
1405
|
+
description: value.description,
|
|
1406
|
+
}));
|
|
1407
|
+
}
|
|
1408
|
+
if (currentModel && !models.some(m => m.key === currentModel)) {
|
|
1409
|
+
models.unshift({
|
|
1410
|
+
key: currentModel,
|
|
1411
|
+
displayName: currentModel,
|
|
1412
|
+
description: 'Selected model',
|
|
1413
|
+
});
|
|
1414
|
+
}
|
|
1415
|
+
break;
|
|
1197
1416
|
}
|
|
1198
|
-
break;
|
|
1199
|
-
}
|
|
1200
1417
|
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
{
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1418
|
+
case 'xai': {
|
|
1419
|
+
currentModel = settings.xai?.model || 'grok-4-fast-non-reasoning';
|
|
1420
|
+
const cachedXai = LLMProviderFactory.getCachedModels('xai');
|
|
1421
|
+
if (cachedXai && cachedXai.length > 0) {
|
|
1422
|
+
models = cachedXai;
|
|
1423
|
+
} else {
|
|
1424
|
+
models = Object.values(XAI_MODELS).map((value) => ({
|
|
1425
|
+
key: value.id,
|
|
1426
|
+
displayName: value.displayName,
|
|
1427
|
+
description: value.description,
|
|
1428
|
+
}));
|
|
1429
|
+
}
|
|
1430
|
+
if (currentModel && !models.some(m => m.key === currentModel)) {
|
|
1431
|
+
models.unshift({
|
|
1432
|
+
key: currentModel,
|
|
1433
|
+
displayName: currentModel,
|
|
1434
|
+
description: 'Selected model',
|
|
1435
|
+
});
|
|
1436
|
+
}
|
|
1437
|
+
break;
|
|
1218
1438
|
}
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
}
|
|
1439
|
+
|
|
1440
|
+
case 'kimi': {
|
|
1441
|
+
currentModel = settings.kimi?.model || 'kimi-k2.5';
|
|
1442
|
+
const cachedKimi = LLMProviderFactory.getCachedModels('kimi');
|
|
1443
|
+
if (cachedKimi && cachedKimi.length > 0) {
|
|
1444
|
+
models = cachedKimi;
|
|
1445
|
+
} else {
|
|
1446
|
+
models = Object.values(KIMI_MODELS).map((value) => ({
|
|
1447
|
+
key: value.id,
|
|
1448
|
+
displayName: value.displayName,
|
|
1449
|
+
description: value.description,
|
|
1450
|
+
}));
|
|
1451
|
+
}
|
|
1452
|
+
if (currentModel && !models.some(m => m.key === currentModel)) {
|
|
1453
|
+
models.unshift({
|
|
1454
|
+
key: currentModel,
|
|
1455
|
+
displayName: currentModel,
|
|
1456
|
+
description: 'Selected model',
|
|
1457
|
+
});
|
|
1458
|
+
}
|
|
1459
|
+
break;
|
|
1226
1460
|
}
|
|
1227
|
-
break;
|
|
1228
|
-
}
|
|
1229
1461
|
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1462
|
+
default:
|
|
1463
|
+
// Fallback to Anthropic models
|
|
1464
|
+
models = Object.entries(MODELS).map(([key, value]) => ({
|
|
1465
|
+
key,
|
|
1466
|
+
displayName: value.displayName,
|
|
1467
|
+
description: 'Claude model',
|
|
1468
|
+
}));
|
|
1469
|
+
}
|
|
1237
1470
|
}
|
|
1238
1471
|
|
|
1239
1472
|
return {
|
|
@@ -1247,27 +1480,48 @@ export async function setupIpcHandlers(
|
|
|
1247
1480
|
// Set the current model (persists selection across sessions)
|
|
1248
1481
|
ipcMain.handle(IPC_CHANNELS.LLM_SET_MODEL, async (_, modelKey: string) => {
|
|
1249
1482
|
const settings = LLMProviderFactory.loadSettings();
|
|
1483
|
+
const resolvedProviderType = resolveCustomProviderId(settings.providerType);
|
|
1250
1484
|
|
|
1251
1485
|
// Update the model key based on the current provider
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1486
|
+
if (CUSTOM_PROVIDER_IDS.has(resolvedProviderType as any)) {
|
|
1487
|
+
const existing = settings.customProviders?.[resolvedProviderType] || {};
|
|
1488
|
+
settings.customProviders = {
|
|
1489
|
+
...(settings.customProviders || {}),
|
|
1490
|
+
[resolvedProviderType]: {
|
|
1491
|
+
...existing,
|
|
1492
|
+
model: modelKey,
|
|
1493
|
+
},
|
|
1494
|
+
};
|
|
1495
|
+
} else {
|
|
1496
|
+
switch (settings.providerType) {
|
|
1497
|
+
case 'gemini':
|
|
1498
|
+
settings.gemini = { ...settings.gemini, model: modelKey };
|
|
1499
|
+
break;
|
|
1500
|
+
case 'openrouter':
|
|
1501
|
+
settings.openrouter = { ...settings.openrouter, model: modelKey };
|
|
1502
|
+
break;
|
|
1503
|
+
case 'ollama':
|
|
1504
|
+
settings.ollama = { ...settings.ollama, model: modelKey };
|
|
1505
|
+
break;
|
|
1506
|
+
case 'openai':
|
|
1507
|
+
settings.openai = { ...settings.openai, model: modelKey };
|
|
1508
|
+
break;
|
|
1509
|
+
case 'groq':
|
|
1510
|
+
settings.groq = { ...settings.groq, model: modelKey };
|
|
1511
|
+
break;
|
|
1512
|
+
case 'xai':
|
|
1513
|
+
settings.xai = { ...settings.xai, model: modelKey };
|
|
1514
|
+
break;
|
|
1515
|
+
case 'kimi':
|
|
1516
|
+
settings.kimi = { ...settings.kimi, model: modelKey };
|
|
1517
|
+
break;
|
|
1518
|
+
case 'anthropic':
|
|
1519
|
+
case 'bedrock':
|
|
1520
|
+
default:
|
|
1521
|
+
// For Anthropic/Bedrock, use the modelKey field
|
|
1522
|
+
settings.modelKey = modelKey as ModelKey;
|
|
1523
|
+
break;
|
|
1524
|
+
}
|
|
1271
1525
|
}
|
|
1272
1526
|
|
|
1273
1527
|
LLMProviderFactory.saveSettings(settings);
|
|
@@ -1302,9 +1556,9 @@ export async function setupIpcHandlers(
|
|
|
1302
1556
|
return models;
|
|
1303
1557
|
});
|
|
1304
1558
|
|
|
1305
|
-
ipcMain.handle(IPC_CHANNELS.LLM_GET_OPENROUTER_MODELS, async (_, apiKey?: string) => {
|
|
1559
|
+
ipcMain.handle(IPC_CHANNELS.LLM_GET_OPENROUTER_MODELS, async (_, apiKey?: string, baseUrl?: string) => {
|
|
1306
1560
|
checkRateLimit(IPC_CHANNELS.LLM_GET_OPENROUTER_MODELS);
|
|
1307
|
-
const models = await LLMProviderFactory.getOpenRouterModels(apiKey);
|
|
1561
|
+
const models = await LLMProviderFactory.getOpenRouterModels(apiKey, baseUrl);
|
|
1308
1562
|
// Cache the models for use in config status
|
|
1309
1563
|
const cachedModels = models.map(m => ({
|
|
1310
1564
|
key: m.id,
|
|
@@ -1329,6 +1583,42 @@ export async function setupIpcHandlers(
|
|
|
1329
1583
|
return models;
|
|
1330
1584
|
});
|
|
1331
1585
|
|
|
1586
|
+
ipcMain.handle(IPC_CHANNELS.LLM_GET_GROQ_MODELS, async (_, apiKey?: string, baseUrl?: string) => {
|
|
1587
|
+
checkRateLimit(IPC_CHANNELS.LLM_GET_GROQ_MODELS);
|
|
1588
|
+
const models = await LLMProviderFactory.getGroqModels(apiKey, baseUrl);
|
|
1589
|
+
const cachedModels = models.map(m => ({
|
|
1590
|
+
key: m.id,
|
|
1591
|
+
displayName: m.name,
|
|
1592
|
+
description: 'Groq model',
|
|
1593
|
+
}));
|
|
1594
|
+
LLMProviderFactory.saveCachedModels('groq', cachedModels);
|
|
1595
|
+
return models;
|
|
1596
|
+
});
|
|
1597
|
+
|
|
1598
|
+
ipcMain.handle(IPC_CHANNELS.LLM_GET_XAI_MODELS, async (_, apiKey?: string, baseUrl?: string) => {
|
|
1599
|
+
checkRateLimit(IPC_CHANNELS.LLM_GET_XAI_MODELS);
|
|
1600
|
+
const models = await LLMProviderFactory.getXAIModels(apiKey, baseUrl);
|
|
1601
|
+
const cachedModels = models.map(m => ({
|
|
1602
|
+
key: m.id,
|
|
1603
|
+
displayName: m.name,
|
|
1604
|
+
description: 'xAI model',
|
|
1605
|
+
}));
|
|
1606
|
+
LLMProviderFactory.saveCachedModels('xai', cachedModels);
|
|
1607
|
+
return models;
|
|
1608
|
+
});
|
|
1609
|
+
|
|
1610
|
+
ipcMain.handle(IPC_CHANNELS.LLM_GET_KIMI_MODELS, async (_, apiKey?: string, baseUrl?: string) => {
|
|
1611
|
+
checkRateLimit(IPC_CHANNELS.LLM_GET_KIMI_MODELS);
|
|
1612
|
+
const models = await LLMProviderFactory.getKimiModels(apiKey, baseUrl);
|
|
1613
|
+
const cachedModels = models.map(m => ({
|
|
1614
|
+
key: m.id,
|
|
1615
|
+
displayName: m.name,
|
|
1616
|
+
description: 'Kimi model',
|
|
1617
|
+
}));
|
|
1618
|
+
LLMProviderFactory.saveCachedModels('kimi', cachedModels);
|
|
1619
|
+
return models;
|
|
1620
|
+
});
|
|
1621
|
+
|
|
1332
1622
|
// OpenAI OAuth handlers
|
|
1333
1623
|
ipcMain.handle(IPC_CHANNELS.LLM_OPENAI_OAUTH_START, async () => {
|
|
1334
1624
|
checkRateLimit(IPC_CHANNELS.LLM_OPENAI_OAUTH_START);
|
|
@@ -1464,6 +1754,228 @@ export async function setupIpcHandlers(
|
|
|
1464
1754
|
};
|
|
1465
1755
|
});
|
|
1466
1756
|
|
|
1757
|
+
// Notion Settings handlers
|
|
1758
|
+
ipcMain.handle(IPC_CHANNELS.NOTION_GET_SETTINGS, async () => {
|
|
1759
|
+
return NotionSettingsManager.loadSettings();
|
|
1760
|
+
});
|
|
1761
|
+
|
|
1762
|
+
ipcMain.handle(IPC_CHANNELS.NOTION_SAVE_SETTINGS, async (_, settings) => {
|
|
1763
|
+
checkRateLimit(IPC_CHANNELS.NOTION_SAVE_SETTINGS);
|
|
1764
|
+
const validated = validateInput(NotionSettingsSchema, settings, 'notion settings') as NotionSettingsData;
|
|
1765
|
+
NotionSettingsManager.saveSettings(validated);
|
|
1766
|
+
NotionSettingsManager.clearCache();
|
|
1767
|
+
return { success: true };
|
|
1768
|
+
});
|
|
1769
|
+
|
|
1770
|
+
ipcMain.handle(IPC_CHANNELS.NOTION_TEST_CONNECTION, async () => {
|
|
1771
|
+
checkRateLimit(IPC_CHANNELS.NOTION_TEST_CONNECTION);
|
|
1772
|
+
const settings = NotionSettingsManager.loadSettings();
|
|
1773
|
+
return testNotionConnection(settings);
|
|
1774
|
+
});
|
|
1775
|
+
|
|
1776
|
+
ipcMain.handle(IPC_CHANNELS.NOTION_GET_STATUS, async () => {
|
|
1777
|
+
checkRateLimit(IPC_CHANNELS.NOTION_GET_STATUS);
|
|
1778
|
+
const settings = NotionSettingsManager.loadSettings();
|
|
1779
|
+
if (!settings.apiKey) {
|
|
1780
|
+
return { configured: false, connected: false };
|
|
1781
|
+
}
|
|
1782
|
+
if (!settings.enabled) {
|
|
1783
|
+
return { configured: true, connected: false };
|
|
1784
|
+
}
|
|
1785
|
+
const result = await testNotionConnection(settings);
|
|
1786
|
+
return {
|
|
1787
|
+
configured: true,
|
|
1788
|
+
connected: result.success,
|
|
1789
|
+
name: result.name,
|
|
1790
|
+
error: result.success ? undefined : result.error,
|
|
1791
|
+
};
|
|
1792
|
+
});
|
|
1793
|
+
|
|
1794
|
+
// Box Settings handlers
|
|
1795
|
+
ipcMain.handle(IPC_CHANNELS.BOX_GET_SETTINGS, async () => {
|
|
1796
|
+
return BoxSettingsManager.loadSettings();
|
|
1797
|
+
});
|
|
1798
|
+
|
|
1799
|
+
ipcMain.handle(IPC_CHANNELS.BOX_SAVE_SETTINGS, async (_, settings) => {
|
|
1800
|
+
checkRateLimit(IPC_CHANNELS.BOX_SAVE_SETTINGS);
|
|
1801
|
+
const validated = validateInput(BoxSettingsSchema, settings, 'box settings') as BoxSettingsData;
|
|
1802
|
+
BoxSettingsManager.saveSettings(validated);
|
|
1803
|
+
BoxSettingsManager.clearCache();
|
|
1804
|
+
return { success: true };
|
|
1805
|
+
});
|
|
1806
|
+
|
|
1807
|
+
ipcMain.handle(IPC_CHANNELS.BOX_TEST_CONNECTION, async () => {
|
|
1808
|
+
checkRateLimit(IPC_CHANNELS.BOX_TEST_CONNECTION);
|
|
1809
|
+
const settings = BoxSettingsManager.loadSettings();
|
|
1810
|
+
return testBoxConnection(settings);
|
|
1811
|
+
});
|
|
1812
|
+
|
|
1813
|
+
ipcMain.handle(IPC_CHANNELS.BOX_GET_STATUS, async () => {
|
|
1814
|
+
checkRateLimit(IPC_CHANNELS.BOX_GET_STATUS);
|
|
1815
|
+
const settings = BoxSettingsManager.loadSettings();
|
|
1816
|
+
if (!settings.accessToken) {
|
|
1817
|
+
return { configured: false, connected: false };
|
|
1818
|
+
}
|
|
1819
|
+
if (!settings.enabled) {
|
|
1820
|
+
return { configured: true, connected: false };
|
|
1821
|
+
}
|
|
1822
|
+
const result = await testBoxConnection(settings);
|
|
1823
|
+
return {
|
|
1824
|
+
configured: true,
|
|
1825
|
+
connected: result.success,
|
|
1826
|
+
name: result.name,
|
|
1827
|
+
error: result.success ? undefined : result.error,
|
|
1828
|
+
};
|
|
1829
|
+
});
|
|
1830
|
+
|
|
1831
|
+
// OneDrive Settings handlers
|
|
1832
|
+
ipcMain.handle(IPC_CHANNELS.ONEDRIVE_GET_SETTINGS, async () => {
|
|
1833
|
+
return OneDriveSettingsManager.loadSettings();
|
|
1834
|
+
});
|
|
1835
|
+
|
|
1836
|
+
ipcMain.handle(IPC_CHANNELS.ONEDRIVE_SAVE_SETTINGS, async (_, settings) => {
|
|
1837
|
+
checkRateLimit(IPC_CHANNELS.ONEDRIVE_SAVE_SETTINGS);
|
|
1838
|
+
const validated = validateInput(OneDriveSettingsSchema, settings, 'onedrive settings') as OneDriveSettingsData;
|
|
1839
|
+
OneDriveSettingsManager.saveSettings(validated);
|
|
1840
|
+
OneDriveSettingsManager.clearCache();
|
|
1841
|
+
return { success: true };
|
|
1842
|
+
});
|
|
1843
|
+
|
|
1844
|
+
ipcMain.handle(IPC_CHANNELS.ONEDRIVE_TEST_CONNECTION, async () => {
|
|
1845
|
+
checkRateLimit(IPC_CHANNELS.ONEDRIVE_TEST_CONNECTION);
|
|
1846
|
+
const settings = OneDriveSettingsManager.loadSettings();
|
|
1847
|
+
return testOneDriveConnection(settings);
|
|
1848
|
+
});
|
|
1849
|
+
|
|
1850
|
+
ipcMain.handle(IPC_CHANNELS.ONEDRIVE_GET_STATUS, async () => {
|
|
1851
|
+
checkRateLimit(IPC_CHANNELS.ONEDRIVE_GET_STATUS);
|
|
1852
|
+
const settings = OneDriveSettingsManager.loadSettings();
|
|
1853
|
+
if (!settings.accessToken) {
|
|
1854
|
+
return { configured: false, connected: false };
|
|
1855
|
+
}
|
|
1856
|
+
if (!settings.enabled) {
|
|
1857
|
+
return { configured: true, connected: false };
|
|
1858
|
+
}
|
|
1859
|
+
const result = await testOneDriveConnection(settings);
|
|
1860
|
+
return {
|
|
1861
|
+
configured: true,
|
|
1862
|
+
connected: result.success,
|
|
1863
|
+
name: result.name,
|
|
1864
|
+
error: result.success ? undefined : result.error,
|
|
1865
|
+
};
|
|
1866
|
+
});
|
|
1867
|
+
|
|
1868
|
+
// Google Drive Settings handlers
|
|
1869
|
+
ipcMain.handle(IPC_CHANNELS.GOOGLE_DRIVE_GET_SETTINGS, async () => {
|
|
1870
|
+
return GoogleDriveSettingsManager.loadSettings();
|
|
1871
|
+
});
|
|
1872
|
+
|
|
1873
|
+
ipcMain.handle(IPC_CHANNELS.GOOGLE_DRIVE_SAVE_SETTINGS, async (_, settings) => {
|
|
1874
|
+
checkRateLimit(IPC_CHANNELS.GOOGLE_DRIVE_SAVE_SETTINGS);
|
|
1875
|
+
const validated = validateInput(GoogleDriveSettingsSchema, settings, 'google drive settings') as GoogleDriveSettingsData;
|
|
1876
|
+
GoogleDriveSettingsManager.saveSettings(validated);
|
|
1877
|
+
GoogleDriveSettingsManager.clearCache();
|
|
1878
|
+
return { success: true };
|
|
1879
|
+
});
|
|
1880
|
+
|
|
1881
|
+
ipcMain.handle(IPC_CHANNELS.GOOGLE_DRIVE_TEST_CONNECTION, async () => {
|
|
1882
|
+
checkRateLimit(IPC_CHANNELS.GOOGLE_DRIVE_TEST_CONNECTION);
|
|
1883
|
+
const settings = GoogleDriveSettingsManager.loadSettings();
|
|
1884
|
+
return testGoogleDriveConnection(settings);
|
|
1885
|
+
});
|
|
1886
|
+
|
|
1887
|
+
ipcMain.handle(IPC_CHANNELS.GOOGLE_DRIVE_GET_STATUS, async () => {
|
|
1888
|
+
checkRateLimit(IPC_CHANNELS.GOOGLE_DRIVE_GET_STATUS);
|
|
1889
|
+
const settings = GoogleDriveSettingsManager.loadSettings();
|
|
1890
|
+
if (!settings.accessToken) {
|
|
1891
|
+
return { configured: false, connected: false };
|
|
1892
|
+
}
|
|
1893
|
+
if (!settings.enabled) {
|
|
1894
|
+
return { configured: true, connected: false };
|
|
1895
|
+
}
|
|
1896
|
+
const result = await testGoogleDriveConnection(settings);
|
|
1897
|
+
return {
|
|
1898
|
+
configured: true,
|
|
1899
|
+
connected: result.success,
|
|
1900
|
+
name: result.name,
|
|
1901
|
+
error: result.success ? undefined : result.error,
|
|
1902
|
+
};
|
|
1903
|
+
});
|
|
1904
|
+
|
|
1905
|
+
// Dropbox Settings handlers
|
|
1906
|
+
ipcMain.handle(IPC_CHANNELS.DROPBOX_GET_SETTINGS, async () => {
|
|
1907
|
+
return DropboxSettingsManager.loadSettings();
|
|
1908
|
+
});
|
|
1909
|
+
|
|
1910
|
+
ipcMain.handle(IPC_CHANNELS.DROPBOX_SAVE_SETTINGS, async (_, settings) => {
|
|
1911
|
+
checkRateLimit(IPC_CHANNELS.DROPBOX_SAVE_SETTINGS);
|
|
1912
|
+
const validated = validateInput(DropboxSettingsSchema, settings, 'dropbox settings') as DropboxSettingsData;
|
|
1913
|
+
DropboxSettingsManager.saveSettings(validated);
|
|
1914
|
+
DropboxSettingsManager.clearCache();
|
|
1915
|
+
return { success: true };
|
|
1916
|
+
});
|
|
1917
|
+
|
|
1918
|
+
ipcMain.handle(IPC_CHANNELS.DROPBOX_TEST_CONNECTION, async () => {
|
|
1919
|
+
checkRateLimit(IPC_CHANNELS.DROPBOX_TEST_CONNECTION);
|
|
1920
|
+
const settings = DropboxSettingsManager.loadSettings();
|
|
1921
|
+
return testDropboxConnection(settings);
|
|
1922
|
+
});
|
|
1923
|
+
|
|
1924
|
+
ipcMain.handle(IPC_CHANNELS.DROPBOX_GET_STATUS, async () => {
|
|
1925
|
+
checkRateLimit(IPC_CHANNELS.DROPBOX_GET_STATUS);
|
|
1926
|
+
const settings = DropboxSettingsManager.loadSettings();
|
|
1927
|
+
if (!settings.accessToken) {
|
|
1928
|
+
return { configured: false, connected: false };
|
|
1929
|
+
}
|
|
1930
|
+
if (!settings.enabled) {
|
|
1931
|
+
return { configured: true, connected: false };
|
|
1932
|
+
}
|
|
1933
|
+
const result = await testDropboxConnection(settings);
|
|
1934
|
+
return {
|
|
1935
|
+
configured: true,
|
|
1936
|
+
connected: result.success,
|
|
1937
|
+
name: result.name,
|
|
1938
|
+
error: result.success ? undefined : result.error,
|
|
1939
|
+
};
|
|
1940
|
+
});
|
|
1941
|
+
|
|
1942
|
+
// SharePoint Settings handlers
|
|
1943
|
+
ipcMain.handle(IPC_CHANNELS.SHAREPOINT_GET_SETTINGS, async () => {
|
|
1944
|
+
return SharePointSettingsManager.loadSettings();
|
|
1945
|
+
});
|
|
1946
|
+
|
|
1947
|
+
ipcMain.handle(IPC_CHANNELS.SHAREPOINT_SAVE_SETTINGS, async (_, settings) => {
|
|
1948
|
+
checkRateLimit(IPC_CHANNELS.SHAREPOINT_SAVE_SETTINGS);
|
|
1949
|
+
const validated = validateInput(SharePointSettingsSchema, settings, 'sharepoint settings') as SharePointSettingsData;
|
|
1950
|
+
SharePointSettingsManager.saveSettings(validated);
|
|
1951
|
+
SharePointSettingsManager.clearCache();
|
|
1952
|
+
return { success: true };
|
|
1953
|
+
});
|
|
1954
|
+
|
|
1955
|
+
ipcMain.handle(IPC_CHANNELS.SHAREPOINT_TEST_CONNECTION, async () => {
|
|
1956
|
+
checkRateLimit(IPC_CHANNELS.SHAREPOINT_TEST_CONNECTION);
|
|
1957
|
+
const settings = SharePointSettingsManager.loadSettings();
|
|
1958
|
+
return testSharePointConnection(settings);
|
|
1959
|
+
});
|
|
1960
|
+
|
|
1961
|
+
ipcMain.handle(IPC_CHANNELS.SHAREPOINT_GET_STATUS, async () => {
|
|
1962
|
+
checkRateLimit(IPC_CHANNELS.SHAREPOINT_GET_STATUS);
|
|
1963
|
+
const settings = SharePointSettingsManager.loadSettings();
|
|
1964
|
+
if (!settings.accessToken) {
|
|
1965
|
+
return { configured: false, connected: false };
|
|
1966
|
+
}
|
|
1967
|
+
if (!settings.enabled) {
|
|
1968
|
+
return { configured: true, connected: false };
|
|
1969
|
+
}
|
|
1970
|
+
const result = await testSharePointConnection(settings);
|
|
1971
|
+
return {
|
|
1972
|
+
configured: true,
|
|
1973
|
+
connected: result.success,
|
|
1974
|
+
name: result.name,
|
|
1975
|
+
error: result.success ? undefined : result.error,
|
|
1976
|
+
};
|
|
1977
|
+
});
|
|
1978
|
+
|
|
1467
1979
|
// Gateway / Channel handlers
|
|
1468
1980
|
ipcMain.handle(IPC_CHANNELS.GATEWAY_GET_CHANNELS, async () => {
|
|
1469
1981
|
if (!gateway) return [];
|
|
@@ -2426,6 +2938,7 @@ function setupMCPHandlers(): void {
|
|
|
2426
2938
|
rateLimiter.configure(IPC_CHANNELS.MCP_CONNECT_SERVER, RATE_LIMIT_CONFIGS.expensive);
|
|
2427
2939
|
rateLimiter.configure(IPC_CHANNELS.MCP_TEST_SERVER, RATE_LIMIT_CONFIGS.expensive);
|
|
2428
2940
|
rateLimiter.configure(IPC_CHANNELS.MCP_REGISTRY_INSTALL, RATE_LIMIT_CONFIGS.expensive);
|
|
2941
|
+
rateLimiter.configure(IPC_CHANNELS.MCP_CONNECTOR_OAUTH_START, RATE_LIMIT_CONFIGS.expensive);
|
|
2429
2942
|
|
|
2430
2943
|
// Initialize MCP settings manager
|
|
2431
2944
|
MCPSettingsManager.initialize();
|
|
@@ -2553,6 +3066,13 @@ function setupMCPHandlers(): void {
|
|
|
2553
3066
|
return MCPRegistryManager.updateServer(validatedId);
|
|
2554
3067
|
});
|
|
2555
3068
|
|
|
3069
|
+
// MCP Connector OAuth (Salesforce/Jira)
|
|
3070
|
+
ipcMain.handle(IPC_CHANNELS.MCP_CONNECTOR_OAUTH_START, async (_, payload) => {
|
|
3071
|
+
checkRateLimit(IPC_CHANNELS.MCP_CONNECTOR_OAUTH_START);
|
|
3072
|
+
const validated = validateInput(MCPConnectorOAuthSchema, payload, 'connector oauth');
|
|
3073
|
+
return startConnectorOAuth(validated);
|
|
3074
|
+
});
|
|
3075
|
+
|
|
2556
3076
|
// MCP Host handlers
|
|
2557
3077
|
ipcMain.handle(IPC_CHANNELS.MCP_HOST_START, async () => {
|
|
2558
3078
|
const hostServer = MCPHostServer.getInstance();
|