@lobehub/chat 1.98.2 → 1.99.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (458) hide show
  1. package/.cursor/rules/backend-architecture.mdc +93 -17
  2. package/.cursor/rules/cursor-ux.mdc +45 -35
  3. package/.cursor/rules/project-introduce.mdc +72 -6
  4. package/.cursor/rules/rules-attach.mdc +16 -7
  5. package/.eslintrc.js +10 -0
  6. package/CHANGELOG.md +44 -0
  7. package/apps/desktop/README.md +7 -0
  8. package/apps/desktop/electron-builder.js +5 -0
  9. package/apps/desktop/package.json +2 -1
  10. package/apps/desktop/src/main/appBrowsers.ts +1 -1
  11. package/apps/desktop/src/main/const/dir.ts +3 -0
  12. package/apps/desktop/src/main/controllers/UploadFileCtr.ts +13 -8
  13. package/apps/desktop/src/main/core/App.ts +8 -0
  14. package/apps/desktop/src/main/core/BrowserManager.ts +5 -2
  15. package/apps/desktop/src/main/core/StaticFileServerManager.ts +221 -0
  16. package/apps/desktop/src/main/services/fileSrv.ts +231 -44
  17. package/apps/desktop/src/main/utils/next-electron-rsc.ts +36 -5
  18. package/changelog/v1.json +14 -0
  19. package/docs/development/database-schema.dbml +70 -0
  20. package/locales/ar/common.json +2 -0
  21. package/locales/ar/components.json +35 -0
  22. package/locales/ar/error.json +2 -0
  23. package/locales/ar/image.json +100 -0
  24. package/locales/ar/metadata.json +4 -0
  25. package/locales/ar/modelProvider.json +1 -0
  26. package/locales/ar/models.json +15 -0
  27. package/locales/ar/plugin.json +22 -0
  28. package/locales/ar/providers.json +3 -0
  29. package/locales/ar/setting.json +5 -0
  30. package/locales/bg-BG/common.json +2 -0
  31. package/locales/bg-BG/components.json +35 -0
  32. package/locales/bg-BG/error.json +2 -0
  33. package/locales/bg-BG/image.json +100 -0
  34. package/locales/bg-BG/metadata.json +4 -0
  35. package/locales/bg-BG/modelProvider.json +1 -0
  36. package/locales/bg-BG/models.json +15 -0
  37. package/locales/bg-BG/plugin.json +22 -0
  38. package/locales/bg-BG/providers.json +3 -0
  39. package/locales/bg-BG/setting.json +5 -0
  40. package/locales/de-DE/common.json +2 -0
  41. package/locales/de-DE/components.json +35 -0
  42. package/locales/de-DE/error.json +2 -0
  43. package/locales/de-DE/image.json +100 -0
  44. package/locales/de-DE/metadata.json +4 -0
  45. package/locales/de-DE/modelProvider.json +1 -0
  46. package/locales/de-DE/models.json +15 -0
  47. package/locales/de-DE/plugin.json +22 -0
  48. package/locales/de-DE/providers.json +3 -0
  49. package/locales/de-DE/setting.json +5 -0
  50. package/locales/en-US/common.json +2 -0
  51. package/locales/en-US/components.json +35 -0
  52. package/locales/en-US/error.json +2 -0
  53. package/locales/en-US/image.json +100 -0
  54. package/locales/en-US/metadata.json +4 -0
  55. package/locales/en-US/modelProvider.json +1 -0
  56. package/locales/en-US/models.json +15 -0
  57. package/locales/en-US/plugin.json +22 -0
  58. package/locales/en-US/providers.json +3 -0
  59. package/locales/en-US/setting.json +5 -0
  60. package/locales/es-ES/common.json +2 -0
  61. package/locales/es-ES/components.json +35 -0
  62. package/locales/es-ES/error.json +2 -0
  63. package/locales/es-ES/image.json +100 -0
  64. package/locales/es-ES/metadata.json +4 -0
  65. package/locales/es-ES/modelProvider.json +1 -0
  66. package/locales/es-ES/models.json +15 -0
  67. package/locales/es-ES/plugin.json +22 -0
  68. package/locales/es-ES/providers.json +3 -0
  69. package/locales/es-ES/setting.json +5 -0
  70. package/locales/fa-IR/common.json +2 -0
  71. package/locales/fa-IR/components.json +35 -0
  72. package/locales/fa-IR/error.json +2 -0
  73. package/locales/fa-IR/image.json +100 -0
  74. package/locales/fa-IR/metadata.json +4 -0
  75. package/locales/fa-IR/modelProvider.json +1 -0
  76. package/locales/fa-IR/models.json +15 -0
  77. package/locales/fa-IR/plugin.json +22 -0
  78. package/locales/fa-IR/providers.json +3 -0
  79. package/locales/fa-IR/setting.json +5 -0
  80. package/locales/fr-FR/common.json +2 -0
  81. package/locales/fr-FR/components.json +35 -0
  82. package/locales/fr-FR/error.json +2 -0
  83. package/locales/fr-FR/image.json +100 -0
  84. package/locales/fr-FR/metadata.json +4 -0
  85. package/locales/fr-FR/modelProvider.json +1 -0
  86. package/locales/fr-FR/models.json +15 -0
  87. package/locales/fr-FR/plugin.json +22 -0
  88. package/locales/fr-FR/providers.json +3 -0
  89. package/locales/fr-FR/setting.json +5 -0
  90. package/locales/it-IT/common.json +2 -0
  91. package/locales/it-IT/components.json +35 -0
  92. package/locales/it-IT/error.json +2 -0
  93. package/locales/it-IT/image.json +100 -0
  94. package/locales/it-IT/metadata.json +4 -0
  95. package/locales/it-IT/modelProvider.json +1 -0
  96. package/locales/it-IT/models.json +15 -0
  97. package/locales/it-IT/plugin.json +22 -0
  98. package/locales/it-IT/providers.json +3 -0
  99. package/locales/it-IT/setting.json +5 -0
  100. package/locales/ja-JP/common.json +2 -0
  101. package/locales/ja-JP/components.json +35 -0
  102. package/locales/ja-JP/error.json +2 -0
  103. package/locales/ja-JP/image.json +100 -0
  104. package/locales/ja-JP/metadata.json +4 -0
  105. package/locales/ja-JP/modelProvider.json +1 -0
  106. package/locales/ja-JP/models.json +15 -0
  107. package/locales/ja-JP/plugin.json +22 -0
  108. package/locales/ja-JP/providers.json +3 -0
  109. package/locales/ja-JP/setting.json +5 -0
  110. package/locales/ko-KR/common.json +2 -0
  111. package/locales/ko-KR/components.json +35 -0
  112. package/locales/ko-KR/error.json +2 -0
  113. package/locales/ko-KR/image.json +100 -0
  114. package/locales/ko-KR/metadata.json +4 -0
  115. package/locales/ko-KR/modelProvider.json +1 -0
  116. package/locales/ko-KR/models.json +15 -0
  117. package/locales/ko-KR/plugin.json +22 -0
  118. package/locales/ko-KR/providers.json +3 -0
  119. package/locales/ko-KR/setting.json +5 -0
  120. package/locales/nl-NL/common.json +2 -0
  121. package/locales/nl-NL/components.json +35 -0
  122. package/locales/nl-NL/error.json +2 -0
  123. package/locales/nl-NL/image.json +100 -0
  124. package/locales/nl-NL/metadata.json +4 -0
  125. package/locales/nl-NL/modelProvider.json +1 -0
  126. package/locales/nl-NL/models.json +15 -0
  127. package/locales/nl-NL/plugin.json +22 -0
  128. package/locales/nl-NL/providers.json +3 -0
  129. package/locales/nl-NL/setting.json +5 -0
  130. package/locales/pl-PL/common.json +2 -0
  131. package/locales/pl-PL/components.json +35 -0
  132. package/locales/pl-PL/error.json +2 -0
  133. package/locales/pl-PL/image.json +100 -0
  134. package/locales/pl-PL/metadata.json +4 -0
  135. package/locales/pl-PL/modelProvider.json +1 -0
  136. package/locales/pl-PL/models.json +15 -0
  137. package/locales/pl-PL/plugin.json +22 -0
  138. package/locales/pl-PL/providers.json +3 -0
  139. package/locales/pl-PL/setting.json +5 -0
  140. package/locales/pt-BR/common.json +2 -0
  141. package/locales/pt-BR/components.json +35 -0
  142. package/locales/pt-BR/error.json +2 -0
  143. package/locales/pt-BR/image.json +100 -0
  144. package/locales/pt-BR/metadata.json +4 -0
  145. package/locales/pt-BR/modelProvider.json +1 -0
  146. package/locales/pt-BR/models.json +15 -0
  147. package/locales/pt-BR/plugin.json +22 -0
  148. package/locales/pt-BR/providers.json +3 -0
  149. package/locales/pt-BR/setting.json +5 -0
  150. package/locales/ru-RU/common.json +2 -0
  151. package/locales/ru-RU/components.json +35 -0
  152. package/locales/ru-RU/error.json +2 -0
  153. package/locales/ru-RU/image.json +100 -0
  154. package/locales/ru-RU/metadata.json +4 -0
  155. package/locales/ru-RU/modelProvider.json +1 -0
  156. package/locales/ru-RU/models.json +15 -0
  157. package/locales/ru-RU/plugin.json +22 -0
  158. package/locales/ru-RU/providers.json +3 -0
  159. package/locales/ru-RU/setting.json +5 -0
  160. package/locales/tr-TR/common.json +2 -0
  161. package/locales/tr-TR/components.json +35 -0
  162. package/locales/tr-TR/error.json +2 -0
  163. package/locales/tr-TR/image.json +100 -0
  164. package/locales/tr-TR/metadata.json +4 -0
  165. package/locales/tr-TR/modelProvider.json +1 -0
  166. package/locales/tr-TR/models.json +15 -0
  167. package/locales/tr-TR/plugin.json +22 -0
  168. package/locales/tr-TR/providers.json +3 -0
  169. package/locales/tr-TR/setting.json +5 -0
  170. package/locales/vi-VN/common.json +2 -0
  171. package/locales/vi-VN/components.json +35 -0
  172. package/locales/vi-VN/error.json +2 -0
  173. package/locales/vi-VN/image.json +100 -0
  174. package/locales/vi-VN/metadata.json +4 -0
  175. package/locales/vi-VN/modelProvider.json +1 -0
  176. package/locales/vi-VN/models.json +15 -0
  177. package/locales/vi-VN/plugin.json +22 -0
  178. package/locales/vi-VN/providers.json +3 -0
  179. package/locales/vi-VN/setting.json +5 -0
  180. package/locales/zh-CN/common.json +2 -0
  181. package/locales/zh-CN/components.json +35 -0
  182. package/locales/zh-CN/error.json +2 -0
  183. package/locales/zh-CN/image.json +100 -0
  184. package/locales/zh-CN/metadata.json +4 -0
  185. package/locales/zh-CN/modelProvider.json +1 -0
  186. package/locales/zh-CN/models.json +15 -0
  187. package/locales/zh-CN/plugin.json +22 -0
  188. package/locales/zh-CN/providers.json +3 -0
  189. package/locales/zh-CN/setting.json +5 -0
  190. package/locales/zh-TW/common.json +2 -0
  191. package/locales/zh-TW/components.json +35 -0
  192. package/locales/zh-TW/error.json +2 -0
  193. package/locales/zh-TW/image.json +100 -0
  194. package/locales/zh-TW/metadata.json +4 -0
  195. package/locales/zh-TW/modelProvider.json +1 -0
  196. package/locales/zh-TW/models.json +15 -0
  197. package/locales/zh-TW/plugin.json +22 -0
  198. package/locales/zh-TW/providers.json +3 -0
  199. package/locales/zh-TW/setting.json +5 -0
  200. package/package.json +11 -4
  201. package/packages/electron-server-ipc/src/events/file.ts +3 -1
  202. package/packages/electron-server-ipc/src/types/file.ts +15 -0
  203. package/src/app/[variants]/(main)/_layout/Desktop/SideBar/TopActions.tsx +11 -1
  204. package/src/app/[variants]/(main)/image/@menu/components/AspectRatioSelect/index.tsx +73 -0
  205. package/src/app/[variants]/(main)/image/@menu/components/SeedNumberInput/index.tsx +39 -0
  206. package/src/app/[variants]/(main)/image/@menu/components/SizeSelect/index.tsx +89 -0
  207. package/src/app/[variants]/(main)/image/@menu/default.tsx +11 -0
  208. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/AspectRatioSelect.tsx +24 -0
  209. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/DimensionControlGroup.tsx +107 -0
  210. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/ImageNum.tsx +290 -0
  211. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/ImageUpload.tsx +504 -0
  212. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/ImageUrl.tsx +18 -0
  213. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/ImageUrlsUpload.tsx +19 -0
  214. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/ModelSelect.tsx +155 -0
  215. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/MultiImagesUpload/ImageManageModal.tsx +415 -0
  216. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/MultiImagesUpload/index.tsx +732 -0
  217. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/SeedNumberInput.tsx +24 -0
  218. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/SizeSelect.tsx +17 -0
  219. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/SizeSliderInput.tsx +15 -0
  220. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/StepsSliderInput.tsx +11 -0
  221. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/constants.ts +1 -0
  222. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/index.tsx +93 -0
  223. package/src/app/[variants]/(main)/image/@topic/default.tsx +17 -0
  224. package/src/app/[variants]/(main)/image/@topic/features/Topics/NewTopicButton.tsx +64 -0
  225. package/src/app/[variants]/(main)/image/@topic/features/Topics/SkeletonList.tsx +34 -0
  226. package/src/app/[variants]/(main)/image/@topic/features/Topics/TopicItem.tsx +136 -0
  227. package/src/app/[variants]/(main)/image/@topic/features/Topics/TopicItemContainer.tsx +91 -0
  228. package/src/app/[variants]/(main)/image/@topic/features/Topics/TopicList.tsx +57 -0
  229. package/src/app/[variants]/(main)/image/@topic/features/Topics/TopicUrlSync.tsx +37 -0
  230. package/src/app/[variants]/(main)/image/@topic/features/Topics/index.tsx +19 -0
  231. package/src/app/[variants]/(main)/image/NotSupportClient.tsx +153 -0
  232. package/src/app/[variants]/(main)/image/_layout/Desktop/Container.tsx +35 -0
  233. package/src/app/[variants]/(main)/image/_layout/Desktop/RegisterHotkeys.tsx +10 -0
  234. package/src/app/[variants]/(main)/image/_layout/Desktop/index.tsx +30 -0
  235. package/src/app/[variants]/(main)/image/_layout/Mobile/index.tsx +14 -0
  236. package/src/app/[variants]/(main)/image/_layout/type.ts +7 -0
  237. package/src/app/[variants]/(main)/image/features/GenerationFeed/BatchItem.tsx +196 -0
  238. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/ActionButtons.tsx +60 -0
  239. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/ElapsedTime.tsx +90 -0
  240. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/ErrorState.tsx +65 -0
  241. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/LoadingState.tsx +44 -0
  242. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/SuccessState.tsx +49 -0
  243. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/index.tsx +154 -0
  244. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/styles.ts +51 -0
  245. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/types.ts +39 -0
  246. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/utils.ts +11 -0
  247. package/src/app/[variants]/(main)/image/features/GenerationFeed/index.tsx +97 -0
  248. package/src/app/[variants]/(main)/image/features/ImageWorkspace/Content.tsx +48 -0
  249. package/src/app/[variants]/(main)/image/features/ImageWorkspace/EmptyState.tsx +37 -0
  250. package/src/app/[variants]/(main)/image/features/ImageWorkspace/SkeletonList.tsx +50 -0
  251. package/src/app/[variants]/(main)/image/features/ImageWorkspace/index.tsx +23 -0
  252. package/src/app/[variants]/(main)/image/features/PromptInput/Title.tsx +38 -0
  253. package/src/app/[variants]/(main)/image/features/PromptInput/index.tsx +114 -0
  254. package/src/app/[variants]/(main)/image/layout.tsx +19 -0
  255. package/src/app/[variants]/(main)/image/loading.tsx +3 -0
  256. package/src/app/[variants]/(main)/image/page.tsx +47 -0
  257. package/src/app/[variants]/(main)/settings/system-agent/index.tsx +2 -1
  258. package/src/chains/summaryGenerationTitle.ts +25 -0
  259. package/src/components/ImageItem/index.tsx +9 -6
  260. package/src/{features/Conversation/Error → components/InvalidAPIKey}/APIKeyForm/Bedrock.tsx +3 -4
  261. package/src/{features/Conversation/Error → components/InvalidAPIKey}/APIKeyForm/ProviderApiKeyForm.tsx +5 -4
  262. package/src/components/InvalidAPIKey/APIKeyForm/index.tsx +108 -0
  263. package/src/{features/Conversation/Error → components/InvalidAPIKey}/APIKeyForm/useApiKey.ts +2 -1
  264. package/src/components/InvalidAPIKey/index.tsx +30 -0
  265. package/src/components/KeyValueEditor/index.tsx +203 -0
  266. package/src/components/KeyValueEditor/utils.ts +42 -0
  267. package/src/config/aiModels/fal.ts +52 -0
  268. package/src/config/aiModels/index.ts +3 -0
  269. package/src/config/aiModels/openai.ts +20 -6
  270. package/src/config/llm.ts +6 -0
  271. package/src/config/modelProviders/fal.ts +21 -0
  272. package/src/config/modelProviders/index.ts +3 -0
  273. package/src/config/paramsSchemas/fal/flux-kontext-dev.ts +8 -0
  274. package/src/config/paramsSchemas/fal/flux-pro-kontext.ts +11 -0
  275. package/src/config/paramsSchemas/fal/flux-schnell.ts +9 -0
  276. package/src/config/paramsSchemas/fal/imagen4.ts +10 -0
  277. package/src/config/paramsSchemas/openai/gpt-image-1.ts +10 -0
  278. package/src/const/hotkeys.ts +2 -2
  279. package/src/const/image.ts +6 -0
  280. package/src/const/settings/systemAgent.ts +1 -0
  281. package/src/database/client/migrations.json +27 -0
  282. package/src/database/migrations/0026_add_autovacuum_tuning.sql +2 -0
  283. package/src/database/migrations/0027_ai_image.sql +47 -0
  284. package/src/database/migrations/meta/0027_snapshot.json +6003 -0
  285. package/src/database/migrations/meta/_journal.json +7 -0
  286. package/src/database/models/__tests__/asyncTask.test.ts +7 -5
  287. package/src/database/models/__tests__/file.test.ts +287 -0
  288. package/src/database/models/__tests__/generation.test.ts +786 -0
  289. package/src/database/models/__tests__/generationBatch.test.ts +614 -0
  290. package/src/database/models/__tests__/generationTopic.test.ts +411 -0
  291. package/src/database/models/aiModel.ts +2 -0
  292. package/src/database/models/asyncTask.ts +1 -1
  293. package/src/database/models/file.ts +28 -20
  294. package/src/database/models/generation.ts +197 -0
  295. package/src/database/models/generationBatch.ts +212 -0
  296. package/src/database/models/generationTopic.ts +131 -0
  297. package/src/database/repositories/aiInfra/index.test.ts +157 -1
  298. package/src/database/repositories/aiInfra/index.ts +37 -19
  299. package/src/database/repositories/tableViewer/index.test.ts +1 -1
  300. package/src/database/schemas/file.ts +8 -0
  301. package/src/database/schemas/generation.ts +127 -0
  302. package/src/database/schemas/index.ts +1 -0
  303. package/src/database/schemas/relations.ts +45 -1
  304. package/src/database/type.ts +2 -0
  305. package/src/database/utils/idGenerator.ts +3 -0
  306. package/src/features/Conversation/Error/ChatInvalidApiKey.tsx +39 -0
  307. package/src/features/Conversation/Error/InvalidAccessCode.tsx +2 -2
  308. package/src/features/Conversation/Error/index.tsx +3 -3
  309. package/src/features/ImageSidePanel/index.tsx +83 -0
  310. package/src/features/ImageTopicPanel/index.tsx +79 -0
  311. package/src/features/PluginDevModal/MCPManifestForm/CollapsibleSection.tsx +62 -0
  312. package/src/features/PluginDevModal/MCPManifestForm/QuickImportSection.tsx +158 -0
  313. package/src/features/PluginDevModal/MCPManifestForm/index.tsx +99 -155
  314. package/src/features/PluginStore/McpList/Detail/Settings/index.tsx +5 -2
  315. package/src/hooks/useDownloadImage.ts +31 -0
  316. package/src/hooks/useFetchGenerationTopics.ts +13 -0
  317. package/src/hooks/useHotkeys/imageScope.ts +48 -0
  318. package/src/libs/mcp/client.ts +55 -22
  319. package/src/libs/mcp/types.ts +42 -6
  320. package/src/libs/model-runtime/BaseAI.ts +3 -1
  321. package/src/libs/model-runtime/ModelRuntime.test.ts +80 -0
  322. package/src/libs/model-runtime/ModelRuntime.ts +15 -1
  323. package/src/libs/model-runtime/UniformRuntime/index.ts +4 -1
  324. package/src/libs/model-runtime/fal/index.test.ts +442 -0
  325. package/src/libs/model-runtime/fal/index.ts +88 -0
  326. package/src/libs/model-runtime/openai/index.test.ts +396 -2
  327. package/src/libs/model-runtime/openai/index.ts +129 -3
  328. package/src/libs/model-runtime/runtimeMap.ts +2 -0
  329. package/src/libs/model-runtime/types/image.ts +25 -0
  330. package/src/libs/model-runtime/types/type.ts +1 -0
  331. package/src/libs/model-runtime/utils/openaiCompatibleFactory/index.ts +10 -0
  332. package/src/libs/standard-parameters/index.ts +1 -0
  333. package/src/libs/standard-parameters/meta-schema.test.ts +214 -0
  334. package/src/libs/standard-parameters/meta-schema.ts +147 -0
  335. package/src/libs/swr/index.ts +1 -0
  336. package/src/libs/trpc/async/asyncAuth.ts +29 -8
  337. package/src/libs/trpc/async/context.ts +42 -4
  338. package/src/libs/trpc/async/index.ts +17 -4
  339. package/src/libs/trpc/async/init.ts +8 -0
  340. package/src/libs/trpc/client/lambda.ts +19 -2
  341. package/src/locales/default/common.ts +2 -0
  342. package/src/locales/default/components.ts +35 -0
  343. package/src/locales/default/error.ts +2 -0
  344. package/src/locales/default/image.ts +100 -0
  345. package/src/locales/default/index.ts +2 -0
  346. package/src/locales/default/metadata.ts +4 -0
  347. package/src/locales/default/modelProvider.ts +2 -0
  348. package/src/locales/default/plugin.ts +22 -0
  349. package/src/locales/default/setting.ts +5 -0
  350. package/src/middleware.ts +1 -0
  351. package/src/server/modules/ElectronIPCClient/index.ts +9 -1
  352. package/src/server/modules/S3/index.ts +15 -0
  353. package/src/server/routers/async/caller.ts +9 -1
  354. package/src/server/routers/async/image.ts +253 -0
  355. package/src/server/routers/async/index.ts +2 -0
  356. package/src/server/routers/lambda/aiProvider.test.ts +2 -0
  357. package/src/server/routers/lambda/generation.test.ts +267 -0
  358. package/src/server/routers/lambda/generation.ts +86 -0
  359. package/src/server/routers/lambda/generationBatch.test.ts +376 -0
  360. package/src/server/routers/lambda/generationBatch.ts +56 -0
  361. package/src/server/routers/lambda/generationTopic.test.ts +508 -0
  362. package/src/server/routers/lambda/generationTopic.ts +93 -0
  363. package/src/server/routers/lambda/image.ts +248 -0
  364. package/src/server/routers/lambda/index.ts +8 -0
  365. package/src/server/routers/tools/mcp.ts +15 -0
  366. package/src/server/services/file/__tests__/index.test.ts +135 -0
  367. package/src/server/services/file/impls/local.test.ts +153 -52
  368. package/src/server/services/file/impls/local.ts +70 -46
  369. package/src/server/services/file/impls/s3.test.ts +114 -0
  370. package/src/server/services/file/impls/s3.ts +40 -0
  371. package/src/server/services/file/impls/type.ts +10 -0
  372. package/src/server/services/file/index.ts +14 -0
  373. package/src/server/services/generation/index.ts +239 -0
  374. package/src/server/services/mcp/index.ts +20 -2
  375. package/src/services/__tests__/generation.test.ts +40 -0
  376. package/src/services/__tests__/generationBatch.test.ts +36 -0
  377. package/src/services/__tests__/generationTopic.test.ts +72 -0
  378. package/src/services/electron/file.ts +3 -1
  379. package/src/services/generation.ts +16 -0
  380. package/src/services/generationBatch.ts +25 -0
  381. package/src/services/generationTopic.ts +28 -0
  382. package/src/services/image.ts +33 -0
  383. package/src/services/mcp.ts +12 -7
  384. package/src/services/upload.ts +43 -9
  385. package/src/store/aiInfra/slices/aiProvider/action.ts +31 -6
  386. package/src/store/aiInfra/slices/aiProvider/initialState.ts +1 -0
  387. package/src/store/aiInfra/slices/aiProvider/selectors.ts +3 -0
  388. package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +5 -5
  389. package/src/store/chat/slices/message/action.ts +2 -2
  390. package/src/store/chat/slices/translate/action.ts +1 -1
  391. package/src/store/global/initialState.ts +9 -0
  392. package/src/store/global/selectors/systemStatus.ts +8 -0
  393. package/src/store/image/index.ts +2 -0
  394. package/src/store/image/initialState.ts +25 -0
  395. package/src/store/image/selectors.ts +4 -0
  396. package/src/store/image/slices/createImage/action.test.ts +330 -0
  397. package/src/store/image/slices/createImage/action.ts +134 -0
  398. package/src/store/image/slices/createImage/initialState.ts +9 -0
  399. package/src/store/image/slices/createImage/selectors.test.ts +114 -0
  400. package/src/store/image/slices/createImage/selectors.ts +9 -0
  401. package/src/store/image/slices/generationBatch/action.test.ts +495 -0
  402. package/src/store/image/slices/generationBatch/action.ts +303 -0
  403. package/src/store/image/slices/generationBatch/initialState.ts +13 -0
  404. package/src/store/image/slices/generationBatch/reducer.test.ts +568 -0
  405. package/src/store/image/slices/generationBatch/reducer.ts +101 -0
  406. package/src/store/image/slices/generationBatch/selectors.test.ts +307 -0
  407. package/src/store/image/slices/generationBatch/selectors.ts +36 -0
  408. package/src/store/image/slices/generationConfig/action.test.ts +351 -0
  409. package/src/store/image/slices/generationConfig/action.ts +295 -0
  410. package/src/store/image/slices/generationConfig/hooks.test.ts +304 -0
  411. package/src/store/image/slices/generationConfig/hooks.ts +118 -0
  412. package/src/store/image/slices/generationConfig/index.ts +1 -0
  413. package/src/store/image/slices/generationConfig/initialState.ts +37 -0
  414. package/src/store/image/slices/generationConfig/selectors.test.ts +204 -0
  415. package/src/store/image/slices/generationConfig/selectors.ts +25 -0
  416. package/src/store/image/slices/generationTopic/action.test.ts +687 -0
  417. package/src/store/image/slices/generationTopic/action.ts +319 -0
  418. package/src/store/image/slices/generationTopic/index.ts +2 -0
  419. package/src/store/image/slices/generationTopic/initialState.ts +14 -0
  420. package/src/store/image/slices/generationTopic/reducer.test.ts +198 -0
  421. package/src/store/image/slices/generationTopic/reducer.ts +66 -0
  422. package/src/store/image/slices/generationTopic/selectors.test.ts +103 -0
  423. package/src/store/image/slices/generationTopic/selectors.ts +15 -0
  424. package/src/store/image/store.ts +42 -0
  425. package/src/store/image/utils/size.ts +51 -0
  426. package/src/store/tool/slices/customPlugin/action.ts +10 -1
  427. package/src/store/tool/slices/mcpStore/action.ts +6 -4
  428. package/src/store/user/slices/settings/selectors/__snapshots__/settings.test.ts.snap +4 -0
  429. package/src/store/user/slices/settings/selectors/systemAgent.ts +2 -0
  430. package/src/types/aiModel.ts +8 -3
  431. package/src/types/aiProvider.ts +2 -0
  432. package/src/types/asyncTask.ts +2 -0
  433. package/src/types/files/index.ts +5 -0
  434. package/src/types/generation/index.ts +80 -0
  435. package/src/types/hotkey.ts +2 -0
  436. package/src/types/plugins/mcp.ts +2 -6
  437. package/src/types/tool/plugin.ts +8 -0
  438. package/src/types/user/settings/keyVaults.ts +5 -0
  439. package/src/types/user/settings/systemAgent.ts +1 -0
  440. package/src/utils/client/downloadFile.ts +33 -4
  441. package/src/utils/number.test.ts +105 -0
  442. package/src/utils/number.ts +25 -0
  443. package/src/utils/server/__tests__/geo.test.ts +6 -3
  444. package/src/utils/storeDebug.test.ts +152 -0
  445. package/src/utils/storeDebug.ts +16 -7
  446. package/src/utils/time.test.ts +259 -0
  447. package/src/utils/time.ts +18 -0
  448. package/src/utils/units.ts +61 -0
  449. package/src/utils/url.test.ts +358 -9
  450. package/src/utils/url.ts +105 -3
  451. package/{vitest.server.config.ts → vitest.config.server.ts} +3 -0
  452. package/.cursor/rules/i18n/i18n-auto-attached.mdc +0 -6
  453. package/src/features/Conversation/Error/APIKeyForm/index.tsx +0 -105
  454. package/src/features/Conversation/Error/InvalidAPIKey.tsx +0 -16
  455. package/src/features/PluginDevModal/MCPManifestForm/EnvEditor.tsx +0 -227
  456. /package/.cursor/rules/{i18n/i18n.mdc → i18n.mdc} +0 -0
  457. /package/src/app/[variants]/(main)/settings/system-agent/features/{createForm.tsx → SystemAgentForm.tsx} +0 -0
  458. /package/src/{features/Conversation/Error → components/InvalidAPIKey}/APIKeyForm/LoadingContext.ts +0 -0
