@lobehub/chat 1.109.1 → 1.110.1
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/.cursor/rules/i18n.mdc +1 -2
- package/CHANGELOG.md +50 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/apps/desktop/electron-builder.js +22 -0
- package/apps/desktop/src/main/controllers/McpInstallCtr.ts +153 -0
- package/apps/desktop/src/main/controllers/index.ts +19 -0
- package/apps/desktop/src/main/core/App.ts +46 -0
- package/apps/desktop/src/main/core/infrastructure/IoCContainer.ts +4 -0
- package/apps/desktop/src/main/core/infrastructure/ProtocolManager.ts +256 -0
- package/apps/desktop/src/main/types/protocol.ts +60 -0
- package/apps/desktop/src/main/utils/__tests__/protocol.test.ts +203 -0
- package/apps/desktop/src/main/utils/protocol.ts +210 -0
- package/changelog/v1.json +18 -0
- package/locales/ar/plugin.json +196 -136
- package/locales/bg-BG/plugin.json +204 -144
- package/locales/de-DE/plugin.json +176 -116
- package/locales/en-US/plugin.json +192 -132
- package/locales/es-ES/plugin.json +203 -143
- package/locales/fa-IR/plugin.json +155 -95
- package/locales/fr-FR/plugin.json +161 -101
- package/locales/it-IT/plugin.json +193 -133
- package/locales/ja-JP/plugin.json +195 -135
- package/locales/ko-KR/plugin.json +163 -103
- package/locales/nl-NL/plugin.json +211 -151
- package/locales/pl-PL/plugin.json +171 -111
- package/locales/pt-BR/plugin.json +180 -120
- package/locales/ru-RU/plugin.json +191 -131
- package/locales/tr-TR/plugin.json +187 -127
- package/locales/vi-VN/plugin.json +152 -92
- package/locales/zh-CN/plugin.json +60 -0
- package/locales/zh-TW/plugin.json +157 -97
- package/package.json +2 -1
- package/packages/electron-client-ipc/src/events/index.ts +5 -2
- package/packages/electron-client-ipc/src/events/protocol.ts +29 -0
- package/packages/electron-client-ipc/src/types/index.ts +1 -0
- package/packages/electron-client-ipc/src/types/mcpInstall.ts +19 -0
- package/packages/types/src/plugins/mcp.ts +38 -1
- package/packages/types/src/plugins/protocol.ts +166 -0
- package/src/app/[variants]/(main)/chat/_layout/Desktop/index.tsx +4 -1
- package/src/app/[variants]/(main)/discover/(detail)/mcp/[slug]/features/Sidebar/ActionButton/index.tsx +1 -2
- package/src/components/KeyValueEditor/index.tsx +4 -2
- package/src/features/ChatItem/index.tsx +25 -2
- package/src/features/MCP/MCPInstallProgress/index.tsx +1 -1
- package/src/features/PluginDevModal/MCPManifestForm/index.tsx +30 -36
- package/src/features/PluginStore/McpList/List/Item.tsx +1 -1
- package/src/features/ProtocolUrlHandler/InstallPlugin/ConfigDisplay.tsx +211 -0
- package/src/features/ProtocolUrlHandler/InstallPlugin/CustomPluginInstallModal.tsx +228 -0
- package/src/features/ProtocolUrlHandler/InstallPlugin/OfficialPluginInstallModal/Detail.tsx +44 -0
- package/src/features/ProtocolUrlHandler/InstallPlugin/OfficialPluginInstallModal/index.tsx +105 -0
- package/src/features/ProtocolUrlHandler/InstallPlugin/index.tsx +55 -0
- package/src/features/ProtocolUrlHandler/InstallPlugin/types.ts +45 -0
- package/src/features/ProtocolUrlHandler/index.tsx +30 -0
- package/src/locales/default/plugin.ts +60 -0
- package/src/store/tool/slices/mcpStore/action.ts +127 -1
- package/src/store/tool/slices/mcpStore/initialState.ts +8 -13
- package/src/store/tool/slices/mcpStore/selectors.ts +13 -0
@@ -0,0 +1,228 @@
|
|
1
|
+
'use client';
|
2
|
+
|
3
|
+
import { Alert, Block, Modal, Text } from '@lobehub/ui';
|
4
|
+
import { App } from 'antd';
|
5
|
+
import { memo, useCallback, useEffect, useState } from 'react';
|
6
|
+
import { useTranslation } from 'react-i18next';
|
7
|
+
import { Flexbox } from 'react-layout-kit';
|
8
|
+
|
9
|
+
import PluginAvatar from '@/components/Plugins/PluginAvatar';
|
10
|
+
import PluginTag from '@/components/Plugins/PluginTag';
|
11
|
+
import { useAgentStore } from '@/store/agent';
|
12
|
+
import { useToolStore } from '@/store/tool';
|
13
|
+
import { mcpStoreSelectors } from '@/store/tool/selectors';
|
14
|
+
import { McpConnectionParams } from '@/types/plugins';
|
15
|
+
import { LobeToolCustomPlugin } from '@/types/tool/plugin';
|
16
|
+
|
17
|
+
import ConfigDisplay from './ConfigDisplay';
|
18
|
+
import { McpInstallRequest, TRUSTED_MARKETPLACES, TrustedMarketplaceId } from './types';
|
19
|
+
|
20
|
+
interface CustomPluginInstallModalProps {
|
21
|
+
installRequest: McpInstallRequest | null;
|
22
|
+
isMarketplace?: boolean;
|
23
|
+
onComplete?: () => void;
|
24
|
+
}
|
25
|
+
|
26
|
+
const CustomPluginInstallModal = memo<CustomPluginInstallModalProps>(
|
27
|
+
({ installRequest, isMarketplace = false, onComplete }) => {
|
28
|
+
const { message } = App.useApp();
|
29
|
+
const { t } = useTranslation('plugin');
|
30
|
+
const [loading, setLoading] = useState(false);
|
31
|
+
|
32
|
+
// 跟踪配置更新
|
33
|
+
const [updatedConfig, setUpdatedConfig] = useState<{
|
34
|
+
env?: Record<string, string>;
|
35
|
+
headers?: Record<string, string>;
|
36
|
+
}>({});
|
37
|
+
|
38
|
+
const [installCustomPlugin] = useToolStore((s) => [s.installCustomPlugin]);
|
39
|
+
const testMcpConnection = useToolStore((s) => s.testMcpConnection);
|
40
|
+
const togglePlugin = useAgentStore((s) => s.togglePlugin);
|
41
|
+
|
42
|
+
// 为自定义插件测试连接生成唯一标识符
|
43
|
+
const identifier = installRequest?.schema?.identifier || '';
|
44
|
+
const testState = useToolStore(mcpStoreSelectors.getMCPConnectionTestState(identifier));
|
45
|
+
|
46
|
+
const schema = installRequest?.schema;
|
47
|
+
const marketId = installRequest?.marketId;
|
48
|
+
const marketplace =
|
49
|
+
isMarketplace && marketId ? TRUSTED_MARKETPLACES[marketId as TrustedMarketplaceId] : null;
|
50
|
+
|
51
|
+
// 重置加载状态和配置
|
52
|
+
useEffect(() => {
|
53
|
+
if (!installRequest) {
|
54
|
+
setLoading(false);
|
55
|
+
setUpdatedConfig({});
|
56
|
+
}
|
57
|
+
}, [installRequest]);
|
58
|
+
|
59
|
+
const handleConfirm = useCallback(async () => {
|
60
|
+
if (!installRequest || !schema) return;
|
61
|
+
|
62
|
+
setLoading(true);
|
63
|
+
try {
|
64
|
+
// 第三方市场和自定义插件:构建自定义插件数据
|
65
|
+
let customPlugin: LobeToolCustomPlugin;
|
66
|
+
|
67
|
+
// 合并原始配置和用户更新的配置
|
68
|
+
const finalConfig = {
|
69
|
+
...schema.config,
|
70
|
+
env: updatedConfig.env || schema.config.env,
|
71
|
+
headers: updatedConfig.headers || schema.config.headers,
|
72
|
+
};
|
73
|
+
|
74
|
+
// 自定义插件:先测试连接获取真实的 manifest
|
75
|
+
const testParams: McpConnectionParams = {
|
76
|
+
connection: finalConfig,
|
77
|
+
identifier: identifier,
|
78
|
+
metadata: {
|
79
|
+
avatar: schema.icon,
|
80
|
+
description: schema.description,
|
81
|
+
},
|
82
|
+
};
|
83
|
+
console.log('testParams:', testParams);
|
84
|
+
|
85
|
+
const testResult = await testMcpConnection(testParams);
|
86
|
+
|
87
|
+
if (!testResult.success) {
|
88
|
+
throw new Error(testResult.error || t('protocolInstall.messages.connectionTestFailed'));
|
89
|
+
}
|
90
|
+
|
91
|
+
if (!testResult.manifest) {
|
92
|
+
throw new Error(t('protocolInstall.messages.manifestNotFound'));
|
93
|
+
}
|
94
|
+
|
95
|
+
// 使用测试连接获取的真实 manifest
|
96
|
+
customPlugin = {
|
97
|
+
customParams: {
|
98
|
+
avatar: schema.icon,
|
99
|
+
description: schema.description,
|
100
|
+
mcp: {
|
101
|
+
...finalConfig, // 使用合并后的配置
|
102
|
+
headers: finalConfig.type === 'http' ? finalConfig.headers : undefined,
|
103
|
+
},
|
104
|
+
},
|
105
|
+
identifier: schema.identifier,
|
106
|
+
manifest: testResult.manifest, // 使用真实的 manifest
|
107
|
+
type: 'customPlugin',
|
108
|
+
};
|
109
|
+
|
110
|
+
await installCustomPlugin(customPlugin);
|
111
|
+
await togglePlugin(schema.identifier);
|
112
|
+
message.success(t('protocolInstall.messages.installSuccess', { name: schema.name }));
|
113
|
+
|
114
|
+
onComplete?.();
|
115
|
+
} catch (error) {
|
116
|
+
console.error('Plugin installation error:', error);
|
117
|
+
message.error(t('protocolInstall.messages.installError'));
|
118
|
+
setLoading(false);
|
119
|
+
}
|
120
|
+
}, [
|
121
|
+
installRequest,
|
122
|
+
schema,
|
123
|
+
updatedConfig,
|
124
|
+
onComplete,
|
125
|
+
installCustomPlugin,
|
126
|
+
testMcpConnection,
|
127
|
+
togglePlugin,
|
128
|
+
message,
|
129
|
+
t,
|
130
|
+
identifier,
|
131
|
+
]);
|
132
|
+
|
133
|
+
const handleCancel = useCallback(() => {
|
134
|
+
onComplete?.();
|
135
|
+
}, [onComplete]);
|
136
|
+
|
137
|
+
if (!installRequest || !schema) return null;
|
138
|
+
|
139
|
+
// 根据类型渲染不同的 Alert 组件
|
140
|
+
const renderAlert = () => {
|
141
|
+
if (!isMarketplace) {
|
142
|
+
return (
|
143
|
+
<Alert
|
144
|
+
message={t('protocolInstall.custom.security.description')}
|
145
|
+
showIcon
|
146
|
+
type="warning"
|
147
|
+
variant={'borderless'}
|
148
|
+
/>
|
149
|
+
);
|
150
|
+
}
|
151
|
+
|
152
|
+
// marketplace 类型
|
153
|
+
return marketplace ? (
|
154
|
+
<Alert
|
155
|
+
message={t('protocolInstall.marketplace.trustedBy', { name: marketplace.name })}
|
156
|
+
showIcon
|
157
|
+
type="success"
|
158
|
+
variant={'borderless'}
|
159
|
+
/>
|
160
|
+
) : (
|
161
|
+
<Alert
|
162
|
+
message={t('protocolInstall.marketplace.unverified.warning')}
|
163
|
+
showIcon
|
164
|
+
type="warning"
|
165
|
+
variant={'borderless'}
|
166
|
+
/>
|
167
|
+
);
|
168
|
+
};
|
169
|
+
|
170
|
+
const modalTitle = isMarketplace
|
171
|
+
? t('protocolInstall.marketplace.title')
|
172
|
+
: t('protocolInstall.custom.title');
|
173
|
+
|
174
|
+
const okText = isMarketplace
|
175
|
+
? t('protocolInstall.actions.install')
|
176
|
+
: t('protocolInstall.actions.installAnyway');
|
177
|
+
|
178
|
+
return (
|
179
|
+
<Modal
|
180
|
+
confirmLoading={loading || testState.loading}
|
181
|
+
okText={okText}
|
182
|
+
onCancel={handleCancel}
|
183
|
+
onOk={handleConfirm}
|
184
|
+
open
|
185
|
+
title={modalTitle}
|
186
|
+
width={680}
|
187
|
+
>
|
188
|
+
<Flexbox gap={24}>
|
189
|
+
{renderAlert()}
|
190
|
+
|
191
|
+
<Block gap={16} horizontal justify={'space-between'} padding={16} variant={'outlined'}>
|
192
|
+
<Flexbox gap={16} horizontal>
|
193
|
+
<PluginAvatar avatar={schema.icon} size={40} />
|
194
|
+
<Flexbox gap={2}>
|
195
|
+
<Flexbox align={'center'} gap={8} horizontal>
|
196
|
+
{schema.name}
|
197
|
+
<PluginTag type={'customPlugin'} />
|
198
|
+
</Flexbox>
|
199
|
+
<Text style={{ fontSize: 12 }} type={'secondary'}>
|
200
|
+
{schema.description}
|
201
|
+
</Text>
|
202
|
+
</Flexbox>
|
203
|
+
</Flexbox>
|
204
|
+
</Block>
|
205
|
+
|
206
|
+
<Flexbox>
|
207
|
+
<ConfigDisplay onConfigUpdate={setUpdatedConfig} schema={schema} />
|
208
|
+
{/* 显示测试连接错误 */}
|
209
|
+
{testState.error && (
|
210
|
+
<Alert
|
211
|
+
closable
|
212
|
+
description={testState.error}
|
213
|
+
message={t('protocolInstall.messages.connectionTestFailed')}
|
214
|
+
showIcon
|
215
|
+
type="error"
|
216
|
+
variant={'filled'}
|
217
|
+
/>
|
218
|
+
)}
|
219
|
+
</Flexbox>
|
220
|
+
</Flexbox>
|
221
|
+
</Modal>
|
222
|
+
);
|
223
|
+
},
|
224
|
+
);
|
225
|
+
|
226
|
+
CustomPluginInstallModal.displayName = 'CustomPluginInstallModal';
|
227
|
+
|
228
|
+
export default CustomPluginInstallModal;
|
@@ -0,0 +1,44 @@
|
|
1
|
+
'use client';
|
2
|
+
|
3
|
+
import { memo, useState } from 'react';
|
4
|
+
import { Flexbox } from 'react-layout-kit';
|
5
|
+
|
6
|
+
import MCPInstallProgress from '@/features/MCP/MCPInstallProgress';
|
7
|
+
import Deployment from '@/features/MCPPluginDetail/Deployment';
|
8
|
+
import { DetailContextConfig, DetailProvider } from '@/features/MCPPluginDetail/DetailProvider';
|
9
|
+
import Header from '@/features/MCPPluginDetail/Header';
|
10
|
+
import Nav from '@/features/MCPPluginDetail/Nav';
|
11
|
+
import Overview from '@/features/MCPPluginDetail/Overview';
|
12
|
+
import Schema from '@/features/MCPPluginDetail/Schema';
|
13
|
+
import Score from '@/features/MCPPluginDetail/Score';
|
14
|
+
import { McpNavKey } from '@/types/discover';
|
15
|
+
|
16
|
+
interface OfficialDetailProps {
|
17
|
+
data: DetailContextConfig;
|
18
|
+
identifier: string;
|
19
|
+
}
|
20
|
+
|
21
|
+
const OfficialDetail = memo<OfficialDetailProps>(({ data, identifier }) => {
|
22
|
+
const [activeTab, setActiveTab] = useState(McpNavKey.Overview);
|
23
|
+
|
24
|
+
return (
|
25
|
+
<DetailProvider config={data}>
|
26
|
+
<Flexbox gap={16}>
|
27
|
+
<Header inModal />
|
28
|
+
<MCPInstallProgress identifier={identifier} />
|
29
|
+
|
30
|
+
<Nav activeTab={activeTab as McpNavKey} inModal noSettings setActiveTab={setActiveTab} />
|
31
|
+
<Flexbox gap={24}>
|
32
|
+
{activeTab === McpNavKey.Overview && <Overview inModal />}
|
33
|
+
{activeTab === McpNavKey.Deployment && <Deployment />}
|
34
|
+
{activeTab === McpNavKey.Schema && <Schema />}
|
35
|
+
{activeTab === McpNavKey.Score && <Score />}
|
36
|
+
</Flexbox>
|
37
|
+
</Flexbox>
|
38
|
+
</DetailProvider>
|
39
|
+
);
|
40
|
+
});
|
41
|
+
|
42
|
+
OfficialDetail.displayName = 'OfficialDetail';
|
43
|
+
|
44
|
+
export default OfficialDetail;
|
@@ -0,0 +1,105 @@
|
|
1
|
+
'use client';
|
2
|
+
|
3
|
+
import { Block, Modal, Text } from '@lobehub/ui';
|
4
|
+
import { App } from 'antd';
|
5
|
+
import { memo, useCallback, useState } from 'react';
|
6
|
+
import { useTranslation } from 'react-i18next';
|
7
|
+
|
8
|
+
import DetailLoading from '@/features/PluginStore/McpList/Detail/Loading';
|
9
|
+
import { useAgentStore } from '@/store/agent';
|
10
|
+
import { useDiscoverStore } from '@/store/discover';
|
11
|
+
import { useToolStore } from '@/store/tool';
|
12
|
+
import { pluginSelectors } from '@/store/tool/slices/plugin/selectors';
|
13
|
+
|
14
|
+
import { McpInstallRequest } from '../types';
|
15
|
+
import OfficialDetail from './Detail';
|
16
|
+
|
17
|
+
interface OfficialPluginInstallModalProps {
|
18
|
+
installRequest: McpInstallRequest | null;
|
19
|
+
onComplete: () => void;
|
20
|
+
}
|
21
|
+
|
22
|
+
const OfficialPluginInstallModal = memo<OfficialPluginInstallModalProps>(
|
23
|
+
({ installRequest, onComplete }) => {
|
24
|
+
const { message } = App.useApp();
|
25
|
+
const { t } = useTranslation(['plugin', 'common']);
|
26
|
+
const [loading, setLoading] = useState(false);
|
27
|
+
|
28
|
+
// 获取 MCP 插件详情
|
29
|
+
const useMcpDetail = useDiscoverStore((s) => s.useFetchMcpDetail);
|
30
|
+
const identifier = installRequest?.pluginId || '';
|
31
|
+
|
32
|
+
const [installed, installMCPPlugin] = useToolStore((s) => [
|
33
|
+
pluginSelectors.isPluginInstalled(identifier!)(s),
|
34
|
+
|
35
|
+
s.installMCPPlugin,
|
36
|
+
]);
|
37
|
+
const togglePlugin = useAgentStore((s) => s.togglePlugin);
|
38
|
+
|
39
|
+
const { data, isLoading } = useMcpDetail({ identifier });
|
40
|
+
|
41
|
+
const handleConfirm = useCallback(async () => {
|
42
|
+
if (!installRequest || !data) return;
|
43
|
+
|
44
|
+
setLoading(true);
|
45
|
+
try {
|
46
|
+
setLoading(true);
|
47
|
+
await installMCPPlugin(identifier);
|
48
|
+
await togglePlugin(identifier);
|
49
|
+
setLoading(false);
|
50
|
+
|
51
|
+
message.success(t('protocolInstall.messages.installSuccess', { name: data.name }));
|
52
|
+
onComplete();
|
53
|
+
} catch (error) {
|
54
|
+
console.error('Official plugin installation error:', error);
|
55
|
+
message.error(t('protocolInstall.messages.installError'));
|
56
|
+
setLoading(false);
|
57
|
+
}
|
58
|
+
}, [installRequest, data]);
|
59
|
+
|
60
|
+
if (!installRequest) return null;
|
61
|
+
|
62
|
+
// 渲染内容
|
63
|
+
const renderContent = () => {
|
64
|
+
// 如果正在加载,显示骨架屏
|
65
|
+
if (isLoading || !identifier) {
|
66
|
+
return <DetailLoading />;
|
67
|
+
}
|
68
|
+
|
69
|
+
// 如果加载失败或没有数据,显示错误信息
|
70
|
+
if (!data) {
|
71
|
+
return (
|
72
|
+
<Block>
|
73
|
+
<Text type="danger">{t('protocolInstall.messages.manifestError')}</Text>
|
74
|
+
</Block>
|
75
|
+
);
|
76
|
+
}
|
77
|
+
|
78
|
+
return <OfficialDetail data={data} identifier={identifier} />;
|
79
|
+
};
|
80
|
+
|
81
|
+
return (
|
82
|
+
<Modal
|
83
|
+
confirmLoading={loading}
|
84
|
+
okButtonProps={{
|
85
|
+
disabled: installed || isLoading,
|
86
|
+
type: installed ? 'default' : 'primary',
|
87
|
+
}}
|
88
|
+
okText={
|
89
|
+
installed ? t('protocolInstall.actions.installed') : t('protocolInstall.actions.install')
|
90
|
+
}
|
91
|
+
onCancel={onComplete}
|
92
|
+
onOk={handleConfirm}
|
93
|
+
open
|
94
|
+
title={t('protocolInstall.official.title')}
|
95
|
+
width={800}
|
96
|
+
>
|
97
|
+
{renderContent()}
|
98
|
+
</Modal>
|
99
|
+
);
|
100
|
+
},
|
101
|
+
);
|
102
|
+
|
103
|
+
OfficialPluginInstallModal.displayName = 'OfficialPluginInstallModal';
|
104
|
+
|
105
|
+
export default OfficialPluginInstallModal;
|
@@ -0,0 +1,55 @@
|
|
1
|
+
'use client';
|
2
|
+
|
3
|
+
import { memo } from 'react';
|
4
|
+
|
5
|
+
import CustomPluginInstallModal from './CustomPluginInstallModal';
|
6
|
+
import OfficialPluginInstallModal from './OfficialPluginInstallModal';
|
7
|
+
import { McpInstallRequest, PluginSource } from './types';
|
8
|
+
|
9
|
+
interface PluginInstallConfirmModalProps {
|
10
|
+
installRequest: McpInstallRequest | null;
|
11
|
+
onComplete: () => void;
|
12
|
+
}
|
13
|
+
|
14
|
+
/**
|
15
|
+
* 根据安装请求的来源确定插件类型
|
16
|
+
*/
|
17
|
+
const getPluginSource = (request: McpInstallRequest): PluginSource => {
|
18
|
+
const { marketId } = request;
|
19
|
+
|
20
|
+
// 官方 LobeHub 插件
|
21
|
+
if (marketId === 'lobehub') {
|
22
|
+
return PluginSource.OFFICIAL;
|
23
|
+
}
|
24
|
+
|
25
|
+
// 第三方市场插件(包括可信和不可信的)
|
26
|
+
if (marketId && marketId !== 'lobehub') {
|
27
|
+
return PluginSource.MARKETPLACE;
|
28
|
+
}
|
29
|
+
|
30
|
+
// 自定义插件(没有 marketId)
|
31
|
+
return PluginSource.CUSTOM;
|
32
|
+
};
|
33
|
+
|
34
|
+
const PluginInstallConfirmModal = memo<PluginInstallConfirmModalProps>(
|
35
|
+
({ installRequest, onComplete }) => {
|
36
|
+
if (!installRequest) return null;
|
37
|
+
|
38
|
+
const pluginSource = getPluginSource(installRequest);
|
39
|
+
|
40
|
+
if (pluginSource === PluginSource.OFFICIAL)
|
41
|
+
return <OfficialPluginInstallModal installRequest={installRequest} onComplete={onComplete} />;
|
42
|
+
|
43
|
+
return (
|
44
|
+
<CustomPluginInstallModal
|
45
|
+
installRequest={installRequest}
|
46
|
+
isMarketplace={pluginSource === PluginSource.MARKETPLACE}
|
47
|
+
onComplete={onComplete}
|
48
|
+
/>
|
49
|
+
);
|
50
|
+
},
|
51
|
+
);
|
52
|
+
|
53
|
+
PluginInstallConfirmModal.displayName = 'PluginInstallConfirmModal';
|
54
|
+
|
55
|
+
export default PluginInstallConfirmModal;
|
@@ -0,0 +1,45 @@
|
|
1
|
+
import { McpInstallSchema } from '@lobechat/electron-client-ipc';
|
2
|
+
|
3
|
+
export enum PluginSource {
|
4
|
+
CUSTOM = 'custom',
|
5
|
+
MARKETPLACE = 'marketplace',
|
6
|
+
OFFICIAL = 'official',
|
7
|
+
}
|
8
|
+
|
9
|
+
export interface McpInstallRequest {
|
10
|
+
marketId?: string;
|
11
|
+
pluginId: string;
|
12
|
+
schema?: McpInstallSchema;
|
13
|
+
source: string;
|
14
|
+
}
|
15
|
+
|
16
|
+
export interface BaseContentProps {
|
17
|
+
installRequest: McpInstallRequest;
|
18
|
+
}
|
19
|
+
|
20
|
+
export interface ModalConfig {
|
21
|
+
okText: string;
|
22
|
+
title: string;
|
23
|
+
width?: number;
|
24
|
+
}
|
25
|
+
|
26
|
+
// 可信的第三方市场列表
|
27
|
+
export const TRUSTED_MARKETPLACES = {
|
28
|
+
higress: {
|
29
|
+
description: 'Enterprise-grade MCP plugins for cloud-native applications',
|
30
|
+
name: 'Higress Marketplace',
|
31
|
+
website: 'https://higress.ai',
|
32
|
+
},
|
33
|
+
mcprouter: {
|
34
|
+
description: 'Community-driven MCP plugin marketplace',
|
35
|
+
name: 'MCPRouter',
|
36
|
+
website: 'https://mcprouter.com',
|
37
|
+
},
|
38
|
+
smithery: {
|
39
|
+
description: 'Professional MCP plugins and tools',
|
40
|
+
name: 'Smithery',
|
41
|
+
website: 'https://smithery.ai',
|
42
|
+
},
|
43
|
+
} as const;
|
44
|
+
|
45
|
+
export type TrustedMarketplaceId = keyof typeof TRUSTED_MARKETPLACES;
|
@@ -0,0 +1,30 @@
|
|
1
|
+
'use client';
|
2
|
+
|
3
|
+
import { useWatchBroadcast } from '@lobechat/electron-client-ipc';
|
4
|
+
import { useCallback, useState } from 'react';
|
5
|
+
|
6
|
+
import { McpInstallRequest } from '@/features/ProtocolUrlHandler/InstallPlugin/types';
|
7
|
+
|
8
|
+
import PluginInstallConfirmModal from './InstallPlugin';
|
9
|
+
|
10
|
+
const ProtocolUrlHandler = () => {
|
11
|
+
const [installRequest, setInstallRequest] = useState<McpInstallRequest | null>(null);
|
12
|
+
|
13
|
+
const handleMcpInstallRequest = useCallback(
|
14
|
+
(data: { marketId?: string; pluginId: string; schema: any }) => {
|
15
|
+
// 将原始数据传递给子组件处理
|
16
|
+
setInstallRequest(data as McpInstallRequest);
|
17
|
+
},
|
18
|
+
[],
|
19
|
+
);
|
20
|
+
|
21
|
+
const handleComplete = useCallback(() => {
|
22
|
+
setInstallRequest(null);
|
23
|
+
}, []);
|
24
|
+
|
25
|
+
useWatchBroadcast('mcpInstallRequest', handleMcpInstallRequest);
|
26
|
+
|
27
|
+
return <PluginInstallConfirmModal installRequest={installRequest} onComplete={handleComplete} />;
|
28
|
+
};
|
29
|
+
|
30
|
+
export default ProtocolUrlHandler;
|
@@ -308,6 +308,66 @@ export default {
|
|
308
308
|
skipDependencies: '跳过检查',
|
309
309
|
},
|
310
310
|
pluginList: '插件列表',
|
311
|
+
protocolInstall: {
|
312
|
+
actions: {
|
313
|
+
install: '安装',
|
314
|
+
installAnyway: '仍要安装',
|
315
|
+
installed: '已安装',
|
316
|
+
},
|
317
|
+
config: {
|
318
|
+
args: '参数',
|
319
|
+
command: '命令',
|
320
|
+
env: '环境变量',
|
321
|
+
headers: '请求头',
|
322
|
+
title: '配置信息',
|
323
|
+
type: {
|
324
|
+
http: '类型: HTTP',
|
325
|
+
label: '类型',
|
326
|
+
stdio: '类型: Stdio',
|
327
|
+
},
|
328
|
+
url: '服务地址',
|
329
|
+
},
|
330
|
+
custom: {
|
331
|
+
badge: '自定义插件',
|
332
|
+
security: {
|
333
|
+
description: '此插件未经过官方验证,安装可能存在安全风险!请确保您信任插件来源。',
|
334
|
+
title: '⚠️ 安全风险提示',
|
335
|
+
},
|
336
|
+
title: '安装自定义插件',
|
337
|
+
},
|
338
|
+
marketplace: {
|
339
|
+
title: '安装第三方插件',
|
340
|
+
trustedBy: '由 {{name}} 提供',
|
341
|
+
unverified: {
|
342
|
+
title: '未经验证的第三方插件',
|
343
|
+
warning: '此插件来自未验证的第三方市场,安装前请确认您信任该来源。',
|
344
|
+
},
|
345
|
+
verified: '已验证',
|
346
|
+
},
|
347
|
+
messages: {
|
348
|
+
connectionTestFailed: '连接测试失败',
|
349
|
+
installError: '插件安装失败,请重试',
|
350
|
+
installSuccess: '插件 {{name}} 安装成功!',
|
351
|
+
manifestError: '获取插件详情失败,请检查网络连接后重试',
|
352
|
+
manifestNotFound: '未能获取插件描述文件',
|
353
|
+
},
|
354
|
+
meta: {
|
355
|
+
author: '作者',
|
356
|
+
homepage: '主页',
|
357
|
+
identifier: '标识符',
|
358
|
+
source: '来源',
|
359
|
+
version: '版本',
|
360
|
+
},
|
361
|
+
official: {
|
362
|
+
badge: 'LobeHub 官方插件',
|
363
|
+
description: '此插件由 LobeHub 官方开发和维护,经过严格的安全审核,可放心使用。',
|
364
|
+
loadingMessage: '正在获取插件详情...',
|
365
|
+
loadingTitle: '加载中',
|
366
|
+
title: '安装官方插件',
|
367
|
+
},
|
368
|
+
title: '安装 MCP 插件',
|
369
|
+
warning: '⚠️ 请确认您信任此插件的来源,恶意插件可能会危害您的系统安全。',
|
370
|
+
},
|
311
371
|
search: {
|
312
372
|
apiName: {
|
313
373
|
crawlMultiPages: '读取多个页面内容',
|