@lobehub/lobehub 2.0.0-next.293 → 2.0.0-next.295

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 (181) hide show
  1. package/.github/workflows/release-desktop-beta.yml +6 -6
  2. package/.github/workflows/release-desktop-stable.yml +11 -11
  3. package/CHANGELOG.md +52 -0
  4. package/apps/desktop/electron.vite.config.ts +0 -1
  5. package/apps/desktop/src/main/__mocks__/node-mac-permissions.ts +0 -1
  6. package/apps/desktop/src/main/__mocks__/setup.ts +0 -1
  7. package/apps/desktop/src/main/controllers/McpCtr.ts +50 -18
  8. package/apps/desktop/src/main/controllers/__tests__/SystemCtr.test.ts +1 -4
  9. package/apps/desktop/src/main/libs/mcp/client.ts +54 -2
  10. package/apps/desktop/tsconfig.json +4 -10
  11. package/changelog/v1.json +14 -0
  12. package/e2e/scripts/setup.ts +45 -32
  13. package/package.json +1 -1
  14. package/packages/database/src/models/__tests__/knowledgeBase.test.ts +1 -1
  15. package/packages/database/src/repositories/knowledge/index.ts +1 -4
  16. package/packages/types/src/discover/assistants.ts +2 -2
  17. package/scripts/migrate-spa-navigation.ts +129 -0
  18. package/src/app/(backend)/api/workflows/memory-user-memory/pipelines/chat-topic/process-topics/route.ts +112 -109
  19. package/src/app/(backend)/api/workflows/memory-user-memory/pipelines/chat-topic/process-user-topics/route.ts +125 -113
  20. package/src/app/(backend)/api/workflows/memory-user-memory/pipelines/chat-topic/process-users/route.ts +74 -65
  21. package/src/app/[variants]/(auth)/auth-error/page.tsx +1 -1
  22. package/src/app/[variants]/(auth)/login/[[...login]]/page.tsx +1 -1
  23. package/src/app/[variants]/(auth)/next-auth/error/AuthErrorPage.tsx +1 -1
  24. package/src/app/[variants]/(auth)/next-auth/signin/AuthSignInBox.tsx +1 -1
  25. package/src/app/[variants]/(auth)/oauth/callback/error/page.tsx +1 -1
  26. package/src/app/[variants]/(auth)/oauth/callback/success/page.tsx +1 -1
  27. package/src/app/[variants]/(auth)/oauth/consent/[uid]/page.tsx +1 -1
  28. package/src/app/[variants]/(auth)/reset-password/layout.tsx +1 -1
  29. package/src/app/[variants]/(auth)/reset-password/page.tsx +2 -2
  30. package/src/app/[variants]/(auth)/signin/layout.tsx +1 -1
  31. package/src/app/[variants]/(auth)/signin/useSignIn.ts +1 -1
  32. package/src/app/[variants]/(auth)/signup/[[...signup]]/BetterAuthSignUpForm.tsx +2 -2
  33. package/src/app/[variants]/(auth)/signup/[[...signup]]/page.tsx +1 -1
  34. package/src/app/[variants]/(auth)/signup/[[...signup]]/useSignUp.tsx +1 -1
  35. package/src/app/[variants]/(auth)/verify-email/layout.tsx +1 -1
  36. package/src/app/[variants]/(auth)/verify-email/page.tsx +2 -2
  37. package/src/app/[variants]/(main)/_layout/index.tsx +1 -1
  38. package/src/app/[variants]/(main)/agent/_layout/AgentIdSync.tsx +12 -1
  39. package/src/app/[variants]/(main)/agent/_layout/Sidebar/Cron/CronTopicGroup.tsx +1 -1
  40. package/src/app/[variants]/(main)/agent/_layout/Sidebar/Header/AddTopicButon.tsx +1 -1
  41. package/src/app/[variants]/(main)/agent/_layout/Sidebar/Header/Nav.tsx +1 -1
  42. package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/AllTopicsDrawer/index.tsx +1 -1
  43. package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/hooks/useThreadNavigation.ts +1 -1
  44. package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/hooks/useTopicNavigation.ts +1 -1
  45. package/src/app/[variants]/(main)/agent/features/TelemetryNotification.tsx +2 -3
  46. package/src/app/[variants]/(main)/community/(detail)/assistant/features/Details/Nav.tsx +9 -9
  47. package/src/app/[variants]/(main)/community/(detail)/assistant/features/Details/Versions/index.tsx +2 -3
  48. package/src/app/[variants]/(main)/community/(detail)/features/MakedownRender.tsx +1 -2
  49. package/src/app/[variants]/(main)/community/(detail)/features/ShareButton.tsx +2 -3
  50. package/src/app/[variants]/(main)/community/(detail)/features/Toc/Heading.tsx +2 -3
  51. package/src/app/[variants]/(main)/community/(detail)/mcp/features/Details/Versions/index.tsx +2 -2
  52. package/src/app/[variants]/(main)/community/(detail)/model/features/Details/Nav.tsx +12 -11
  53. package/src/app/[variants]/(main)/community/(detail)/model/features/Details/Parameter/ParameterItem.tsx +2 -3
  54. package/src/app/[variants]/(main)/community/(detail)/provider/features/Details/Nav.tsx +11 -10
  55. package/src/app/[variants]/(main)/community/(detail)/provider/features/Header.tsx +10 -9
  56. package/src/app/[variants]/(main)/community/(list)/(home)/index.tsx +1 -1
  57. package/src/app/[variants]/(main)/community/(list)/assistant/features/Category/useCategory.tsx +1 -1
  58. package/src/app/[variants]/(main)/community/(list)/features/SortButton/index.tsx +2 -3
  59. package/src/app/[variants]/(main)/community/_layout/Sidebar/Header/Nav.tsx +1 -1
  60. package/src/app/[variants]/(main)/community/features/CreateButton/Inner.tsx +1 -1
  61. package/src/app/[variants]/(main)/community/features/CreateButton/index.tsx +1 -1
  62. package/src/app/[variants]/(main)/community/features/Search.tsx +1 -2
  63. package/src/app/[variants]/(main)/community/features/Title.tsx +5 -5
  64. package/src/app/[variants]/(main)/group/_layout/GroupIdSync.tsx +12 -1
  65. package/src/app/[variants]/(main)/group/_layout/Sidebar/Header/Nav.tsx +1 -1
  66. package/src/app/[variants]/(main)/group/_layout/Sidebar/Topic/AllTopicsDrawer/index.tsx +1 -1
  67. package/src/app/[variants]/(main)/group/_layout/Sidebar/Topic/hooks/useThreadNavigation.ts +1 -1
  68. package/src/app/[variants]/(main)/group/features/Conversation/Header/ShareButton/index.tsx +1 -1
  69. package/src/app/[variants]/(main)/group/features/TelemetryNotification.tsx +2 -3
  70. package/src/app/[variants]/(main)/home/_layout/Body/Agent/AllAgentsDrawer/index.tsx +1 -1
  71. package/src/app/[variants]/(main)/hooks/useActiveTabKey.ts +6 -11
  72. package/src/app/[variants]/(main)/image/NotSupportClient.tsx +4 -3
  73. package/src/app/[variants]/(main)/image/_layout/ConfigPanel/components/ImageUpload.tsx +1 -1
  74. package/src/app/[variants]/(main)/image/_layout/ConfigPanel/components/MultiImagesUpload/ImageManageModal.tsx +1 -1
  75. package/src/app/[variants]/(main)/image/_layout/ConfigPanel/components/MultiImagesUpload/index.tsx +1 -1
  76. package/src/app/[variants]/(main)/memory/(home)/features/RoleTagCloud/index.tsx +1 -1
  77. package/src/app/[variants]/(main)/memory/_layout/Sidebar/Header/Nav.tsx +1 -1
  78. package/src/app/[variants]/(main)/memory/features/SourceLink.tsx +1 -1
  79. package/src/app/[variants]/(main)/page/_layout/Body/AllPagesDrawer/index.tsx +1 -1
  80. package/src/app/[variants]/(main)/settings/about/features/ItemCard.tsx +2 -3
  81. package/src/app/[variants]/(main)/settings/about/features/ItemLink.tsx +2 -3
  82. package/src/app/[variants]/(main)/settings/about/features/Version.tsx +6 -7
  83. package/src/app/[variants]/(main)/settings/features/SettingsContent.tsx +1 -1
  84. package/src/app/[variants]/(main)/settings/features/UpgradeAlert.tsx +4 -4
  85. package/src/app/[variants]/(main)/settings/provider/(list)/Footer.tsx +2 -2
  86. package/src/app/[variants]/(main)/settings/provider/detail/index.tsx +1 -1
  87. package/src/app/[variants]/(main)/settings/provider/detail/ollama/CheckError.tsx +1 -1
  88. package/src/app/[variants]/(main)/settings/provider/features/ProviderConfig/index.tsx +12 -6
  89. package/src/app/[variants]/(main)/settings/security/index.tsx +1 -1
  90. package/src/app/[variants]/(main)/settings/stats/features/overview/ShareButton/ShareModal.tsx +1 -1
  91. package/src/app/[variants]/(main)/settings/stats/features/rankings/AssistantsRank.tsx +1 -1
  92. package/src/app/[variants]/(main)/settings/stats/features/rankings/TopicsRank.tsx +1 -1
  93. package/src/app/[variants]/(mobile)/_layout/index.tsx +1 -1
  94. package/src/app/[variants]/(mobile)/chat/settings/features/AgentInfoDescription/index.tsx +1 -1
  95. package/src/app/[variants]/(mobile)/chat/settings/features/SettingButton.tsx +1 -1
  96. package/src/app/[variants]/(mobile)/router/index.tsx +1 -1
  97. package/src/app/[variants]/page.tsx +1 -1
  98. package/src/app/[variants]/router/index.tsx +1 -1
  99. package/src/components/404/index.tsx +4 -4
  100. package/src/components/Analytics/index.tsx +1 -1
  101. package/src/components/BrandWatermark/index.tsx +4 -4
  102. package/src/components/Branding/ProductLogo/Custom.tsx +1 -1
  103. package/src/components/Error/index.tsx +3 -4
  104. package/src/components/GoBack/index.tsx +2 -2
  105. package/src/components/LabsModal/LabCard.tsx +1 -1
  106. package/src/components/Link.tsx +25 -5
  107. package/src/components/OllamaSetupGuide/index.tsx +5 -4
  108. package/src/components/WebFavicon/index.tsx +1 -1
  109. package/src/components/client/ClientResponsiveContent/index.tsx +1 -1
  110. package/src/components/client/ClientResponsiveLayout.tsx +1 -1
  111. package/src/components/mdx/Image.tsx +1 -1
  112. package/src/components/mdx/Link.tsx +26 -9
  113. package/src/features/AlertBanner/CloudBanner.tsx +2 -3
  114. package/src/features/ChatInput/ActionBar/Model/ControlsForm.tsx +8 -7
  115. package/src/features/ChatInput/ActionBar/Params/Controls.tsx +1 -3
  116. package/src/features/ChatInput/ActionBar/Token/index.tsx +1 -1
  117. package/src/features/ChatInput/Mobile/index.tsx +1 -1
  118. package/src/features/Conversation/ChatItem/components/MessageContent/index.tsx +1 -1
  119. package/src/features/Conversation/Error/OllamaBizError/index.tsx +1 -1
  120. package/src/features/Conversation/Error/OllamaSetupGuide/Desktop.tsx +1 -2
  121. package/src/features/Conversation/Error/index.tsx +1 -1
  122. package/src/features/Conversation/Messages/AssistantGroup/Tool/Actions/Settings.tsx +1 -1
  123. package/src/features/Conversation/Messages/AssistantGroup/Tool/index.tsx +1 -1
  124. package/src/features/Conversation/Messages/AssistantGroup/index.tsx +1 -1
  125. package/src/features/Conversation/Messages/Tool/Tool/index.tsx +1 -1
  126. package/src/features/Conversation/Messages/components/SearchGrounding.tsx +1 -1
  127. package/src/features/DataImporter/Error.tsx +3 -3
  128. package/src/features/DevPanel/CacheViewer/cacheProvider.tsx +2 -1
  129. package/src/features/DevPanel/MetadataViewer/Og.tsx +1 -1
  130. package/src/features/DevPanel/features/FloatPanel.tsx +1 -1
  131. package/src/features/DevPanel/features/Table/TooltipContent.tsx +3 -4
  132. package/src/features/DevPanel/index.tsx +1 -1
  133. package/src/features/EditorCanvas/InlineToolbar.tsx +1 -6
  134. package/src/features/FileViewer/NotSupport/index.tsx +2 -3
  135. package/src/features/Follow/index.tsx +8 -9
  136. package/src/features/LibraryModal/AddFilesToKnowledgeBase/SelectForm.tsx +2 -2
  137. package/src/features/MCP/MCPInstallProgress/InstallError/ErrorDetails.tsx +61 -83
  138. package/src/features/MCP/Scores.tsx +1 -1
  139. package/src/features/MCPPluginDetail/Nav.tsx +8 -8
  140. package/src/features/MCPPluginDetail/Overview/TagList.tsx +1 -1
  141. package/src/features/MobileTabBar/index.tsx +2 -1
  142. package/src/features/OllamaSetupGuide/Desktop.tsx +1 -2
  143. package/src/features/PWAInstall/Install.tsx +1 -1
  144. package/src/features/PWAInstall/index.tsx +1 -1
  145. package/src/features/PluginDevModal/MCPManifestForm/index.tsx +30 -3
  146. package/src/features/PluginStore/McpList/index.tsx +1 -1
  147. package/src/features/PluginStore/PluginList/Detail/Header.tsx +6 -6
  148. package/src/features/PluginsUI/Render/DefaultType/index.tsx +1 -1
  149. package/src/features/Portal/Artifacts/Body/Renderer/index.tsx +1 -1
  150. package/src/features/ResourceManager/components/ChunkDrawer/index.tsx +1 -1
  151. package/src/features/ResourceManager/components/Explorer/ListView/Skeleton.tsx +26 -26
  152. package/src/features/ResourceManager/components/Explorer/ToolBar/BatchActionsDropdown.tsx +147 -149
  153. package/src/features/ResourceManager/index.tsx +1 -1
  154. package/src/features/Setting/Footer.tsx +4 -5
  155. package/src/features/User/UserPanel/PanelContent.tsx +1 -1
  156. package/src/hooks/useActiveTabKey.ts +6 -3
  157. package/src/hooks/useIsSingleMode.test.ts +10 -24
  158. package/src/hooks/useIsSingleMode.ts +4 -2
  159. package/src/hooks/useIsSubSlug.ts +2 -1
  160. package/src/hooks/useQuery.ts +5 -5
  161. package/src/layout/GlobalProvider/AppTheme.tsx +2 -2
  162. package/src/layout/GlobalProvider/StyleRegistry.tsx +1 -1
  163. package/src/layout/GlobalProvider/useUserStateRedirect.ts +13 -25
  164. package/src/libs/mcp/types.ts +31 -0
  165. package/src/libs/next/Image.tsx +13 -0
  166. package/src/libs/next/Link.tsx +13 -0
  167. package/src/libs/next/dynamic.tsx +13 -0
  168. package/src/libs/next/index.ts +22 -0
  169. package/src/libs/next/navigation.ts +22 -0
  170. package/src/libs/router/Link.tsx +30 -0
  171. package/src/libs/router/index.ts +18 -0
  172. package/src/libs/router/navigation.ts +72 -0
  173. package/src/server/modules/AgentRuntime/AgentStateManager.ts +5 -1
  174. package/src/store/chat/slices/portal/selectors.test.ts +5 -15
  175. package/src/store/page/index.ts +1 -1
  176. package/src/store/page/slices/crud/index.ts +1 -1
  177. package/src/store/tool/slices/mcpStore/action.ts +26 -11
  178. package/src/app/[variants]/(main)/hooks/usePathname.ts +0 -10
  179. package/src/app/[variants]/(main)/hooks/useQuery.ts +0 -12
  180. package/src/app/[variants]/(main)/hooks/useRouter.ts +0 -22
  181. package/src/app/[variants]/(main)/hooks/useSearchParams.ts +0 -11
