@lobehub/lobehub 2.0.4 → 2.0.6

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 (30) hide show
  1. package/.eslintrc.js +1 -0
  2. package/CHANGELOG.md +50 -0
  3. package/changelog/v2.json +18 -0
  4. package/docs/self-hosting/advanced/auth/clerk-to-betterauth.mdx +2 -0
  5. package/docs/self-hosting/advanced/auth/clerk-to-betterauth.zh-CN.mdx +2 -0
  6. package/docs/self-hosting/advanced/auth/nextauth-to-betterauth.mdx +2 -0
  7. package/docs/self-hosting/advanced/auth/nextauth-to-betterauth.zh-CN.mdx +2 -0
  8. package/docs/self-hosting/platform/docker.mdx +6 -0
  9. package/docs/self-hosting/platform/docker.zh-CN.mdx +6 -0
  10. package/docs/self-hosting/platform/vercel.mdx +0 -49
  11. package/docs/self-hosting/platform/vercel.zh-CN.mdx +0 -47
  12. package/docs/self-hosting/start.mdx +0 -20
  13. package/docs/self-hosting/start.zh-CN.mdx +0 -18
  14. package/package.json +1 -1
  15. package/src/app/(backend)/api/workflows/memory-user-memory/pipelines/persona/update-writing/route.ts +19 -19
  16. package/src/app/[variants]/(main)/agent/features/Conversation/AgentWelcome/ToolAuthAlert.tsx +3 -2
  17. package/src/app/[variants]/(main)/settings/skill/features/KlavisSkillItem.tsx +3 -3
  18. package/src/app/[variants]/onboarding/components/KlavisServerList/hooks/useKlavisOAuth.ts +12 -5
  19. package/src/components/DebugNode.tsx +21 -0
  20. package/src/features/ChatInput/ActionBar/Tools/KlavisServerItem.tsx +3 -3
  21. package/src/features/ChatInput/ActionBar/Tools/ToolItem.tsx +16 -13
  22. package/src/features/ChatInput/ActionBar/Tools/ToolsList.tsx +51 -40
  23. package/src/features/ChatInput/ActionBar/components/ActionDropdown.tsx +7 -1
  24. package/src/features/ChatInput/ActionBar/components/ActionPopover.tsx +14 -11
  25. package/src/server/routers/lambda/klavis.ts +38 -10
  26. package/src/server/services/memory/userMemory/extract.ts +1 -1
  27. package/src/store/tool/slices/klavisStore/action.ts +20 -0
  28. package/src/tools/artifacts/systemRole.ts +84 -120
  29. package/docs/self-hosting/server-database.mdx +0 -157
  30. package/docs/self-hosting/server-database.zh-CN.mdx +0 -146
package/.eslintrc.js CHANGED
@@ -20,6 +20,7 @@ config.rules['unicorn/no-array-for-each'] = 0;
20
20
  config.rules['unicorn/prefer-number-properties'] = 0;
21
21
  config.rules['unicorn/prefer-query-selector'] = 0;
22
22
  config.rules['unicorn/no-array-callback-reference'] = 0;
23
+ config.rules['@typescript-eslint/no-use-before-define'] = 0;
23
24
  // FIXME: Linting error in src/app/[variants]/(main)/chat/features/Migration/DBReader.ts, the fundamental solution should be upgrading typescript-eslint
24
25
  config.rules['@typescript-eslint/no-useless-constructor'] = 0;
25
26
  config.rules['@next/next/no-img-element'] = 0;
