@lobehub/lobehub 2.0.0-next.232 → 2.0.0-next.234
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/.github/workflows/bundle-analyzer.yml +1 -1
- package/.github/workflows/e2e.yml +62 -53
- package/.github/workflows/manual-build-desktop.yml +5 -5
- package/.github/workflows/pr-build-desktop.yml +4 -4
- package/.github/workflows/pr-build-docker.yml +2 -2
- package/.github/workflows/release-desktop-beta.yml +4 -4
- package/.github/workflows/release-docker.yml +2 -2
- package/.github/workflows/test.yml +44 -7
- package/CHANGELOG.md +59 -0
- package/CLAUDE.md +1 -1
- package/changelog/v1.json +14 -0
- package/docs/development/basic/feature-development.mdx +4 -5
- package/docs/development/basic/feature-development.zh-CN.mdx +4 -5
- package/docs/self-hosting/environment-variables/auth.mdx +7 -0
- package/docs/self-hosting/environment-variables/auth.zh-CN.mdx +7 -0
- package/e2e/README.md +6 -6
- package/e2e/src/features/community/detail-pages.feature +9 -9
- package/e2e/src/features/community/interactions.feature +13 -13
- package/e2e/src/features/community/smoke.feature +6 -6
- package/e2e/src/steps/agent/conversation-mgmt.steps.ts +196 -25
- package/e2e/src/steps/agent/conversation.steps.ts +58 -0
- package/e2e/src/steps/agent/message-ops.steps.ts +20 -15
- package/e2e/src/steps/community/detail-pages.steps.ts +60 -19
- package/e2e/src/steps/community/interactions.steps.ts +145 -32
- package/e2e/src/steps/hooks.ts +12 -2
- package/locales/en-US/setting.json +3 -0
- package/locales/zh-CN/file.json +4 -0
- package/locales/zh-CN/setting.json +3 -0
- package/package.json +5 -5
- package/packages/business/config/src/llm.ts +6 -1
- package/packages/const/src/index.ts +1 -0
- package/packages/const/src/lobehubSkill.ts +55 -0
- package/packages/const/src/settings/image.ts +1 -1
- package/packages/model-bank/src/aiModels/azure.ts +2 -2
- package/packages/model-bank/src/aiModels/google.ts +1 -0
- package/packages/model-bank/src/aiModels/lobehub.ts +33 -13
- package/packages/model-bank/src/aiModels/openai.ts +21 -4
- package/packages/model-runtime/src/core/openaiCompatibleFactory/createImage.ts +4 -1
- package/packages/model-runtime/src/providers/openai/__snapshots__/index.test.ts.snap +1 -1
- package/packages/ssrf-safe-fetch/index.test.ts +5 -34
- package/packages/ssrf-safe-fetch/index.ts +12 -2
- package/packages/types/package.json +1 -1
- package/packages/types/src/files/upload.ts +11 -1
- package/packages/types/src/message/common/tools.ts +1 -1
- package/packages/types/src/serverConfig.ts +1 -0
- package/public/not-compatible.html +1296 -0
- package/src/app/[variants]/(main)/image/_layout/ConfigPanel/components/MultiImagesUpload/index.tsx +3 -3
- package/src/app/[variants]/(main)/image/features/GenerationFeed/index.tsx +3 -10
- package/src/app/[variants]/(main)/image/index.tsx +1 -1
- package/src/app/[variants]/(main)/resource/features/FileDetail.tsx +20 -12
- package/src/app/[variants]/(main)/resource/features/modal/FullscreenModal.tsx +2 -4
- package/src/app/[variants]/layout.tsx +50 -1
- package/src/envs/auth.ts +15 -0
- package/src/features/ChatInput/ActionBar/Tools/LobehubSkillServerItem.tsx +304 -0
- package/src/features/ChatInput/ActionBar/Tools/useControls.tsx +74 -10
- package/src/features/Conversation/Messages/AssistantGroup/Tool/Inspector/ToolTitle.tsx +9 -0
- package/src/features/FileViewer/Renderer/Code/index.tsx +224 -0
- package/src/features/FileViewer/Renderer/Image/index.tsx +8 -1
- package/src/features/FileViewer/Renderer/PDF/index.tsx +3 -1
- package/src/features/FileViewer/Renderer/PDF/style.ts +2 -1
- package/src/features/FileViewer/index.tsx +135 -24
- package/src/features/PageEditor/EditorCanvas/useSlashItems.tsx +7 -4
- package/src/features/PageEditor/store/initialState.ts +2 -1
- package/src/features/ResourceManager/components/Editor/FileContent.tsx +1 -4
- package/src/features/ResourceManager/components/Editor/FileCopilot.tsx +64 -0
- package/src/features/ResourceManager/components/Editor/index.tsx +98 -31
- package/src/features/ResourceManager/components/Explorer/ItemDropdown/useFileItemDropdown.tsx +3 -2
- package/src/features/ResourceManager/components/Explorer/ListView/ColumnResizeHandle.tsx +119 -0
- package/src/features/ResourceManager/components/Explorer/ListView/ListItem/index.tsx +67 -22
- package/src/features/ResourceManager/components/Explorer/ListView/Skeleton.tsx +46 -11
- package/src/features/ResourceManager/components/Explorer/ListView/index.tsx +140 -81
- package/src/features/ResourceManager/components/Explorer/ToolBar/SortDropdown.tsx +20 -12
- package/src/features/ResourceManager/components/Explorer/ToolBar/ViewSwitcher.tsx +18 -10
- package/src/features/ResourceManager/components/UploadDock/Item.tsx +38 -6
- package/src/features/ResourceManager/components/UploadDock/index.tsx +62 -41
- package/src/features/ResourceManager/index.tsx +1 -0
- package/src/helpers/toolEngineering/index.test.ts +3 -0
- package/src/helpers/toolEngineering/index.ts +12 -1
- package/src/hooks/useFetchAiImageConfig.ts +54 -10
- package/src/libs/trpc/utils/internalJwt.ts +2 -2
- package/src/locales/default/file.ts +4 -0
- package/src/locales/default/setting.ts +3 -0
- package/src/server/globalConfig/index.ts +1 -0
- package/src/server/modules/ModelRuntime/index.test.ts +214 -1
- package/src/server/modules/ModelRuntime/index.ts +43 -7
- package/src/server/routers/lambda/document.ts +44 -0
- package/src/server/routers/tools/market.ts +261 -0
- package/src/server/services/document/index.ts +22 -0
- package/src/services/document/index.ts +4 -0
- package/src/services/upload.ts +22 -2
- package/src/store/chat/slices/plugin/actions/internals.ts +15 -2
- package/src/store/chat/slices/plugin/actions/pluginTypes.ts +104 -0
- package/src/store/file/slices/fileManager/action.test.ts +9 -3
- package/src/store/file/slices/fileManager/action.ts +165 -70
- package/src/store/file/slices/upload/action.ts +3 -0
- package/src/store/global/actions/general.ts +15 -0
- package/src/store/global/initialState.ts +13 -0
- package/src/store/image/slices/generationConfig/initialState.ts +5 -5
- package/src/store/image/slices/generationConfig/selectors.test.ts +11 -4
- package/src/store/serverConfig/selectors.ts +1 -0
- package/src/store/tool/initialState.ts +11 -2
- package/src/store/tool/selectors/index.ts +1 -0
- package/src/store/tool/selectors/tool.ts +3 -1
- package/src/store/tool/slices/lobehubSkillStore/action.ts +361 -0
- package/src/store/tool/slices/lobehubSkillStore/index.ts +4 -0
- package/src/store/tool/slices/lobehubSkillStore/initialState.ts +24 -0
- package/src/store/tool/slices/lobehubSkillStore/selectors.ts +145 -0
- package/src/store/tool/slices/lobehubSkillStore/types.ts +100 -0
- package/src/store/tool/store.ts +8 -2
- package/vitest.config.mts +11 -6
- package/src/features/FileViewer/Renderer/JavaScript/index.tsx +0 -66
- package/src/features/FileViewer/Renderer/TXT/index.tsx +0 -50
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import { getLobehubSkillProviderById } from '@lobechat/const';
|
|
2
|
+
import { enableMapSet, produce } from 'immer';
|
|
3
|
+
import useSWR, { type SWRResponse } from 'swr';
|
|
4
|
+
import { type StateCreator } from 'zustand/vanilla';
|
|
5
|
+
|
|
6
|
+
import { toolsClient } from '@/libs/trpc/client';
|
|
7
|
+
import { setNamespace } from '@/utils/storeDebug';
|
|
8
|
+
|
|
9
|
+
import { type ToolStore } from '../../store';
|
|
10
|
+
import { type LobehubSkillStoreState } from './initialState';
|
|
11
|
+
import {
|
|
12
|
+
type CallLobehubSkillToolParams,
|
|
13
|
+
type CallLobehubSkillToolResult,
|
|
14
|
+
type LobehubSkillServer,
|
|
15
|
+
LobehubSkillStatus,
|
|
16
|
+
type LobehubSkillTool,
|
|
17
|
+
} from './types';
|
|
18
|
+
|
|
19
|
+
enableMapSet();
|
|
20
|
+
|
|
21
|
+
const n = setNamespace('lobehubSkillStore');
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* LobeHub Skill Store Actions
|
|
25
|
+
*/
|
|
26
|
+
export interface LobehubSkillStoreAction {
|
|
27
|
+
/**
|
|
28
|
+
* 调用 LobeHub Skill 工具
|
|
29
|
+
*/
|
|
30
|
+
callLobehubSkillTool: (params: CallLobehubSkillToolParams) => Promise<CallLobehubSkillToolResult>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 获取单个 Provider 的连接状态
|
|
34
|
+
* @param provider - Provider ID (如 'linear')
|
|
35
|
+
*/
|
|
36
|
+
checkLobehubSkillStatus: (provider: string) => Promise<LobehubSkillServer | undefined>;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 获取 Provider 的授权信息(URL、code、过期时间)
|
|
40
|
+
* @param provider - Provider ID (如 'linear')
|
|
41
|
+
* @param options - 可选的 scopes 和 redirectUri
|
|
42
|
+
* @returns 授权 URL 和相关信息
|
|
43
|
+
*/
|
|
44
|
+
getLobehubSkillAuthorizeUrl: (
|
|
45
|
+
provider: string,
|
|
46
|
+
options?: { redirectUri?: string; scopes?: string[] },
|
|
47
|
+
) => Promise<{ authorizeUrl: string; code: string; expiresIn: number }>;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 内部方法: 更新 Server 状态
|
|
51
|
+
*/
|
|
52
|
+
internal_updateLobehubSkillServer: (
|
|
53
|
+
provider: string,
|
|
54
|
+
update: Partial<LobehubSkillServer>,
|
|
55
|
+
) => void;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 刷新 Provider 的 Token (如果支持)
|
|
59
|
+
* @param provider - Provider ID
|
|
60
|
+
*/
|
|
61
|
+
refreshLobehubSkillToken: (provider: string) => Promise<boolean>;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 刷新 Provider 的工具列表
|
|
65
|
+
* @param provider - Provider ID
|
|
66
|
+
*/
|
|
67
|
+
refreshLobehubSkillTools: (provider: string) => Promise<void>;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 断开 Provider 连接
|
|
71
|
+
* @param provider - Provider ID
|
|
72
|
+
*/
|
|
73
|
+
revokeLobehubSkill: (provider: string) => Promise<void>;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 使用 SWR 获取用户的所有连接状态
|
|
77
|
+
* @param enabled - 是否启用获取
|
|
78
|
+
*/
|
|
79
|
+
useFetchLobehubSkillConnections: (enabled: boolean) => SWRResponse<LobehubSkillServer[]>;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export const createLobehubSkillStoreSlice: StateCreator<
|
|
83
|
+
ToolStore,
|
|
84
|
+
[['zustand/devtools', never]],
|
|
85
|
+
[],
|
|
86
|
+
LobehubSkillStoreAction
|
|
87
|
+
> = (set, get) => ({
|
|
88
|
+
callLobehubSkillTool: async (params) => {
|
|
89
|
+
const { provider, toolName, args } = params;
|
|
90
|
+
const toolId = `${provider}:${toolName}`;
|
|
91
|
+
|
|
92
|
+
set(
|
|
93
|
+
produce((draft: LobehubSkillStoreState) => {
|
|
94
|
+
draft.lobehubSkillExecutingToolIds.add(toolId);
|
|
95
|
+
}),
|
|
96
|
+
false,
|
|
97
|
+
n('callLobehubSkillTool/start'),
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
const response = await toolsClient.market.connectCallTool.mutate({
|
|
102
|
+
args,
|
|
103
|
+
provider,
|
|
104
|
+
toolName,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
set(
|
|
108
|
+
produce((draft: LobehubSkillStoreState) => {
|
|
109
|
+
draft.lobehubSkillExecutingToolIds.delete(toolId);
|
|
110
|
+
}),
|
|
111
|
+
false,
|
|
112
|
+
n('callLobehubSkillTool/success'),
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
return { data: response.data, success: true };
|
|
116
|
+
} catch (error) {
|
|
117
|
+
console.error('[LobehubSkill] Failed to call tool:', error);
|
|
118
|
+
|
|
119
|
+
set(
|
|
120
|
+
produce((draft: LobehubSkillStoreState) => {
|
|
121
|
+
draft.lobehubSkillExecutingToolIds.delete(toolId);
|
|
122
|
+
}),
|
|
123
|
+
false,
|
|
124
|
+
n('callLobehubSkillTool/error'),
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
128
|
+
|
|
129
|
+
if (errorMessage.includes('NOT_CONNECTED') || errorMessage.includes('TOKEN_EXPIRED')) {
|
|
130
|
+
return {
|
|
131
|
+
error: errorMessage,
|
|
132
|
+
errorCode: 'NOT_CONNECTED',
|
|
133
|
+
success: false,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
error: errorMessage,
|
|
139
|
+
success: false,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
checkLobehubSkillStatus: async (provider) => {
|
|
145
|
+
set(
|
|
146
|
+
produce((draft: LobehubSkillStoreState) => {
|
|
147
|
+
draft.lobehubSkillLoadingIds.add(provider);
|
|
148
|
+
}),
|
|
149
|
+
false,
|
|
150
|
+
n('checkLobehubSkillStatus/start'),
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
const response = await toolsClient.market.connectGetStatus.query({ provider });
|
|
155
|
+
// Get provider config from local definition for correct display name
|
|
156
|
+
const providerConfig = getLobehubSkillProviderById(provider);
|
|
157
|
+
|
|
158
|
+
const server: LobehubSkillServer = {
|
|
159
|
+
cachedAt: Date.now(),
|
|
160
|
+
icon: response.icon,
|
|
161
|
+
identifier: provider,
|
|
162
|
+
isConnected: response.connected,
|
|
163
|
+
// Use local config label (e.g., "Linear") instead of API's providerName
|
|
164
|
+
name: providerConfig?.label || provider,
|
|
165
|
+
providerUsername: response.connection?.providerUsername,
|
|
166
|
+
scopes: response.connection?.scopes,
|
|
167
|
+
status: response.connected
|
|
168
|
+
? LobehubSkillStatus.CONNECTED
|
|
169
|
+
: LobehubSkillStatus.NOT_CONNECTED,
|
|
170
|
+
tokenExpiresAt: response.connection?.tokenExpiresAt,
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
set(
|
|
174
|
+
produce((draft: LobehubSkillStoreState) => {
|
|
175
|
+
const existingIndex = draft.lobehubSkillServers.findIndex(
|
|
176
|
+
(s) => s.identifier === provider,
|
|
177
|
+
);
|
|
178
|
+
if (existingIndex >= 0) {
|
|
179
|
+
draft.lobehubSkillServers[existingIndex] = server;
|
|
180
|
+
} else {
|
|
181
|
+
draft.lobehubSkillServers.push(server);
|
|
182
|
+
}
|
|
183
|
+
draft.lobehubSkillLoadingIds.delete(provider);
|
|
184
|
+
}),
|
|
185
|
+
false,
|
|
186
|
+
n('checkLobehubSkillStatus/success'),
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
if (server.isConnected) {
|
|
190
|
+
get().refreshLobehubSkillTools(provider);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return server;
|
|
194
|
+
} catch (error) {
|
|
195
|
+
console.error('[LobehubSkill] Failed to check status:', error);
|
|
196
|
+
|
|
197
|
+
set(
|
|
198
|
+
produce((draft: LobehubSkillStoreState) => {
|
|
199
|
+
draft.lobehubSkillLoadingIds.delete(provider);
|
|
200
|
+
}),
|
|
201
|
+
false,
|
|
202
|
+
n('checkLobehubSkillStatus/error'),
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
return undefined;
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
getLobehubSkillAuthorizeUrl: async (provider, options) => {
|
|
210
|
+
const response = await toolsClient.market.connectGetAuthorizeUrl.query({
|
|
211
|
+
provider,
|
|
212
|
+
redirectUri: options?.redirectUri,
|
|
213
|
+
scopes: options?.scopes,
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
authorizeUrl: response.authorizeUrl,
|
|
218
|
+
code: response.code,
|
|
219
|
+
expiresIn: response.expiresIn,
|
|
220
|
+
};
|
|
221
|
+
},
|
|
222
|
+
|
|
223
|
+
internal_updateLobehubSkillServer: (provider, update) => {
|
|
224
|
+
set(
|
|
225
|
+
produce((draft: LobehubSkillStoreState) => {
|
|
226
|
+
const serverIndex = draft.lobehubSkillServers.findIndex((s) => s.identifier === provider);
|
|
227
|
+
if (serverIndex >= 0) {
|
|
228
|
+
draft.lobehubSkillServers[serverIndex] = {
|
|
229
|
+
...draft.lobehubSkillServers[serverIndex],
|
|
230
|
+
...update,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
}),
|
|
234
|
+
false,
|
|
235
|
+
n('internal_updateLobehubSkillServer'),
|
|
236
|
+
);
|
|
237
|
+
},
|
|
238
|
+
|
|
239
|
+
refreshLobehubSkillToken: async (provider) => {
|
|
240
|
+
try {
|
|
241
|
+
const response = await toolsClient.market.connectRefresh.mutate({ provider });
|
|
242
|
+
|
|
243
|
+
if (response.refreshed) {
|
|
244
|
+
get().internal_updateLobehubSkillServer(provider, {
|
|
245
|
+
status: LobehubSkillStatus.CONNECTED,
|
|
246
|
+
tokenExpiresAt: response.connection?.tokenExpiresAt,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return response.refreshed;
|
|
251
|
+
} catch (error) {
|
|
252
|
+
console.error('[LobehubSkill] Failed to refresh token:', error);
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
|
|
257
|
+
refreshLobehubSkillTools: async (provider) => {
|
|
258
|
+
try {
|
|
259
|
+
const response = await toolsClient.market.connectListTools.query({ provider });
|
|
260
|
+
|
|
261
|
+
set(
|
|
262
|
+
produce((draft: LobehubSkillStoreState) => {
|
|
263
|
+
const serverIndex = draft.lobehubSkillServers.findIndex((s) => s.identifier === provider);
|
|
264
|
+
if (serverIndex >= 0) {
|
|
265
|
+
draft.lobehubSkillServers[serverIndex].tools = response.tools as LobehubSkillTool[];
|
|
266
|
+
}
|
|
267
|
+
}),
|
|
268
|
+
false,
|
|
269
|
+
n('refreshLobehubSkillTools/success'),
|
|
270
|
+
);
|
|
271
|
+
} catch (error) {
|
|
272
|
+
console.error('[LobehubSkill] Failed to refresh tools:', error);
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
|
|
276
|
+
revokeLobehubSkill: async (provider) => {
|
|
277
|
+
set(
|
|
278
|
+
produce((draft: LobehubSkillStoreState) => {
|
|
279
|
+
draft.lobehubSkillLoadingIds.add(provider);
|
|
280
|
+
}),
|
|
281
|
+
false,
|
|
282
|
+
n('revokeLobehubSkill/start'),
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
try {
|
|
286
|
+
await toolsClient.market.connectRevoke.mutate({ provider });
|
|
287
|
+
|
|
288
|
+
set(
|
|
289
|
+
produce((draft: LobehubSkillStoreState) => {
|
|
290
|
+
draft.lobehubSkillServers = draft.lobehubSkillServers.filter(
|
|
291
|
+
(s) => s.identifier !== provider,
|
|
292
|
+
);
|
|
293
|
+
draft.lobehubSkillLoadingIds.delete(provider);
|
|
294
|
+
}),
|
|
295
|
+
false,
|
|
296
|
+
n('revokeLobehubSkill/success'),
|
|
297
|
+
);
|
|
298
|
+
} catch (error) {
|
|
299
|
+
console.error('[LobehubSkill] Failed to revoke:', error);
|
|
300
|
+
|
|
301
|
+
set(
|
|
302
|
+
produce((draft: LobehubSkillStoreState) => {
|
|
303
|
+
draft.lobehubSkillLoadingIds.delete(provider);
|
|
304
|
+
}),
|
|
305
|
+
false,
|
|
306
|
+
n('revokeLobehubSkill/error'),
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
|
|
311
|
+
useFetchLobehubSkillConnections: (enabled) =>
|
|
312
|
+
useSWR<LobehubSkillServer[]>(
|
|
313
|
+
enabled ? 'fetchLobehubSkillConnections' : null,
|
|
314
|
+
async () => {
|
|
315
|
+
const response = await toolsClient.market.connectListConnections.query();
|
|
316
|
+
|
|
317
|
+
// Debug logging
|
|
318
|
+
console.log('[useFetchLobehubSkillConnections] raw response:', response);
|
|
319
|
+
|
|
320
|
+
return response.connections.map((conn: any) => {
|
|
321
|
+
// Debug logging for each connection
|
|
322
|
+
console.log('[useFetchLobehubSkillConnections] connection:', conn);
|
|
323
|
+
// Get provider config from local definition for correct display name
|
|
324
|
+
const providerConfig = getLobehubSkillProviderById(conn.providerId);
|
|
325
|
+
return {
|
|
326
|
+
cachedAt: Date.now(),
|
|
327
|
+
icon: conn.icon,
|
|
328
|
+
identifier: conn.providerId,
|
|
329
|
+
isConnected: true,
|
|
330
|
+
// Use local config label (e.g., "Linear") instead of API's providerName (which is user's name on that service)
|
|
331
|
+
name: providerConfig?.label || conn.providerId,
|
|
332
|
+
providerUsername: conn.providerUsername,
|
|
333
|
+
scopes: conn.scopes,
|
|
334
|
+
status: LobehubSkillStatus.CONNECTED,
|
|
335
|
+
tokenExpiresAt: conn.tokenExpiresAt,
|
|
336
|
+
};
|
|
337
|
+
});
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
fallbackData: [],
|
|
341
|
+
onSuccess: (data) => {
|
|
342
|
+
if (data.length > 0) {
|
|
343
|
+
set(
|
|
344
|
+
produce((draft: LobehubSkillStoreState) => {
|
|
345
|
+
const existingIds = new Set(draft.lobehubSkillServers.map((s) => s.identifier));
|
|
346
|
+
const newServers = data.filter((s) => !existingIds.has(s.identifier));
|
|
347
|
+
draft.lobehubSkillServers = [...draft.lobehubSkillServers, ...newServers];
|
|
348
|
+
}),
|
|
349
|
+
false,
|
|
350
|
+
n('useFetchLobehubSkillConnections'),
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
for (const server of data) {
|
|
354
|
+
get().refreshLobehubSkillTools(server.identifier);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
revalidateOnFocus: false,
|
|
359
|
+
},
|
|
360
|
+
),
|
|
361
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type LobehubSkillServer } from './types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* LobeHub Skill Store 状态接口
|
|
5
|
+
*
|
|
6
|
+
* NOTE: 所有连接状态和工具数据都从 Market API 实时获取,不存储到本地数据库
|
|
7
|
+
*/
|
|
8
|
+
export interface LobehubSkillStoreState {
|
|
9
|
+
/** 正在执行的工具调用 ID 集合 */
|
|
10
|
+
lobehubSkillExecutingToolIds: Set<string>;
|
|
11
|
+
/** 正在加载的 Provider ID 集合 */
|
|
12
|
+
lobehubSkillLoadingIds: Set<string>;
|
|
13
|
+
/** 已连接的 LobeHub Skill Server 列表 */
|
|
14
|
+
lobehubSkillServers: LobehubSkillServer[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* LobeHub Skill Store 初始状态
|
|
19
|
+
*/
|
|
20
|
+
export const initialLobehubSkillStoreState: LobehubSkillStoreState = {
|
|
21
|
+
lobehubSkillExecutingToolIds: new Set(),
|
|
22
|
+
lobehubSkillLoadingIds: new Set(),
|
|
23
|
+
lobehubSkillServers: [],
|
|
24
|
+
};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { type ToolStoreState } from '../../initialState';
|
|
2
|
+
import { type LobehubSkillServer, LobehubSkillStatus } from './types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* LobeHub Skill Store Selectors
|
|
6
|
+
*/
|
|
7
|
+
export const lobehubSkillStoreSelectors = {
|
|
8
|
+
/**
|
|
9
|
+
* 获取所有 LobeHub Skill 服务器的 identifier 集合
|
|
10
|
+
*/
|
|
11
|
+
getAllServerIdentifiers: (s: ToolStoreState): Set<string> => {
|
|
12
|
+
const servers = s.lobehubSkillServers || [];
|
|
13
|
+
return new Set(servers.map((server) => server.identifier));
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 获取所有可用的工具(来自所有已连接的服务器)
|
|
18
|
+
*/
|
|
19
|
+
getAllTools: (s: ToolStoreState) => {
|
|
20
|
+
const connectedServers = lobehubSkillStoreSelectors.getConnectedServers(s);
|
|
21
|
+
return connectedServers.flatMap((server) =>
|
|
22
|
+
(server.tools || []).map((tool) => ({
|
|
23
|
+
...tool,
|
|
24
|
+
provider: server.identifier,
|
|
25
|
+
})),
|
|
26
|
+
);
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 获取所有已连接的服务器
|
|
31
|
+
*/
|
|
32
|
+
getConnectedServers: (s: ToolStoreState): LobehubSkillServer[] =>
|
|
33
|
+
(s.lobehubSkillServers || []).filter(
|
|
34
|
+
(server) => server.status === LobehubSkillStatus.CONNECTED,
|
|
35
|
+
),
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 根据 identifier 获取服务器
|
|
39
|
+
* @param identifier - Provider 标识符 (e.g., 'linear')
|
|
40
|
+
*/
|
|
41
|
+
getServerByIdentifier: (identifier: string) => (s: ToolStoreState) =>
|
|
42
|
+
s.lobehubSkillServers?.find((server) => server.identifier === identifier),
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 获取所有 LobeHub Skill 服务器
|
|
46
|
+
*/
|
|
47
|
+
getServers: (s: ToolStoreState): LobehubSkillServer[] => s.lobehubSkillServers || [],
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 检查给定的 identifier 是否是 LobeHub Skill 服务器
|
|
51
|
+
* @param identifier - Provider 标识符 (e.g., 'linear')
|
|
52
|
+
*/
|
|
53
|
+
isLobehubSkillServer:
|
|
54
|
+
(identifier: string) =>
|
|
55
|
+
(s: ToolStoreState): boolean => {
|
|
56
|
+
const servers = s.lobehubSkillServers || [];
|
|
57
|
+
return servers.some((server) => server.identifier === identifier);
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 检查服务器是否正在加载
|
|
62
|
+
* @param identifier - Provider 标识符 (e.g., 'linear')
|
|
63
|
+
*/
|
|
64
|
+
isServerLoading: (identifier: string) => (s: ToolStoreState) =>
|
|
65
|
+
s.lobehubSkillLoadingIds?.has(identifier) || false,
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 检查工具是否正在执行
|
|
69
|
+
*/
|
|
70
|
+
isToolExecuting: (provider: string, toolName: string) => (s: ToolStoreState) => {
|
|
71
|
+
const toolId = `${provider}:${toolName}`;
|
|
72
|
+
return s.lobehubSkillExecutingToolIds?.has(toolId) || false;
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get all LobeHub Skill tools as LobeTool format for agent use
|
|
77
|
+
* Converts LobeHub Skill tools into the format expected by ToolNameResolver
|
|
78
|
+
*/
|
|
79
|
+
lobehubSkillAsLobeTools: (s: ToolStoreState) => {
|
|
80
|
+
const servers = s.lobehubSkillServers || [];
|
|
81
|
+
const tools: any[] = [];
|
|
82
|
+
|
|
83
|
+
for (const server of servers) {
|
|
84
|
+
if (!server.tools || server.status !== LobehubSkillStatus.CONNECTED) continue;
|
|
85
|
+
|
|
86
|
+
const apis = server.tools.map((tool) => ({
|
|
87
|
+
description: tool.description || '',
|
|
88
|
+
name: tool.name,
|
|
89
|
+
parameters: tool.inputSchema || {},
|
|
90
|
+
}));
|
|
91
|
+
|
|
92
|
+
if (apis.length > 0) {
|
|
93
|
+
tools.push({
|
|
94
|
+
identifier: server.identifier,
|
|
95
|
+
manifest: {
|
|
96
|
+
api: apis,
|
|
97
|
+
author: 'LobeHub Market',
|
|
98
|
+
homepage: 'https://lobehub.com/market',
|
|
99
|
+
identifier: server.identifier,
|
|
100
|
+
meta: {
|
|
101
|
+
avatar: server.icon || '🔗',
|
|
102
|
+
description: `LobeHub Skill: ${server.name}`,
|
|
103
|
+
tags: ['lobehub-skill', server.identifier],
|
|
104
|
+
title: server.name,
|
|
105
|
+
},
|
|
106
|
+
type: 'builtin',
|
|
107
|
+
version: '1.0.0',
|
|
108
|
+
},
|
|
109
|
+
type: 'plugin',
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return tools;
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get metadata list for all connected LobeHub Skill servers
|
|
119
|
+
* Used by toolSelectors.metaList for unified tool metadata resolution
|
|
120
|
+
*/
|
|
121
|
+
metaList: (s: ToolStoreState) => {
|
|
122
|
+
const servers = s.lobehubSkillServers || [];
|
|
123
|
+
const result = servers
|
|
124
|
+
.filter((server) => server.status === LobehubSkillStatus.CONNECTED)
|
|
125
|
+
.map((server) => {
|
|
126
|
+
// Debug logging
|
|
127
|
+
console.log('[lobehubSkillStoreSelectors.metaList] server:', {
|
|
128
|
+
icon: server.icon,
|
|
129
|
+
identifier: server.identifier,
|
|
130
|
+
name: server.name,
|
|
131
|
+
status: server.status,
|
|
132
|
+
});
|
|
133
|
+
return {
|
|
134
|
+
identifier: server.identifier,
|
|
135
|
+
meta: {
|
|
136
|
+
avatar: server.icon || '🔗',
|
|
137
|
+
description: `LobeHub Skill: ${server.name}`,
|
|
138
|
+
title: server.name,
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
console.log('[lobehubSkillStoreSelectors.metaList] result:', result);
|
|
143
|
+
return result;
|
|
144
|
+
},
|
|
145
|
+
};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LobeHub Skill Server 连接状态
|
|
3
|
+
*/
|
|
4
|
+
export enum LobehubSkillStatus {
|
|
5
|
+
/** 已连接,可以使用 */
|
|
6
|
+
CONNECTED = 'connected',
|
|
7
|
+
/** 连接中 */
|
|
8
|
+
CONNECTING = 'connecting',
|
|
9
|
+
/** 连接失败或 Token 过期 */
|
|
10
|
+
ERROR = 'error',
|
|
11
|
+
/** 未连接 */
|
|
12
|
+
NOT_CONNECTED = 'not_connected',
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* LobeHub Skill Tool 定义 (来自 Market API)
|
|
17
|
+
*/
|
|
18
|
+
export interface LobehubSkillTool {
|
|
19
|
+
/** 工具描述 */
|
|
20
|
+
description?: string;
|
|
21
|
+
/** 工具输入的 JSON Schema */
|
|
22
|
+
inputSchema: {
|
|
23
|
+
additionalProperties?: boolean;
|
|
24
|
+
properties?: Record<string, any>;
|
|
25
|
+
required?: string[];
|
|
26
|
+
type: string;
|
|
27
|
+
};
|
|
28
|
+
/** 工具名称 */
|
|
29
|
+
name: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* LobeHub Skill Provider 定义 (来自 Market API)
|
|
34
|
+
*/
|
|
35
|
+
export interface LobehubSkillProvider {
|
|
36
|
+
/** Provider 图标 URL */
|
|
37
|
+
icon?: string;
|
|
38
|
+
/** Provider ID (如 'linear', 'github') */
|
|
39
|
+
id: string;
|
|
40
|
+
/** 显示名称 */
|
|
41
|
+
name: string;
|
|
42
|
+
/** 是否支持刷新 Token */
|
|
43
|
+
refreshSupported?: boolean;
|
|
44
|
+
/** Provider 类型 */
|
|
45
|
+
type?: 'mcp' | 'rest';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* LobeHub Skill Server 实例 (用户已连接的 provider)
|
|
50
|
+
*/
|
|
51
|
+
export interface LobehubSkillServer {
|
|
52
|
+
/** 缓存时间戳 */
|
|
53
|
+
cachedAt?: number;
|
|
54
|
+
/** 错误信息 */
|
|
55
|
+
errorMessage?: string;
|
|
56
|
+
/** Provider 图标 URL */
|
|
57
|
+
icon?: string;
|
|
58
|
+
/** Provider ID (如 'linear') */
|
|
59
|
+
identifier: string;
|
|
60
|
+
/** 是否已认证 */
|
|
61
|
+
isConnected: boolean;
|
|
62
|
+
/** Provider 显示名称 */
|
|
63
|
+
name: string;
|
|
64
|
+
/** Provider 用户名 (如 GitHub username) */
|
|
65
|
+
providerUsername?: string;
|
|
66
|
+
/** 授权的 scopes */
|
|
67
|
+
scopes?: string[];
|
|
68
|
+
/** 连接状态 */
|
|
69
|
+
status: LobehubSkillStatus;
|
|
70
|
+
/** Token 过期时间 */
|
|
71
|
+
tokenExpiresAt?: string;
|
|
72
|
+
/** 工具列表 (已连接后可用) */
|
|
73
|
+
tools?: LobehubSkillTool[];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* 调用 LobeHub Skill 工具的参数
|
|
78
|
+
*/
|
|
79
|
+
export interface CallLobehubSkillToolParams {
|
|
80
|
+
/** 工具参数 */
|
|
81
|
+
args?: Record<string, unknown>;
|
|
82
|
+
/** Provider ID (如 'linear') */
|
|
83
|
+
provider: string;
|
|
84
|
+
/** 工具名称 */
|
|
85
|
+
toolName: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 调用 LobeHub Skill 工具的结果
|
|
90
|
+
*/
|
|
91
|
+
export interface CallLobehubSkillToolResult {
|
|
92
|
+
/** 返回数据 */
|
|
93
|
+
data?: any;
|
|
94
|
+
/** 错误信息 */
|
|
95
|
+
error?: string;
|
|
96
|
+
/** 错误代码 */
|
|
97
|
+
errorCode?: string;
|
|
98
|
+
/** 是否成功 */
|
|
99
|
+
success: boolean;
|
|
100
|
+
}
|
package/src/store/tool/store.ts
CHANGED
|
@@ -7,9 +7,13 @@ import { type ToolStoreState, initialState } from './initialState';
|
|
|
7
7
|
import { type BuiltinToolAction, createBuiltinToolSlice } from './slices/builtin';
|
|
8
8
|
import { type CustomPluginAction, createCustomPluginSlice } from './slices/customPlugin';
|
|
9
9
|
import { type KlavisStoreAction, createKlavisStoreSlice } from './slices/klavisStore';
|
|
10
|
+
import {
|
|
11
|
+
type LobehubSkillStoreAction,
|
|
12
|
+
createLobehubSkillStoreSlice,
|
|
13
|
+
} from './slices/lobehubSkillStore';
|
|
10
14
|
import { type PluginMCPStoreAction, createMCPPluginStoreSlice } from './slices/mcpStore';
|
|
11
|
-
import { type PluginAction, createPluginSlice } from './slices/plugin';
|
|
12
15
|
import { type PluginStoreAction, createPluginStoreSlice } from './slices/oldStore';
|
|
16
|
+
import { type PluginAction, createPluginSlice } from './slices/plugin';
|
|
13
17
|
|
|
14
18
|
// =============== Aggregate createStoreFn ============ //
|
|
15
19
|
|
|
@@ -19,7 +23,8 @@ export type ToolStore = ToolStoreState &
|
|
|
19
23
|
PluginStoreAction &
|
|
20
24
|
BuiltinToolAction &
|
|
21
25
|
PluginMCPStoreAction &
|
|
22
|
-
KlavisStoreAction
|
|
26
|
+
KlavisStoreAction &
|
|
27
|
+
LobehubSkillStoreAction;
|
|
23
28
|
|
|
24
29
|
const createStore: StateCreator<ToolStore, [['zustand/devtools', never]]> = (...parameters) => ({
|
|
25
30
|
...initialState,
|
|
@@ -29,6 +34,7 @@ const createStore: StateCreator<ToolStore, [['zustand/devtools', never]]> = (...
|
|
|
29
34
|
...createBuiltinToolSlice(...parameters),
|
|
30
35
|
...createMCPPluginStoreSlice(...parameters),
|
|
31
36
|
...createKlavisStoreSlice(...parameters),
|
|
37
|
+
...createLobehubSkillStoreSlice(...parameters),
|
|
32
38
|
});
|
|
33
39
|
|
|
34
40
|
// =============== Implement useStore ============ //
|