@lobehub/chat 1.109.0 → 1.110.0
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 +59 -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 +21 -0
- package/locales/ar/models.json +6 -0
- package/locales/ar/plugin.json +196 -136
- package/locales/ar/providers.json +3 -0
- package/locales/bg-BG/models.json +6 -0
- package/locales/bg-BG/plugin.json +204 -144
- package/locales/bg-BG/providers.json +3 -0
- package/locales/de-DE/models.json +6 -0
- package/locales/de-DE/plugin.json +176 -116
- package/locales/de-DE/providers.json +3 -0
- package/locales/en-US/models.json +6 -0
- package/locales/en-US/plugin.json +192 -132
- package/locales/en-US/providers.json +3 -0
- package/locales/es-ES/models.json +6 -0
- package/locales/es-ES/plugin.json +203 -143
- package/locales/es-ES/providers.json +3 -0
- package/locales/fa-IR/models.json +6 -0
- package/locales/fa-IR/plugin.json +155 -95
- package/locales/fa-IR/providers.json +3 -0
- package/locales/fr-FR/models.json +6 -0
- package/locales/fr-FR/plugin.json +161 -101
- package/locales/fr-FR/providers.json +3 -0
- package/locales/it-IT/models.json +6 -0
- package/locales/it-IT/plugin.json +193 -133
- package/locales/it-IT/providers.json +3 -0
- package/locales/ja-JP/models.json +6 -0
- package/locales/ja-JP/plugin.json +195 -135
- package/locales/ja-JP/providers.json +3 -0
- package/locales/ko-KR/models.json +6 -0
- package/locales/ko-KR/plugin.json +163 -103
- package/locales/ko-KR/providers.json +3 -0
- package/locales/nl-NL/models.json +6 -0
- package/locales/nl-NL/plugin.json +211 -151
- package/locales/nl-NL/providers.json +3 -0
- package/locales/pl-PL/models.json +6 -0
- package/locales/pl-PL/plugin.json +171 -111
- package/locales/pl-PL/providers.json +3 -0
- package/locales/pt-BR/models.json +6 -0
- package/locales/pt-BR/plugin.json +180 -120
- package/locales/pt-BR/providers.json +3 -0
- package/locales/ru-RU/models.json +6 -0
- package/locales/ru-RU/plugin.json +191 -131
- package/locales/ru-RU/providers.json +3 -0
- package/locales/tr-TR/models.json +6 -0
- package/locales/tr-TR/plugin.json +187 -127
- package/locales/tr-TR/providers.json +3 -0
- package/locales/vi-VN/models.json +6 -0
- package/locales/vi-VN/plugin.json +152 -92
- package/locales/vi-VN/providers.json +3 -0
- package/locales/zh-CN/models.json +6 -0
- package/locales/zh-CN/plugin.json +60 -0
- package/locales/zh-CN/providers.json +3 -0
- package/locales/zh-TW/models.json +6 -0
- package/locales/zh-TW/plugin.json +157 -97
- package/locales/zh-TW/providers.json +3 -0
- 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/config/aiModels/aihubmix.ts +465 -30
- package/src/config/aiModels/anthropic.ts +27 -1
- package/src/config/aiModels/groq.ts +40 -4
- package/src/config/aiModels/qwen.ts +24 -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/libs/model-runtime/anthropic/index.ts +15 -2
- package/src/libs/model-runtime/utils/modelParse.ts +2 -2
- package/src/libs/model-runtime/utils/streams/ollama.test.ts +97 -51
- package/src/libs/model-runtime/utils/streams/ollama.ts +4 -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,60 @@
|
|
1
|
+
/**
|
2
|
+
* MCP Schema - stdio 配置类型
|
3
|
+
*/
|
4
|
+
export interface McpStdioConfig {
|
5
|
+
args?: string[];
|
6
|
+
command: string;
|
7
|
+
env?: Record<string, string>;
|
8
|
+
type: 'stdio';
|
9
|
+
}
|
10
|
+
|
11
|
+
/**
|
12
|
+
* MCP Schema - http 配置类型
|
13
|
+
*/
|
14
|
+
export interface McpHttpConfig {
|
15
|
+
headers?: Record<string, string>;
|
16
|
+
type: 'http';
|
17
|
+
url: string;
|
18
|
+
}
|
19
|
+
|
20
|
+
/**
|
21
|
+
* MCP Schema 配置类型
|
22
|
+
*/
|
23
|
+
export type McpConfig = McpStdioConfig | McpHttpConfig;
|
24
|
+
|
25
|
+
/**
|
26
|
+
* MCP Schema 对象
|
27
|
+
* 符合 RFC 0001 定义
|
28
|
+
*/
|
29
|
+
export interface McpSchema {
|
30
|
+
/** 插件作者 */
|
31
|
+
author: string;
|
32
|
+
/** 插件配置 */
|
33
|
+
config: McpConfig;
|
34
|
+
/** 插件描述 */
|
35
|
+
description: string;
|
36
|
+
/** 插件主页 */
|
37
|
+
homepage?: string;
|
38
|
+
/** 插件图标 */
|
39
|
+
icon?: string;
|
40
|
+
/** 插件唯一标识符,必须与URL中的id参数匹配 */
|
41
|
+
identifier: string;
|
42
|
+
/** 插件名称 */
|
43
|
+
name: string;
|
44
|
+
/** 插件版本 (semver) */
|
45
|
+
version: string;
|
46
|
+
}
|
47
|
+
|
48
|
+
/**
|
49
|
+
* 协议URL解析结果
|
50
|
+
*/
|
51
|
+
export interface ProtocolUrlParsed {
|
52
|
+
/** 操作类型 (如: 'install') */
|
53
|
+
action: string;
|
54
|
+
/** 原始URL */
|
55
|
+
originalUrl: string;
|
56
|
+
/** 解析后的所有查询参数 */
|
57
|
+
params: Record<string, string>;
|
58
|
+
/** URL类型 (如: 'plugin') */
|
59
|
+
urlType: string;
|
60
|
+
}
|
@@ -0,0 +1,203 @@
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
2
|
+
|
3
|
+
import { McpSchema } from '../../types/protocol';
|
4
|
+
import { generateRFCProtocolUrl, parseProtocolUrl } from '../protocol';
|
5
|
+
|
6
|
+
describe('Protocol', () => {
|
7
|
+
describe('generateRFCProtocolUrl', () => {
|
8
|
+
it('should generate valid RFC protocol URL for stdio type', () => {
|
9
|
+
const schema: McpSchema = {
|
10
|
+
identifier: 'edgeone-mcp',
|
11
|
+
name: 'EdgeOne MCP',
|
12
|
+
author: 'Higress Team',
|
13
|
+
description: 'EdgeOne API integration for LobeChat',
|
14
|
+
version: '1.0.0',
|
15
|
+
homepage: 'https://github.com/higress/edgeone-mcp',
|
16
|
+
config: {
|
17
|
+
type: 'stdio',
|
18
|
+
command: 'npx',
|
19
|
+
args: ['-y', '@higress/edgeone-mcp'],
|
20
|
+
env: { NODE_ENV: 'production' },
|
21
|
+
},
|
22
|
+
};
|
23
|
+
|
24
|
+
const url = generateRFCProtocolUrl({
|
25
|
+
id: 'edgeone-mcp',
|
26
|
+
schema,
|
27
|
+
marketId: 'higress',
|
28
|
+
});
|
29
|
+
|
30
|
+
expect(url).toMatch(/^lobehub:\/\/plugin\/install\?/);
|
31
|
+
expect(url).toContain('id=edgeone-mcp');
|
32
|
+
expect(url).toContain('marketId=higress');
|
33
|
+
|
34
|
+
// Verify schema is URL encoded
|
35
|
+
const urlObj = new URL(url);
|
36
|
+
const schemaParam = urlObj.searchParams.get('schema');
|
37
|
+
expect(schemaParam).toBeTruthy();
|
38
|
+
// URLSearchParams.get() 自动解码,所以这里得到的是解码后的JSON
|
39
|
+
expect(schemaParam).toContain('"'); // 解码后的引号
|
40
|
+
});
|
41
|
+
|
42
|
+
it('should generate valid RFC protocol URL for http type', () => {
|
43
|
+
const schema: McpSchema = {
|
44
|
+
identifier: 'awesome-api',
|
45
|
+
name: 'Awesome API',
|
46
|
+
author: 'Smithery',
|
47
|
+
description: 'Awesome API integration',
|
48
|
+
version: '2.0.0',
|
49
|
+
config: {
|
50
|
+
type: 'http',
|
51
|
+
url: 'https://api.smithery.ai/v1/mcp',
|
52
|
+
headers: {
|
53
|
+
'Authorization': 'Bearer token123',
|
54
|
+
'X-Custom-Header': 'value',
|
55
|
+
},
|
56
|
+
},
|
57
|
+
};
|
58
|
+
|
59
|
+
const url = generateRFCProtocolUrl({
|
60
|
+
id: 'awesome-api',
|
61
|
+
schema,
|
62
|
+
marketId: 'smithery',
|
63
|
+
});
|
64
|
+
|
65
|
+
expect(url).toMatch(/^lobehub:\/\/plugin\/install\?/);
|
66
|
+
expect(url).toContain('id=awesome-api');
|
67
|
+
expect(url).toContain('marketId=smithery');
|
68
|
+
});
|
69
|
+
|
70
|
+
it('should throw error if schema identifier does not match id', () => {
|
71
|
+
const schema: McpSchema = {
|
72
|
+
identifier: 'wrong-id',
|
73
|
+
name: 'Test',
|
74
|
+
author: 'Test',
|
75
|
+
description: 'Test',
|
76
|
+
version: '1.0.0',
|
77
|
+
config: { type: 'stdio', command: 'test' },
|
78
|
+
};
|
79
|
+
|
80
|
+
expect(() => generateRFCProtocolUrl({ id: 'different-id', schema })).toThrowError(
|
81
|
+
'Schema identifier must match the id parameter',
|
82
|
+
);
|
83
|
+
});
|
84
|
+
});
|
85
|
+
|
86
|
+
describe('parseProtocolUrl', () => {
|
87
|
+
it('should parse RFC protocol URL correctly', () => {
|
88
|
+
const schema: McpSchema = {
|
89
|
+
identifier: 'test-mcp',
|
90
|
+
name: 'Test MCP',
|
91
|
+
author: 'Test Author',
|
92
|
+
description: 'Test Description',
|
93
|
+
version: '1.0.0',
|
94
|
+
config: {
|
95
|
+
type: 'stdio',
|
96
|
+
command: 'test',
|
97
|
+
args: ['arg1', 'arg2'],
|
98
|
+
},
|
99
|
+
};
|
100
|
+
|
101
|
+
const url = generateRFCProtocolUrl({
|
102
|
+
id: 'test-mcp',
|
103
|
+
schema,
|
104
|
+
marketId: 'lobehub',
|
105
|
+
});
|
106
|
+
|
107
|
+
const parsed = parseProtocolUrl(url);
|
108
|
+
|
109
|
+
expect(parsed).toBeTruthy();
|
110
|
+
expect(parsed?.urlType).toBe('plugin');
|
111
|
+
expect(parsed?.action).toBe('install');
|
112
|
+
expect(parsed?.params.type).toBe('mcp');
|
113
|
+
expect(parsed?.params.id).toBe('test-mcp');
|
114
|
+
expect(parsed?.params.marketId).toBe('lobehub');
|
115
|
+
expect(parsed?.originalUrl).toBe(url);
|
116
|
+
|
117
|
+
// 验证 schema 可以被解析
|
118
|
+
const parsedSchema = JSON.parse(parsed?.params.schema || '{}');
|
119
|
+
expect(parsedSchema).toEqual(schema);
|
120
|
+
});
|
121
|
+
|
122
|
+
it('should return null for invalid protocol', () => {
|
123
|
+
const result = parseProtocolUrl('http://example.com');
|
124
|
+
expect(result).toBeNull();
|
125
|
+
});
|
126
|
+
|
127
|
+
it('should parse URLs with any action', () => {
|
128
|
+
const result = parseProtocolUrl('lobehub://plugin/configure?id=test');
|
129
|
+
expect(result).toBeTruthy();
|
130
|
+
expect(result?.urlType).toBe('plugin');
|
131
|
+
expect(result?.action).toBe('configure');
|
132
|
+
expect(result?.params.id).toBe('test');
|
133
|
+
});
|
134
|
+
|
135
|
+
it('should parse URLs with any query parameters', () => {
|
136
|
+
const result = parseProtocolUrl('lobehub://plugin/install?custom=value&another=param');
|
137
|
+
expect(result).toBeTruthy();
|
138
|
+
expect(result?.urlType).toBe('plugin');
|
139
|
+
expect(result?.action).toBe('install');
|
140
|
+
expect(result?.params.custom).toBe('value');
|
141
|
+
expect(result?.params.another).toBe('param');
|
142
|
+
});
|
143
|
+
|
144
|
+
it('should handle URLs without query parameters', () => {
|
145
|
+
const result = parseProtocolUrl('lobehub://plugin/install');
|
146
|
+
expect(result).toBeTruthy();
|
147
|
+
expect(result?.urlType).toBe('plugin');
|
148
|
+
expect(result?.action).toBe('install');
|
149
|
+
expect(Object.keys(result?.params || {})).toHaveLength(0);
|
150
|
+
});
|
151
|
+
|
152
|
+
it('should return null for URLs without action', () => {
|
153
|
+
const result = parseProtocolUrl('lobehub://plugin/');
|
154
|
+
expect(result).toBeNull();
|
155
|
+
});
|
156
|
+
});
|
157
|
+
|
158
|
+
describe('URL encoding/decoding', () => {
|
159
|
+
it('should handle special characters correctly', () => {
|
160
|
+
const schema: McpSchema = {
|
161
|
+
identifier: 'special-chars',
|
162
|
+
name: '特殊字符 ñ 🚀',
|
163
|
+
author: 'Test <test@example.com>',
|
164
|
+
description: 'Description with "quotes" and \'apostrophes\'',
|
165
|
+
version: '1.0.0',
|
166
|
+
config: {
|
167
|
+
type: 'stdio',
|
168
|
+
command: 'cmd',
|
169
|
+
args: ['arg with spaces', 'arg/with/slashes'],
|
170
|
+
},
|
171
|
+
};
|
172
|
+
|
173
|
+
const url = generateRFCProtocolUrl({ id: 'special-chars', schema });
|
174
|
+
const parsed = parseProtocolUrl(url);
|
175
|
+
|
176
|
+
expect(parsed).toBeTruthy();
|
177
|
+
expect(parsed?.params.id).toBe('special-chars');
|
178
|
+
expect(parsed?.params.type).toBe('mcp');
|
179
|
+
|
180
|
+
// 验证 schema 可以正确解析
|
181
|
+
const parsedSchema = JSON.parse(parsed?.params.schema || '{}');
|
182
|
+
expect(parsedSchema).toEqual(schema);
|
183
|
+
});
|
184
|
+
|
185
|
+
it('should handle different protocol schemes', () => {
|
186
|
+
const testCases = [
|
187
|
+
'lobehub://plugin/install?test=value',
|
188
|
+
'lobehub-dev://plugin/install?test=value',
|
189
|
+
'lobehub-beta://plugin/install?test=value',
|
190
|
+
'lobehub-nightly://plugin/install?test=value',
|
191
|
+
];
|
192
|
+
|
193
|
+
testCases.forEach((url) => {
|
194
|
+
const parsed = parseProtocolUrl(url);
|
195
|
+
expect(parsed).toBeTruthy();
|
196
|
+
expect(parsed?.urlType).toBe('plugin');
|
197
|
+
expect(parsed?.action).toBe('install');
|
198
|
+
expect(parsed?.params.test).toBe('value');
|
199
|
+
expect(parsed?.originalUrl).toBe(url);
|
200
|
+
});
|
201
|
+
});
|
202
|
+
});
|
203
|
+
});
|
@@ -0,0 +1,210 @@
|
|
1
|
+
import { app } from 'electron';
|
2
|
+
|
3
|
+
import { McpSchema, ProtocolUrlParsed } from '../types/protocol';
|
4
|
+
|
5
|
+
export type AppChannel = 'stable' | 'beta' | 'nightly';
|
6
|
+
|
7
|
+
export const getProtocolScheme = (): string => {
|
8
|
+
// 在 Electron 环境中可以通过多种方式判断版本
|
9
|
+
const bundleId = app.name;
|
10
|
+
const appPath = app.getPath('exe');
|
11
|
+
|
12
|
+
// 通过 bundle identifier 判断
|
13
|
+
if (bundleId?.toLowerCase().includes('nightly')) return 'lobehub-nightly';
|
14
|
+
if (bundleId?.toLowerCase().includes('beta')) return 'lobehub-beta';
|
15
|
+
if (bundleId?.includes('dev')) return 'lobehub-dev';
|
16
|
+
|
17
|
+
// 通过可执行文件路径判断
|
18
|
+
if (appPath?.toLowerCase().includes('nightly')) return 'lobehub-nightly';
|
19
|
+
if (appPath?.toLowerCase().includes('beta')) return 'lobehub-beta';
|
20
|
+
if (appPath?.includes('dev')) return 'lobehub-dev';
|
21
|
+
|
22
|
+
return 'lobehub';
|
23
|
+
};
|
24
|
+
|
25
|
+
export const getVersionInfo = (): { channel: AppChannel; protocolScheme: string } => {
|
26
|
+
const protocolScheme = getProtocolScheme();
|
27
|
+
|
28
|
+
let appChannel: AppChannel = 'stable';
|
29
|
+
if (protocolScheme.includes('nightly')) {
|
30
|
+
appChannel = 'nightly';
|
31
|
+
} else if (protocolScheme.includes('beta')) {
|
32
|
+
appChannel = 'beta';
|
33
|
+
}
|
34
|
+
|
35
|
+
return {
|
36
|
+
channel: appChannel,
|
37
|
+
protocolScheme,
|
38
|
+
};
|
39
|
+
};
|
40
|
+
|
41
|
+
/**
|
42
|
+
* 验证 MCP Schema 对象结构
|
43
|
+
* @param schema 待验证的对象
|
44
|
+
* @returns 是否为有效的 MCP Schema
|
45
|
+
*/
|
46
|
+
function validateMcpSchema(schema: any): schema is McpSchema {
|
47
|
+
if (!schema || typeof schema !== 'object') return false;
|
48
|
+
|
49
|
+
// 必填字段验证
|
50
|
+
if (typeof schema.identifier !== 'string' || !schema.identifier) return false;
|
51
|
+
if (typeof schema.name !== 'string' || !schema.name) return false;
|
52
|
+
if (typeof schema.author !== 'string' || !schema.author) return false;
|
53
|
+
if (typeof schema.description !== 'string' || !schema.description) return false;
|
54
|
+
if (typeof schema.version !== 'string' || !schema.version) return false;
|
55
|
+
|
56
|
+
// 可选字段验证
|
57
|
+
if (schema.homepage !== undefined && typeof schema.homepage !== 'string') return false;
|
58
|
+
if (schema.icon !== undefined && typeof schema.icon !== 'string') return false;
|
59
|
+
|
60
|
+
// config 字段验证
|
61
|
+
if (!schema.config || typeof schema.config !== 'object') return false;
|
62
|
+
const config = schema.config;
|
63
|
+
|
64
|
+
if (config.type === 'stdio') {
|
65
|
+
if (typeof config.command !== 'string' || !config.command) return false;
|
66
|
+
if (config.args !== undefined && !Array.isArray(config.args)) return false;
|
67
|
+
if (config.env !== undefined && typeof config.env !== 'object') return false;
|
68
|
+
} else if (config.type === 'http') {
|
69
|
+
if (typeof config.url !== 'string' || !config.url) return false;
|
70
|
+
try {
|
71
|
+
new URL(config.url); // 验证URL格式
|
72
|
+
} catch {
|
73
|
+
return false;
|
74
|
+
}
|
75
|
+
if (config.headers !== undefined && typeof config.headers !== 'object') return false;
|
76
|
+
} else {
|
77
|
+
return false; // 未知的 config type
|
78
|
+
}
|
79
|
+
|
80
|
+
return true;
|
81
|
+
}
|
82
|
+
|
83
|
+
/**
|
84
|
+
* 解析 lobehub:// 协议 URL (支持多版本协议)
|
85
|
+
*
|
86
|
+
* 支持的URL格式:
|
87
|
+
* - lobehub://plugin/install?id=figma&schema=xxx&marketId=lobehub
|
88
|
+
* - lobehub://plugin/configure?id=xxx&...
|
89
|
+
* - lobehub-bet://plugin/install?id=figma&schema=xxx&marketId=lobehub
|
90
|
+
* - lobehub-nightly://plugin/install?id=figma&schema=xxx&marketId=lobehub
|
91
|
+
* - lobehub-dev://plugin/install?id=figma&schema=xxx&marketId=lobehub
|
92
|
+
*
|
93
|
+
* @param url 协议 URL
|
94
|
+
* @returns 解析结果,包含基本结构和所有查询参数
|
95
|
+
*/
|
96
|
+
export const parseProtocolUrl = (url: string): ProtocolUrlParsed | null => {
|
97
|
+
try {
|
98
|
+
const parsedUrl = new URL(url);
|
99
|
+
|
100
|
+
// 支持多种协议 scheme
|
101
|
+
const validProtocols = ['lobehub:', 'lobehub-dev:', 'lobehub-nightly:', 'lobehub-beta:'];
|
102
|
+
if (!validProtocols.includes(parsedUrl.protocol)) {
|
103
|
+
return null;
|
104
|
+
}
|
105
|
+
|
106
|
+
// 对于自定义协议,URL 解析后:
|
107
|
+
// lobehub://plugin/install -> hostname: "plugin", pathname: "/install"
|
108
|
+
const urlType = parsedUrl.hostname; // "plugin"
|
109
|
+
const pathParts = parsedUrl.pathname.split('/').filter(Boolean); // ["install"]
|
110
|
+
|
111
|
+
if (pathParts.length < 1) {
|
112
|
+
return null;
|
113
|
+
}
|
114
|
+
|
115
|
+
const action = pathParts[0]; // "install"
|
116
|
+
|
117
|
+
// 解析所有查询参数
|
118
|
+
const params: Record<string, string> = {};
|
119
|
+
const searchParams = new URLSearchParams(parsedUrl.search);
|
120
|
+
|
121
|
+
for (const [key, value] of searchParams.entries()) {
|
122
|
+
params[key] = value;
|
123
|
+
}
|
124
|
+
|
125
|
+
return {
|
126
|
+
action,
|
127
|
+
originalUrl: url,
|
128
|
+
params,
|
129
|
+
urlType,
|
130
|
+
};
|
131
|
+
} catch (error) {
|
132
|
+
console.error('Failed to parse protocol URL:', error);
|
133
|
+
return null;
|
134
|
+
}
|
135
|
+
};
|
136
|
+
|
137
|
+
/**
|
138
|
+
* 生成符合 RFC 0001 的协议 URL
|
139
|
+
*
|
140
|
+
* @param params 协议参数
|
141
|
+
* @returns 生成的协议URL
|
142
|
+
*/
|
143
|
+
export function generateRFCProtocolUrl(params: {
|
144
|
+
/** 插件唯一标识符 */
|
145
|
+
id: string;
|
146
|
+
/** Marketplace ID */
|
147
|
+
marketId?: string;
|
148
|
+
/** MCP Schema 对象 */
|
149
|
+
schema: McpSchema;
|
150
|
+
/** 协议 scheme (默认: lobehub) */
|
151
|
+
scheme?: string;
|
152
|
+
}): string {
|
153
|
+
const { id, schema, marketId, scheme = 'lobehub' } = params;
|
154
|
+
|
155
|
+
// 验证 schema.identifier 与 id 匹配
|
156
|
+
if (schema.identifier !== id) {
|
157
|
+
throw new Error('Schema identifier must match the id parameter');
|
158
|
+
}
|
159
|
+
|
160
|
+
// 验证 schema 结构
|
161
|
+
if (!validateMcpSchema(schema)) {
|
162
|
+
throw new Error('Invalid MCP Schema structure');
|
163
|
+
}
|
164
|
+
|
165
|
+
// 构建基础 URL
|
166
|
+
const baseUrl = `${scheme}://plugin/install`;
|
167
|
+
|
168
|
+
// 构建查询参数
|
169
|
+
const searchParams = new URLSearchParams();
|
170
|
+
|
171
|
+
// 必需参数
|
172
|
+
searchParams.set('type', 'mcp');
|
173
|
+
searchParams.set('id', id);
|
174
|
+
|
175
|
+
// 编码 schema - 直接传 JSON 字符串,让 URLSearchParams 自动编码
|
176
|
+
const schemaJson = JSON.stringify(schema);
|
177
|
+
searchParams.set('schema', schemaJson);
|
178
|
+
|
179
|
+
// 可选参数
|
180
|
+
if (marketId) {
|
181
|
+
searchParams.set('marketId', marketId);
|
182
|
+
}
|
183
|
+
|
184
|
+
return `${baseUrl}?${searchParams.toString()}`;
|
185
|
+
}
|
186
|
+
|
187
|
+
/**
|
188
|
+
* 生成协议 URL 示例
|
189
|
+
*
|
190
|
+
* @example
|
191
|
+
* ```typescript
|
192
|
+
* const url = generateRFCProtocolUrl({
|
193
|
+
* id: 'edgeone-mcp',
|
194
|
+
* schema: {
|
195
|
+
* identifier: 'edgeone-mcp',
|
196
|
+
* name: 'EdgeOne MCP',
|
197
|
+
* author: 'Higress Team',
|
198
|
+
* description: 'EdgeOne API integration for LobeChat',
|
199
|
+
* version: '1.0.0',
|
200
|
+
* config: {
|
201
|
+
* type: 'stdio',
|
202
|
+
* command: 'npx',
|
203
|
+
* args: ['-y', '@higress/edgeone-mcp']
|
204
|
+
* }
|
205
|
+
* },
|
206
|
+
* marketId: 'higress'
|
207
|
+
* });
|
208
|
+
* // Result: lobehub://plugin/install?id=edgeone-mcp&schema=%7B%22identifier%22%3A...&marketId=higress
|
209
|
+
* ```
|
210
|
+
*/
|
package/changelog/v1.json
CHANGED
@@ -1,4 +1,25 @@
|
|
1
1
|
[
|
2
|
+
{
|
3
|
+
"children": {
|
4
|
+
"features": [
|
5
|
+
"Support mcp plugin install from web."
|
6
|
+
]
|
7
|
+
},
|
8
|
+
"date": "2025-08-06",
|
9
|
+
"version": "1.110.0"
|
10
|
+
},
|
11
|
+
{
|
12
|
+
"children": {
|
13
|
+
"fixes": [
|
14
|
+
"Fix ollama model output without thinking."
|
15
|
+
],
|
16
|
+
"improvements": [
|
17
|
+
"Add Claude Opus 4.1 model, update i18n."
|
18
|
+
]
|
19
|
+
},
|
20
|
+
"date": "2025-08-06",
|
21
|
+
"version": "1.109.1"
|
22
|
+
},
|
2
23
|
{
|
3
24
|
"children": {
|
4
25
|
"features": [
|
package/locales/ar/models.json
CHANGED
@@ -1475,6 +1475,12 @@
|
|
1475
1475
|
"gpt-image-1": {
|
1476
1476
|
"description": "نموذج توليد الصور متعدد الوسائط الأصلي من ChatGPT"
|
1477
1477
|
},
|
1478
|
+
"gpt-oss": {
|
1479
|
+
"description": "GPT-OSS 20B هو نموذج لغة كبير مفتوح المصدر أصدرته OpenAI، يستخدم تقنية التكميم MXFP4، ومناسب للتشغيل على وحدات معالجة الرسومات الاستهلاكية المتقدمة أو أجهزة Mac بمعالج Apple Silicon. يتميز هذا النموذج بأداء ممتاز في توليد المحادثات، وكتابة الأكواد، ومهام الاستدلال، ويدعم استدعاء الدوال واستخدام الأدوات."
|
1480
|
+
},
|
1481
|
+
"gpt-oss:120b": {
|
1482
|
+
"description": "GPT-OSS 120B هو نموذج لغة كبير مفتوح المصدر أصدرته OpenAI، يستخدم تقنية التكميم MXFP4، ويعتبر نموذجًا رائدًا. يتطلب تشغيله بيئة متعددة وحدات معالجة الرسومات أو محطة عمل عالية الأداء، ويتميز بأداء متفوق في الاستدلال المعقد، وتوليد الأكواد، ومعالجة اللغات المتعددة، ويدعم استدعاء الدوال المتقدمة وتكامل الأدوات."
|
1483
|
+
},
|
1478
1484
|
"grok-2-1212": {
|
1479
1485
|
"description": "لقد تم تحسين هذا النموذج في الدقة، والامتثال للتعليمات، والقدرة على التعامل مع لغات متعددة."
|
1480
1486
|
},
|