@lobehub/lobehub 2.0.0-next.236 → 2.0.0-next.238
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.
- package/CHANGELOG.md +66 -0
- package/changelog/v1.json +17 -0
- package/locales/en-US/oauth.json +1 -0
- package/locales/zh-CN/oauth.json +1 -0
- package/locales/zh-CN/subscription.json +1 -1
- package/package.json +1 -1
- package/packages/const/src/klavis.ts +1 -7
- package/packages/types/src/user/onboarding.ts +3 -1
- package/src/app/[variants]/(auth)/oauth/callback/success/page.tsx +44 -2
- package/src/app/[variants]/(main)/chat/_layout/Sidebar/Header/Nav.tsx +19 -1
- package/src/app/[variants]/(main)/chat/_layout/Sidebar/Header/index.tsx +1 -2
- package/src/app/[variants]/(main)/chat/_layout/Sidebar/Topic/List/Item/Editing.tsx +18 -9
- package/src/app/[variants]/(main)/settings/profile/features/KlavisAuthorizationList/index.tsx +6 -24
- package/src/app/[variants]/(main)/settings/profile/index.tsx +17 -3
- package/src/app/[variants]/onboarding/features/FullNameStep.tsx +18 -5
- package/src/app/[variants]/onboarding/features/InterestsStep.tsx +19 -7
- package/src/app/[variants]/onboarding/features/ProSettingsStep.tsx +30 -8
- package/src/app/[variants]/onboarding/features/ResponseLanguageStep.tsx +18 -4
- package/src/app/[variants]/onboarding/features/TelemetryStep.tsx +14 -5
- package/src/app/[variants]/onboarding/index.tsx +2 -1
- package/src/features/ChatInput/ActionBar/Tools/LobehubSkillServerItem.tsx +42 -2
- package/src/server/services/search/impls/exa/index.ts +1 -1
- package/src/server/services/search/impls/search1api/index.ts +1 -1
- package/src/server/services/search/impls/tavily/index.ts +1 -1
- package/src/store/tool/slices/klavisStore/action.test.ts +167 -2
- package/src/store/tool/slices/klavisStore/action.ts +9 -8
- package/src/store/tool/slices/klavisStore/initialState.ts +3 -0
- package/src/store/user/slices/auth/action.ts +1 -0
- package/src/store/user/slices/onboarding/action.test.ts +342 -0
- package/src/store/user/slices/onboarding/action.ts +4 -9
- package/src/store/user/slices/onboarding/selectors.test.ts +222 -0
- package/src/store/user/slices/onboarding/selectors.ts +6 -1
|
@@ -4,7 +4,7 @@ import { SendButton } from '@lobehub/editor/react';
|
|
|
4
4
|
import { Button, Flexbox, Select, Text } from '@lobehub/ui';
|
|
5
5
|
import { cssVar } from 'antd-style';
|
|
6
6
|
import { Undo2Icon } from 'lucide-react';
|
|
7
|
-
import { memo, useCallback, useState } from 'react';
|
|
7
|
+
import { memo, useCallback, useRef, useState } from 'react';
|
|
8
8
|
import { useTranslation } from 'react-i18next';
|
|
9
9
|
|
|
10
10
|
import { type Locales, localeOptions, normalizeLocale } from '@/locales/resources';
|
|
@@ -24,11 +24,23 @@ const ResponseLanguageStep = memo<ResponseLanguageStepProps>(({ onBack, onNext }
|
|
|
24
24
|
const setSettings = useUserStore((s) => s.setSettings);
|
|
25
25
|
|
|
26
26
|
const [value, setValue] = useState<Locales | ''>(normalizeLocale(navigator.language));
|
|
27
|
+
const [isNavigating, setIsNavigating] = useState(false);
|
|
28
|
+
const isNavigatingRef = useRef(false);
|
|
27
29
|
|
|
28
|
-
const handleNext = () => {
|
|
30
|
+
const handleNext = useCallback(() => {
|
|
31
|
+
if (isNavigatingRef.current) return;
|
|
32
|
+
isNavigatingRef.current = true;
|
|
33
|
+
setIsNavigating(true);
|
|
29
34
|
setSettings({ general: { responseLanguage: value || '' } });
|
|
30
35
|
onNext();
|
|
31
|
-
};
|
|
36
|
+
}, [value, setSettings, onNext]);
|
|
37
|
+
|
|
38
|
+
const handleBack = useCallback(() => {
|
|
39
|
+
if (isNavigatingRef.current) return;
|
|
40
|
+
isNavigatingRef.current = true;
|
|
41
|
+
setIsNavigating(true);
|
|
42
|
+
onBack();
|
|
43
|
+
}, [onBack]);
|
|
32
44
|
|
|
33
45
|
const Message = useCallback(
|
|
34
46
|
() => (
|
|
@@ -73,6 +85,7 @@ const ResponseLanguageStep = memo<ResponseLanguageStepProps>(({ onBack, onNext }
|
|
|
73
85
|
value={value}
|
|
74
86
|
/>
|
|
75
87
|
<SendButton
|
|
88
|
+
disabled={isNavigating}
|
|
76
89
|
onClick={handleNext}
|
|
77
90
|
style={{
|
|
78
91
|
zoom: 1.5,
|
|
@@ -85,8 +98,9 @@ const ResponseLanguageStep = memo<ResponseLanguageStepProps>(({ onBack, onNext }
|
|
|
85
98
|
</Text>
|
|
86
99
|
<Flexbox horizontal justify={'flex-start'} style={{ marginTop: 32 }}>
|
|
87
100
|
<Button
|
|
101
|
+
disabled={isNavigating}
|
|
88
102
|
icon={Undo2Icon}
|
|
89
|
-
onClick={
|
|
103
|
+
onClick={handleBack}
|
|
90
104
|
style={{
|
|
91
105
|
color: cssVar.colorTextDescription,
|
|
92
106
|
}}
|
|
@@ -7,7 +7,7 @@ import { LoadingDots } from '@lobehub/ui/chat';
|
|
|
7
7
|
import { Steps, Switch } from 'antd';
|
|
8
8
|
import { cssVar } from 'antd-style';
|
|
9
9
|
import { BrainIcon, HeartHandshakeIcon, PencilRulerIcon, ShieldCheck } from 'lucide-react';
|
|
10
|
-
import { memo, useCallback, useState } from 'react';
|
|
10
|
+
import { memo, useCallback, useRef, useState } from 'react';
|
|
11
11
|
import { Trans, useTranslation } from 'react-i18next';
|
|
12
12
|
|
|
13
13
|
import { ProductLogo } from '@/components/Branding';
|
|
@@ -21,12 +21,20 @@ interface TelemetryStepProps {
|
|
|
21
21
|
const TelemetryStep = memo<TelemetryStepProps>(({ onNext }) => {
|
|
22
22
|
const { t } = useTranslation('onboarding');
|
|
23
23
|
const [check, setCheck] = useState(true);
|
|
24
|
+
const [isNavigating, setIsNavigating] = useState(false);
|
|
25
|
+
const isNavigatingRef = useRef(false);
|
|
24
26
|
const updateGeneralConfig = useUserStore((s) => s.updateGeneralConfig);
|
|
25
27
|
|
|
26
|
-
const handleChoice = (
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
const handleChoice = useCallback(
|
|
29
|
+
(enabled: boolean) => {
|
|
30
|
+
if (isNavigatingRef.current) return;
|
|
31
|
+
isNavigatingRef.current = true;
|
|
32
|
+
setIsNavigating(true);
|
|
33
|
+
updateGeneralConfig({ telemetry: enabled });
|
|
34
|
+
onNext();
|
|
35
|
+
},
|
|
36
|
+
[updateGeneralConfig, onNext],
|
|
37
|
+
);
|
|
30
38
|
|
|
31
39
|
const IconAvatar = useCallback(({ icon }: { icon: IconProps['icon'] }) => {
|
|
32
40
|
return (
|
|
@@ -123,6 +131,7 @@ const TelemetryStep = memo<TelemetryStepProps>(({ onNext }) => {
|
|
|
123
131
|
</Flexbox>
|
|
124
132
|
</Flexbox>
|
|
125
133
|
<Button
|
|
134
|
+
disabled={isNavigating}
|
|
126
135
|
onClick={() => handleChoice(check)}
|
|
127
136
|
size={'large'}
|
|
128
137
|
style={{
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
import { MAX_ONBOARDING_STEPS } from '@lobechat/types';
|
|
3
4
|
import { Flexbox } from '@lobehub/ui';
|
|
4
5
|
import { memo } from 'react';
|
|
5
6
|
|
|
@@ -40,7 +41,7 @@ const OnboardingPage = memo(() => {
|
|
|
40
41
|
case 4: {
|
|
41
42
|
return <ResponseLanguageStep onBack={goToPreviousStep} onNext={goToNextStep} />;
|
|
42
43
|
}
|
|
43
|
-
case
|
|
44
|
+
case MAX_ONBOARDING_STEPS: {
|
|
44
45
|
return <ProSettingsStep onBack={goToPreviousStep} />;
|
|
45
46
|
}
|
|
46
47
|
default: {
|
|
@@ -135,13 +135,52 @@ const LobehubSkillServerItem = memo<LobehubSkillServerItemProps>(({ provider, la
|
|
|
135
135
|
s.togglePlugin,
|
|
136
136
|
]);
|
|
137
137
|
|
|
138
|
+
// Listen for OAuth success message from popup window
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
const handleMessage = async (event: MessageEvent) => {
|
|
141
|
+
// Verify origin for security
|
|
142
|
+
if (event.origin !== window.location.origin) return;
|
|
143
|
+
|
|
144
|
+
if (event.data?.type === 'LOBEHUB_SKILL_AUTH_SUCCESS' && event.data?.provider === provider) {
|
|
145
|
+
console.log('[LobehubSkill] OAuth success message received for provider:', provider);
|
|
146
|
+
|
|
147
|
+
// Cleanup polling/window monitoring
|
|
148
|
+
cleanup();
|
|
149
|
+
|
|
150
|
+
// Refresh status to get the connected state
|
|
151
|
+
await checkStatus(provider);
|
|
152
|
+
|
|
153
|
+
// Auto-enable the plugin after successful OAuth
|
|
154
|
+
// Need to get the latest server state after checkStatus
|
|
155
|
+
const latestServer = useToolStore
|
|
156
|
+
.getState()
|
|
157
|
+
.lobehubSkillServers?.find((s) => s.identifier === provider);
|
|
158
|
+
if (latestServer?.status === LobehubSkillStatus.CONNECTED) {
|
|
159
|
+
const newPluginId = latestServer.identifier;
|
|
160
|
+
const isAlreadyEnabled = agentSelectors
|
|
161
|
+
.currentAgentPlugins(useAgentStore.getState())
|
|
162
|
+
.includes(newPluginId);
|
|
163
|
+
if (!isAlreadyEnabled) {
|
|
164
|
+
console.log('[LobehubSkill] Auto-enabling plugin:', newPluginId);
|
|
165
|
+
togglePlugin(newPluginId);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
window.addEventListener('message', handleMessage);
|
|
172
|
+
return () => window.removeEventListener('message', handleMessage);
|
|
173
|
+
}, [provider, cleanup, checkStatus, togglePlugin]);
|
|
174
|
+
|
|
138
175
|
const handleConnect = async () => {
|
|
139
176
|
// 只有已连接状态才阻止重新连接
|
|
140
177
|
if (server?.isConnected) return;
|
|
141
178
|
|
|
142
179
|
setIsConnecting(true);
|
|
143
180
|
try {
|
|
144
|
-
|
|
181
|
+
// Use /oauth/callback/success as redirect URI with provider param for auto-enable
|
|
182
|
+
const redirectUri = `${window.location.origin}/oauth/callback/success?provider=${encodeURIComponent(provider)}`;
|
|
183
|
+
const { authorizeUrl } = await getAuthorizeUrl(provider, { redirectUri });
|
|
145
184
|
openOAuthWindow(authorizeUrl);
|
|
146
185
|
} catch (error) {
|
|
147
186
|
console.error('[LobehubSkill] Failed to get authorize URL:', error);
|
|
@@ -236,7 +275,8 @@ const LobehubSkillServerItem = memo<LobehubSkillServerItemProps>(({ provider, la
|
|
|
236
275
|
onClick={async (e) => {
|
|
237
276
|
e.stopPropagation();
|
|
238
277
|
try {
|
|
239
|
-
const
|
|
278
|
+
const redirectUri = `${window.location.origin}/oauth/callback/success?provider=${encodeURIComponent(provider)}`;
|
|
279
|
+
const { authorizeUrl } = await getAuthorizeUrl(provider, { redirectUri });
|
|
240
280
|
openOAuthWindow(authorizeUrl);
|
|
241
281
|
} catch (error) {
|
|
242
282
|
console.error('[LobehubSkill] Failed to get authorize URL:', error);
|
|
@@ -41,7 +41,7 @@ export class Search1APIImpl implements SearchServiceImpl {
|
|
|
41
41
|
const { searchEngines } = params;
|
|
42
42
|
|
|
43
43
|
const defaultQueryParams: Search1APIQueryParams = {
|
|
44
|
-
crawl_results: 0, //
|
|
44
|
+
crawl_results: 0, // Default is no crawling
|
|
45
45
|
image: false,
|
|
46
46
|
max_results: 15, // Default max results
|
|
47
47
|
query,
|
|
@@ -42,7 +42,7 @@ export class TavilyImpl implements SearchServiceImpl {
|
|
|
42
42
|
params?.searchTimeRange && params.searchTimeRange !== 'anytime'
|
|
43
43
|
? params.searchTimeRange
|
|
44
44
|
: undefined,
|
|
45
|
-
topic: // Tavily
|
|
45
|
+
topic: // Tavily only supports news and general types
|
|
46
46
|
params?.searchCategories?.filter((cat) => ['news', 'general'].includes(cat))?.[0],
|
|
47
47
|
};
|
|
48
48
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { act, renderHook } from '@testing-library/react';
|
|
2
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
1
|
+
import { act, renderHook, waitFor } from '@testing-library/react';
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
3
|
|
|
4
4
|
import { lambdaClient, toolsClient } from '@/libs/trpc/client';
|
|
5
5
|
|
|
@@ -8,6 +8,10 @@ import { KlavisServerStatus } from './types';
|
|
|
8
8
|
|
|
9
9
|
vi.mock('zustand/traditional');
|
|
10
10
|
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
vi.clearAllMocks();
|
|
13
|
+
});
|
|
14
|
+
|
|
11
15
|
vi.mock('@/libs/trpc/client', () => ({
|
|
12
16
|
lambdaClient: {
|
|
13
17
|
klavis: {
|
|
@@ -509,4 +513,165 @@ describe('klavisStore actions', () => {
|
|
|
509
513
|
expect(lambdaClient.klavis.getServerInstance.query).toHaveBeenCalled();
|
|
510
514
|
});
|
|
511
515
|
});
|
|
516
|
+
|
|
517
|
+
describe('useFetchUserKlavisServers', () => {
|
|
518
|
+
it('should set isServersInit to true on success with empty data', async () => {
|
|
519
|
+
act(() => {
|
|
520
|
+
useToolStore.setState({
|
|
521
|
+
servers: [],
|
|
522
|
+
loadingServerIds: new Set(),
|
|
523
|
+
executingToolIds: new Set(),
|
|
524
|
+
isServersInit: false,
|
|
525
|
+
});
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
vi.mocked(lambdaClient.klavis.getKlavisPlugins.query).mockResolvedValue([]);
|
|
529
|
+
|
|
530
|
+
renderHook(() => useToolStore.getState().useFetchUserKlavisServers(true));
|
|
531
|
+
|
|
532
|
+
await waitFor(() => {
|
|
533
|
+
expect(useToolStore.getState().isServersInit).toBe(true);
|
|
534
|
+
});
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
it('should not fetch when disabled', () => {
|
|
538
|
+
act(() => {
|
|
539
|
+
useToolStore.setState({
|
|
540
|
+
servers: [],
|
|
541
|
+
loadingServerIds: new Set(),
|
|
542
|
+
executingToolIds: new Set(),
|
|
543
|
+
isServersInit: false,
|
|
544
|
+
});
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
vi.mocked(lambdaClient.klavis.getKlavisPlugins.query).mockClear();
|
|
548
|
+
|
|
549
|
+
renderHook(() => useToolStore.getState().useFetchUserKlavisServers(false));
|
|
550
|
+
|
|
551
|
+
expect(lambdaClient.klavis.getKlavisPlugins.query).not.toHaveBeenCalled();
|
|
552
|
+
expect(useToolStore.getState().isServersInit).toBe(false);
|
|
553
|
+
});
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
describe('server deduplication logic', () => {
|
|
557
|
+
it('should deduplicate servers by identifier when adding new servers', () => {
|
|
558
|
+
// This tests the deduplication logic used in useFetchUserKlavisServers onSuccess
|
|
559
|
+
act(() => {
|
|
560
|
+
useToolStore.setState({
|
|
561
|
+
servers: [
|
|
562
|
+
{
|
|
563
|
+
identifier: 'gmail',
|
|
564
|
+
serverName: 'Gmail',
|
|
565
|
+
instanceId: 'existing-inst',
|
|
566
|
+
serverUrl: 'https://klavis.ai/gmail',
|
|
567
|
+
status: KlavisServerStatus.CONNECTED,
|
|
568
|
+
isAuthenticated: true,
|
|
569
|
+
createdAt: Date.now(),
|
|
570
|
+
},
|
|
571
|
+
],
|
|
572
|
+
loadingServerIds: new Set(),
|
|
573
|
+
executingToolIds: new Set(),
|
|
574
|
+
isServersInit: false,
|
|
575
|
+
});
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
// Simulate what onSuccess does
|
|
579
|
+
const incomingServers = [
|
|
580
|
+
{
|
|
581
|
+
identifier: 'gmail',
|
|
582
|
+
serverName: 'Gmail',
|
|
583
|
+
instanceId: 'new-inst',
|
|
584
|
+
serverUrl: 'https://klavis.ai/gmail',
|
|
585
|
+
status: KlavisServerStatus.CONNECTED,
|
|
586
|
+
isAuthenticated: true,
|
|
587
|
+
createdAt: Date.now(),
|
|
588
|
+
},
|
|
589
|
+
{
|
|
590
|
+
identifier: 'github',
|
|
591
|
+
serverName: 'GitHub',
|
|
592
|
+
instanceId: 'github-inst',
|
|
593
|
+
serverUrl: 'https://klavis.ai/github',
|
|
594
|
+
status: KlavisServerStatus.CONNECTED,
|
|
595
|
+
isAuthenticated: true,
|
|
596
|
+
createdAt: Date.now(),
|
|
597
|
+
},
|
|
598
|
+
];
|
|
599
|
+
|
|
600
|
+
act(() => {
|
|
601
|
+
const existingServers = useToolStore.getState().servers;
|
|
602
|
+
const existingIdentifiers = new Set(existingServers.map((s) => s.identifier));
|
|
603
|
+
const newServers = incomingServers.filter((s) => !existingIdentifiers.has(s.identifier));
|
|
604
|
+
|
|
605
|
+
useToolStore.setState({
|
|
606
|
+
servers: [...existingServers, ...newServers],
|
|
607
|
+
isServersInit: true,
|
|
608
|
+
});
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
const finalServers = useToolStore.getState().servers;
|
|
612
|
+
expect(finalServers).toHaveLength(2);
|
|
613
|
+
// Existing gmail should keep its original instanceId
|
|
614
|
+
expect(finalServers.find((s) => s.identifier === 'gmail')?.instanceId).toBe('existing-inst');
|
|
615
|
+
// New github should be added
|
|
616
|
+
expect(finalServers.find((s) => s.identifier === 'github')?.instanceId).toBe('github-inst');
|
|
617
|
+
expect(useToolStore.getState().isServersInit).toBe(true);
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
it('should add all servers when none exist', () => {
|
|
621
|
+
act(() => {
|
|
622
|
+
useToolStore.setState({
|
|
623
|
+
servers: [],
|
|
624
|
+
loadingServerIds: new Set(),
|
|
625
|
+
executingToolIds: new Set(),
|
|
626
|
+
isServersInit: false,
|
|
627
|
+
});
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
const incomingServers = [
|
|
631
|
+
{
|
|
632
|
+
identifier: 'gmail',
|
|
633
|
+
serverName: 'Gmail',
|
|
634
|
+
instanceId: 'inst-1',
|
|
635
|
+
serverUrl: 'https://klavis.ai/gmail',
|
|
636
|
+
status: KlavisServerStatus.CONNECTED,
|
|
637
|
+
isAuthenticated: true,
|
|
638
|
+
createdAt: Date.now(),
|
|
639
|
+
},
|
|
640
|
+
];
|
|
641
|
+
|
|
642
|
+
act(() => {
|
|
643
|
+
const existingServers = useToolStore.getState().servers;
|
|
644
|
+
const existingIdentifiers = new Set(existingServers.map((s) => s.identifier));
|
|
645
|
+
const newServers = incomingServers.filter((s) => !existingIdentifiers.has(s.identifier));
|
|
646
|
+
|
|
647
|
+
useToolStore.setState({
|
|
648
|
+
servers: [...existingServers, ...newServers],
|
|
649
|
+
isServersInit: true,
|
|
650
|
+
});
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
expect(useToolStore.getState().servers).toHaveLength(1);
|
|
654
|
+
expect(useToolStore.getState().isServersInit).toBe(true);
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
it('should set isServersInit even when no servers are added', () => {
|
|
658
|
+
act(() => {
|
|
659
|
+
useToolStore.setState({
|
|
660
|
+
servers: [],
|
|
661
|
+
loadingServerIds: new Set(),
|
|
662
|
+
executingToolIds: new Set(),
|
|
663
|
+
isServersInit: false,
|
|
664
|
+
});
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
// Simulate empty data case
|
|
668
|
+
act(() => {
|
|
669
|
+
useToolStore.setState({
|
|
670
|
+
isServersInit: true,
|
|
671
|
+
});
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
expect(useToolStore.getState().isServersInit).toBe(true);
|
|
675
|
+
});
|
|
676
|
+
});
|
|
512
677
|
});
|
|
@@ -356,18 +356,19 @@ export const createKlavisStoreSlice: StateCreator<
|
|
|
356
356
|
{
|
|
357
357
|
fallbackData: [],
|
|
358
358
|
onSuccess: (data) => {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
359
|
+
set(
|
|
360
|
+
produce((draft: KlavisStoreState) => {
|
|
361
|
+
if (data.length > 0) {
|
|
362
362
|
// 使用 identifier 检查是否已存在
|
|
363
363
|
const existingIdentifiers = new Set(draft.servers.map((s) => s.identifier));
|
|
364
364
|
const newServers = data.filter((s) => !existingIdentifiers.has(s.identifier));
|
|
365
365
|
draft.servers = [...draft.servers, ...newServers];
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
366
|
+
}
|
|
367
|
+
draft.isServersInit = true;
|
|
368
|
+
}),
|
|
369
|
+
false,
|
|
370
|
+
n('useFetchUserKlavisServers'),
|
|
371
|
+
);
|
|
371
372
|
},
|
|
372
373
|
revalidateOnFocus: false,
|
|
373
374
|
},
|
|
@@ -9,6 +9,8 @@ import { type KlavisServer } from './types';
|
|
|
9
9
|
export interface KlavisStoreState {
|
|
10
10
|
/** 正在执行的工具调用 ID 集合 */
|
|
11
11
|
executingToolIds: Set<string>;
|
|
12
|
+
/** 是否已完成初始化加载 */
|
|
13
|
+
isServersInit: boolean;
|
|
12
14
|
/** 正在加载的服务器 ID 集合 */
|
|
13
15
|
loadingServerIds: Set<string>;
|
|
14
16
|
/** 已创建的 Klavis Server 列表 */
|
|
@@ -20,6 +22,7 @@ export interface KlavisStoreState {
|
|
|
20
22
|
*/
|
|
21
23
|
export const initialKlavisStoreState: KlavisStoreState = {
|
|
22
24
|
executingToolIds: new Set(),
|
|
25
|
+
isServersInit: false,
|
|
23
26
|
loadingServerIds: new Set(),
|
|
24
27
|
servers: [],
|
|
25
28
|
};
|
|
@@ -41,6 +41,7 @@ const fetchAuthProvidersData = async (): Promise<AuthProvidersData> => {
|
|
|
41
41
|
accounts
|
|
42
42
|
.filter((account) => account.providerId !== 'credential')
|
|
43
43
|
.map(async (account) => {
|
|
44
|
+
// In theory, the id_token could be decrypted from the accounts table, but I found that better-auth on GitHub does not save the id_token
|
|
44
45
|
const info = await accountInfo({
|
|
45
46
|
query: { accountId: account.accountId },
|
|
46
47
|
});
|