@@ -4,7 +4,7 @@ name: Release Desktop Beta
4
4
  # Beta/Nightly 频道发版工作流
5
5
  # ============================================
6
6
  # 触发条件: 发布包含 pre-release 标识的 release
7
- # 如: v2.0.0-beta.1, v2.0.0-alpha.1, v2.0.0-rc.1, v2.0.0-nightly.xxx
7
+ # 如: v2.0.0-beta.1, v2.0.0-alpha.1, v2.0.0-rc.1, v2.0.0-nightly.xxx, v2.0.0-next.292
8
8
  #
9
9
  # 注意: Stable 版本 (如 v2.0.0) 由 release-desktop-stable.yml 处理
10
10
  # ============================================
@@ -24,10 +24,10 @@ env:
24
24
 
25
25
  jobs:
26
26
  # ============================================
27
- # 检查是否为 Beta/Nightly 版本 (排除 Stable)
27
+ # 检查是否为 Beta/Nightly/Next 版本 (排除 Stable)
28
28
  # ============================================
29
29
  check-beta:
30
- name: Check if Beta/Nightly Release
30
+ name: Check if Beta/Nightly/Next Release
31
31
  runs-on: ubuntu-latest
32
32
  outputs:
33
33
  is_beta: ${{ steps.check.outputs.is_beta }}
