@lobehub/lobehub 2.0.0-next.47 → 2.0.0-next.48

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 (228) hide show
  1. package/.env.example +11 -0
  2. package/CHANGELOG.md +17 -0
  3. package/apps/desktop/src/main/controllers/AuthCtr.ts +27 -2
  4. package/apps/desktop/src/main/core/infrastructure/ProtocolManager.ts +9 -4
  5. package/changelog/v1.json +5 -0
  6. package/docs/development/database-schema.dbml +2 -0
  7. package/docs/self-hosting/environment-variables/basic.mdx +49 -3
  8. package/docs/self-hosting/environment-variables/basic.zh-CN.mdx +49 -4
  9. package/locales/ar/discover.json +45 -0
  10. package/locales/ar/marketAuth.json +42 -0
  11. package/locales/ar/setting.json +94 -1
  12. package/locales/bg-BG/discover.json +45 -0
  13. package/locales/bg-BG/marketAuth.json +42 -0
  14. package/locales/bg-BG/setting.json +94 -1
  15. package/locales/de-DE/discover.json +45 -0
  16. package/locales/de-DE/marketAuth.json +42 -0
  17. package/locales/de-DE/setting.json +94 -1
  18. package/locales/en-US/discover.json +45 -0
  19. package/locales/en-US/marketAuth.json +42 -0
  20. package/locales/en-US/setting.json +94 -1
  21. package/locales/es-ES/discover.json +45 -0
  22. package/locales/es-ES/marketAuth.json +42 -0
  23. package/locales/es-ES/setting.json +94 -1
  24. package/locales/fa-IR/discover.json +45 -0
  25. package/locales/fa-IR/marketAuth.json +42 -0
  26. package/locales/fa-IR/setting.json +94 -1
  27. package/locales/fr-FR/discover.json +45 -0
  28. package/locales/fr-FR/marketAuth.json +42 -0
  29. package/locales/fr-FR/setting.json +94 -1
  30. package/locales/it-IT/discover.json +45 -0
  31. package/locales/it-IT/marketAuth.json +42 -0
  32. package/locales/it-IT/setting.json +94 -1
  33. package/locales/ja-JP/discover.json +45 -0
  34. package/locales/ja-JP/marketAuth.json +42 -0
  35. package/locales/ja-JP/setting.json +94 -1
  36. package/locales/ko-KR/discover.json +45 -0
  37. package/locales/ko-KR/marketAuth.json +42 -0
  38. package/locales/ko-KR/setting.json +94 -1
  39. package/locales/nl-NL/discover.json +45 -0
  40. package/locales/nl-NL/marketAuth.json +42 -0
  41. package/locales/nl-NL/setting.json +94 -1
  42. package/locales/pl-PL/discover.json +45 -0
  43. package/locales/pl-PL/marketAuth.json +42 -0
  44. package/locales/pl-PL/setting.json +94 -1
  45. package/locales/pt-BR/discover.json +45 -0
  46. package/locales/pt-BR/marketAuth.json +42 -0
  47. package/locales/pt-BR/setting.json +94 -1
  48. package/locales/ru-RU/discover.json +45 -0
  49. package/locales/ru-RU/marketAuth.json +42 -0
  50. package/locales/ru-RU/setting.json +94 -1
  51. package/locales/tr-TR/discover.json +45 -0
  52. package/locales/tr-TR/marketAuth.json +42 -0
  53. package/locales/tr-TR/setting.json +94 -1
  54. package/locales/vi-VN/discover.json +45 -0
  55. package/locales/vi-VN/marketAuth.json +42 -0
  56. package/locales/vi-VN/setting.json +94 -1
  57. package/locales/zh-CN/discover.json +45 -0
  58. package/locales/zh-CN/marketAuth.json +42 -0
  59. package/locales/zh-CN/setting.json +94 -1
  60. package/locales/zh-TW/discover.json +45 -0
  61. package/locales/zh-TW/marketAuth.json +42 -0
  62. package/locales/zh-TW/setting.json +94 -1
  63. package/package.json +27 -26
  64. package/packages/const/src/url.ts +1 -0
  65. package/packages/database/migrations/0044_add_tool_intervention.sql +1 -0
  66. package/packages/database/migrations/0044_high_toxin.sql +1 -0
  67. package/packages/database/migrations/0045_add_tool_intervention.sql +1 -0
  68. package/packages/database/migrations/meta/0039_snapshot.json +1 -1
  69. package/packages/database/migrations/meta/0044_snapshot.json +7813 -0
  70. package/packages/database/migrations/meta/0045_snapshot.json +8431 -0
  71. package/packages/database/migrations/meta/_journal.json +14 -0
  72. package/packages/database/src/core/migrations.json +36 -7
  73. package/packages/database/src/models/message.ts +1 -1
  74. package/packages/database/src/models/session.ts +42 -1
  75. package/packages/database/src/schemas/agent.ts +1 -0
  76. package/packages/database/src/schemas/message.ts +5 -8
  77. package/packages/electron-client-ipc/src/events/index.ts +6 -1
  78. package/packages/electron-client-ipc/src/events/remoteServer.ts +8 -0
  79. package/packages/fetch-sse/package.json +29 -0
  80. package/packages/{utils/src/fetch → fetch-sse/src}/__tests__/fetchSSE.test.ts +4 -4
  81. package/packages/{utils/src/fetch → fetch-sse/src}/__tests__/parseError.test.ts +7 -4
  82. package/packages/{utils/src/fetch → fetch-sse/src}/fetchSSE.ts +2 -2
  83. package/packages/{utils/src/fetch → fetch-sse/src}/parseError.ts +3 -3
  84. package/packages/model-bank/src/aiModels/mistral.ts +2 -1
  85. package/packages/model-runtime/src/core/contextBuilders/anthropic.test.ts +17 -11
  86. package/packages/model-runtime/src/core/contextBuilders/anthropic.ts +1 -1
  87. package/packages/model-runtime/src/core/contextBuilders/google.test.ts +1 -1
  88. package/packages/model-runtime/src/core/contextBuilders/google.ts +3 -6
  89. package/packages/model-runtime/src/core/contextBuilders/openai.test.ts +4 -2
  90. package/packages/model-runtime/src/core/contextBuilders/openai.ts +1 -1
  91. package/packages/model-runtime/src/core/openaiCompatibleFactory/createImage.test.ts +1 -1
  92. package/packages/model-runtime/src/core/openaiCompatibleFactory/createImage.ts +1 -1
  93. package/packages/model-runtime/src/core/openaiCompatibleFactory/index.test.ts +3 -6
  94. package/packages/model-runtime/src/core/streams/openai/responsesStream.test.ts +1 -1
  95. package/packages/model-runtime/src/helpers/mergeChatMethodOptions.ts +2 -1
  96. package/packages/model-runtime/src/providers/aihubmix/index.test.ts +1 -1
  97. package/packages/model-runtime/src/providers/anthropic/generateObject.test.ts +1 -1
  98. package/packages/model-runtime/src/providers/anthropic/index.test.ts +1 -1
  99. package/packages/model-runtime/src/providers/baichuan/index.test.ts +1 -1
  100. package/packages/model-runtime/src/providers/bedrock/index.test.ts +1 -1
  101. package/packages/model-runtime/src/providers/bfl/createImage.test.ts +4 -4
  102. package/packages/model-runtime/src/providers/bfl/createImage.ts +1 -1
  103. package/packages/model-runtime/src/providers/cloudflare/index.test.ts +1 -1
  104. package/packages/model-runtime/src/providers/cohere/index.test.ts +1 -1
  105. package/packages/model-runtime/src/providers/google/createImage.test.ts +2 -2
  106. package/packages/model-runtime/src/providers/google/createImage.ts +1 -1
  107. package/packages/model-runtime/src/providers/google/generateObject.test.ts +1 -1
  108. package/packages/model-runtime/src/providers/google/index.test.ts +1 -4
  109. package/packages/model-runtime/src/providers/groq/index.test.ts +1 -1
  110. package/packages/model-runtime/src/providers/hunyuan/index.test.ts +1 -1
  111. package/packages/model-runtime/src/providers/minimax/createImage.test.ts +1 -1
  112. package/packages/model-runtime/src/providers/mistral/index.test.ts +1 -1
  113. package/packages/model-runtime/src/providers/moonshot/index.test.ts +1 -1
  114. package/packages/model-runtime/src/providers/novita/index.test.ts +1 -1
  115. package/packages/model-runtime/src/providers/ollama/index.test.ts +43 -32
  116. package/packages/model-runtime/src/providers/ollama/index.ts +31 -7
  117. package/packages/model-runtime/src/providers/openrouter/index.test.ts +1 -1
  118. package/packages/model-runtime/src/providers/perplexity/index.test.ts +1 -1
  119. package/packages/model-runtime/src/providers/ppio/index.test.ts +1 -1
  120. package/packages/model-runtime/src/providers/qwen/createImage.test.ts +1 -1
  121. package/packages/model-runtime/src/providers/search1api/index.test.ts +1 -1
  122. package/packages/model-runtime/src/providers/siliconcloud/createImage.ts +1 -1
  123. package/packages/model-runtime/src/providers/taichu/index.test.ts +1 -1
  124. package/packages/model-runtime/src/providers/wenxin/index.test.ts +1 -1
  125. package/packages/model-runtime/src/providers/zhipu/index.test.ts +1 -1
  126. package/packages/model-runtime/src/utils/errorResponse.test.ts +1 -1
  127. package/packages/ssrf-safe-fetch/index.browser.ts +14 -0
  128. package/packages/ssrf-safe-fetch/package.json +8 -1
  129. package/packages/types/src/discover/assistants.ts +16 -0
  130. package/packages/types/src/index.ts +1 -0
  131. package/packages/types/src/message/common/tools.ts +10 -0
  132. package/packages/types/src/message/db/item.ts +15 -1
  133. package/packages/types/src/message/ui/params.ts +15 -1
  134. package/packages/types/src/meta.ts +4 -0
  135. package/packages/types/src/session/agentSession.ts +2 -0
  136. package/packages/utils/src/imageToBase64.ts +17 -10
  137. package/packages/utils/src/index.ts +1 -1
  138. package/src/app/(backend)/market/agent/[[...segments]]/route.ts +153 -0
  139. package/src/app/(backend)/market/oidc/[[...segments]]/route.ts +207 -0
  140. package/src/app/[variants]/(main)/(mobile)/me/settings/features/useCategory.tsx +1 -0
  141. package/src/app/[variants]/(main)/_layout/Desktop/SideBar/PinList/index.tsx +4 -2
  142. package/src/app/[variants]/(main)/chat/settings/features/AgentInfoDescription/index.tsx +349 -0
  143. package/src/app/[variants]/(main)/chat/settings/features/HeaderContent.tsx +2 -2
  144. package/src/app/[variants]/(main)/chat/settings/features/PublishResultModal/index.tsx +64 -0
  145. package/src/app/[variants]/(main)/chat/settings/features/SmartAgentActionButton/MarketPublishButton.tsx +196 -0
  146. package/src/app/[variants]/(main)/chat/settings/features/SmartAgentActionButton/MarketPublishModal.tsx +358 -0
  147. package/src/app/[variants]/(main)/chat/settings/features/SmartAgentActionButton/index.tsx +75 -0
  148. package/src/app/[variants]/(main)/discover/(detail)/assistant/AssistantDetailPage.tsx +11 -2
  149. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/Client.tsx +12 -1
  150. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Details/Nav.tsx +19 -12
  151. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Details/Overview/TagList.tsx +14 -5
  152. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Details/Overview/index.tsx +2 -0
  153. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Details/Related/index.tsx +14 -5
  154. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Details/SystemRole/TagList.tsx +14 -5
  155. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Details/SystemRole/index.tsx +43 -29
  156. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Details/Versions/index.tsx +137 -0
  157. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Details/index.tsx +2 -0
  158. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Header.tsx +9 -10
  159. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Sidebar/ActionButton/AddAgent.tsx +105 -14
  160. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Sidebar/Related/index.tsx +20 -6
  161. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/StatusPage/index.tsx +113 -0
  162. package/src/app/[variants]/(main)/discover/(detail)/features/Breadcrumb.tsx +4 -3
  163. package/src/app/[variants]/(main)/discover/(list)/_layout/Desktop/Nav.tsx +3 -1
  164. package/src/app/[variants]/(main)/discover/(list)/assistant/AssistantPage.tsx +4 -1
  165. package/src/app/[variants]/(main)/discover/(list)/assistant/Client.tsx +6 -2
  166. package/src/app/[variants]/(main)/discover/(list)/assistant/features/Category/index.tsx +7 -3
  167. package/src/app/[variants]/(main)/discover/(list)/assistant/features/List/Item.tsx +13 -2
  168. package/src/app/[variants]/(main)/discover/(list)/assistant/features/MarketSourceSwitch.tsx +64 -0
  169. package/src/app/[variants]/(main)/discover/(list)/features/SortButton/index.tsx +26 -7
  170. package/src/app/[variants]/(main)/profile/_layout/Desktop/index.tsx +10 -10
  171. package/src/app/[variants]/(main)/settings/_layout/type.ts +1 -1
  172. package/src/app/[variants]/(main)/settings/agent/index.tsx +11 -10
  173. package/src/app/[variants]/(main)/settings/common/index.tsx +1 -1
  174. package/src/app/[variants]/(main)/settings/page.tsx +13 -10
  175. package/src/app/[variants]/(main)/settings/provider/ProviderMenu/Item.tsx +35 -36
  176. package/src/app/[variants]/(main)/settings/provider/ProviderMenu/SearchResult.tsx +5 -5
  177. package/src/app/[variants]/(main)/settings/provider/_layout/Desktop/Container.tsx +10 -4
  178. package/src/app/market-auth-callback/layout.tsx +15 -0
  179. package/src/app/market-auth-callback/page.tsx +196 -0
  180. package/src/features/AgentSetting/AgentPrompt/TokenTag.tsx +3 -2
  181. package/src/features/AgentSetting/AgentTTS/SelectWithTTSPreview.tsx +1 -1
  182. package/src/features/AgentSetting/store/action.ts +1 -1
  183. package/src/features/ChatInput/ActionBar/STT/browser.tsx +1 -1
  184. package/src/features/ChatInput/ActionBar/STT/openai.tsx +1 -1
  185. package/src/features/Conversation/components/Extras/TTS/InitPlayer.tsx +1 -1
  186. package/src/features/PluginTag/PluginStatus.tsx +1 -1
  187. package/src/hooks/useAgentOwnershipCheck.ts +143 -0
  188. package/src/instrumentation.node.ts +3 -2
  189. package/src/layout/AuthProvider/MarketAuth/MarketAuthProvider.tsx +364 -0
  190. package/src/layout/AuthProvider/MarketAuth/errors.ts +75 -0
  191. package/src/layout/AuthProvider/MarketAuth/index.ts +2 -0
  192. package/src/layout/AuthProvider/MarketAuth/oidc.ts +382 -0
  193. package/src/layout/AuthProvider/MarketAuth/types.ts +64 -0
  194. package/src/layout/AuthProvider/index.tsx +17 -4
  195. package/src/locales/default/discover.ts +46 -0
  196. package/src/locales/default/index.ts +2 -0
  197. package/src/locales/default/marketAuth.ts +42 -0
  198. package/src/locales/default/setting.ts +94 -1
  199. package/src/server/globalConfig/genServerAiProviderConfig.test.ts +5 -5
  200. package/src/server/globalConfig/genServerAiProviderConfig.ts +1 -1
  201. package/src/server/routers/lambda/market/index.ts +36 -14
  202. package/src/server/routers/lambda/message.ts +2 -2
  203. package/src/server/services/discover/index.test.ts +153 -11
  204. package/src/server/services/discover/index.ts +339 -40
  205. package/src/server/sitemap.ts +49 -35
  206. package/src/services/_url.ts +15 -1
  207. package/src/services/chat/chat.test.ts +5 -5
  208. package/src/services/chat/clientModelRuntime.test.ts +1 -1
  209. package/src/services/chat/index.ts +6 -6
  210. package/src/services/chat/types.ts +1 -2
  211. package/src/services/discover.ts +16 -5
  212. package/src/services/electron/remoteServer.ts +8 -1
  213. package/src/services/marketApi.ts +124 -0
  214. package/src/services/models.ts +2 -1
  215. package/src/store/discover/slices/assistant/action.ts +20 -7
  216. package/{packages/utils/src → src/utils}/electron/desktopRemoteRPCFetch.ts +1 -1
  217. package/{packages/utils/src → src/utils/server}/parseModels.ts +1 -2
  218. package/vitest.config.mts +2 -0
  219. package/packages/model-runtime/src/utils/imageToBase64.test.ts +0 -91
  220. package/packages/model-runtime/src/utils/imageToBase64.ts +0 -62
  221. package/src/app/[variants]/(main)/chat/settings/features/SubmitAgentButton/SubmitAgentModal.tsx +0 -98
  222. package/src/app/[variants]/(main)/chat/settings/features/SubmitAgentButton/index.tsx +0 -35
  223. package/src/app/[variants]/(main)/chat/settings/features/SubmitAgentButton/style.ts +0 -47
  224. /package/packages/{utils/src/fetch → fetch-sse/src}/headers.ts +0 -0
  225. /package/packages/{utils/src/fetch → fetch-sse/src}/index.ts +0 -0
  226. /package/packages/{utils/src/fetch → fetch-sse/src}/request.ts +0 -0
  227. /package/{packages/utils/src → src/utils/server}/__snapshots__/parseModels.test.ts.snap +0 -0
  228. /package/{packages/utils/src → src/utils/server}/parseModels.test.ts +0 -0