@@ -1,21 +1,20 @@
1
1
  import { LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk';
2
- import { Alert, FormItem, Input, TextArea } from '@lobehub/ui';
3
- import { Button, Divider, Form, FormInstance } from 'antd';
2
+ import { Alert, FormItem, Input, InputPassword } from '@lobehub/ui';
3
+ import { Button, Divider, Form, FormInstance, Radio } from 'antd';
4
4
  import { useState } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
6
6
  import { Flexbox } from 'react-layout-kit';
7
7
 
8
+ import KeyValueEditor from '@/components/KeyValueEditor';
8
9
  import MCPStdioCommandInput from '@/components/MCPStdioCommandInput';
9
- import { isDesktop } from '@/const/version';
10
10
  import { mcpService } from '@/services/mcp';
11
11
  import { useToolStore } from '@/store/tool';
12
12
  import { pluginSelectors } from '@/store/tool/selectors';
13
- import { electronStylish } from '@/styles/electron';
14
13
 
15
14
  import ArgsInput from './ArgsInput';
16
- import EnvEditor from './EnvEditor';
15
+ import CollapsibleSection from './CollapsibleSection';
17
16
  import MCPTypeSelect from './MCPTypeSelect';
18
- import { parseMcpInput } from './utils';
17
+ import QuickImportSection from './QuickImportSection';
19
18
 
20
19
  interface MCPManifestFormProps {
21
20
  form: FormInstance;
@@ -28,81 +27,20 @@ const STDIO_ARGS = ['customParams', 'mcp', 'args'];
28
27
  const STDIO_ENV = ['customParams', 'mcp', 'env'];
29
28
  const MCP_TYPE = ['customParams', 'mcp', 'type'];
30
29
  const DESC_TYPE = ['customParams', 'description'];
30
+ // 新增认证相关常量
31
+ const AUTH_TYPE = ['customParams', 'mcp', 'auth', 'type'];
32
+ const AUTH_TOKEN = ['customParams', 'mcp', 'auth', 'token'];
33
+ // 新增 headers 相关常量
34
+ const HEADERS = ['customParams', 'mcp', 'headers'];
31
35
 
32
36
  const MCPManifestForm = ({ form, isEditMode }: MCPManifestFormProps) => {
33
37
  const { t } = useTranslation('plugin');
34
38
  const mcpType = Form.useWatch(MCP_TYPE, form);
39
+ const authType = Form.useWatch(AUTH_TYPE, form);
35
40
 
36
41
  const pluginIds = useToolStore(pluginSelectors.storeAndInstallPluginsIdList);
37
42
  const [isTesting, setIsTesting] = useState(false);
38
43
  const [connectionError, setConnectionError] = useState<string | null>(null);
39
- const [isImportModalVisible, setIsImportModalVisible] = useState(false);
40
- const [jsonInput, setJsonInput] = useState('');
41
- const [importError, setImportError] = useState<string | null>(null);
42
-
43
- const handleImportConfirm = () => {
44
- setImportError(null); // Clear previous import error
45
- setConnectionError(null); // Clear connection error
46
-
47
- const value = jsonInput.trim(); // Use the text area input
48
- if (!value) {
49
- setImportError(t('dev.mcp.quickImportError.empty'));
50
- return;
51
- }
52
-
53
- // Use the existing parseMcpInput function
54
- const parseResult = parseMcpInput(value);
55
-
56
- // Handle parsing errors from parseMcpInput
57
- if (parseResult.status === 'error') {
58
- // Assuming parseMcpInput returns an error message or code in parseResult
59
- // We might need a more specific error message based on parseResult.error
60
- setImportError(parseResult.errorCode);
61
- return;
62
- }
63
-
64
- if (parseResult.status === 'noop') {
65
- setImportError(t('dev.mcp.quickImportError.invalidJson'));
66
- return;
67
- }
68
-
69
- // Extract identifier and mcpConfig from the successful parse result
70
- const { identifier, mcpConfig } = parseResult;
71
-
72
- // Check for desktop requirement for stdio
73
- if (!isDesktop && mcpConfig.type === 'stdio') {
74
- setImportError(t('dev.mcp.stdioNotSupported'));
75
- return;
76
- }
77
-
78
- // Check for duplicate identifier (only in create mode)
79
- if (!isEditMode && pluginIds.includes(identifier)) {
80
- // Update form fields even if duplicate, so user sees the pasted values
81
- form.setFieldsValue({
82
- customParams: { mcp: mcpConfig },
83
- identifier: identifier,
84
- });
85
- // Trigger validation to show Form.Item error
86
- form.validateFields(['identifier']);
87
- setIsImportModalVisible(false); // Close modal even on duplicate error
88
- setJsonInput(''); // Clear modal input
89
- return;
90
- }
91
-
92
- // All checks passed, fill the form
93
- form.setFieldsValue({
94
- customParams: { mcp: mcpConfig },
95
- identifier: identifier,
96
- });
97
-
98
- // Clear potential old validation error on identifier field
99
- form.setFields([{ errors: [], name: 'identifier' }]);
100
-
101
- // Clear modal state and close (or rather, hide the import UI)
102
- setIsImportModalVisible(false);
103
- // setJsonInput(''); // Keep input for potential edits?
104
- setImportError(null);
105
- };
106
44
 
107
45
  const handleTestConnection = async () => {
108
46
  setIsTesting(true);
@@ -111,9 +49,20 @@ const MCPManifestForm = ({ form, isEditMode }: MCPManifestFormProps) => {
111
49
  // Manually trigger validation for fields needed for the test
112
50
  let isValid = false;
113
51
  try {
114
- await form.validateFields([
52
+ const fieldsToValidate = [
115
53
  ...(mcpType === 'http' ? [HTTP_URL_KEY] : [STDIO_COMMAND, STDIO_ARGS]),
116
- ]);
54
+ ];
55
+
56
+ // 如果是 HTTP 类型,还需要验证认证字段
57
+ if (mcpType === 'http') {
58
+ fieldsToValidate.push(AUTH_TYPE);
59
+ const currentAuthType = form.getFieldValue(AUTH_TYPE);
60
+ if (currentAuthType === 'bearer') {
61
+ fieldsToValidate.push(AUTH_TOKEN);
62
+ }
63
+ }
64
+
65
+ await form.validateFields(fieldsToValidate);
117
66
  isValid = true;
118
67
  } catch {
119
68
  // no-thing
@@ -135,9 +84,12 @@ const MCPManifestForm = ({ form, isEditMode }: MCPManifestFormProps) => {
135
84
 
136
85
  if (mcp.type === 'http') {
137
86
  if (!mcp.url) throw new Error(t('dev.mcp.url.required'));
138
- data = await mcpService.getStreamableMcpServerManifest(id, mcp.url, {
139
- avatar,
140
- description,
87
+ data = await mcpService.getStreamableMcpServerManifest({
88
+ auth: mcp.auth,
89
+ headers: mcp.headers,
90
+ identifier: id,
91
+ metadata: { avatar, description },
92
+ url: mcp.url,
141
93
  });
142
94
  } else if (mcp.type === 'stdio') {
143
95
  if (!mcp.command) throw new Error(t('dev.mcp.command.required'));
@@ -173,67 +125,15 @@ const MCPManifestForm = ({ form, isEditMode }: MCPManifestFormProps) => {
173
125
 
174
126
  return (
175
127
  <>
176
- {isImportModalVisible ? (
177
- <Flexbox gap={8}>
178
- {importError && (
179
- <Alert message={importError} showIcon style={{ marginBottom: 8 }} type="error" />
180
- )}
181
- <TextArea
182
- autoSize={{ maxRows: 15, minRows: 10 }}
183
- onChange={(e) => {
184
- setJsonInput(e.target.value);
185
- if (importError) setImportError(null);
186
- }}
187
- placeholder={`{
188
- "mcpServers": {
189
- "github": {
190
- "command": "npx",
191
- "args": [
192
- "-y",
193
- "@modelcontextprotocol/server-github"
194
- ],
195
- "env": {
196
- "GITHUB_PERSONAL_ACCESS_TOKEN": "<your-api-key>"
197
- }
198
- }
199
- }
200
- }`}
201
- value={jsonInput}
202
- />
203
- <Flexbox horizontal justify={'space-between'}>
204
- <Button
205
- className={electronStylish.nodrag}
206
- onClick={() => {
207
- setIsImportModalVisible(false);
208
- }}
209
- size={'small'}
210
- >
211
- 取消
212
- </Button>
213
- <Button onClick={handleImportConfirm} size={'small'} type={'primary'}>
214
- 导入
215
- </Button>
216
- </Flexbox>
217
- </Flexbox>
218
- ) : (
219
- <div>
220
- <Button
221
- block // Make button full width
222
- onClick={() => {
223
- setImportError(null); // Clear previous errors when opening
224
- setIsImportModalVisible(true);
225
- }}
226
- style={{ marginBottom: 16 }} // Add some spacing
227
- type="dashed"
228
- >
229
- {t('dev.mcp.quickImport')}
230
- </Button>
231
- </div>
232
- )}
233
-
128
+ <QuickImportSection
129
+ form={form}
130
+ isEditMode={isEditMode}
131
+ onClearConnectionError={() => setConnectionError(null)}
132
+ />
234
133
  <Form form={form} layout={'vertical'}>
235
134
  <Flexbox>
236
135
  <Form.Item
136
+ initialValue={'http'}
237
137
  label={t('dev.mcp.type.title')}
238
138
  name={['customParams', 'mcp', 'type']}
239
139
  rules={[{ required: true }]}
@@ -268,26 +168,67 @@ const MCPManifestForm = ({ form, isEditMode }: MCPManifestFormProps) => {
268
168
  <Input placeholder={t('dev.mcp.identifier.placeholder')} />
269
169
  </FormItem>
270
170
  {mcpType === 'http' && (
271
- <FormItem
272
- desc={t('dev.mcp.url.desc')}
273
- label={t('dev.mcp.url.label')}
274
- name={HTTP_URL_KEY}
275
- rules={[
276
- { message: t('dev.mcp.url.required'), required: true },
277
- {
278
- message: t('dev.mcp.url.invalid'),
279
- validator: async (_, value) => {
280
- if (!value) return true;
171
+ <>
172
+ <FormItem
173
+ desc={t('dev.mcp.url.desc')}
174
+ label={t('dev.mcp.url.label')}
175
+ name={HTTP_URL_KEY}
176
+ rules={[
177
+ { message: t('dev.mcp.url.required'), required: true },
178
+ {
179
+ message: t('dev.mcp.url.invalid'),
180
+ validator: async (_, value) => {
181
+ if (!value) return true;
281
182
 
282
- // 如果不是 URL 就会自动抛出错误
283
- new URL(value);
183
+ // 如果不是 URL 就会自动抛出错误
184
+ new URL(value);
185
+ },
284
186
  },
285
- },
286
- ]}
287
- tag={'url'}
288
- >
289
- <Input placeholder="https://mcp.higress.ai/mcp-github/xxxxx" />
290
- </FormItem>
187
+ ]}
188
+ tag={'url'}
189
+ >
190
+ <Input placeholder="https://mcp.higress.ai/mcp-github/xxxxx" />
191
+ </FormItem>
192
+ <FormItem
193
+ desc={t('dev.mcp.auth.desc')}
194
+ initialValue={'none'}
195
+ label={t('dev.mcp.auth.label')}
196
+ name={AUTH_TYPE}
197
+ >
198
+ <Radio.Group
199
+ options={[
200
+ {
201
+ label: t('dev.mcp.auth.none'),
202
+ value: 'none',
203
+ },
204
+ {
205
+ label: t('dev.mcp.auth.bear'),
206
+ value: 'bearer',
207
+ },
208
+ ]}
209
+ style={{ width: '100%' }}
210
+ />
211
+ </FormItem>
212
+ {authType === 'bearer' && (
213
+ <FormItem
214
+ desc={t('dev.mcp.auth.token.desc')}
215
+ label={t('dev.mcp.auth.token.label')}
216
+ name={AUTH_TOKEN}
217
+ rules={[{ message: t('dev.mcp.auth.token.required'), required: true }]}
218
+ >
219
+ <InputPassword placeholder={t('dev.mcp.auth.token.placeholder')} />
220
+ </FormItem>
221
+ )}
222
+ <CollapsibleSection title={t('dev.mcp.advanced.title')}>
223
+ <FormItem
224
+ desc={t('dev.mcp.headers.desc')}
225
+ label={t('dev.mcp.headers.label')}
226
+ name={HEADERS}
227
+ >
228
+ <KeyValueEditor addButtonText={t('dev.mcp.headers.add')} />
229
+ </FormItem>
230
+ </CollapsibleSection>
231
+ </>
291
232
  )}
292
233
  {mcpType === 'stdio' && (
293
234
  <>
@@ -315,7 +256,10 @@ const MCPManifestForm = ({ form, isEditMode }: MCPManifestFormProps) => {
315
256
  name={STDIO_ENV}
316
257
  tag={'env'}
317
258
  >
318
- <EnvEditor />
259
+ <KeyValueEditor
260
+ addButtonText={t('dev.mcp.env.add')}
261
+ keyPlaceholder="VARIABLE_NAME"
262
+ />
319
263
  </FormItem>
320
264
  </>
321
265
  )}
@@ -6,9 +6,9 @@ import { memo, useState } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
  import { Flexbox } from 'react-layout-kit';
8
8
 
9
+ import KeyValueEditor from '@/components/KeyValueEditor';
9
10
  import MCPStdioCommandInput from '@/components/MCPStdioCommandInput';
10
11
  import ArgsInput from '@/features/PluginDevModal/MCPManifestForm/ArgsInput';
11
- import EnvEditor from '@/features/PluginDevModal/MCPManifestForm/EnvEditor';
12
12
  import { useToolStore } from '@/store/tool';
13
13
  import { pluginSelectors } from '@/store/tool/selectors';
14
14
 
@@ -342,7 +342,10 @@ const Settings = memo<{ identifier: string }>(({ identifier }) => {
342
342
  onFinish={handleEnvSubmit}
343
343
  >
344
344
  <AForm.Item name="env" style={{ marginBottom: 0 }}>
345
- <EnvEditor />
345
+ <KeyValueEditor
346
+ addButtonText={t('dev.mcp.env.add')}
347
+ keyPlaceholder="VARIABLE_NAME"
348
+ />
346
349
  </AForm.Item>
347
350
  <div className={styles.footer}>
348
351
  <Space>
@@ -0,0 +1,31 @@
1
+ import { useMemoizedFn } from 'ahooks';
2
+ import { App } from 'antd';
3
+ import { useState } from 'react';
4
+ import { useTranslation } from 'react-i18next';
5
+
6
+ import { downloadFile } from '@/utils/client/downloadFile';
7
+
8
+ export function useDownloadImage() {
9
+ const [isDownloading, setIsDownloading] = useState(false);
10
+ const { message } = App.useApp();
11
+ const { t } = useTranslation('image');
12
+
13
+ return {
14
+ downloadImage: useMemoizedFn(async (url: string, fileName: string) => {
15
+ setIsDownloading(true);
16
+ try {
17
+ await downloadFile(url, fileName, false);
18
+ } catch (error) {
19
+ console.error('Failed to download image:', error);
20
+
21
+ // Show a general error message that covers network issues and CORS problems
22
+ message.error(t('generation.actions.downloadFailed'));
23
+
24
+ throw error;
25
+ } finally {
26
+ setIsDownloading(false);
27
+ }
28
+ }),
29
+ isDownloading,
30
+ };
31
+ }
@@ -0,0 +1,13 @@
1
+ import { useGlobalStore } from '@/store/global';
2
+ import { systemStatusSelectors } from '@/store/global/selectors';
3
+ import { useImageStore } from '@/store/image';
4
+ import { useUserStore } from '@/store/user';
5
+ import { authSelectors } from '@/store/user/slices/auth/selectors';
6
+
7
+ export const useFetchGenerationTopics = () => {
8
+ const isDBInited = useGlobalStore(systemStatusSelectors.isDBInited);
9
+ const isLogin = useUserStore(authSelectors.isLogin);
10
+ const useFetchGenerationTopics = useImageStore((s) => s.useFetchGenerationTopics);
11
+
12
+ useFetchGenerationTopics(isDBInited, isLogin);
13
+ };
@@ -0,0 +1,48 @@
1
+ import { useEffect } from 'react';
2
+ import { useHotkeysContext } from 'react-hotkeys-hook';
3
+
4
+ import { FOLDER_WIDTH } from '@/const/layoutTokens';
5
+ import { useGlobalStore } from '@/store/global';
6
+ import { systemStatusSelectors } from '@/store/global/selectors';
7
+ import { HotkeyEnum, HotkeyScopeEnum } from '@/types/hotkey';
8
+
9
+ import { useHotkeyById } from './useHotkeyById';
10
+
11
+ export const useToggleImageLeftPanelHotkey = () => {
12
+ const showImagePanel = useGlobalStore(systemStatusSelectors.showImagePanel);
13
+ const updateSystemStatus = useGlobalStore((s) => s.updateSystemStatus);
14
+
15
+ return useHotkeyById(HotkeyEnum.ToggleLeftPanel, () =>
16
+ updateSystemStatus({
17
+ imagePanelWidth: showImagePanel ? 0 : FOLDER_WIDTH,
18
+ showImagePanel: !showImagePanel,
19
+ }),
20
+ );
21
+ };
22
+
23
+ export const useToggleImageRightPanelHotkey = () => {
24
+ const showImageTopicPanel = useGlobalStore(systemStatusSelectors.showImageTopicPanel);
25
+ const updateSystemStatus = useGlobalStore((s) => s.updateSystemStatus);
26
+
27
+ return useHotkeyById(HotkeyEnum.ToggleRightPanel, () =>
28
+ updateSystemStatus({
29
+ imageTopicPanelWidth: showImageTopicPanel ? 0 : 80,
30
+ showImageTopicPanel: !showImageTopicPanel,
31
+ }),
32
+ );
33
+ };
34
+
35
+ // 注册聚合
36
+
37
+ export const useRegisterImageHotkeys = () => {
38
+ const { enableScope, disableScope } = useHotkeysContext();
39
+
40
+ // Layout
41
+ useToggleImageLeftPanelHotkey();
42
+ useToggleImageRightPanelHotkey();
43
+
44
+ useEffect(() => {
45
+ enableScope(HotkeyScopeEnum.Image);
46
+ return () => disableScope(HotkeyScopeEnum.Image);
47
+ }, []);
48
+ };
@@ -164,7 +164,42 @@ export class MCPClient {
164
164
  switch (params.type) {
165
165
  case 'http': {
166
166
  log('Using HTTP transport with url: %s', params.url);
167
- this.transport = new StreamableHTTPClientTransport(new URL(params.url));
167
+
168
+ // 构建头部信息,包括用户自定义的 headers 和认证信息
169
+ const headers: Record<string, string> = { ...params.headers };
170
+
171
+ // 处理认证配置
172
+ if (params.auth) {
173
+ switch (params.auth.type) {
174
+ case 'bearer': {
175
+ if (params.auth.token) {
176
+ headers['Authorization'] = `Bearer ${params.auth.token}`;
177
+ log('Added Bearer token authentication');
178
+ }
179
+ break;
180
+ }
181
+ case 'oauth2': {
182
+ if (params.auth.accessToken) {
183
+ headers['Authorization'] = `Bearer ${params.auth.accessToken}`;
184
+ log('Added OAuth2 access token authentication');
185
+ }
186
+ break;
187
+ }
188
+
189
+ default: {
190
+ // 不需要认证
191
+ break;
192
+ }
193
+ }
194
+ }
195
+
196
+ // 创建 StreamableHTTPClientTransport 并传递 headers
197
+ this.transport = new StreamableHTTPClientTransport(new URL(params.url), {
198
+ requestInit: { headers },
199
+ });
200
+
201
+ log('HTTP transport created with headers: %O', Object.keys(headers));
202
+
168
203
  break;
169
204
  }
170
205
 
@@ -205,6 +240,14 @@ export class MCPClient {
205
240
  } catch (e) {
206
241
  log('MCP connection failed:', e);
207
242
 
243
+ if (this.params.type === 'http') {
244
+ const error = e as Error;
245
+ if (error.message.includes('401'))
246
+ throw createMCPError('AUTHORIZATION_ERROR', error.message);
247
+
248
+ throw e;
249
+ }
250
+
208
251
  // 对于 stdio 连接失败,尝试预检查命令以获取详细错误信息
209
252
  if (this.params.type === 'stdio') {
210
253
  log('Attempting to pre-check stdio command for detailed error information...');
@@ -228,34 +271,24 @@ export class MCPClient {
228
271
  'Failed to connect to MCP server, please check your configuration',
229
272
  {
230
273
  originalError: (e as Error).message,
231
- params:
232
- this.params.type === 'stdio'
233
- ? {
234
- args: this.params.args,
235
- command: this.params.command,
236
- type: this.params.type,
237
- }
238
- : {
239
- type: this.params.type,
240
- },
274
+ params: {
275
+ args: this.params.args,
276
+ command: this.params.command,
277
+ type: this.params.type,
278
+ },
241
279
  step: 'mcp_connect',
242
280
  },
243
281
  );
244
282
  }
245
283
 
246
284
  // Wrap other unknown errors
247
- throw createMCPError('UNKNOWN_ERROR', 'Unknown error occurred', {
285
+ throw createMCPError('UNKNOWN_ERROR', (e as Error).message, {
248
286
  originalError: (e as Error).message,
249
- params:
250
- this.params.type === 'stdio'
251
- ? {
252
- args: this.params.args,
253
- command: this.params.command,
254
- type: this.params.type,
255
- }
256
- : {
257
- type: this.params.type,
258
- },
287
+ params: {
288
+ args: this.params.args,
289
+ command: this.params.command,
290
+ type: this.params.type,
291
+ },
259
292
  step: 'mcp_connect',
260
293
  });
261
294
  }
@@ -30,7 +30,41 @@ export interface McpPrompt {
30
30
  name: string;
31
31
  }
32
32
 
33
+ /**
34
+ * MCP 认证配置接口
35
+ * 支持第一阶段的手动配置和未来的 OAuth 2.1 自动化流程
36
+ */
37
+ export interface AuthConfig {
38
+ // C. 用户授权后获取的【用户令牌】
39
+ accessToken?: string;
40
+
41
+ // 用户手动粘贴的 Bearer Token
42
+ // --- Stage 2 & 3: OAuth 2.1 自动化流程 ---
43
+ // A. 静态配置 或 动态注册获取的【客户端凭证】
44
+ clientId?: string;
45
+
46
+ clientSecret?: string;
47
+ refreshToken?: string; // 如果是机密客户端
48
+ scope?: string; // 想要申请的权限范围, e.g., "repo user:email"
49
+
50
+ // B. 服务器发现机制获取的【授权服务器元数据】
51
+ serverMetadata?: {
52
+ authorization_endpoint?: string;
53
+ registration_endpoint?: string;
54
+ token_endpoint?: string;
55
+ // ... and other RFC8414 fields
56
+ };
57
+
58
+ // --- Stage 1: 手动配置 ---
59
+ token?: string;
60
+ tokenExpiresAt?: number;
61
+ // 认证类型
62
+ type: 'none' | 'bearer' | 'oauth2'; // accessToken 的过期时间戳
63
+ }
64
+
33
65
  interface HttpMCPClientParams {
66
+ auth?: AuthConfig;
67
+ headers?: Record<string, string>;
34
68
  name: string;
35
69
  type: 'http';
36
70
  url: string;
@@ -46,6 +80,13 @@ export interface StdioMCPParams {
46
80
 
47
81
  export type MCPClientParams = HttpMCPClientParams | StdioMCPParams;
48
82
 
83
+ export type MCPErrorType =
84
+ | 'CONNECTION_FAILED'
85
+ | 'PROCESS_SPAWN_ERROR'
86
+ | 'INITIALIZATION_TIMEOUT'
87
+ | 'VALIDATION_ERROR'
88
+ | 'UNKNOWN_ERROR'
89
+ | 'AUTHORIZATION_ERROR';
49
90
  export interface MCPErrorData {
50
91
  message: string;
51
92
  /**
@@ -89,12 +130,7 @@ export interface MCPErrorData {
89
130
  /**
90
131
  * 错误类型
91
132
  */
92
- type:
93
- | 'CONNECTION_FAILED'
94
- | 'PROCESS_SPAWN_ERROR'
95
- | 'INITIALIZATION_TIMEOUT'
96
- | 'VALIDATION_ERROR'
97
- | 'UNKNOWN_ERROR';
133
+ type: MCPErrorType;
98
134
  }
99
135
 
100
136
  /**
@@ -14,17 +14,19 @@ import {
14
14
  TextToSpeechOptions,
15
15
  TextToSpeechPayload,
16
16
  } from './types';
17
+ import { CreateImagePayload, CreateImageResponse } from './types/image';
17
18
 
18
19
  /* eslint-disable sort-keys-fix/sort-keys-fix , typescript-sort-keys/interface */
19
20
  export interface LobeRuntimeAI {
20
21
  baseURL?: string;
21
- chat(payload: ChatStreamPayload, options?: ChatMethodOptions): Promise<Response>;
22
+ chat?(payload: ChatStreamPayload, options?: ChatMethodOptions): Promise<Response>;
22
23
 
23
24
  embeddings?(payload: EmbeddingsPayload, options?: EmbeddingsOptions): Promise<Embeddings[]>;
24
25
 
25
26
  models?(): Promise<any>;
26
27
 
27
28
  textToImage?: (payload: TextToImagePayload) => Promise<string[]>;
29
+ createImage?: (payload: CreateImagePayload) => Promise<CreateImageResponse>;
28
30
 
29
31
  textToSpeech?: (
30
32
  payload: TextToSpeechPayload,