@lobehub/lobehub 2.0.0-next.142 → 2.0.0-next.143
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/CHANGELOG.md +25 -0
- package/apps/desktop/package.json +1 -0
- package/apps/desktop/src/main/core/ui/__tests__/MenuManager.test.ts +320 -0
- package/apps/desktop/src/main/core/ui/__tests__/Tray.test.ts +518 -0
- package/apps/desktop/src/main/core/ui/__tests__/TrayManager.test.ts +360 -0
- package/apps/desktop/src/main/menus/impls/BaseMenuPlatform.test.ts +49 -0
- package/apps/desktop/src/main/menus/impls/linux.test.ts +552 -0
- package/apps/desktop/src/main/menus/impls/macOS.test.ts +464 -0
- package/apps/desktop/src/main/menus/impls/windows.test.ts +429 -0
- package/apps/desktop/src/main/modules/fileSearch/__tests__/macOS.integration.test.ts +2 -2
- package/apps/desktop/src/main/services/__tests__/fileSearchSrv.test.ts +402 -0
- package/apps/desktop/src/main/utils/__tests__/file-system.test.ts +91 -0
- package/apps/desktop/src/main/utils/__tests__/logger.test.ts +229 -0
- package/apps/desktop/src/preload/electronApi.test.ts +142 -0
- package/apps/desktop/src/preload/invoke.test.ts +145 -0
- package/apps/desktop/src/preload/routeInterceptor.test.ts +374 -0
- package/apps/desktop/src/preload/streamer.test.ts +365 -0
- package/apps/desktop/vitest.config.mts +1 -0
- package/changelog/v1.json +9 -0
- package/locales/ar/marketAuth.json +13 -0
- package/locales/bg-BG/marketAuth.json +13 -0
- package/locales/de-DE/marketAuth.json +13 -0
- package/locales/en-US/marketAuth.json +13 -0
- package/locales/es-ES/marketAuth.json +13 -0
- package/locales/fa-IR/marketAuth.json +13 -0
- package/locales/fr-FR/marketAuth.json +13 -0
- package/locales/it-IT/marketAuth.json +13 -0
- package/locales/ja-JP/marketAuth.json +13 -0
- package/locales/ko-KR/marketAuth.json +13 -0
- package/locales/nl-NL/marketAuth.json +13 -0
- package/locales/pl-PL/marketAuth.json +13 -0
- package/locales/pt-BR/marketAuth.json +13 -0
- package/locales/ru-RU/marketAuth.json +13 -0
- package/locales/tr-TR/marketAuth.json +13 -0
- package/locales/vi-VN/marketAuth.json +13 -0
- package/locales/zh-CN/marketAuth.json +13 -0
- package/locales/zh-TW/marketAuth.json +13 -0
- package/package.json +1 -1
- package/packages/database/src/models/user.ts +2 -0
- package/packages/types/src/discover/mcp.ts +2 -1
- package/packages/types/src/tool/plugin.ts +2 -1
- package/src/app/[variants]/(main)/chat/settings/features/SmartAgentActionButton/MarketPublishButton.tsx +0 -2
- package/src/app/[variants]/(main)/discover/(detail)/mcp/features/Sidebar/ActionButton/index.tsx +33 -7
- package/src/features/PluginStore/McpList/List/Action.tsx +20 -1
- package/src/layout/AuthProvider/MarketAuth/MarketAuthConfirmModal.tsx +158 -0
- package/src/layout/AuthProvider/MarketAuth/MarketAuthProvider.tsx +130 -14
- package/src/libs/mcp/types.ts +8 -0
- package/src/locales/default/marketAuth.ts +13 -0
- package/src/server/routers/lambda/market/index.ts +85 -2
- package/src/server/services/discover/index.ts +45 -4
- package/src/services/discover.ts +1 -1
- package/src/services/mcp.ts +18 -3
- package/src/store/tool/slices/mcpStore/action.test.ts +141 -0
- package/src/store/tool/slices/mcpStore/action.ts +153 -11
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CURRENT_VERSION, isDesktop } from '@lobechat/const';
|
|
1
2
|
import { LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk';
|
|
2
3
|
import { PluginItem, PluginListResponse } from '@lobehub/market-sdk';
|
|
3
4
|
import { TRPCClientError } from '@trpc/client';
|
|
@@ -8,7 +9,6 @@ import { gt, valid } from 'semver';
|
|
|
8
9
|
import useSWR, { SWRResponse } from 'swr';
|
|
9
10
|
import { StateCreator } from 'zustand/vanilla';
|
|
10
11
|
|
|
11
|
-
import { CURRENT_VERSION, isDesktop } from '@lobechat/const';
|
|
12
12
|
import { MCPErrorData } from '@/libs/mcp/types';
|
|
13
13
|
import { discoverService } from '@/services/discover';
|
|
14
14
|
import { mcpService } from '@/services/mcp';
|
|
@@ -65,6 +65,71 @@ const toNonEmptyStringRecord = (input?: Record<string, any>) => {
|
|
|
65
65
|
}, {});
|
|
66
66
|
};
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Build manifest for cloud MCP connection from market data
|
|
70
|
+
* 从市场数据构建 Cloud MCP 的 manifest
|
|
71
|
+
*/
|
|
72
|
+
const buildCloudMcpManifest = (params: {
|
|
73
|
+
data: any;
|
|
74
|
+
plugin: { description?: string, icon?: string; identifier: string; };
|
|
75
|
+
}): LobeChatPluginManifest => {
|
|
76
|
+
const { data, plugin } = params;
|
|
77
|
+
|
|
78
|
+
log('Using cloud connection, building manifest from market data');
|
|
79
|
+
|
|
80
|
+
// 从 data 中获取 tools(MCP 格式)或 api(LobeChat 格式)
|
|
81
|
+
const mcpTools = data.tools;
|
|
82
|
+
const lobeChatApi = data.api;
|
|
83
|
+
|
|
84
|
+
// 如果是 MCP 格式的 tools,需要转换为 LobeChat 的 api 格式
|
|
85
|
+
// MCP: { name, description, inputSchema }
|
|
86
|
+
// LobeChat: { name, description, parameters }
|
|
87
|
+
let apiArray: any[] = [];
|
|
88
|
+
|
|
89
|
+
if (lobeChatApi) {
|
|
90
|
+
// 已经是 LobeChat 格式,直接使用
|
|
91
|
+
apiArray = lobeChatApi;
|
|
92
|
+
log('[Cloud MCP] Using existing LobeChat API format');
|
|
93
|
+
} else if (mcpTools && Array.isArray(mcpTools)) {
|
|
94
|
+
// 转换 MCP tools 格式到 LobeChat api 格式
|
|
95
|
+
apiArray = mcpTools.map((tool: any) => ({
|
|
96
|
+
description: tool.description || '',
|
|
97
|
+
name: tool.name,
|
|
98
|
+
parameters: tool.inputSchema || {},
|
|
99
|
+
}));
|
|
100
|
+
log('[Cloud MCP] Converted %d MCP tools to LobeChat API format', apiArray.length);
|
|
101
|
+
} else {
|
|
102
|
+
console.warn('[Cloud MCP] No tools or api found in manifest data');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// 构建完整的 manifest
|
|
106
|
+
const manifest: LobeChatPluginManifest = {
|
|
107
|
+
api: apiArray,
|
|
108
|
+
author: data.author?.name || data.author || '',
|
|
109
|
+
createAt: data.createdAt || new Date().toISOString(),
|
|
110
|
+
homepage: data.homepage || '',
|
|
111
|
+
identifier: plugin.identifier,
|
|
112
|
+
manifest: data.manifestUrl || '',
|
|
113
|
+
meta: {
|
|
114
|
+
avatar: data.icon || plugin.icon,
|
|
115
|
+
description: plugin.description || data.description,
|
|
116
|
+
tags: data.tags || [],
|
|
117
|
+
title: data.name || plugin.identifier,
|
|
118
|
+
},
|
|
119
|
+
name: data.name || plugin.identifier,
|
|
120
|
+
type: 'mcp',
|
|
121
|
+
version: data.version,
|
|
122
|
+
} as unknown as LobeChatPluginManifest;
|
|
123
|
+
|
|
124
|
+
log('[Cloud MCP] Final manifest built:', {
|
|
125
|
+
apiCount: manifest.api?.length,
|
|
126
|
+
identifier: manifest.identifier,
|
|
127
|
+
version: manifest.version,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
return manifest;
|
|
131
|
+
};
|
|
132
|
+
|
|
68
133
|
// 测试连接结果类型
|
|
69
134
|
export interface TestMcpConnectionResult {
|
|
70
135
|
error?: string;
|
|
@@ -139,6 +204,9 @@ export const createMCPPluginStoreSlice: StateCreator<
|
|
|
139
204
|
const normalizedConfig = toNonEmptyStringRecord(config);
|
|
140
205
|
let plugin = mcpStoreSelectors.getPluginById(identifier)(get());
|
|
141
206
|
|
|
207
|
+
// @ts-expect-error
|
|
208
|
+
const { haveCloudEndpoint } = plugin || {};
|
|
209
|
+
|
|
142
210
|
if (!plugin || !plugin.manifestUrl) {
|
|
143
211
|
const data = await discoverService.getMcpDetail({ identifier });
|
|
144
212
|
if (!data) return;
|
|
@@ -211,12 +279,18 @@ export const createMCPPluginStoreSlice: StateCreator<
|
|
|
211
279
|
? data.deploymentOptions
|
|
212
280
|
: [];
|
|
213
281
|
|
|
214
|
-
const httpOption =
|
|
215
|
-
(option) => option?.connection?.url && option?.connection?.type === 'http',
|
|
216
|
-
) ||
|
|
282
|
+
const httpOption =
|
|
217
283
|
deploymentOptions.find(
|
|
218
|
-
(option) => option?.connection?.url &&
|
|
219
|
-
)
|
|
284
|
+
(option) => option?.connection?.url && option?.connection?.type === 'http',
|
|
285
|
+
) ||
|
|
286
|
+
deploymentOptions.find((option) => option?.connection?.url && !option?.connection?.type);
|
|
287
|
+
|
|
288
|
+
// 查找 stdio 类型的部署选项
|
|
289
|
+
const stdioOption = deploymentOptions.find(
|
|
290
|
+
(option) =>
|
|
291
|
+
option?.connection?.type === 'stdio' ||
|
|
292
|
+
(!option?.connection?.type && !option?.connection?.url),
|
|
293
|
+
);
|
|
220
294
|
|
|
221
295
|
const hasNonHttpDeployment = deploymentOptions.some((option) => {
|
|
222
296
|
const type = option?.connection?.type;
|
|
@@ -225,9 +299,46 @@ export const createMCPPluginStoreSlice: StateCreator<
|
|
|
225
299
|
return type && type !== 'http';
|
|
226
300
|
});
|
|
227
301
|
|
|
228
|
-
|
|
302
|
+
// 🌐 检查是否有 cloudEndPoint:网页端 + stdio 类型 + 存在 haveCloudEndpoint
|
|
303
|
+
const hasCloudEndpoint = !isDesktop && stdioOption && haveCloudEndpoint;
|
|
304
|
+
|
|
305
|
+
console.log('hasCloudEndpoint', hasCloudEndpoint);
|
|
306
|
+
|
|
307
|
+
let shouldUseHttpDeployment = !!httpOption && (!hasNonHttpDeployment || !isDesktop);
|
|
308
|
+
|
|
309
|
+
if (hasCloudEndpoint) {
|
|
310
|
+
// 🌐 使用 cloudEndPoint,创建 cloud 类型的 connection
|
|
311
|
+
log('Using cloudEndPoint for stdio plugin: %s', haveCloudEndpoint);
|
|
312
|
+
|
|
313
|
+
connection = {
|
|
314
|
+
auth: stdioOption?.connection?.auth || { type: 'none' },
|
|
315
|
+
cloudEndPoint: haveCloudEndpoint,
|
|
316
|
+
headers: stdioOption?.connection?.headers,
|
|
317
|
+
type: 'cloud',
|
|
318
|
+
} as any;
|
|
319
|
+
|
|
320
|
+
log('Using cloud connection: %O', {
|
|
321
|
+
cloudEndPoint: haveCloudEndpoint,
|
|
322
|
+
type: connection.type,
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
const configSchema = stdioOption?.connection?.configSchema;
|
|
326
|
+
const needsConfig = doesConfigSchemaRequireInput(configSchema);
|
|
327
|
+
|
|
328
|
+
if (needsConfig && !normalizedConfig) {
|
|
329
|
+
updateMCPInstallProgress(identifier, {
|
|
330
|
+
configSchema,
|
|
331
|
+
connection,
|
|
332
|
+
manifest: data,
|
|
333
|
+
needsConfig: true,
|
|
334
|
+
progress: 50,
|
|
335
|
+
step: MCPInstallStep.CONFIGURATION_REQUIRED,
|
|
336
|
+
});
|
|
229
337
|
|
|
230
|
-
|
|
338
|
+
updateInstallLoadingState(identifier, undefined);
|
|
339
|
+
return false;
|
|
340
|
+
}
|
|
341
|
+
} else if (shouldUseHttpDeployment && httpOption) {
|
|
231
342
|
// ✅ HTTP 类型:跳过系统依赖检查,直接使用 URL
|
|
232
343
|
log('HTTP MCP detected, skipping system dependency check');
|
|
233
344
|
|
|
@@ -317,6 +428,7 @@ export const createMCPPluginStoreSlice: StateCreator<
|
|
|
317
428
|
|
|
318
429
|
let mergedHttpHeaders: Record<string, string> | undefined;
|
|
319
430
|
let mergedStdioEnv: Record<string, string> | undefined;
|
|
431
|
+
let mergedCloudHeaders: Record<string, string> | undefined;
|
|
320
432
|
|
|
321
433
|
if (connection?.type === 'http') {
|
|
322
434
|
const baseHeaders = toNonEmptyStringRecord(connection.headers);
|
|
@@ -340,6 +452,17 @@ export const createMCPPluginStoreSlice: StateCreator<
|
|
|
340
452
|
}
|
|
341
453
|
}
|
|
342
454
|
|
|
455
|
+
if (connection?.type === 'cloud') {
|
|
456
|
+
const baseHeaders = toNonEmptyStringRecord(connection.headers);
|
|
457
|
+
|
|
458
|
+
if (baseHeaders || normalizedConfig) {
|
|
459
|
+
mergedCloudHeaders = {
|
|
460
|
+
...baseHeaders,
|
|
461
|
+
...normalizedConfig,
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
343
466
|
// 获取服务器清单逻辑
|
|
344
467
|
updateInstallLoadingState(identifier, true);
|
|
345
468
|
|
|
@@ -383,6 +506,10 @@ export const createMCPPluginStoreSlice: StateCreator<
|
|
|
383
506
|
abortController.signal,
|
|
384
507
|
);
|
|
385
508
|
}
|
|
509
|
+
if (connection?.type === 'cloud') {
|
|
510
|
+
// 🌐 Cloud 类型:直接从市场数据构建 manifest
|
|
511
|
+
manifest = buildCloudMcpManifest({ data, plugin });
|
|
512
|
+
}
|
|
386
513
|
|
|
387
514
|
// set version
|
|
388
515
|
if (manifest) {
|
|
@@ -425,9 +552,21 @@ export const createMCPPluginStoreSlice: StateCreator<
|
|
|
425
552
|
return;
|
|
426
553
|
}
|
|
427
554
|
|
|
555
|
+
// 更新 connection 对象,将合并后的配置写入
|
|
556
|
+
const finalConnection = { ...connection };
|
|
557
|
+
if (finalConnection.type === 'http' && mergedHttpHeaders) {
|
|
558
|
+
finalConnection.headers = mergedHttpHeaders;
|
|
559
|
+
}
|
|
560
|
+
if (finalConnection.type === 'stdio' && mergedStdioEnv) {
|
|
561
|
+
finalConnection.env = mergedStdioEnv;
|
|
562
|
+
}
|
|
563
|
+
if (finalConnection.type === 'cloud' && mergedCloudHeaders) {
|
|
564
|
+
finalConnection.headers = mergedCloudHeaders;
|
|
565
|
+
}
|
|
566
|
+
|
|
428
567
|
await pluginService.installPlugin({
|
|
429
568
|
// 针对 mcp 先将 connection 信息存到 customParams 字段里
|
|
430
|
-
customParams: { mcp:
|
|
569
|
+
customParams: { mcp: finalConnection },
|
|
431
570
|
identifier: plugin.identifier,
|
|
432
571
|
manifest: manifest,
|
|
433
572
|
settings: normalizedConfig,
|
|
@@ -693,7 +832,9 @@ export const createMCPPluginStoreSlice: StateCreator<
|
|
|
693
832
|
|
|
694
833
|
useFetchMCPPluginList: (params) => {
|
|
695
834
|
const locale = globalHelpers.getCurrentLanguage();
|
|
696
|
-
const requestParams = isDesktop
|
|
835
|
+
const requestParams = isDesktop
|
|
836
|
+
? params
|
|
837
|
+
: { ...params, connectionType: McpConnectionType.http };
|
|
697
838
|
const swrKeyParts = [
|
|
698
839
|
'useFetchMCPPluginList',
|
|
699
840
|
locale,
|
|
@@ -702,7 +843,8 @@ export const createMCPPluginStoreSlice: StateCreator<
|
|
|
702
843
|
requestParams.q,
|
|
703
844
|
requestParams.connectionType,
|
|
704
845
|
];
|
|
705
|
-
const swrKey = swrKeyParts
|
|
846
|
+
const swrKey = swrKeyParts
|
|
847
|
+
.filter((part) => part !== undefined && part !== null && part !== '')
|
|
706
848
|
.join('-');
|
|
707
849
|
const page = requestParams.page ?? 1;
|
|
708
850
|
|