@@ -40,10 +40,10 @@ jobs:
40
40
  version="${version#v}"
41
41
  echo "version=${version}" >> $GITHUB_OUTPUT
42
42
 
43
- # Beta/Nightly 版本包含 beta/alpha/rc/nightly
44
- if [[ "$version" == *"beta"* ]] || [[ "$version" == *"alpha"* ]] || [[ "$version" == *"rc"* ]] || [[ "$version" == *"nightly"* ]]; then
43
+ # Beta/Nightly/Next 版本包含 beta/alpha/rc/nightly/next
44
+ if [[ "$version" == *"beta"* ]] || [[ "$version" == *"alpha"* ]] || [[ "$version" == *"rc"* ]] || [[ "$version" == *"nightly"* ]] || [[ "$version" == *"next"* ]]; then
45
45
  echo "is_beta=true" >> $GITHUB_OUTPUT
46
- echo "✅ Beta/Nightly release detected: $version"
46
+ echo "✅ Beta/Nightly/Next release detected: $version"
47
47
  else
48
48
  echo "is_beta=false" >> $GITHUB_OUTPUT
49
49
  echo "⏭️ Skipping: $version is a stable release (handled by release-desktop-stable.yml)"
@@ -3,10 +3,10 @@ name: Release Desktop Stable
3
3
  # ============================================
