@lobehub/lobehub 2.0.0-next.233 → 2.0.0-next.235

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.
Files changed (169) hide show
  1. package/.github/workflows/e2e.yml +6 -12
  2. package/.github/workflows/test.yml +3 -3
  3. package/CHANGELOG.md +59 -0
  4. package/CLAUDE.md +1 -1
  5. package/changelog/v1.json +18 -0
  6. package/docs/development/basic/feature-development.mdx +4 -5
  7. package/docs/development/basic/feature-development.zh-CN.mdx +4 -5
  8. package/e2e/README.md +6 -6
  9. package/e2e/src/features/community/detail-pages.feature +9 -9
  10. package/e2e/src/features/community/interactions.feature +13 -13
  11. package/e2e/src/features/community/smoke.feature +6 -6
  12. package/e2e/src/steps/agent/conversation-mgmt.steps.ts +196 -25
  13. package/e2e/src/steps/agent/conversation.steps.ts +58 -0
  14. package/e2e/src/steps/agent/message-ops.steps.ts +20 -15
  15. package/e2e/src/steps/community/detail-pages.steps.ts +60 -19
  16. package/e2e/src/steps/community/interactions.steps.ts +145 -32
  17. package/e2e/src/steps/hooks.ts +12 -2
  18. package/locales/ar/components.json +1 -0
  19. package/locales/ar/file.json +4 -0
  20. package/locales/ar/models.json +29 -0
  21. package/locales/ar/setting.json +7 -0
  22. package/locales/bg-BG/components.json +1 -0
  23. package/locales/bg-BG/file.json +4 -0
  24. package/locales/bg-BG/models.json +1 -0
  25. package/locales/bg-BG/setting.json +7 -0
  26. package/locales/de-DE/components.json +1 -0
  27. package/locales/de-DE/file.json +4 -0
  28. package/locales/de-DE/models.json +29 -0
  29. package/locales/de-DE/setting.json +7 -0
  30. package/locales/en-US/common.json +0 -1
  31. package/locales/en-US/components.json +1 -0
  32. package/locales/en-US/file.json +4 -0
  33. package/locales/en-US/models.json +1 -0
  34. package/locales/en-US/setting.json +3 -0
  35. package/locales/es-ES/components.json +1 -0
  36. package/locales/es-ES/file.json +4 -0
  37. package/locales/es-ES/models.json +43 -0
  38. package/locales/es-ES/setting.json +7 -0
  39. package/locales/fa-IR/components.json +1 -0
  40. package/locales/fa-IR/file.json +4 -0
  41. package/locales/fa-IR/models.json +54 -0
  42. package/locales/fa-IR/setting.json +7 -0
  43. package/locales/fr-FR/components.json +1 -0
  44. package/locales/fr-FR/file.json +4 -0
  45. package/locales/fr-FR/models.json +31 -0
  46. package/locales/fr-FR/setting.json +7 -0
  47. package/locales/it-IT/components.json +1 -0
  48. package/locales/it-IT/file.json +4 -0
  49. package/locales/it-IT/models.json +43 -0
  50. package/locales/it-IT/setting.json +7 -0
  51. package/locales/ja-JP/components.json +1 -0
  52. package/locales/ja-JP/file.json +4 -0
  53. package/locales/ja-JP/models.json +28 -0
  54. package/locales/ja-JP/setting.json +7 -0
  55. package/locales/ko-KR/components.json +1 -0
  56. package/locales/ko-KR/file.json +4 -0
  57. package/locales/ko-KR/models.json +37 -0
  58. package/locales/ko-KR/setting.json +7 -0
  59. package/locales/nl-NL/components.json +1 -0
  60. package/locales/nl-NL/file.json +4 -0
  61. package/locales/nl-NL/models.json +13 -0
  62. package/locales/nl-NL/setting.json +7 -0
  63. package/locales/pl-PL/components.json +1 -0
  64. package/locales/pl-PL/file.json +4 -0
  65. package/locales/pl-PL/models.json +13 -0
  66. package/locales/pl-PL/setting.json +7 -0
  67. package/locales/pt-BR/components.json +1 -0
  68. package/locales/pt-BR/file.json +4 -0
  69. package/locales/pt-BR/models.json +29 -0
  70. package/locales/pt-BR/setting.json +7 -0
  71. package/locales/ru-RU/components.json +1 -0
  72. package/locales/ru-RU/file.json +4 -0
  73. package/locales/ru-RU/models.json +1 -0
  74. package/locales/ru-RU/setting.json +7 -0
  75. package/locales/tr-TR/components.json +1 -0
  76. package/locales/tr-TR/file.json +4 -0
  77. package/locales/tr-TR/models.json +29 -0
  78. package/locales/tr-TR/setting.json +7 -0
  79. package/locales/vi-VN/components.json +1 -0
  80. package/locales/vi-VN/file.json +4 -0
  81. package/locales/vi-VN/models.json +1 -0
  82. package/locales/vi-VN/setting.json +7 -0
  83. package/locales/zh-CN/file.json +4 -0
  84. package/locales/zh-CN/models.json +46 -0
  85. package/locales/zh-CN/setting.json +3 -0
  86. package/locales/zh-TW/components.json +1 -0
  87. package/locales/zh-TW/file.json +4 -0
  88. package/locales/zh-TW/models.json +35 -0
  89. package/locales/zh-TW/setting.json +7 -0
  90. package/package.json +5 -5
  91. package/packages/const/src/index.ts +1 -0
  92. package/packages/const/src/lobehubSkill.ts +55 -0
  93. package/packages/types/package.json +1 -1
  94. package/packages/types/src/files/upload.ts +11 -1
  95. package/packages/types/src/message/common/tools.ts +1 -1
  96. package/packages/types/src/serverConfig.ts +1 -0
  97. package/public/not-compatible.html +1296 -0
  98. package/src/app/[variants]/(main)/resource/features/FileDetail.tsx +20 -12
  99. package/src/app/[variants]/(main)/resource/features/modal/FullscreenModal.tsx +2 -4
  100. package/src/app/[variants]/layout.tsx +50 -1
  101. package/src/features/ChatInput/ActionBar/Tools/LobehubSkillServerItem.tsx +304 -0
  102. package/src/features/ChatInput/ActionBar/Tools/useControls.tsx +74 -10
  103. package/src/features/Conversation/Messages/AssistantGroup/Tool/Inspector/ToolTitle.tsx +9 -0
  104. package/src/features/FileViewer/Renderer/Code/index.tsx +224 -0
  105. package/src/features/FileViewer/Renderer/Image/index.tsx +8 -1
  106. package/src/features/FileViewer/Renderer/PDF/index.tsx +3 -1
  107. package/src/features/FileViewer/Renderer/PDF/style.ts +2 -1
  108. package/src/features/FileViewer/index.tsx +135 -24
  109. package/src/features/PageEditor/EditorCanvas/useSlashItems.tsx +7 -4
  110. package/src/features/PageEditor/store/initialState.ts +2 -1
  111. package/src/features/ResourceManager/components/Editor/FileContent.tsx +1 -4
  112. package/src/features/ResourceManager/components/Editor/FileCopilot.tsx +64 -0
  113. package/src/features/ResourceManager/components/Editor/index.tsx +98 -31
  114. package/src/features/ResourceManager/components/Explorer/ItemDropdown/useFileItemDropdown.tsx +3 -2
  115. package/src/features/ResourceManager/components/Explorer/ListView/ColumnResizeHandle.tsx +119 -0
  116. package/src/features/ResourceManager/components/Explorer/ListView/ListItem/index.tsx +67 -22
  117. package/src/features/ResourceManager/components/Explorer/ListView/Skeleton.tsx +46 -11
  118. package/src/features/ResourceManager/components/Explorer/ListView/index.tsx +140 -81
  119. package/src/features/ResourceManager/components/Explorer/ToolBar/SortDropdown.tsx +20 -12
  120. package/src/features/ResourceManager/components/Explorer/ToolBar/ViewSwitcher.tsx +18 -10
  121. package/src/features/ResourceManager/components/UploadDock/Item.tsx +38 -6
  122. package/src/features/ResourceManager/components/UploadDock/index.tsx +62 -41
  123. package/src/features/ResourceManager/index.tsx +1 -0
  124. package/src/helpers/toolEngineering/index.test.ts +3 -0
  125. package/src/helpers/toolEngineering/index.ts +12 -1
  126. package/src/locales/default/file.ts +4 -0
  127. package/src/locales/default/setting.ts +3 -0
  128. package/src/server/globalConfig/index.ts +1 -0
  129. package/src/server/modules/ModelRuntime/index.test.ts +214 -1
  130. package/src/server/modules/ModelRuntime/index.ts +43 -7
  131. package/src/server/routers/lambda/_helpers/resolveContext.ts +8 -8
  132. package/src/server/routers/lambda/agent.ts +1 -1
  133. package/src/server/routers/lambda/aiModel.ts +1 -1
  134. package/src/server/routers/lambda/comfyui.ts +1 -1
  135. package/src/server/routers/lambda/document.ts +44 -0
  136. package/src/server/routers/lambda/exporter.ts +1 -1
  137. package/src/server/routers/lambda/image.ts +13 -13
  138. package/src/server/routers/lambda/klavis.ts +10 -10
  139. package/src/server/routers/lambda/market/index.ts +6 -6
  140. package/src/server/routers/lambda/message.ts +2 -2
  141. package/src/server/routers/lambda/plugin.ts +1 -1
  142. package/src/server/routers/lambda/ragEval.ts +2 -2
  143. package/src/server/routers/lambda/topic.ts +3 -3
  144. package/src/server/routers/lambda/user.ts +10 -10
  145. package/src/server/routers/lambda/userMemories.ts +6 -6
  146. package/src/server/routers/tools/market.ts +261 -0
  147. package/src/server/services/document/index.ts +22 -0
  148. package/src/services/document/index.ts +4 -0
  149. package/src/services/upload.ts +22 -2
  150. package/src/store/chat/slices/plugin/actions/internals.ts +15 -2
  151. package/src/store/chat/slices/plugin/actions/pluginTypes.ts +104 -0
  152. package/src/store/file/slices/fileManager/action.test.ts +9 -3
  153. package/src/store/file/slices/fileManager/action.ts +165 -70
  154. package/src/store/file/slices/upload/action.ts +3 -0
  155. package/src/store/global/actions/general.ts +15 -0
  156. package/src/store/global/initialState.ts +13 -0
  157. package/src/store/serverConfig/selectors.ts +1 -0
  158. package/src/store/tool/initialState.ts +11 -2
  159. package/src/store/tool/selectors/index.ts +1 -0
  160. package/src/store/tool/selectors/tool.ts +3 -1
  161. package/src/store/tool/slices/lobehubSkillStore/action.ts +361 -0
  162. package/src/store/tool/slices/lobehubSkillStore/index.ts +4 -0
  163. package/src/store/tool/slices/lobehubSkillStore/initialState.ts +24 -0
  164. package/src/store/tool/slices/lobehubSkillStore/selectors.ts +145 -0
  165. package/src/store/tool/slices/lobehubSkillStore/types.ts +100 -0
  166. package/src/store/tool/store.ts +8 -2
  167. package/vitest.config.mts +1 -0
  168. package/src/features/FileViewer/Renderer/JavaScript/index.tsx +0 -66
  169. 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,4 @@
1
+ export * from './action';
2
+ export * from './initialState';
3
+ export * from './selectors';
4
+ export * from './types';
@@ -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
+ }
@@ -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 ============ //
package/vitest.config.mts CHANGED
@@ -92,6 +92,7 @@ export default defineConfig({
92
92
  '**/e2e/**',
93
93
  ],
94
94
  globals: true,
95
+ reporters: ['default', 'blob'],
95
96
  server: {
96
97
  deps: {
97
98
  inline: ['vitest-canvas-mock', '@lobehub/ui', '@lobehub/fluent-emoji'],