package/CHANGELOG.md CHANGED
@@ -2,6 +2,56 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 2.0.6](https://github.com/lobehub/lobe-chat/compare/v2.0.5...v2.0.6)
6
+
7
+ <sup>Released on **2026-01-27**</sup>
8
+
9
+ #### 🐛 Bug Fixes
10
+
11
+ - **misc**: The klavis in onboarding connect timeout fixed.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's fixed
19
+
20
+ - **misc**: The klavis in onboarding connect timeout fixed, closes [#11918](https://github.com/lobehub/lobe-chat/issues/11918) ([bc165be](https://github.com/lobehub/lobe-chat/commit/bc165be))
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.5](https://github.com/lobehub/lobe-chat/compare/v2.0.4...v2.0.5)
31
+
32
+ <sup>Released on **2026-01-27**</sup>
33
+
34
+ #### 🐛 Bug Fixes
35
+
36
+ - **misc**: Update the artifact prompt.
37
+
38
+ <br/>
39
+
40
+ <details>
41
+ <summary><kbd>Improvements and Fixes</kbd></summary>
42
+
43
+ #### What's fixed
44
+
45
+ - **misc**: Update the artifact prompt, closes [#11907](https://github.com/lobehub/lobe-chat/issues/11907) ([217e689](https://github.com/lobehub/lobe-chat/commit/217e689))
46
+
47
+ </details>
48
+
49
+ <div align="right">
50
+
51
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
52
+
53
+ </div>
54
+
5
55
  ### [Version 2.0.4](https://github.com/lobehub/lobe-chat/compare/v2.0.3...v2.0.4)
6
56
 
7
57
  <sup>Released on **2026-01-27**</sup>
package/changelog/v2.json CHANGED
@@ -1,4 +1,22 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "fixes": [
5
+ "The klavis in onboarding connect timeout fixed."
6
+ ]
7
+ },
8
+ "date": "2026-01-27",
9
+ "version": "2.0.6"
10
+ },
11
+ {
12
+ "children": {
13
+ "fixes": [
14
+ "Update the artifact prompt."
15
+ ]
16
+ },
17
+ "date": "2026-01-27",
18
+ "version": "2.0.5"
19
+ },
2
20
  {
3
21
  "children": {
4
22
  "fixes": [
@@ -63,6 +63,8 @@ For small self-hosted deployments, the simplest approach is to let users reset t
63
63
 
64
64
  Remove Clerk variables and add Better Auth variables:
65
65
 
66
+ <GenerateSecret envName="AUTH_SECRET" />
67
+
66
68
  ```bash
67
69
  # Remove these
68
70
  # NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=xxx
@@ -61,6 +61,8 @@ tags:
61
61
 
62
62
  移除 Clerk 变量并添加 Better Auth 变量:
63
63
 
64
+ <GenerateSecret envName="AUTH_SECRET" />
65
+
64
66
  ```bash
65
67
  # 移除这些
66
68
  # NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=xxx
@@ -116,6 +116,8 @@ For small self-hosted deployments, the simplest approach is to let users re-logi
116
116
 
117
117
  Remove NextAuth variables and add Better Auth variables:
118
118
 
119
+ <GenerateSecret envName="AUTH_SECRET" />
120
+
119
121
  ```bash
120
122
  # Remove these
121
123
  # NEXT_AUTH_SECRET=xxx
@@ -111,6 +111,8 @@ Better Auth 支持更多功能,以下是新增的环境变量:
111
111
 
112
112
  移除 NextAuth 变量并添加 Better Auth 变量:
113
113
 
114
+ <GenerateSecret envName="AUTH_SECRET" />
115
+
114
116
  ```bash
115
117
  # 移除这些
116
118
  # NEXT_AUTH_SECRET=xxx
@@ -58,6 +58,12 @@ Here is the process for deploying the LobeHub server database version on a Linux
58
58
 
59
59
  ### Create a file named `lobe-chat.env` to store environment variables:
60
60
 
61
+ Click the buttons below to generate required secrets:
62
+
63
+ <GenerateSecret envName="KEY_VAULTS_SECRET" />
64
+
65
+ <GenerateSecret envName="AUTH_SECRET" />
66
+
61
67
  ```shell
62
68
  # Website domain
63
69
  APP_URL=https://your-prod-domain.com
@@ -54,6 +54,12 @@ tags:
54
54
 
55
55
  ### 创建名为 `lobe-chat.env` 文件用于存放环境变量:
56
56
 
57
+ 点击下方按钮生成所需密钥:
58
+
59
+ <GenerateSecret envName="KEY_VAULTS_SECRET" />
60
+
61
+ <GenerateSecret envName="AUTH_SECRET" />
62
+
57
63
  ```shell
58
64
  # 网站域名
59
65
  APP_URL=https://your-prod-domain.com
@@ -114,55 +114,6 @@ The server-side database needs to be paired with a user authentication service t
114
114
  <Callout type={'info'}>
115
115
  For advanced features like SSO providers, magic link login, and email verification, see [Authentication Service](/docs/self-hosting/advanced/auth).
116
116
  </Callout>
117
-
118
- ### Add Public and Private Key Environment Variables in Vercel
119
-
120
- In Vercel's deployment environment variables, add the `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` and `CLERK_SECRET_KEY` environment variables. You can click on "API Keys" in the menu, then copy the corresponding values and paste them into Vercel's environment variables.
121
-
122
- <Image alt={'Find the corresponding public and private key environment variables in Clerk'} src={'/blog/assets28616219/89883703-7a1a-4a11-b944-5d804544e57c.webp'} />
123
-
124
- The environment variables required for this step are as follows:
125
-
126
- ```shell
127
- NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_xxxxxxxxxxx
128
- CLERK_SECRET_KEY=sk_live_xxxxxxxxxxxxxxxxxxxxxx
129
- ```
130
-
131
- Add the above variables to Vercel:
132
-
133
- <Image alt={'Add Clerk public and private key environment variables in Vercel'} src={'/blog/assets28616219/2bfa13df-6e20-4768-97c0-4dad06c85a2f.webp'} />
134
-
135
- ### Create and Configure Webhook in Clerk
136
-
137
- Since we let Clerk fully handle user authentication and management, we need Clerk to notify our application and store data in the database when there are changes in the user's lifecycle (create, update, delete). We achieve this requirement through the Webhook provided by Clerk.
138
-
139
- We need to add an endpoint in Clerk's Webhooks to inform Clerk to send notifications to this endpoint when a user's information changes.
140
-
141
- <Image alt={'Add Webhooks endpoint in Clerk'} src={'/blog/assets28616219/f50f47fb-5e8e-4930-bf4e-8cf6f5b8afb9.webp'} />
142
-
143
- Fill in the endpoint with the URL of your Vercel project, such as `https://your-project.vercel.app/api/webhooks/clerk`. Then, subscribe to events by checking the three user events (`user.created`, `user.deleted`, `user.updated`), and click create.
144
-
145
- <Callout type={'warning'}>
146
- The `https://` in the URL is essential to maintain the integrity of the URL.
147
- </Callout>
148
-
149
- <Image alt={'Configure URL and user events when adding Clerk Webhooks'} src={'/blog/assets28616219/0249ea56-ab17-4aa9-a56c-9ebd556c2645.webp'} />
150
-
151
- ### Add Webhook Secret to Vercel Environment Variables
152
-
153
- After creation, you can find the secret of this Webhook in the bottom right corner:
154
-
155
- <Image alt={'View Clerk Webhooks secret'} src={'/blog/assets28616219/fab4abb2-584b-49de-9340-813382951635.webp'} />
156
-
157
- The environment variable corresponding to this secret is `CLERK_WEBHOOK_SECRET`:
158
-
159
- ```shell
160
- CLERK_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxxx
161
- ```
162
-
163
- Add it to Vercel's environment variables:
164
-
165
- <Image alt={'Add Clerk Webhooks secret in Vercel'} src={'/blog/assets28616219/5fdc9479-007f-46ab-9d6e-a9603e949116.webp'} />
166
117
  </Steps>
167
118
 
168
119
  By completing these steps, you have successfully configured the authentication service. Next, we will configure the S3 storage service.
@@ -114,53 +114,6 @@ tags:
114
114
  <Callout type={'info'}>
115
115
  如需 SSO 登录、魔法链接登录、邮箱验证等高级功能,请参阅 [身份验证服务](/zh/docs/self-hosting/advanced/auth)。
116
116
  </Callout>
117
-
118
- ### 在 Vercel 中添加公、私钥环境变量
119
-
120
- 在 Vercel 的部署环境变量中,添加 `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` 和 `CLERK_SECRET_KEY` 环境变量。你可以在菜单中点击「API Keys」,然后复制对应的值填入 Vercel 的环境变量中。
121
-
122
- <Image alt={'在 Clerk 中找到对应的公私钥环境变量'} src={'/blog/assets28616219/89883703-7a1a-4a11-b944-5d804544e57c.webp'} />
123
-
124
- 此步骤所需的环境变量如下:
125
-
126
- ```shell
127
- NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_xxxxxxxxxxx
128
- CLERK_SECRET_KEY=sk_live_xxxxxxxxxxxxxxxxxxxxxx
129
- ```
130
-
131
- 添加上述变量到 Vercel 中:
132
-
133
- <Image alt={'在 Vercel 中添加 Clerk 公私钥环境变量'} src={'/blog/assets28616219/2bfa13df-6e20-4768-97c0-4dad06c85a2f.webp'} />
134
-
135
- ### 在 Clerk 中创建并配置 Webhook
136
-
137
- 由于我们让 Clerk 完全接管用户鉴权与管理,因此我们需要在 Clerk 用户生命周期变更时(创建、更新、删除)中通知我们的应用并存储落库。我们通过 Clerk 提供的 Webhook 来实现这一诉求。
138
-
139
- 我们需要在 Clerk 的 Webhooks 中添加一个端点(Endpoint),告诉 Clerk 当用户发生变更时,向这个端点发送通知。
140
-
141
- <Image alt={'Clerk 添加 Webhooks 端点'} src={'/blog/assets28616219/f50f47fb-5e8e-4930-bf4e-8cf6f5b8afb9.webp'} />
142
-
143
- 在 endpoint 中填写你的 Vercel 项目的 URL,如 `https://your-project.vercel.app/api/webhooks/clerk`。然后在订阅事件(Subscribe to events)中,勾选 user 的三个事件(`user.created` 、`user.deleted`、`user.updated`),然后点击创建。
144
-
145
- <Callout type={'warning'}>URL 的`https://`不可缺失,须保持 URL 的完整性</Callout>
146
-
147
- <Image alt={'添加 Clerk Webhooks 时,配置 URL 和用户事件'} src={'/blog/assets28616219/0249ea56-ab17-4aa9-a56c-9ebd556c2645.webp'} />
148
-
149
- ### 将 Webhook 秘钥添加到 Vercel 环境变量
150
-
151
- 创建完毕后,可以在右下角找到该 Webhook 的秘钥:
152
-
153
- <Image alt={'查看 Clerk Webhooks 秘钥'} src={'/blog/assets28616219/fab4abb2-584b-49de-9340-813382951635.webp'} />
154
-
155
- 这个秘钥所对应的环境变量名为 `CLERK_WEBHOOK_SECRET`:
156
-
157
- ```shell
158
- CLERK_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxxx
159
- ```
160
-
161
- 将其添加到 Vercel 的环境变量中:
162
-
163
- <Image alt={'在 Vercel 中 添加 Clerk Webhooks 秘钥'} src={'/blog/assets28616219/5fdc9479-007f-46ab-9d6e-a9603e949116.webp'} />
164
117
  </Steps>
165
118
 
166
119
  这样,你已经成功配置了身份验证服务。接下来我们将配置 S3 存储服务。
@@ -16,24 +16,4 @@ tags:
16
16
 
17
17
  LobeHub supports various deployment platforms, including Vercel, Docker, and Docker Compose. You can choose a deployment platform that suits you to build your own Lobe Chat.
18
18
 
19
- ## Quick Deployment
20
-
21
- For users who are new to LobeHub, we recommend using the client-side database mode for quick deployment. The advantage of this mode is that deployment can be quickly completed with just one command/button, making it easy for you to quickly get started and experience LobeHub.
22
-
23
- You can follow the guide below for quick deployment of LobeHub:
24
-
25
19
  <PlatformCards urlPrefix={'platform'} />
26
-
27
- <Callout>
28
- In the client-side database mode, data is stored locally on the user's device, without
29
- cross-device synchronization, and does not support advanced features such as file uploads and
30
- knowledge base.
31
- </Callout>
32
-
33
- ## Advanced Mode: Server-Side Database
34
-
35
- For users who are already familiar with LobeHub or need cross-device synchronization, you can deploy a version with a server-side database to access a more complete and powerful LobeHub.
36
-
37
- <Cards>
38
- <Card href={'/docs/self-hosting/server-database'} title={'Server-Side Database Deployment Guide'} />
39
- </Cards>
@@ -20,22 +20,4 @@ tags:
20
20
 
21
21
  LobeHub 支持多种部署平台,包括 Vercel、Docker、 Docker Compose 、阿里云计算巢 和腾讯轻量云 等,你可以选择适合自己的部署平台进行部署,构建属于自己的 Lobe Chat。
22
22
 
23
- ## 快速部署
24
-
25
- 对于第一次了解 LobeHub 的用户,我们推荐使用客户端数据库的模式快速部署,该模式的优势是一行指令 / 一个按钮即可快捷完成部署,便于你快速上手与体验 LobeHub。
26
-
27
- 你可以通过以下指南快速部署 LobeHub:
28
-
29
23
  <PlatformCards urlPrefix={'platform'} />
30
-
31
- <Callout>
32
- 客户端数据库模式下数据均保留在用户本地,不会跨多端同步,也不支持文件上传、知识库等进阶功能。
33
- </Callout>
34
-
35
- ## 进阶模式:服务端数据库
36
-
37
- 针对已经了解 LobeHub 的用户,或需要多端同步的用户,可以自行部署带有服务端数据库的版本,进而获得更完整、功能更强大的 LobeHub。
38
-
39
- <Cards rows={1}>
40
- <Card href={'/zh/docs/self-hosting/server-database'} title={'服务端数据库部署指南'} />
41
- </Cards>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/lobehub",
3
- "version": "2.0.4",
3
+ "version": "2.0.6",
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",
@@ -8,39 +8,39 @@ import {
8
8
  } from '@/server/services/memory/userMemory/persona/service';
9
9
 
10
10
  const workflowPayloadSchema = z.object({
11
- userId: z.string().optional(),
12
11
  userIds: z.array(z.string()).optional(),
13
12
  });
14
13
 
15
14
  export const { POST } = serve(async (context) => {
16
- const payload = workflowPayloadSchema.parse(context.requestPayload || {});
15
+ const payload = await context.run('memory:pipelines:persona:update-writing:parse-payload', () =>
16
+ workflowPayloadSchema.parse(context.requestPayload || {}),
17
+ );
17
18
  const db = await getServerDB();
18
19
 
19
- const userIds = Array.from(
20
- new Set([...(payload.userIds || []), ...(payload.userId ? [payload.userId] : [])]),
21
- ).filter(Boolean);
22
-
20
+ const userIds = Array.from(new Set(payload.userIds || [])).filter(Boolean);
23
21
  if (userIds.length === 0) {
24
- return { message: 'userId or userIds is required', processedUsers: 0 };
22
+ throw new Error('No user IDs provided for persona update.');
25
23
  }
26
24
 
27
25
  const service = new UserPersonaService(db);
28
- const results = [];
29
26
 
30
- for (const userId of userIds) {
31
- const context = await buildUserPersonaJobInput(db, userId);
32
- const result = await service.composeWriting({ ...context, userId });
33
- results.push({
34
- diffId: result.diff?.id,
35
- documentId: result.document.id,
36
- userId,
37
- version: result.document.version,
38
- });
39
- }
27
+ await Promise.all(
28
+ userIds.map(async (userId) =>
29
+ context.run(`memory:pipelines:persona:update-writing:users:${userId}`, async () => {
30
+ const context = await buildUserPersonaJobInput(db, userId);
31
+ const result = await service.composeWriting({ ...context, userId });
32
+ return {
33
+ diffId: result.diff?.id,
34
+ documentId: result.document.id,
35
+ userId,
36
+ version: result.document.version,
37
+ };
38
+ }),
39
+ ),
40
+ );
40
41
 
41
42
  return {
42
43
  message: 'User persona processed via workflow.',
43
44
  processedUsers: userIds.length,
44
- results,
45
45
  };
46
46
  });
@@ -104,7 +104,7 @@ const KlavisToolAuthItem = memo<KlavisToolAuthItemProps>(({ tool, onAuthComplete
104
104
  try {
105
105
  await refreshKlavisServerTools(identifier);
106
106
  } catch (error) {
107
- console.error('[Klavis] Failed to check auth status:', error);
107
+ console.debug('[Klavis] Polling check (expected during auth):', error);
108
108
  }
109
109
  }, POLL_INTERVAL_MS);
110
110
 
@@ -129,7 +129,8 @@ const KlavisToolAuthItem = memo<KlavisToolAuthItemProps>(({ tool, onAuthComplete
129
129
  windowCheckIntervalRef.current = null;
130
130
  }
131
131
  oauthWindowRef.current = null;
132
- refreshKlavisServerTools(identifier);
132
+ // Start polling after window closes
133
+ startFallbackPolling(identifier);
133
134
  }
134
135
  } catch {
135
136
  if (windowCheckIntervalRef.current) {
@@ -120,7 +120,7 @@ const KlavisSkillItem = memo<KlavisSkillItemProps>(({ serverType, server }) => {
120
120
  try {
121
121
  await refreshKlavisServerTools(serverName);
122
122
  } catch (error) {
123
- console.error('[Klavis] Failed to check auth status:', error);
123
+ console.debug('[Klavis] Polling check (expected during auth):', error);
124
124
  }
125
125
  }, POLL_INTERVAL_MS);
126
126
 
@@ -145,8 +145,8 @@ const KlavisSkillItem = memo<KlavisSkillItemProps>(({ serverType, server }) => {
145
145
  windowCheckIntervalRef.current = null;
146
146
  }
147
147
  oauthWindowRef.current = null;
148
- await refreshKlavisServerTools(serverName);
149
- setIsWaitingAuth(false);
148
+ // Start polling after window closes
149
+ startFallbackPolling(serverName);
150
150
  }
151
151
  } catch {
152
152
  console.log('[Klavis] COOP blocked window.closed access, falling back to polling');
@@ -5,6 +5,7 @@ import { KlavisServerStatus } from '@/store/tool/slices/klavisStore';
5
5
 
6
6
  const POLL_INTERVAL_MS = 1000;
7
7
  const POLL_TIMEOUT_MS = 15_000;
8
+ const WINDOW_CLOSED_POLL_TIMEOUT_MS = 4000; // Shorter timeout when window is closed
8
9
 
9
10
  interface UseKlavisOAuthProps {
10
11
  serverStatus?: KlavisServerStatus;
@@ -50,14 +51,14 @@ export const useKlavisOAuth = ({ serverStatus }: UseKlavisOAuthProps) => {
50
51
  }, [serverStatus, isWaitingAuth, cleanup]);
51
52
 
52
53
  const startFallbackPolling = useCallback(
53
- (serverName: string) => {
54
+ (serverName: string, timeoutMs: number = POLL_TIMEOUT_MS) => {
54
55
  if (pollIntervalRef.current) return;
55
56
 
56
57
  pollIntervalRef.current = setInterval(async () => {
57
58
  try {
58
59
  await refreshKlavisServerTools(serverName);
59
60
  } catch (error) {
60
- console.error('[Klavis] Failed to check auth status:', error);
61
+ console.debug('[Klavis] Polling check (expected during auth):', error);
61
62
  }
62
63
  }, POLL_INTERVAL_MS);
63
64
 
@@ -67,7 +68,7 @@ export const useKlavisOAuth = ({ serverStatus }: UseKlavisOAuthProps) => {
67
68
  pollIntervalRef.current = null;
68
69
  }
69
70
  setIsWaitingAuth(false);
70
- }, POLL_TIMEOUT_MS);
71
+ }, timeoutMs);
71
72
  },
72
73
  [refreshKlavisServerTools],
73
74
  );
@@ -77,23 +78,29 @@ export const useKlavisOAuth = ({ serverStatus }: UseKlavisOAuthProps) => {
77
78
  windowCheckIntervalRef.current = setInterval(() => {
78
79
  try {
79
80
  if (oauthWindow.closed) {
81
+ // Stop monitoring window
80
82
  if (windowCheckIntervalRef.current) {
81
83
  clearInterval(windowCheckIntervalRef.current);
82
84
  windowCheckIntervalRef.current = null;
83
85
  }
84
86
  oauthWindowRef.current = null;
85
- refreshKlavisServerTools(serverName);
87
+
88
+ // Start polling to check auth status after window closes
89
+ // Use shorter timeout since user has closed the window
90
+ // Keep loading state until we confirm success or timeout
91
+ startFallbackPolling(serverName, WINDOW_CLOSED_POLL_TIMEOUT_MS);
86
92
  }
87
93
  } catch {
88
94
  if (windowCheckIntervalRef.current) {
89
95
  clearInterval(windowCheckIntervalRef.current);
90
96
  windowCheckIntervalRef.current = null;
91
97
  }
98
+ // Use default timeout for fallback polling
92
99
  startFallbackPolling(serverName);
93
100
  }
94
101
  }, 500);
95
102
  },
96
- [refreshKlavisServerTools, startFallbackPolling],
103
+ [startFallbackPolling],
97
104
  );
98
105
 
99
106
  const openOAuthWindow = useCallback(
@@ -0,0 +1,21 @@
1
+ 'use client';
2
+
3
+ import { type ReactNode, useEffect } from 'react';
4
+
5
+ interface DebugNodeProps {
6
+ children?: ReactNode;
7
+ trace: string;
8
+ }
9
+
10
+ const DebugNode = ({ children, trace }: DebugNodeProps) => {
11
+ if (process.env.NODE_ENV !== 'development') return null;
12
+ // eslint-disable-next-line react-hooks/rules-of-hooks
13
+ useEffect(() => {
14
+ // eslint-disable-next-line no-console
15
+ console.log(`[DebugNode] Suspense fallback active: ${trace}`);
16
+ }, [trace]);
17
+
18
+ return children ?? null;
19
+ };
20
+
21
+ export default DebugNode;
@@ -88,7 +88,7 @@ const KlavisServerItem = memo<KlavisServerItemProps>(
88
88
  try {
89
89
  await refreshKlavisServerTools(serverName);
90
90
  } catch (error) {
91
- console.error('[Klavis] Failed to check auth status:', error);
91
+ console.debug('[Klavis] Polling check (expected during auth):', error);
92
92
  }
93
93
  }, POLL_INTERVAL_MS);
94
94
 
@@ -121,8 +121,8 @@ const KlavisServerItem = memo<KlavisServerItemProps>(
121
121
  }
122
122
  oauthWindowRef.current = null;
123
123
 
124
- // 窗口关闭后立即检查一次认证状态
125
- refreshKlavisServerTools(serverName);
124
+ // 窗口关闭后开始轮询检查认证状态
125
+ startFallbackPolling(serverName);
126
126
  }
127
127
  } catch {
128
128
  // COOP 阻止了访问,降级到轮询方案
@@ -1,6 +1,7 @@
1
1
  import { Flexbox } from '@lobehub/ui';
2
- import { memo } from 'react';
2
+ import { Suspense, memo } from 'react';
3
3
 
4
+ import DebugNode from '@/components/DebugNode';
4
5
  import PluginTag from '@/components/Plugins/PluginTag';
5
6
  import { useToolStore } from '@/store/tool';
6
7
  import { customPluginSelectors } from '@/store/tool/selectors';
@@ -11,18 +12,20 @@ const ToolItem = memo<CheckboxItemProps>(({ id, onUpdate, label, checked }) => {
11
12
  const isCustom = useToolStore((s) => customPluginSelectors.isCustomPlugin(id)(s));
12
13
 
13
14
  return (
14
- <CheckboxItem
15
- checked={checked}
16
- hasPadding={false}
17
- id={id}
18
- label={
19
- <Flexbox align={'center'} gap={8} horizontal>
20
- {label || id}
21
- {isCustom && <PluginTag showText={false} type={'customPlugin'} />}
22
- </Flexbox>
23
- }
24
- onUpdate={onUpdate}
25
- />
15
+ <Suspense fallback={<DebugNode trace="ActionBar/Tools/ToolItem" />}>
16
+ <CheckboxItem
17
+ checked={checked}
18
+ hasPadding={false}
19
+ id={id}
20
+ label={
21
+ <Flexbox align={'center'} gap={8} horizontal>
22
+ {label || id}
23
+ {isCustom && <PluginTag showText={false} type={'customPlugin'} />}
24
+ </Flexbox>
25
+ }
26
+ onUpdate={onUpdate}
27
+ />
28
+ </Suspense>
26
29
  );
27
30
  });
28
31