4
4
  # Stable 频道发版工作流
5
5
  # ============================================
6
- # 触发条件: 发布不含 pre-release 标识的 release (如 v2.0.0)
6
+ # 触发条件: 发布不含 pre-release 后缀的 release (如 v2.0.0)
7
7
  #
8
8
  # 与 Beta 的区别:
9
- # 1. 仅响应 stable 版本 tag (不含 beta/alpha/rc/nightly)
9
+ # 1. 仅响应 stable 版本 tag (不含任何 '-' 后缀)
10
10
  # 2. 使用 STABLE 专用的 Umami 配置
11
11
  # 3. 额外上传到 S3 更新服务器
12
12
  # 4. 构建时注入 UPDATE_SERVER_URL 让客户端从 S3 检查更新
@@ -89,8 +89,8 @@ jobs:
89
89
  if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
90
90
  # 手动触发: 使用输入的版本号
91
91
  version="${{ inputs.version }}"
92
+ version="${version#v}"
92
93
  echo "is_manual=true" >> $GITHUB_OUTPUT
93
- echo "is_stable=true" >> $GITHUB_OUTPUT
94
94
  echo "version=${version}" >> $GITHUB_OUTPUT
95
95
  echo "release_notes=" >> $GITHUB_OUTPUT
96
96
  echo "🔧 Manual trigger: version=${version}"
@@ -106,15 +106,15 @@ jobs:
106
106
  printf '%s\n' "$release_body"
107
107
  echo "EOF"
108
108
  } >> $GITHUB_OUTPUT
109
+ fi
109
110
 
110
- # 检查是否为 stable 版本 (不含 beta/alpha/rc/nightly)
111
- if [[ "$version" == *"beta"* ]] || [[ "$version" == *"alpha"* ]] || [[ "$version" == *"rc"* ]] || [[ "$version" == *"nightly"* ]]; then
112
- echo "is_stable=false" >> $GITHUB_OUTPUT
113
- echo "⏭️ Skipping: $version is not a stable release"
114
- else
115
- echo "is_stable=true" >> $GITHUB_OUTPUT
116
- echo "✅ Stable release detected: $version"
117
- fi
111
+ # 检查是否为 stable 版本 (不含任何 '-' 后缀)
112
+ if [[ "$version" == *"-"* ]]; then
113
+ echo "is_stable=false" >> $GITHUB_OUTPUT
114
+ echo "⏭️ Skipping: $version is not a stable release"
115
+ else
116
+ echo "is_stable=true" >> $GITHUB_OUTPUT
117
+ echo "✅ Stable release detected: $version"
118
118
  fi
119
119
 
120
120
  # ============================================