@@ -27,7 +27,7 @@ vi.stubGlobal(
27
27
  );
28
28
 
29
29
  // Mock image processing utilities
30
- vi.mock('@/utils/fetch', async (importOriginal) => {
30
+ vi.mock('@lobechat/fetch-sse', async (importOriginal) => {
31
31
  const module = await importOriginal();
32
32
 
33
33
  return { ...(module as any), getMessageError: vi.fn() };
@@ -988,7 +988,7 @@ describe('ChatService', () => {
988
988
 
989
989
  beforeEach(async () => {
990
990
  // Setup common fetchSSE mock for getChatCompletion tests
991
- const { fetchSSE } = await import('@/utils/fetch');
991
+ const { fetchSSE } = await import('@lobechat/fetch-sse');
992
992
  mockFetchSSE = vi.fn().mockResolvedValue(new Response('mock response'));
993
993
  vi.mocked(fetchSSE).mockImplementation(mockFetchSSE);
994
994
  });
@@ -1049,7 +1049,7 @@ describe('ChatService', () => {
1049
1049
 
1050
1050
  it('should return InvalidAccessCode error when enableFetchOnClient is true and auth is enabled but user is not signed in', async () => {
1051
1051
  // Mock fetchSSE to call onErrorHandle with the error
1052
- const { fetchSSE } = await import('@/utils/fetch');
1052
+ const { fetchSSE } = await import('@lobechat/fetch-sse');
1053
1053
 
1054
1054
  const mockFetchSSEWithError = vi.fn().mockImplementation((url, options) => {
1055
1055
  // Simulate the error being caught and passed to onErrorHandle
@@ -1211,8 +1211,8 @@ vi.mock('../_auth', async (importOriginal) => {
1211
1211
  describe('ChatService private methods', () => {
1212
1212
  describe('getChatCompletion', () => {
1213
1213
  it('should merge responseAnimation styles correctly', async () => {
1214
- const { fetchSSE } = await import('@/utils/fetch');
1215
- vi.mock('@/utils/fetch', async (importOriginal) => {
1214
+ const { fetchSSE } = await import('@lobechat/fetch-sse');
1215
+ vi.mock('@lobechat/fetch-sse', async (importOriginal) => {
1216
1216
  const module = await importOriginal();
1217
1217
  return {
1218
1218
  ...(module as any),
@@ -38,7 +38,7 @@ vi.stubGlobal(
38
38
  vi.fn(() => Promise.resolve(new Response(JSON.stringify({ some: 'data' })))),
39
39
  );
40
40
 
41
- vi.mock('@/utils/fetch', async (importOriginal) => {
41
+ vi.mock('@lobechat/fetch-sse', async (importOriginal) => {
42
42
  const module = await importOriginal();
43
43
 
44
44
  return { ...(module as any), getMessageError: vi.fn() };
@@ -1,3 +1,9 @@
1
+ import {
2
+ FetchSSEOptions,
3
+ fetchSSE,
4
+ getMessageError,
5
+ standardizeAnimationStyle,
6
+ } from '@lobechat/fetch-sse';
1
7
  import { AgentRuntimeError, ChatCompletionErrorPayload } from '@lobechat/model-runtime';
2
8
  import { ChatErrorType, TracePayload, TraceTagMap, UIChatMessage } from '@lobechat/types';
3
9
  import { PluginRequestPayload, createHeadersWithPluginSettings } from '@lobehub/chat-plugin-sdk';
@@ -25,12 +31,6 @@ import {
25
31
  import type { ChatStreamPayload, OpenAIChatMessage } from '@/types/openai/chat';
26
32
  import { fetchWithInvokeStream } from '@/utils/electron/desktopRemoteRPCFetch';
27
33
  import { createErrorResponse } from '@/utils/errorResponse';
28
- import {
29
- FetchSSEOptions,
30
- fetchSSE,
31
- getMessageError,
32
- standardizeAnimationStyle,
33
- } from '@/utils/fetch';
34
34
  import { createTraceHeader, getTraceId } from '@/utils/trace';
35
35
 
36
36
  import { createHeaderWithAuth } from '../_auth';
@@ -1,7 +1,6 @@
1
+ import { FetchSSEOptions } from '@lobechat/fetch-sse';
1
2
  import { TracePayload } from '@lobechat/types';
2
3
 
3
- import { FetchSSEOptions } from '@/utils/fetch';
4
-
5
4
  export interface FetchOptions extends FetchSSEOptions {
6
5
  historySummary?: string;
7
6
  signal?: AbortSignal | undefined;
@@ -7,6 +7,7 @@ import { useUserStore } from '@/store/user';
7
7
  import { preferenceSelectors } from '@/store/user/selectors';
8
8
  import {
9
9
  AssistantListResponse,
10
+ AssistantMarketSource,
10
11
  AssistantQueryParams,
11
12
  DiscoverAssistantDetail,
12
13
  DiscoverMcpDetail,
@@ -30,27 +31,37 @@ class DiscoverService {
30
31
  private _isRetrying = false;
31
32
 
32
33
  // ============================== Assistant Market ==============================
33
- getAssistantCategories = async (params: CategoryListQuery = {}): Promise<CategoryItem[]> => {
34
+ getAssistantCategories = async (
35
+ params: CategoryListQuery & { source?: AssistantMarketSource } = {},
36
+ ): Promise<CategoryItem[]> => {
34
37
  const locale = globalHelpers.getCurrentLanguage();
38
+ const { source, ...rest } = params;
35
39
  return lambdaClient.market.getAssistantCategories.query({
36
- ...params,
40
+ ...rest,
37
41
  locale,
42
+ source,
38
43
  });
39
44
  };
40
45
 
41
46
  getAssistantDetail = async (params: {
42
47
  identifier: string;
43
48
  locale?: string;
49
+ source?: AssistantMarketSource;
50
+ version?: string;
44
51
  }): Promise<DiscoverAssistantDetail | undefined> => {
45
52
  const locale = globalHelpers.getCurrentLanguage();
46
53
  return lambdaClient.market.getAssistantDetail.query({
47
- ...params,
54
+ identifier: params.identifier,
48
55
  locale,
56
+ source: params.source,
57
+ version: params.version,
49
58
  });
50
59
  };
51
60
 
52
- getAssistantIdentifiers = async (): Promise<IdentifiersResponse> => {
53
- return lambdaClient.market.getAssistantIdentifiers.query();
61
+ getAssistantIdentifiers = async (
62
+ params: { source?: AssistantMarketSource } = {},
63
+ ): Promise<IdentifiersResponse> => {
64
+ return lambdaClient.market.getAssistantIdentifiers.query(params);
54
65
  };
55
66
 
56
67
  getAssistantList = async (params: AssistantQueryParams = {}): Promise<AssistantListResponse> => {
@@ -1,4 +1,4 @@
1
- import { DataSyncConfig, dispatch } from '@lobechat/electron-client-ipc';
1
+ import { DataSyncConfig, MarketAuthorizationParams, dispatch } from '@lobechat/electron-client-ipc';
2
2
 
3
3
  class RemoteServerService {
4
4
  /**
@@ -28,6 +28,13 @@ class RemoteServerService {
28
28
  requestAuthorization = async (config: DataSyncConfig) => {
29
29
  return dispatch('requestAuthorization', config);
30
30
  };
31
+
32
+ /**
33
+ * 请求 Market 授权
34
+ */
35
+ requestMarketAuthorization = async (params: MarketAuthorizationParams) => {
36
+ return dispatch('requestMarketAuthorization', params);
37
+ };
31
38
  }
32
39
 
33
40
  export const remoteServerService = new RemoteServerService();
@@ -0,0 +1,124 @@
1
+ import { AgentItemDetail } from '@lobehub/market-sdk';
2
+
3
+ import { MARKET_ENDPOINTS } from '@/services/_url';
4
+
5
+ export class MarketApiService {
6
+ private accessToken?: string;
7
+
8
+ // eslint-disable-next-line no-undef
9
+ private async request<T>(endpoint: string, init?: RequestInit): Promise<T> {
10
+ const headers = new Headers(init?.headers);
11
+
12
+ if (init?.body && !headers.has('content-type')) {
13
+ headers.set('content-type', 'application/json');
14
+ }
15
+
16
+ if (this.accessToken && !headers.has('authorization')) {
17
+ headers.set('authorization', `Bearer ${this.accessToken}`);
18
+ }
19
+
20
+ const response = await fetch(endpoint, {
21
+ ...init,
22
+ credentials: init?.credentials ?? 'same-origin',
23
+ headers,
24
+ });
25
+
26
+ if (!response.ok) {
27
+ let message = 'Unknown error';
28
+
29
+ try {
30
+ const errorBody = await response.json();
31
+ message = errorBody?.message ?? message;
32
+ } catch {
33
+ message = await response.text();
34
+ }
35
+
36
+ throw new Error(message || 'Market request failed');
37
+ }
38
+
39
+ if (response.status === 204) {
40
+ return undefined as T;
41
+ }
42
+
43
+ return (await response.json()) as T;
44
+ }
45
+
46
+ setAccessToken(token: string) {
47
+ this.accessToken = token;
48
+ }
49
+ // Create new agent
50
+ async createAgent(agentData: {
51
+ homepage?: string;
52
+ identifier: string;
53
+ isFeatured?: boolean;
54
+ name: string;
55
+ status?: 'published' | 'unpublished' | 'archived' | 'deprecated';
56
+ tokenUsage?: number;
57
+ visibility?: 'public' | 'private' | 'internal';
58
+ }): Promise<AgentItemDetail> {
59
+ return this.request(MARKET_ENDPOINTS.createAgent, {
60
+ body: JSON.stringify(agentData),
61
+ method: 'POST',
62
+ });
63
+ }
64
+
65
+ // Get agent detail by identifier
66
+ async getAgentDetail(identifier: string): Promise<AgentItemDetail> {
67
+ return this.request(MARKET_ENDPOINTS.getAgentDetail(identifier), {
68
+ method: 'GET',
69
+ });
70
+ }
71
+
72
+ // Check if agent exists (returns true if exists, false if not)
73
+ async checkAgentExists(identifier: string): Promise<boolean> {
74
+ try {
75
+ await this.getAgentDetail(identifier);
76
+ return true;
77
+ } catch {
78
+ return false;
79
+ }
80
+ }
81
+
82
+ // Create agent version
83
+ async createAgentVersion(versionData: {
84
+ a2aProtocolVersion?: string;
85
+ avatar?: string;
86
+ category?: string;
87
+ changelog?: string;
88
+ config?: Record<string, any>;
89
+ defaultInputModes?: string[];
90
+ defaultOutputModes?: string[];
91
+ description?: string;
92
+ documentationUrl?: string;
93
+ extensions?: Record<string, any>[];
94
+ hasPushNotifications?: boolean;
95
+ hasStateTransitionHistory?: boolean;
96
+ hasStreaming?: boolean;
97
+ identifier: string;
98
+ interfaces?: Record<string, any>[];
99
+ name?: string;
100
+ preferredTransport?: string;
101
+ providerId?: number;
102
+ securityRequirements?: Record<string, any>[];
103
+ securitySchemes?: Record<string, any>;
104
+ setAsCurrent?: boolean;
105
+ summary?: string;
106
+ supportsAuthenticatedExtendedCard?: boolean;
107
+ tokenUsage?: number;
108
+ url?: string;
109
+ }): Promise<AgentItemDetail> {
110
+ const { identifier, ...rest } = versionData;
111
+ const targetIdentifier = identifier;
112
+ if (!targetIdentifier) throw new Error('Identifier is required');
113
+
114
+ return this.request(MARKET_ENDPOINTS.createAgentVersion, {
115
+ body: JSON.stringify({
116
+ identifier: targetIdentifier,
117
+ ...rest,
118
+ }),
119
+ method: 'POST',
120
+ });
121
+ }
122
+ }
123
+
124
+ export const marketApiService = new MarketApiService();
@@ -1,7 +1,8 @@
1
+ import { getMessageError } from '@lobechat/fetch-sse';
2
+
1
3
  import { createHeaderWithAuth } from '@/services/_auth';
2
4
  import { aiProviderSelectors, getAiInfraStoreState } from '@/store/aiInfra';
3
5
  import { ChatModelCard } from '@/types/llm';
4
- import { getMessageError } from '@/utils/fetch';
5
6
 
6
7
  import { API_ENDPOINTS } from './_url';
7
8
  import { initializeWithClientStore } from './chat/clientModelRuntime';
@@ -7,17 +7,24 @@ import { DiscoverStore } from '@/store/discover';
7
7
  import { globalHelpers } from '@/store/global/helpers';
8
8
  import {
9
9
  AssistantListResponse,
10
+ AssistantMarketSource,
10
11
  AssistantQueryParams,
11
12
  DiscoverAssistantDetail,
12
13
  IdentifiersResponse,
13
14
  } from '@/types/discover';
14
15
 
15
16
  export interface AssistantAction {
16
- useAssistantCategories: (params: CategoryListQuery) => SWRResponse<CategoryItem[]>;
17
+ useAssistantCategories: (
18
+ params: CategoryListQuery & { source?: AssistantMarketSource },
19
+ ) => SWRResponse<CategoryItem[]>;
17
20
  useAssistantDetail: (params: {
18
21
  identifier: string;
22
+ source?: AssistantMarketSource;
23
+ version?: string;
19
24
  }) => SWRResponse<DiscoverAssistantDetail | undefined>;
20
- useAssistantIdentifiers: () => SWRResponse<IdentifiersResponse>;
25
+ useAssistantIdentifiers: (params?: {
26
+ source?: AssistantMarketSource;
27
+ }) => SWRResponse<IdentifiersResponse>;
21
28
  useAssistantList: (params?: AssistantQueryParams) => SWRResponse<AssistantListResponse>;
22
29
  }
23
30
 
@@ -41,7 +48,9 @@ export const createAssistantSlice: StateCreator<
41
48
  useAssistantDetail: (params) => {
42
49
  const locale = globalHelpers.getCurrentLanguage();
43
50
  return useSWR(
44
- ['assistant-details', locale, params.identifier].filter(Boolean).join('-'),
51
+ ['assistant-details', locale, params.identifier, params.version, params.source]
52
+ .filter(Boolean)
53
+ .join('-'),
45
54
  async () => discoverService.getAssistantDetail(params),
46
55
  {
47
56
  revalidateOnFocus: false,
@@ -49,10 +58,14 @@ export const createAssistantSlice: StateCreator<
49
58
  );
50
59
  },
51
60
 
52
- useAssistantIdentifiers: () => {
53
- return useSWR('assistant-identifiers', async () => discoverService.getAssistantIdentifiers(), {
54
- revalidateOnFocus: false,
55
- });
61
+ useAssistantIdentifiers: (params) => {
62
+ return useSWR(
63
+ ['assistant-identifiers', params?.source].filter(Boolean).join('-') || 'assistant-identifiers',
64
+ async () => discoverService.getAssistantIdentifiers(params),
65
+ {
66
+ revalidateOnFocus: false,
67
+ },
68
+ );
56
69
  },
57
70
 
58
71
  useAssistantList: (params = {}) => {
@@ -1,10 +1,10 @@
1
1
  import { isDesktop } from '@lobechat/const';
2
2
  import { ProxyTRPCRequestParams, dispatch, streamInvoke } from '@lobechat/electron-client-ipc';
3
+ import { getRequestBody, headersToRecord } from '@lobechat/fetch-sse';
3
4
  import debug from 'debug';
4
5
 
5
6
  import { getElectronStoreState } from '@/store/electron';
6
7
  import { electronSyncSelectors } from '@/store/electron/selectors';
7
- import { getRequestBody, headersToRecord } from '@/utils/fetch';
8
8
 
9
9
  const log = debug('utils:desktopRemoteRPCFetch');
10
10
 
@@ -1,9 +1,8 @@
1
1
  import { getModelPropertyWithFallback } from '@lobechat/model-runtime';
2
+ import { merge } from '@lobechat/utils';
2
3
  import { produce } from 'immer';
3
4
  import { AiFullModelCard, AiModelType } from 'model-bank';
4
5
 
5
- import { merge } from './merge';
6
-
7
6
  /**
8
7
  * Parse model string to add or remove models.
9
8
  */
package/vitest.config.mts CHANGED
@@ -16,6 +16,8 @@ export default defineConfig({
16
16
  // TODO: after refactor the errorResponse, we can remove it
17
17
  '@/utils/errorResponse': resolve(__dirname, './src/utils/errorResponse'),
18
18
  '@/utils/unzipFile': resolve(__dirname, './src/utils/unzipFile'),
19
+ '@/utils/server': resolve(__dirname, './src/utils/server'),
20
+ '@/utils/electron': resolve(__dirname, './src/utils/electron'),
19
21
  '@/utils': resolve(__dirname, './packages/utils/src'),
20
22
  '@/types': resolve(__dirname, './packages/types/src'),
21
23
  '@/const': resolve(__dirname, './packages/const/src'),
@@ -1,91 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
-
3
- import { imageToBase64, imageUrlToBase64 } from './imageToBase64';
4
-
5
- describe('imageToBase64', () => {
6
- let mockImage: HTMLImageElement;
7
- let mockCanvas: HTMLCanvasElement;
8
- let mockContext: CanvasRenderingContext2D;
9
-
10
- beforeEach(() => {
11
- mockImage = {
12
- width: 200,
13
- height: 100,
14
- } as HTMLImageElement;
15
-
16
- mockContext = {
17
- drawImage: vi.fn(),
18
- } as unknown as CanvasRenderingContext2D;
19
-
20
- mockCanvas = {
21
- width: 0,
22
- height: 0,
23
- getContext: vi.fn().mockReturnValue(mockContext),
24
- toDataURL: vi.fn().mockReturnValue('data:image/webp;base64,mockBase64Data'),
25
- } as unknown as HTMLCanvasElement;
26
-
27
- vi.spyOn(document, 'createElement').mockReturnValue(mockCanvas);
28
- });
29
-
30
- afterEach(() => {
31
- vi.restoreAllMocks();
32
- });
33
-
34
- it('should convert image to base64 with correct size and type', () => {
35
- const result = imageToBase64({ img: mockImage, size: 100, type: 'image/jpeg' });
36
-
37
- expect(document.createElement).toHaveBeenCalledWith('canvas');
38
- expect(mockCanvas.width).toBe(100);
39
- expect(mockCanvas.height).toBe(100);
40
- expect(mockCanvas.getContext).toHaveBeenCalledWith('2d');
41
- expect(mockContext.drawImage).toHaveBeenCalledWith(mockImage, 50, 0, 100, 100, 0, 0, 100, 100);
42
- expect(mockCanvas.toDataURL).toHaveBeenCalledWith('image/jpeg');
43
- expect(result).toBe('data:image/webp;base64,mockBase64Data');
44
- });
45
-
46
- it('should use default type when not specified', () => {
47
- imageToBase64({ img: mockImage, size: 100 });
48
- expect(mockCanvas.toDataURL).toHaveBeenCalledWith('image/webp');
49
- });
50
-
51
- it('should handle taller images correctly', () => {
52
- mockImage.width = 100;
53
- mockImage.height = 200;
54
- imageToBase64({ img: mockImage, size: 100 });
55
- expect(mockContext.drawImage).toHaveBeenCalledWith(mockImage, 0, 50, 100, 100, 0, 0, 100, 100);
56
- });
57
- });
58
-
59
- describe('imageUrlToBase64', () => {
60
- const mockFetch = vi.fn();
61
- const mockArrayBuffer = new ArrayBuffer(8);
62
-
63
- beforeEach(() => {
64
- global.fetch = mockFetch;
65
- global.btoa = vi.fn().mockReturnValue('mockBase64String');
66
- });
67
-
68
- afterEach(() => {
69
- vi.restoreAllMocks();
70
- });
71
-
72
- it('should convert image URL to base64 string', async () => {
73
- mockFetch.mockResolvedValue({
74
- arrayBuffer: () => Promise.resolve(mockArrayBuffer),
75
- blob: () => Promise.resolve(new Blob([mockArrayBuffer], { type: 'image/jpg' })),
76
- });
77
-
78
- const result = await imageUrlToBase64('https://example.com/image.jpg');
79
-
80
- expect(mockFetch).toHaveBeenCalledWith('https://example.com/image.jpg');
81
- expect(global.btoa).toHaveBeenCalled();
82
- expect(result).toEqual({ base64: 'mockBase64String', mimeType: 'image/jpg' });
83
- });
84
-
85
- it('should throw an error when fetch fails', async () => {
86
- const mockError = new Error('Fetch failed');
87
- mockFetch.mockRejectedValue(mockError);
88
-
89
- await expect(imageUrlToBase64('https://example.com/image.jpg')).rejects.toThrow('Fetch failed');
90
- });
91
- });
@@ -1,62 +0,0 @@
1
- export const imageToBase64 = ({
2
- size,
3
- img,
4
- type = 'image/webp',
5
- }: {
6
- img: HTMLImageElement;
7
- size: number;
8
- type?: string;
9
- }) => {
10
- const canvas = document.createElement('canvas');
11
- const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
12
- let startX = 0;
13
- let startY = 0;
14
-
15
- if (img.width > img.height) {
16
- startX = (img.width - img.height) / 2;
17
- } else {
18
- startY = (img.height - img.width) / 2;
19
- }
20
-
21
- canvas.width = size;
22
- canvas.height = size;
23
-
24
- ctx.drawImage(
25
- img,
26
- startX,
27
- startY,
28
- Math.min(img.width, img.height),
29
- Math.min(img.width, img.height),
30
- 0,
31
- 0,
32
- size,
33
- size,
34
- );
35
-
36
- return canvas.toDataURL(type);
37
- };
38
-
39
- export const imageUrlToBase64 = async (
40
- imageUrl: string,
41
- ): Promise<{ base64: string; mimeType: string }> => {
42
- try {
43
- const res = await fetch(imageUrl);
44
- const blob = await res.blob();
45
- const arrayBuffer = await blob.arrayBuffer();
46
-
47
- const base64 =
48
- typeof btoa === 'function'
49
- ? btoa(
50
- new Uint8Array(arrayBuffer).reduce(
51
- (data, byte) => data + String.fromCharCode(byte),
52
- '',
53
- ),
54
- )
55
- : Buffer.from(arrayBuffer).toString('base64');
56
-
57
- return { base64, mimeType: blob.type };
58
- } catch (error) {
59
- console.error('Error converting image to base64:', error);
60
- throw error;
61
- }
62
- };
@@ -1,98 +0,0 @@
1
- 'use client';
2
-
3
- import { Alert, Button, Input, Modal, type ModalProps } from '@lobehub/ui';
4
- import { Divider } from 'antd';
5
- import { useTheme } from 'antd-style';
6
- import isEqual from 'fast-deep-equal';
7
- import { kebabCase } from 'lodash-es';
8
- import qs from 'query-string';
9
- import { memo, useState } from 'react';
10
- import { useTranslation } from 'react-i18next';
11
- import { Flexbox } from 'react-layout-kit';
12
-
13
- import { AGENTS_INDEX_GITHUB_ISSUE } from '@/const/url';
14
- import AgentInfo from '@/features/AgentInfo';
15
- import { useAgentStore } from '@/store/agent';
16
- import { agentSelectors } from '@/store/agent/selectors';
17
- import { useGlobalStore } from '@/store/global';
18
- import { globalGeneralSelectors } from '@/store/global/selectors';
19
- import { useSessionStore } from '@/store/session';
20
- import { sessionMetaSelectors } from '@/store/session/selectors';
21
-
22
- const SubmitAgentModal = memo<ModalProps>(({ open, onCancel }) => {
23
- const { t } = useTranslation('setting');
24
- const [identifier, setIdentifier] = useState('');
25
- const systemRole = useAgentStore(agentSelectors.currentAgentSystemRole);
26
- const theme = useTheme();
27
- const meta = useSessionStore(sessionMetaSelectors.currentAgentMeta, isEqual);
28
- const language = useGlobalStore(globalGeneralSelectors.currentLanguage);
29
-
30
- const isMetaPass = Boolean(
31
- meta && meta.title && meta.description && (meta.tags as string[])?.length > 0 && meta.avatar,
32
- );
33
-
34
- const handleSubmit = () => {
35
- const body = [
36
- '### systemRole',
37
- systemRole,
38
- '### identifier',
39
- kebabCase(identifier),
40
- '### avatar',
41
- meta.avatar,
42
- '### title',
43
- meta.title,
44
- '### description',
45
- meta.description,
46
- '### tags',
47
- (meta.tags as string[]).join(', '),
48
- '### locale',
49
- language,
50
- ].join('\n\n');
51
-
52
- const url = qs.stringifyUrl({
53
- query: { body, labels: '🤖 Agent PR', title: `[Agent] ${meta.title}` },
54
- url: AGENTS_INDEX_GITHUB_ISSUE,
55
- });
56
-
57
- window.open(url, '_blank');
58
- };
59
-
60
- return (
61
- <Modal
62
- allowFullscreen
63
- footer={
64
- <Button
65
- block
66
- disabled={!isMetaPass || !identifier}
67
- onClick={handleSubmit}
68
- size={'large'}
69
- type={'primary'}
70
- >
71
- {t('submitAgentModal.button')}
72
- </Button>
73
- }
74
- onCancel={onCancel}
75
- open={open}
76
- title={t('submitAgentModal.tooltips')}
77
- >
78
- <Flexbox gap={16}>
79
- {!isMetaPass && (
80
- <Alert message={t('submitAgentModal.metaMiss')} showIcon type={'warning'} />
81
- )}
82
- <AgentInfo meta={meta} systemRole={systemRole} />
83
- <Divider style={{ margin: '8px 0' }} />
84
- <strong>
85
- <span style={{ color: theme.colorError, marginRight: 4 }}>*</span>
86
- {t('submitAgentModal.identifier')}
87
- </strong>
88
- <Input
89
- onChange={(e) => setIdentifier(e.target.value)}
90
- placeholder={t('submitAgentModal.placeholder')}
91
- value={identifier}
92
- />
93
- </Flexbox>
94
- </Modal>
95
- );
96
- });
97
-
98
- export default SubmitAgentModal;
@@ -1,35 +0,0 @@
1
- import { ActionIcon, Button } from '@lobehub/ui';
2
- import { Share2 } from 'lucide-react';
3
- import { memo, useState } from 'react';
4
- import { useTranslation } from 'react-i18next';
5
-
6
- import { HEADER_ICON_SIZE } from '@/const/layoutTokens';
7
- import { useServerConfigStore } from '@/store/serverConfig';
8
-
9
- import SubmitAgentModal from './SubmitAgentModal';
10
-
11
- const SubmitAgentButton = memo<{ modal?: boolean }>(({ modal }) => {
12
- const { t } = useTranslation('setting');
13
- const mobile = useServerConfigStore((s) => s.isMobile);
14
- const [isModalOpen, setIsModalOpen] = useState(false);
15
-
16
- return (
17
- <>
18
- {modal ? (
19
- <Button block icon={Share2} onClick={() => setIsModalOpen(true)} variant={'filled'}>
20
- {t('submitAgentModal.tooltips')}
21
- </Button>
22
- ) : (
23
- <ActionIcon
24
- icon={Share2}
25
- onClick={() => setIsModalOpen(true)}
26
- size={HEADER_ICON_SIZE(mobile)}
27
- title={t('submitAgentModal.tooltips')}
28
- />
29
- )}
30
- <SubmitAgentModal onCancel={() => setIsModalOpen(false)} open={isModalOpen} />
31
- </>
32
- );
33
- });
34
-
35
- export default SubmitAgentButton;