@lobehub/lobehub 2.0.0-next.304 → 2.0.0-next.305
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/.github/workflows/manual-build-desktop.yml +11 -1
- package/CHANGELOG.md +25 -0
- package/apps/desktop/.i18nrc.js +3 -3
- package/apps/desktop/electron.vite.config.ts +0 -2
- package/apps/desktop/resources/locales/ar/dialog.json +5 -1
- package/apps/desktop/resources/locales/ar/menu.json +16 -0
- package/apps/desktop/resources/locales/bg-BG/dialog.json +5 -1
- package/apps/desktop/resources/locales/bg-BG/menu.json +16 -0
- package/apps/desktop/resources/locales/de-DE/dialog.json +5 -1
- package/apps/desktop/resources/locales/de-DE/menu.json +16 -0
- package/apps/desktop/resources/locales/en/common.json +26 -0
- package/apps/desktop/resources/locales/en/dialog.json +27 -0
- package/apps/desktop/resources/locales/en/menu.json +73 -0
- package/apps/desktop/resources/locales/es-ES/dialog.json +5 -1
- package/apps/desktop/resources/locales/es-ES/menu.json +16 -0
- package/apps/desktop/resources/locales/fa-IR/dialog.json +5 -1
- package/apps/desktop/resources/locales/fa-IR/menu.json +16 -0
- package/apps/desktop/resources/locales/fr-FR/dialog.json +5 -1
- package/apps/desktop/resources/locales/fr-FR/menu.json +16 -0
- package/apps/desktop/resources/locales/it-IT/dialog.json +5 -1
- package/apps/desktop/resources/locales/it-IT/menu.json +16 -0
- package/apps/desktop/resources/locales/ja-JP/dialog.json +5 -1
- package/apps/desktop/resources/locales/ja-JP/menu.json +16 -0
- package/apps/desktop/resources/locales/ko-KR/dialog.json +5 -1
- package/apps/desktop/resources/locales/ko-KR/menu.json +16 -0
- package/apps/desktop/resources/locales/nl-NL/dialog.json +5 -1
- package/apps/desktop/resources/locales/nl-NL/menu.json +16 -0
- package/apps/desktop/resources/locales/pl-PL/dialog.json +5 -1
- package/apps/desktop/resources/locales/pl-PL/menu.json +16 -0
- package/apps/desktop/resources/locales/pt-BR/dialog.json +5 -1
- package/apps/desktop/resources/locales/pt-BR/menu.json +16 -0
- package/apps/desktop/resources/locales/ru-RU/dialog.json +5 -1
- package/apps/desktop/resources/locales/ru-RU/menu.json +16 -0
- package/apps/desktop/resources/locales/tr-TR/dialog.json +5 -1
- package/apps/desktop/resources/locales/tr-TR/menu.json +16 -0
- package/apps/desktop/resources/locales/vi-VN/dialog.json +5 -1
- package/apps/desktop/resources/locales/vi-VN/menu.json +16 -0
- package/apps/desktop/resources/locales/zh-TW/dialog.json +5 -1
- package/apps/desktop/resources/locales/zh-TW/menu.json +16 -0
- package/apps/desktop/scripts/update-test/README.md +15 -0
- package/apps/desktop/src/main/core/infrastructure/BackendProxyProtocolManager.ts +7 -6
- package/apps/desktop/src/main/core/infrastructure/UpdaterManager.ts +38 -5
- package/apps/desktop/src/main/utils/logger.ts +2 -2
- package/changelog/v1.json +5 -0
- package/locales/en-US/auth.json +5 -0
- package/locales/zh-CN/auth.json +5 -0
- package/package.json +6 -5
- package/packages/builtin-tool-agent-builder/src/ExecutionRuntime/index.ts +362 -30
- package/packages/builtin-tool-agent-builder/src/client/Intervention/InstallPlugin.tsx +28 -4
- package/scripts/electronWorkflow/buildDesktopChannel.ts +135 -0
- package/src/app/[variants]/(main)/_layout/index.tsx +2 -0
- package/src/features/DesktopNavigationBridge/index.tsx +0 -9
- package/src/features/Electron/AuthRequiredModal/index.tsx +151 -0
- package/src/locales/default/auth.ts +6 -0
- package/src/utils/errorResponse.ts +21 -1
|
@@ -4,8 +4,6 @@ import { useWatchBroadcast } from '@lobechat/electron-client-ipc';
|
|
|
4
4
|
import { memo, useCallback } from 'react';
|
|
5
5
|
import { useNavigate } from 'react-router-dom';
|
|
6
6
|
|
|
7
|
-
import { clearDesktopOnboardingCompleted } from '@/app/[variants]/(desktop)/desktop-onboarding/storage';
|
|
8
|
-
|
|
9
7
|
const DesktopNavigationBridge = memo(() => {
|
|
10
8
|
const navigate = useNavigate();
|
|
11
9
|
|
|
@@ -19,13 +17,6 @@ const DesktopNavigationBridge = memo(() => {
|
|
|
19
17
|
|
|
20
18
|
useWatchBroadcast('navigate', handleNavigate);
|
|
21
19
|
|
|
22
|
-
const handleAuthorizationRequired = useCallback(() => {
|
|
23
|
-
clearDesktopOnboardingCompleted();
|
|
24
|
-
navigate('/desktop-onboarding#5', { replace: true });
|
|
25
|
-
}, [navigate]);
|
|
26
|
-
|
|
27
|
-
useWatchBroadcast('authorizationRequired', handleAuthorizationRequired);
|
|
28
|
-
|
|
29
20
|
return null;
|
|
30
21
|
});
|
|
31
22
|
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useWatchBroadcast } from '@lobechat/electron-client-ipc';
|
|
4
|
+
import { Button, Flexbox, Icon, type ModalInstance, createModal } from '@lobehub/ui';
|
|
5
|
+
import { AlertCircle, LogIn } from 'lucide-react';
|
|
6
|
+
import { type ReactNode, memo, useCallback, useRef, useState } from 'react';
|
|
7
|
+
import { useTranslation } from 'react-i18next';
|
|
8
|
+
|
|
9
|
+
import { useElectronStore } from '@/store/electron';
|
|
10
|
+
|
|
11
|
+
interface ModalUpdateOptions {
|
|
12
|
+
closable?: boolean;
|
|
13
|
+
keyboard?: boolean;
|
|
14
|
+
maskClosable?: boolean;
|
|
15
|
+
title?: ReactNode;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface AuthRequiredModalContentProps {
|
|
19
|
+
onClose: () => void;
|
|
20
|
+
setModalProps: (props: ModalUpdateOptions) => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const AuthRequiredModalContent = memo<AuthRequiredModalContentProps>(
|
|
24
|
+
({ onClose, setModalProps }) => {
|
|
25
|
+
const { t } = useTranslation('auth');
|
|
26
|
+
const [isSigningIn, setIsSigningIn] = useState(false);
|
|
27
|
+
const isClosingRef = useRef(false);
|
|
28
|
+
|
|
29
|
+
const [dataSyncConfig, connectRemoteServer, refreshServerConfig, clearRemoteServerSyncError] =
|
|
30
|
+
useElectronStore((s) => [
|
|
31
|
+
s.dataSyncConfig,
|
|
32
|
+
s.connectRemoteServer,
|
|
33
|
+
s.refreshServerConfig,
|
|
34
|
+
s.clearRemoteServerSyncError,
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
// Update modal props based on signing in state
|
|
38
|
+
setModalProps({
|
|
39
|
+
closable: !isSigningIn,
|
|
40
|
+
keyboard: !isSigningIn,
|
|
41
|
+
maskClosable: !isSigningIn,
|
|
42
|
+
title: (
|
|
43
|
+
<Flexbox align="center" gap={8} horizontal>
|
|
44
|
+
<Icon icon={AlertCircle} />
|
|
45
|
+
{t('authModal.title')}
|
|
46
|
+
</Flexbox>
|
|
47
|
+
),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Listen for successful authorization to close the modal
|
|
51
|
+
useWatchBroadcast('authorizationSuccessful', async () => {
|
|
52
|
+
if (isClosingRef.current) return;
|
|
53
|
+
isClosingRef.current = true;
|
|
54
|
+
setIsSigningIn(false);
|
|
55
|
+
onClose();
|
|
56
|
+
await refreshServerConfig();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Listen for authorization failure
|
|
60
|
+
useWatchBroadcast('authorizationFailed', () => {
|
|
61
|
+
setIsSigningIn(false);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const handleSignIn = useCallback(async () => {
|
|
65
|
+
setIsSigningIn(true);
|
|
66
|
+
clearRemoteServerSyncError();
|
|
67
|
+
|
|
68
|
+
await connectRemoteServer({
|
|
69
|
+
remoteServerUrl: dataSyncConfig?.remoteServerUrl,
|
|
70
|
+
storageMode: dataSyncConfig?.storageMode || 'cloud',
|
|
71
|
+
});
|
|
72
|
+
}, [clearRemoteServerSyncError, connectRemoteServer, dataSyncConfig]);
|
|
73
|
+
|
|
74
|
+
const handleLater = useCallback(() => {
|
|
75
|
+
if (isClosingRef.current) return;
|
|
76
|
+
isClosingRef.current = true;
|
|
77
|
+
onClose();
|
|
78
|
+
}, [onClose]);
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<Flexbox gap={16} style={{ padding: 16 }}>
|
|
82
|
+
<p style={{ margin: 0 }}>{t('authModal.description')}</p>
|
|
83
|
+
<Flexbox gap={8} horizontal justify="flex-end">
|
|
84
|
+
<Button disabled={isSigningIn} onClick={handleLater}>
|
|
85
|
+
{t('authModal.later')}
|
|
86
|
+
</Button>
|
|
87
|
+
<Button
|
|
88
|
+
icon={<Icon icon={LogIn} />}
|
|
89
|
+
loading={isSigningIn}
|
|
90
|
+
onClick={handleSignIn}
|
|
91
|
+
type="primary"
|
|
92
|
+
>
|
|
93
|
+
{isSigningIn ? t('authModal.signingIn') : t('authModal.signIn')}
|
|
94
|
+
</Button>
|
|
95
|
+
</Flexbox>
|
|
96
|
+
</Flexbox>
|
|
97
|
+
);
|
|
98
|
+
},
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
AuthRequiredModalContent.displayName = 'AuthRequiredModalContent';
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Hook to create and manage the auth required modal
|
|
105
|
+
*/
|
|
106
|
+
export const useAuthRequiredModal = () => {
|
|
107
|
+
const instanceRef = useRef<ModalInstance | null>(null);
|
|
108
|
+
|
|
109
|
+
const open = useCallback(() => {
|
|
110
|
+
// Don't open multiple modals
|
|
111
|
+
if (instanceRef.current) return;
|
|
112
|
+
|
|
113
|
+
const setModalProps = (nextProps: ModalUpdateOptions) => {
|
|
114
|
+
instanceRef.current?.update?.(nextProps);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const handleClose = () => {
|
|
118
|
+
instanceRef.current?.close();
|
|
119
|
+
instanceRef.current = null;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
instanceRef.current = createModal({
|
|
123
|
+
children: <AuthRequiredModalContent onClose={handleClose} setModalProps={setModalProps} />,
|
|
124
|
+
closable: false,
|
|
125
|
+
footer: null,
|
|
126
|
+
keyboard: false,
|
|
127
|
+
maskClosable: false,
|
|
128
|
+
title: '',
|
|
129
|
+
});
|
|
130
|
+
}, []);
|
|
131
|
+
|
|
132
|
+
return { open };
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Component that listens for authorizationRequired IPC events and opens the modal
|
|
137
|
+
*/
|
|
138
|
+
const AuthRequiredModal = memo(() => {
|
|
139
|
+
const { open } = useAuthRequiredModal();
|
|
140
|
+
|
|
141
|
+
// Listen for IPC event to open the modal
|
|
142
|
+
useWatchBroadcast('authorizationRequired', () => {
|
|
143
|
+
open();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
return null;
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
AuthRequiredModal.displayName = 'AuthRequiredModal';
|
|
150
|
+
|
|
151
|
+
export default AuthRequiredModal;
|
|
@@ -28,6 +28,12 @@ export default {
|
|
|
28
28
|
'apikey.list.columns.status': 'Enabled Status',
|
|
29
29
|
'apikey.list.title': 'API Key List',
|
|
30
30
|
'apikey.validation.required': 'This field cannot be empty',
|
|
31
|
+
'authModal.description':
|
|
32
|
+
'Your login session has expired. Please sign in again to continue using cloud sync features.',
|
|
33
|
+
'authModal.later': 'Later',
|
|
34
|
+
'authModal.signIn': 'Sign In Again',
|
|
35
|
+
'authModal.signingIn': 'Signing in...',
|
|
36
|
+
'authModal.title': 'Session Expired',
|
|
31
37
|
'betterAuth.errors.confirmPasswordRequired': 'Please confirm your password',
|
|
32
38
|
'betterAuth.errors.emailExists': 'This email is already registered. Please sign in instead',
|
|
33
39
|
'betterAuth.errors.emailInvalid': 'Please enter a valid email address or username',
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import { AgentRuntimeErrorType, type ILobeAgentRuntimeErrorType } from '@lobechat/model-runtime';
|
|
2
2
|
import { ChatErrorType, type ErrorResponse, type ErrorType } from '@lobechat/types';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Error types that indicate a real authentication failure.
|
|
6
|
+
* When these errors occur, the response will include X-Auth-Required header
|
|
7
|
+
* to signal the client that re-authentication is needed.
|
|
8
|
+
*/
|
|
9
|
+
const AUTH_REQUIRED_ERROR_TYPES = new Set<ErrorType>([
|
|
10
|
+
ChatErrorType.Unauthorized,
|
|
11
|
+
ChatErrorType.InvalidClerkUser,
|
|
12
|
+
]);
|
|
13
|
+
|
|
4
14
|
const getStatus = (errorType: ILobeAgentRuntimeErrorType | ErrorType) => {
|
|
5
15
|
// InvalidAccessCode / InvalidAzureAPIKey / InvalidOpenAIAPIKey / InvalidZhipuAPIKey ....
|
|
6
16
|
if (errorType.toString().includes('Invalid')) return 401;
|
|
@@ -71,5 +81,15 @@ export const createErrorResponse = (
|
|
|
71
81
|
);
|
|
72
82
|
}
|
|
73
83
|
|
|
74
|
-
|
|
84
|
+
const headers: Record<string, string> = {
|
|
85
|
+
'Content-Type': 'application/json',
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// Add X-Auth-Required header for real authentication failures
|
|
89
|
+
// This allows the client to distinguish between auth failures and other 401 errors (e.g., invalid API keys)
|
|
90
|
+
if (AUTH_REQUIRED_ERROR_TYPES.has(errorType as ErrorType)) {
|
|
91
|
+
headers['X-Auth-Required'] = 'true';
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return new Response(JSON.stringify(data), { headers, status: statusCode });
|
|
75
95
|
};
|