package/CHANGELOG.md CHANGED
@@ -2,6 +2,58 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 2.0.0-next.295](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.294...v2.0.0-next.295)
6
+
7
+ <sup>Released on **2026-01-15**</sup>
8
+
9
+ #### ♻ Code Refactoring
10
+
11
+ - **misc**: Migrate Next.js navigation APIs to React Router for SPA.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### Code refactoring
19
+
20
+ - **misc**: Migrate Next.js navigation APIs to React Router for SPA, closes [#11394](https://github.com/lobehub/lobe-chat/issues/11394) ([2253d46](https://github.com/lobehub/lobe-chat/commit/2253d46))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
30
+ ## [Version 2.0.0-next.294](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.293...v2.0.0-next.294)
31
+
32
+ <sup>Released on **2026-01-15**</sup>
33
+
34
+ #### 🐛 Bug Fixes
35
+
36
+ - **chat**: Reset activeTopicId when switching agent/group.
37
+ - **mcp**: Fix installation check hanging issue in desktop app.
38
+
39
+ <br/>
40
+
41
+ <details>
42
+ <summary><kbd>Improvements and Fixes</kbd></summary>
43
+
44
+ #### What's fixed
45
+
46
+ - **chat**: Reset activeTopicId when switching agent/group, closes [#11523](https://github.com/lobehub/lobe-chat/issues/11523) ([fde54b0](https://github.com/lobehub/lobe-chat/commit/fde54b0))
47
+ - **mcp**: Fix installation check hanging issue in desktop app, closes [#11524](https://github.com/lobehub/lobe-chat/issues/11524) ([b9341c3](https://github.com/lobehub/lobe-chat/commit/b9341c3))
48
+
49
+ </details>
50
+
51
+ <div align="right">
52
+
53
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
54
+
55
+ </div>
56
+
5
57
  ## [Version 2.0.0-next.293](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.292...v2.0.0-next.293)
6
58
 
7
59
  <sup>Released on **2026-01-15**</sup>
@@ -21,7 +21,6 @@ export default defineConfig({
21
21
  },
22
22
  sourcemap: isDev ? 'inline' : false,
23
23
  },
24
-
25
24
  define: {
26
25
  'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
27
26
  'process.env.UPDATE_CHANNEL': JSON.stringify(process.env.UPDATE_CHANNEL),
@@ -2,7 +2,6 @@
2
2
  * Mock for node-mac-permissions native module
3
3
  * Used in tests since the native module only works on macOS
4
4
  */
5
-
6
5
  import { vi } from 'vitest';
7
6
 
8
7
  export const askForAccessibilityAccess = vi.fn(() => undefined);
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * Vitest setup file for mocking native modules
3
3
  */
4
-
5
4
  import { vi } from 'vitest';
6
5
 
7
6
  // Mock node-mac-permissions before any imports
@@ -7,7 +7,7 @@ import superjson from 'superjson';
7
7
  import FileService from '@/services/fileSrv';
8
8
  import { createLogger } from '@/utils/logger';
9
9
 
10
- import { MCPClient } from '../libs/mcp/client';
10
+ import { MCPClient, MCPConnectionError } from '../libs/mcp/client';
11
11
  import type { MCPClientParams, ToolCallContent, ToolCallResult } from '../libs/mcp/types';
12
12
  import { ControllerModule, IpcMethod } from './index';
13
13
 
@@ -228,8 +228,9 @@ export default class McpCtr extends ControllerModule {
228
228
  type: 'stdio',
229
229
  };
230
230
 
231
- const client = await this.createClient(params);
231
+ let client: MCPClient | undefined;
232
232
  try {
233
+ client = await this.createClient(params);
233
234
  const manifest = await client.listManifests();
234
235
  const identifier = input.name;
235
236
 
@@ -257,8 +258,25 @@ export default class McpCtr extends ControllerModule {
257
258
  mcpParams: params,
258
259
  type: 'mcp' as any,
259
260
  });
261
+ } catch (error) {
262
+ // If it's an MCPConnectionError with stderr logs, enhance the error message
263
+ if (error instanceof MCPConnectionError && error.stderrLogs.length > 0) {
264
+ const stderrOutput = error.stderrLogs.join('\n');
265
+ const enhancedError = new Error(
266
+ `${error.message}\n\n--- STDIO Process Output ---\n${stderrOutput}`,
267
+ );
268
+ enhancedError.name = error.name;
269
+ logger.error('getStdioMcpServerManifest failed with STDIO logs:', {
270
+ message: error.message,
271
+ stderrLogs: error.stderrLogs,
272
+ });
273
+ throw enhancedError;
274
+ }
275
+ throw error;
260
276
  } finally {
261
- await client.disconnect();
277
+ if (client) {
278
+ await client.disconnect();
279
+ }
262
280
  }
263
281
  }
264
282
 
@@ -313,8 +331,9 @@ export default class McpCtr extends ControllerModule {
313
331
  type: 'stdio',
314
332
  };
315
333
 
316
- const client = await this.createClient(params);
334
+ let client: MCPClient | undefined;
317
335
  try {
336
+ client = await this.createClient(params);
318
337
  const args = safeParseToRecord(input.args);
319
338
 
320
339
  const raw = (await client.callTool(input.toolName, args)) as ToolCallResult;
@@ -328,10 +347,25 @@ export default class McpCtr extends ControllerModule {
328
347
  success: true,
329
348
  });
330
349
  } catch (error) {
350
+ // If it's an MCPConnectionError with stderr logs, enhance the error message
351
+ if (error instanceof MCPConnectionError && error.stderrLogs.length > 0) {
352
+ const stderrOutput = error.stderrLogs.join('\n');
353
+ const enhancedError = new Error(
354
+ `${error.message}\n\n--- STDIO Process Output ---\n${stderrOutput}`,
355
+ );
356
+ enhancedError.name = error.name;
357
+ logger.error('callTool failed with STDIO logs:', {
358
+ message: error.message,
359
+ stderrLogs: error.stderrLogs,
360
+ });
361
+ throw enhancedError;
362
+ }
331
363
  logger.error('callTool failed:', error);
332
364
  throw error;
333
365
  } finally {
334
- await client.disconnect();
366
+ if (client) {
367
+ await client.disconnect();
368
+ }
335
369
  }
336
370
  }
337
371
 
@@ -361,8 +395,9 @@ export default class McpCtr extends ControllerModule {
361
395
  }
362
396
 
363
397
  private async checkSystemDependency(dependency: any) {
398
+ const checkCommand = dependency.checkCommand || `${dependency.name} --version`;
399
+
364
400
  try {
365
- const checkCommand = dependency.checkCommand || `${dependency.name} --version`;
366
401
  const { stdout, stderr } = await execPromise(checkCommand);
367
402
 
368
403
  if (stderr && !stdout) {
@@ -444,22 +479,19 @@ export default class McpCtr extends ControllerModule {
444
479
  const packageName = details?.packageName;
445
480
  if (!packageName) return { installed: false };
446
481
 
482
+ // Only check global npm list - do NOT use npx as it may download packages
447
483
  try {
448
484
  const { stdout } = await execPromise(`npm list -g ${packageName} --depth=0`);
449
- if (!stdout.includes('(empty)') && stdout.includes(packageName)) return { installed: true };
485
+ if (!stdout.includes('(empty)') && stdout.includes(packageName)) {
486
+ return { installed: true };
487
+ }
450
488
  } catch {
451
- // ignore
489
+ // ignore - package not found in global list
452
490
  }
453
491
 
454
- try {
455
- await execPromise(`npx -y ${packageName} --version`);
456
- return { installed: true };
457
- } catch (error) {
458
- return {
459
- error: error instanceof Error ? error.message : 'Unknown error',
460
- installed: false,
461
- };
462
- }
492
+ // For npm packages, we don't require pre-installation
493
+ // npx will handle downloading and running on-demand during actual MCP connection
494
+ return { installed: false };
463
495
  }
464
496
 
465
497
  if (installationMethod === 'python') {
@@ -553,7 +585,7 @@ export default class McpCtr extends ControllerModule {
553
585
  const bestResult = recommendedResult || firstInstallableResult || results[0];
554
586
 
555
587
  const checkResult: CheckMcpInstallResult = {
556
- ...(bestResult || {}),
588
+ ...bestResult,
557
589
  allOptions: results as any,
558
590
  platform: process.platform,
559
591
  success: true,
@@ -4,10 +4,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
4
4
  import type { App } from '@/core/App';
5
5
  import type { IpcContext } from '@/utils/ipc';
6
6
  import { IpcHandler } from '@/utils/ipc/base';
7
- import {
8
- __resetMacPermissionsModuleCache,
9
- __setMacPermissionsModule,
10
- } from '@/utils/permissions';
7
+ import { __resetMacPermissionsModuleCache, __setMacPermissionsModule } from '@/utils/permissions';
11
8
 
12
9
  import SystemController from '../SystemCtr';
13
10
 
@@ -6,15 +6,31 @@ import {
6
6
  import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
7
7
  import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
8
8
  import type { Progress } from '@modelcontextprotocol/sdk/types.js';
9
+ import type { Readable } from 'node:stream';
9
10
 
10
11
  import { getDesktopEnv } from '@/env';
11
12
 
12
13
  import type { MCPClientParams, McpPrompt, McpResource, McpTool, ToolCallResult } from './types';
13
14
 
15
+ /**
16
+ * Custom error class for MCP connection errors that includes STDIO logs
17
+ */
18
+ export class MCPConnectionError extends Error {
19
+ readonly stderrLogs: string[];
20
+
21
+ constructor(message: string, stderrLogs: string[] = []) {
22
+ super(message);
23
+ this.name = 'MCPConnectionError';
24
+ this.stderrLogs = stderrLogs;
25
+ }
26
+ }
27
+
14
28
  export class MCPClient {
15
29
  private readonly mcp: Client;
16
30
 
17
31
  private transport: Transport;
32
+ private stderrLogs: string[] = [];
33
+ private isStdio: boolean = false;
18
34
 
19
35
  constructor(params: MCPClientParams) {
20
36
  this.mcp = new Client({ name: 'lobehub-desktop-mcp-client', version: '1.0.0' });
@@ -40,14 +56,21 @@ export class MCPClient {
40
56
  }
41
57
 
42
58
  case 'stdio': {
43
- this.transport = new StdioClientTransport({
59
+ this.isStdio = true;
60
+ const stdioTransport = new StdioClientTransport({
44
61
  args: params.args,
45
62
  command: params.command,
46
63
  env: {
47
64
  ...getDefaultEnvironment(),
48
65
  ...params.env,
49
66
  },
67
+ stderr: 'pipe', // Capture stderr for better error messages
50
68
  });
69
+
70
+ // Listen to stderr stream to collect logs
71
+ this.setupStderrListener(stdioTransport);
72
+
73
+ this.transport = stdioTransport;
51
74
  break;
52
75
  }
53
76
 
@@ -60,16 +83,45 @@ export class MCPClient {
60
83
  }
61
84
  }
62
85
 
86
+ private setupStderrListener(transport: StdioClientTransport) {
87
+ const stderr = transport.stderr as Readable | null;
88
+ if (stderr) {
89
+ stderr.on('data', (chunk: Buffer) => {
90
+ const text = chunk.toString('utf8');
91
+ // Split by newlines and filter empty lines
92
+ const lines = text.split('\n').filter((line) => line.trim());
93
+ this.stderrLogs.push(...lines);
94
+ });
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Get collected stderr logs from the STDIO process
100
+ */
101
+ getStderrLogs(): string[] {
102
+ return this.stderrLogs;
103
+ }
104
+
63
105
  private isMethodNotFoundError(error: unknown) {
64
106
  const err = error as any;
65
107
  if (!err) return false;
108
+ // eslint-disable-next-line unicorn/numeric-separators-style
66
109
  if (err.code === -32601) return true;
67
110
  if (typeof err.message === 'string' && err.message.includes('Method not found')) return true;
68
111
  return false;
69
112
  }
70
113
 
71
114
  async initialize(options: { onProgress?: (progress: Progress) => void } = {}) {
72
- await this.mcp.connect(this.transport, { onprogress: options.onProgress });
115
+ try {
116
+ await this.mcp.connect(this.transport, { onprogress: options.onProgress });
117
+ } catch (error) {
118
+ // If this is a STDIO connection and we have stderr logs, enhance the error
119
+ if (this.isStdio && this.stderrLogs.length > 0) {
120
+ const originalMessage = error instanceof Error ? error.message : String(error);
121
+ throw new MCPConnectionError(originalMessage, this.stderrLogs);
122
+ }
123
+ throw error;
124
+ }
73
125
  }
74
126
 
75
127
  async disconnect() {
@@ -15,15 +15,9 @@
15
15
  "moduleResolution": "bundler",
16
16
  "resolveJsonModule": true,
17
17
  "paths": {
18
- "@/*": [
19
- "./src/main/*"
20
- ],
21
- "~common/*": [
22
- "./src/common/*"
23
- ],
24
- "*": [
25
- "./*"
26
- ]
18
+ "@/*": ["./src/main/*"],
19
+ "~common/*": ["./src/common/*"],
20
+ "*": ["./*"]
27
21
  }
28
22
  },
29
23
  "include": [
@@ -33,4 +27,4 @@
33
27
  "electron-builder.js",
34
28
  "native-deps.config.js"
35
29
  ]
36
- }
30
+ }
package/changelog/v1.json CHANGED
@@ -1,4 +1,18 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "improvements": [
5
+ "Migrate Next.js navigation APIs to React Router for SPA."
6
+ ]
7
+ },
8
+ "date": "2026-01-15",
9
+ "version": "2.0.0-next.295"
10
+ },
11
+ {
12
+ "children": {},
13
+ "date": "2026-01-15",
14
+ "version": "2.0.0-next.294"
15
+ },
2
16
  {
3
17
  "children": {},
4
18
  "date": "2026-01-15",
@@ -16,9 +16,7 @@
16
16
  * --port <port> Server port (default: 3006)
17
17
  * --help Show help message
18
18
  */
19
-
20
- import { type ChildProcess, spawn, spawnSync } from 'node:child_process';
21
- import { existsSync, unlinkSync, writeFileSync } from 'node:fs';
19
+ import { spawn, spawnSync } from 'node:child_process';
22
20
  import { resolve } from 'node:path';
23
21
 
24
22
  // ============================================================================
@@ -33,21 +31,27 @@ const CONFIG = {
33
31
  defaultPort: 3006,
34
32
  dockerImage: 'paradedb/paradedb:latest',
35
33
  projectRoot: resolve(__dirname, '../..'),
36
- serverTimeout: 120_000, // 2 minutes
37
-
38
- // Secrets (for e2e testing only)
39
- secrets: {
40
- betterAuthSecret: 'e2e-test-secret-key-for-better-auth-32chars!',
41
- keyVaultsSecret: 'LA7n9k3JdEcbSgml2sxfw+4TV1AzaaFU5+R176aQz4s=',
42
- },
34
+
43
35
 
44
- // S3 Mock (required even if not testing file uploads)
45
- s3Mock: {
36
+ // S3 Mock (required even if not testing file uploads)
37
+ s3Mock: {
46
38
  accessKeyId: 'e2e-mock-access-key',
47
39
  bucket: 'e2e-mock-bucket',
48
40
  endpoint: 'https://e2e-mock-s3.localhost',
49
41
  secretAccessKey: 'e2e-mock-secret-key',
42
+ },
43
+
44
+
45
+
46
+ // 2 minutes
47
+ // Secrets (for e2e testing only)
48
+ secrets: {
49
+ betterAuthSecret: 'e2e-test-secret-key-for-better-auth-32chars!',
50
+ keyVaultsSecret: 'LA7n9k3JdEcbSgml2sxfw+4TV1AzaaFU5+R176aQz4s=',
50
51
  },
52
+
53
+
54
+ serverTimeout: 120_000,
51
55
  };
52
56
 
53
57
  // ============================================================================
@@ -55,11 +59,11 @@ const CONFIG = {
55
59
  // ============================================================================
56
60
 
57
61
  const colors = {
58
- cyan: (s: string) => `\x1b[36m${s}\x1b[0m`,
59
- dim: (s: string) => `\x1b[2m${s}\x1b[0m`,
60
- green: (s: string) => `\x1b[32m${s}\x1b[0m`,
61
- red: (s: string) => `\x1b[31m${s}\x1b[0m`,
62
- yellow: (s: string) => `\x1b[33m${s}\x1b[0m`,
62
+ cyan: (s: string) => `\u001B[36m${s}\u001B[0m`,
63
+ dim: (s: string) => `\u001B[2m${s}\u001B[0m`,
64
+ green: (s: string) => `\u001B[32m${s}\u001B[0m`,
65
+ red: (s: string) => `\u001B[31m${s}\u001B[0m`,
66
+ yellow: (s: string) => `\u001B[33m${s}\u001B[0m`,
63
67
  };
64
68
 
65
69
  function log(emoji: string, message: string) {
@@ -73,12 +77,12 @@ function logStep(step: number, total: number, message: string) {
73
77
  function exec(
74
78
  command: string,
75
79
  args: string[] = [],
76
- options: { cwd?: string; silent?: boolean } = {}
80
+ options: { cwd?: string; silent?: boolean } = {},
77
81
  ) {
78
82
  const { cwd = CONFIG.projectRoot, silent = false } = options;
79
83
  const result = spawnSync(command, args, {
80
84
  cwd,
81
- encoding: 'utf-8',
85
+ encoding: 'utf8',
82
86
  shell: true,
83
87
  stdio: silent ? 'pipe' : 'inherit',
84
88
  });
@@ -88,7 +92,7 @@ function exec(
88
92
  function execAsync(
89
93
  command: string,
90
94
  args: string[] = [],
91
- env: Record<string, string> = {}
95
+ env: Record<string, string> = {},
92
96
  ): Promise<void> {
93
97
  return new Promise((resolve, reject) => {
94
98
  const child = spawn(command, args, {
@@ -111,14 +115,16 @@ function execAsync(
111
115
  }
112
116
 
113
117
  async function sleep(ms: number): Promise<void> {
114
- return new Promise((resolve) => setTimeout(resolve, ms));
118
+ return new Promise((resolve) => {
119
+ setTimeout(resolve, ms);
120
+ });
115
121
  }
116
122
 
117
123
  async function waitForCondition(
118
124
  check: () => Promise<boolean>,
119
125
  timeout: number,
120
126
  interval: number = 1000,
121
- onWait?: () => void
127
+ onWait?: () => void,
122
128
  ): Promise<boolean> {
123
129
  const startTime = Date.now();
124
130
  while (Date.now() - startTime < timeout) {
@@ -204,7 +210,7 @@ async function startPostgres(): Promise<void> {
204
210
  },
205
211
  30_000,
206
212
  2000,
207
- () => process.stdout.write('.')
213
+ () => process.stdout.write('.'),
208
214
  );
209
215
 
210
216
  console.log();
@@ -327,7 +333,7 @@ async function startServer(port: number): Promise<void> {
327
333
  () => isServerRunning(port),
328
334
  CONFIG.serverTimeout,
329
335
  2000,
330
- () => process.stdout.write('.')
336
+ () => process.stdout.write('.'),
331
337
  );
332
338
 
333
339
  console.log();
@@ -412,27 +418,34 @@ function parseArgs(): Options {
412
418
  for (let i = 0; i < args.length; i++) {
413
419
  switch (args[i]) {
414
420
  case '--help':
415
- case '-h':
421
+ case '-h': {
416
422
  options.help = true;
417
423
  break;
418
- case '--clean':
424
+ }
425
+ case '--clean': {
419
426
  options.clean = true;
420
427
  break;
421
- case '--skip-db':
428
+ }
429
+ case '--skip-db': {
422
430
  options.skipDb = true;
423
431
  break;
424
- case '--skip-migrate':
432
+ }
433
+ case '--skip-migrate': {
425
434
  options.skipMigrate = true;
426
435
  break;
427
- case '--build':
436
+ }
437
+ case '--build': {
428
438
  options.build = true;
429
439
  break;
430
- case '--start':
440
+ }
441
+ case '--start': {
431
442
  options.start = true;
432
443
  break;
433
- case '--port':
444
+ }
445
+ case '--port': {
434
446
  options.port = parseInt(args[++i], 10) || CONFIG.defaultPort;
435
447
  break;
448
+ }
436
449
  }
437
450
  }
438
451
 
@@ -526,4 +539,4 @@ ${colors.green('✅ E2E environment setup completed!')}
526
539
  }
527
540
  }
528
541
 
529
- main();
542
+ await main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/lobehub",
3
- "version": "2.0.0-next.293",
3
+ "version": "2.0.0-next.295",
4
4
  "description": "LobeHub - an open-source,comprehensive AI Agent framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
5
5
  "keywords": [
6
6
  "framework",
@@ -4,6 +4,7 @@ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
4
4
 
5
5
  import { sleep } from '@/utils/sleep';
6
6
 
7
+ import { getTestDB } from '../../core/getTestDB';
7
8
  import {
8
9
  NewKnowledgeBase,
9
10
  documents,
@@ -15,7 +16,6 @@ import {
15
16
  } from '../../schemas';
16
17
  import { LobeChatDatabase } from '../../type';
17
18
  import { KnowledgeBaseModel } from '../knowledgeBase';
18
- import { getTestDB } from '../../core/getTestDB';
19
19
 
20
20
  const serverDB: LobeChatDatabase = await getTestDB();
21
21
 
@@ -556,10 +556,7 @@ export class KnowledgeRepo {
556
556
  // When in a knowledge base, return standalone documents (folders and notes without fileId)
557
557
  // that have the knowledgeBaseId column set. Documents with fileId are already
558
558
  // returned by the file query via their linked file records.
559
- kbWhereConditions.push(
560
- sql`d.file_id IS NULL`,
561
- sql`d.knowledge_base_id = ${knowledgeBaseId}`,
562
- );
559
+ kbWhereConditions.push(sql`d.file_id IS NULL`, sql`d.knowledge_base_id = ${knowledgeBaseId}`);
563
560
 
564
561
  return sql`
565
562
  SELECT