diff --git a/README.md b/README.md index f97816d3f..1468b541d 100644 --- a/README.md +++ b/README.md @@ -317,13 +317,10 @@ For more information please contact info@eigent.ai - **X (Twitter):** Follow for updates, AI insights, and key announcements. [Follow us][social-x-link] -- **WeChat Community:** Scan the QR codes below to join our WeChat community groups. +- **WeChat Community:** Scan the QR code below to add our WeChat assistant, and join our WeChat community group.
- - - - +
diff --git a/README_CN.md b/README_CN.md index 73fc41dd9..4ebeb982b 100644 --- a/README_CN.md +++ b/README_CN.md @@ -310,13 +310,10 @@ Eigent 基于 [CAMEL-AI.org][camel-ai-org-github] 的研究和基础设施构建 - **X (Twitter):** 关注更新、AI 见解和重要公告。[关注我们][social-x-link] -- **微信社区:** 扫描下方二维码加入我们的微信社区群。 +- **微信社区:** 扫描下方二维码添加我们的微信助手,加入我们的微信社区群。
- - - - +
diff --git a/backend/app/utils/workforce.py b/backend/app/utils/workforce.py index 5eb77bcac..52cda36d4 100644 --- a/backend/app/utils/workforce.py +++ b/backend/app/utils/workforce.py @@ -94,7 +94,7 @@ class Workforce(BaseWorkforce): self._state = WorkforceState.RUNNING task.state = TaskState.OPEN - subtasks = asyncio.run(self.handle_decompose_append_task(task)) + subtasks = asyncio.run(self.handle_decompose_append_task(task, reset=False, coordinator_context=coordinator_context)) logger.info("Task decomposition completed", extra={ "api_task_id": self.api_task_id, "task_id": task.id, diff --git a/electron/preload/index.ts b/electron/preload/index.ts index 7ef37abf1..9e76c3827 100644 --- a/electron/preload/index.ts +++ b/electron/preload/index.ts @@ -40,7 +40,15 @@ contextBridge.exposeInMainWorld('electronAPI', { createWebView: (id: string, url: string) => ipcRenderer.invoke('create-webview', id, url), hideWebView: (id: string) => ipcRenderer.invoke('hide-webview', id), changeViewSize: (id: string, size: Size) => ipcRenderer.invoke('change-view-size', id, size), - onWebviewNavigated: (callback: (id: string, url: string) => void) => ipcRenderer.on('webview-navigated', (event, id, url) => callback(id, url)), + onWebviewNavigated: (callback: (id: string, url: string) => void) => { + const channel = 'webview-navigated' + const listener = (event: any, id: string, url: string) => callback(id, url); + ipcRenderer.on(channel, listener); + // Return cleanup function to remove listener + return () => { + ipcRenderer.off(channel, listener); + }; + }, showWebview: (id: string) => ipcRenderer.invoke('show-webview', id), getActiveWebview: () => ipcRenderer.invoke('get-active-webview'), setSize: (size: Size) => ipcRenderer.invoke('set-size', size), @@ -68,6 +76,7 @@ contextBridge.exposeInMainWorld('electronAPI', { checkAndInstallDepsOnUpdate: () => ipcRenderer.invoke('install-dependencies'), checkInstallBrowser: () => ipcRenderer.invoke('check-install-browser'), getInstallationStatus: () => ipcRenderer.invoke('get-installation-status'), + getBackendPort: () => ipcRenderer.invoke('get-backend-port'), restartBackend: () => ipcRenderer.invoke('restart-backend'), onInstallDependenciesStart: (callback: () => void) => { ipcRenderer.on('install-dependencies-start', callback); @@ -192,4 +201,4 @@ window.onmessage = (ev) => { ev.data.payload === 'removeLoading' && removeLoading() } -setTimeout(removeLoading, 4999) \ No newline at end of file +setTimeout(removeLoading, 4999) diff --git a/src/assets/wechat_qr.jpg b/src/assets/wechat_qr.jpg new file mode 100644 index 000000000..036a35834 Binary files /dev/null and b/src/assets/wechat_qr.jpg differ diff --git a/src/assets/wechat_qr_1.jpg b/src/assets/wechat_qr_1.jpg deleted file mode 100644 index ef5bb4477..000000000 Binary files a/src/assets/wechat_qr_1.jpg and /dev/null differ diff --git a/src/assets/wechat_qr_2.jpg b/src/assets/wechat_qr_2.jpg deleted file mode 100644 index 2a3b2a851..000000000 Binary files a/src/assets/wechat_qr_2.jpg and /dev/null differ diff --git a/src/assets/wechat_qr_3.jpg b/src/assets/wechat_qr_3.jpg deleted file mode 100644 index 1b1a2b0eb..000000000 Binary files a/src/assets/wechat_qr_3.jpg and /dev/null differ diff --git a/src/assets/wechat_qr_4.jpg b/src/assets/wechat_qr_4.jpg deleted file mode 100644 index 8ecb3b5ae..000000000 Binary files a/src/assets/wechat_qr_4.jpg and /dev/null differ diff --git a/src/components/ChatBox/MessageItem/MarkDown.tsx b/src/components/ChatBox/MessageItem/MarkDown.tsx index 22978a128..fc8b98c73 100644 --- a/src/components/ChatBox/MessageItem/MarkDown.tsx +++ b/src/components/ChatBox/MessageItem/MarkDown.tsx @@ -124,17 +124,25 @@ export const MarkDown = memo( em: ({ children }) => ( {children} ), - a: ({ children, href }) => ( - - {children} - - ), + a: ({ children, href }) => { + const cleanChildren = typeof children === 'string' + ? children.replace(/^[.,"'{}()\[\]]+|[.,"'{}()\[\]]+$/g, '') + : children; + const cleanHref = typeof href === 'string' + ? href.replace(/^[.,"'{}()\[\]]+|[.,"'{}()\[\]]+$/g, '').replace(/(%[0-9A-Fa-f]{2})+$/g, '') + : href; + return ( + + {cleanChildren} + + ); + }, table: ({ children }) => (
)} */} - {!isOngoing && hasIssue && ( + {/* {!isOngoing && hasIssue && ( - )} + )} */} )} */} - {!isOngoing && hasIssue && ( + {/* {!isOngoing && hasIssue && ( {t("layout.issue") || "Issue"} - )} - + )} */} + {/* Menu button */} diff --git a/src/components/WorkSpaceMenu/index.tsx b/src/components/WorkSpaceMenu/index.tsx index 9c3522926..c8d76ec92 100644 --- a/src/components/WorkSpaceMenu/index.tsx +++ b/src/components/WorkSpaceMenu/index.tsx @@ -80,7 +80,7 @@ export function WorkSpaceMenu() { ]); useEffect(() => { - window.electronAPI.onWebviewNavigated((id: string, url: string) => { + const cleanup = window.electronAPI.onWebviewNavigated((id: string, url: string) => { let webViewUrls = [ ...chatStore.tasks[chatStore.activeTaskId as string].webViewUrls, ]; @@ -99,7 +99,7 @@ export function WorkSpaceMenu() { const activeAgentIndex = taskAssigning.findIndex((item) => item.tasks.find((task) => task.id === hasUrl?.processTaskId) ); - + if (activeAgentIndex === -1) { const searchAgentIndex = taskAssigning.findIndex((item) => item.type === 'search_agent'); if (searchAgentIndex !== -1) { @@ -203,9 +203,13 @@ export function WorkSpaceMenu() { captureWebview(); }, 200); }); + + // Cleanup function to remove listener when component unmounts or dependencies change + return cleanup; }, [ chatStore.activeTaskId, chatStore.tasks[chatStore.activeTaskId as string]?.webViewUrls, + chatStore.tasks[chatStore.activeTaskId as string]?.taskAssigning, ]); const agentMap = { diff --git a/src/hooks/useInstallationSetup.ts b/src/hooks/useInstallationSetup.ts index b7bca2e3e..4654cd72f 100644 --- a/src/hooks/useInstallationSetup.ts +++ b/src/hooks/useInstallationSetup.ts @@ -1,4 +1,4 @@ -import { useEffect, useRef } from 'react'; +import { useEffect, useRef, useCallback } from 'react'; import { useInstallationStore } from '@/store/installationStore'; import { useAuthStore } from '@/store/authStore'; @@ -7,7 +7,7 @@ import { useAuthStore } from '@/store/authStore'; * This should be called once in your App component or Layout component */ export const useInstallationSetup = () => { - const { initState, setInitState } = useAuthStore(); + const { initState, setInitState, email } = useAuthStore(); const hasCheckedOnMount = useRef(false); const installationCompleted = useRef(false); @@ -19,6 +19,100 @@ export const useInstallationSetup = () => { const setError = useInstallationStore(state => state.setError); const setBackendError = useInstallationStore(state => state.setBackendError); const setWaitingBackend = useInstallationStore(state => state.setWaitingBackend); + const needsBackendRestart = useInstallationStore(state => state.needsBackendRestart); + const setNeedsBackendRestart = useInstallationStore(state => state.setNeedsBackendRestart); + + // Shared function to poll backend status + const startBackendPolling = useCallback(() => { + console.log('[useInstallationSetup] Starting backend polling'); + + // Immediately check backend status once + const checkBackendStatus = async () => { + try { + const backendPort = await window.electronAPI.getBackendPort(); + if (backendPort && backendPort > 0) { + console.log('[useInstallationSetup] Backend immediately detected on port:', backendPort); + + // Verify backend is actually responding + const response = await fetch(`http://localhost:${backendPort}/health`).catch(() => null); + if (response && response.ok) { + console.log('[useInstallationSetup] Backend health check passed immediately'); + backendReady.current = true; + setSuccess(); + setInitState('done'); + setNeedsBackendRestart(false); + return true; // Backend is ready, no need to poll + } + } + } catch (error) { + console.log('[useInstallationSetup] Initial backend check failed:', error); + } + return false; // Backend not ready, need to poll + }; + + // Check immediately, then start polling if needed + checkBackendStatus().then((isReady) => { + if (isReady) { + console.log('[useInstallationSetup] Backend already ready, skipping polling'); + return; + } + + console.log('[useInstallationSetup] Backend not ready, starting polling'); + + // Poll backend status every 2 seconds to ensure we catch when it's ready + // This is a fallback in case the backend-ready event is missed + const pollInterval = setInterval(async () => { + try { + const backendPort = await window.electronAPI.getBackendPort(); + if (backendPort && backendPort > 0) { + console.log('[useInstallationSetup] Backend poll detected ready on port:', backendPort); + + // Verify backend is actually responding + const response = await fetch(`http://localhost:${backendPort}/health`).catch(() => null); + if (response && response.ok) { + console.log('[useInstallationSetup] Backend health check passed'); + clearInterval(pollInterval); + + if (!backendReady.current) { + backendReady.current = true; + setSuccess(); + setInitState('done'); + // Clear the flag after backend is ready + setNeedsBackendRestart(false); + } + } + } + } catch (error) { + console.log('[useInstallationSetup] Backend poll check failed:', error); + } + }, 2000); + + // Clear polling after 30 seconds to prevent infinite polling + setTimeout(() => { + clearInterval(pollInterval); + }, 30000); + }); + }, [setSuccess, setInitState, setNeedsBackendRestart]); + + // Monitor for backend restart after logout + useEffect(() => { + // When user logs in after logout, needsBackendRestart will be true + if (needsBackendRestart && email !== null) { + console.log('[useInstallationSetup] Detected login after logout, waiting for backend restart'); + + // For account switching, tools are already installed, only backend needs restart + // So we mark installation as completed and only wait for backend + installationCompleted.current = true; + backendReady.current = false; + + // Set to waiting-backend state + setWaitingBackend(); + + // Start polling for backend + startBackendPolling(); + } + }, [needsBackendRestart, email, setWaitingBackend, startBackendPolling]); + useEffect(() => { if (hasCheckedOnMount.current) { @@ -36,6 +130,9 @@ export const useInstallationSetup = () => { console.log('[useInstallationSetup] Tools already installed, waiting for backend'); installationCompleted.current = true; setWaitingBackend(); + + // Start polling for backend when tools are already installed + startBackendPolling(); } if (initState !== 'done') { diff --git a/src/i18n/locales/ar/dashboard.json b/src/i18n/locales/ar/dashboard.json index 11e1a9cce..3f3c000e8 100644 --- a/src/i18n/locales/ar/dashboard.json +++ b/src/i18n/locales/ar/dashboard.json @@ -22,5 +22,6 @@ "search-agent": "وكيل البحث", "document-agent": "وكيل المستندات", "multi-modal-agent": "وكيل متعدد الوسائط", - "social-media-agent": "وكيل وسائل التواصل الاجتماعي" + "social-media-agent": "وكيل وسائل التواصل الاجتماعي", + "no-projects-found": "لا توجد مشاريع" } diff --git a/src/i18n/locales/de/dashboard.json b/src/i18n/locales/de/dashboard.json index dc2921bb5..365be9711 100644 --- a/src/i18n/locales/de/dashboard.json +++ b/src/i18n/locales/de/dashboard.json @@ -22,5 +22,6 @@ "search-agent": "Such-Agent", "document-agent": "Dokument-Agent", "multi-modal-agent": "Multi-Modal-Agent", - "social-media-agent": "Social-Media-Agent" + "social-media-agent": "Social-Media-Agent", + "no-projects-found": "Keine Projekte gefunden." } diff --git a/src/i18n/locales/en-us/dashboard.json b/src/i18n/locales/en-us/dashboard.json index cc9fd130a..9b2e6cd17 100644 --- a/src/i18n/locales/en-us/dashboard.json +++ b/src/i18n/locales/en-us/dashboard.json @@ -22,5 +22,6 @@ "search-agent": "Search Agent", "document-agent": "Document Agent", "multi-modal-agent": "Multi Modal Agent", - "social-media-agent": "Social Media Agent" + "social-media-agent": "Social Media Agent", + "no-projects-found": "No projects found." } diff --git a/src/i18n/locales/es/dashboard.json b/src/i18n/locales/es/dashboard.json index fee7a535a..77c301f61 100644 --- a/src/i18n/locales/es/dashboard.json +++ b/src/i18n/locales/es/dashboard.json @@ -22,5 +22,6 @@ "search-agent": "Agente de Búsqueda", "document-agent": "Agente de Documentos", "multi-modal-agent": "Agente Multi Modal", - "social-media-agent": "Agente de Redes Sociales" + "social-media-agent": "Agente de Redes Sociales", + "no-projects-found": "No se encontraron proyectos." } diff --git a/src/i18n/locales/fr/dashboard.json b/src/i18n/locales/fr/dashboard.json index 5b3d290f0..447e5d94e 100644 --- a/src/i18n/locales/fr/dashboard.json +++ b/src/i18n/locales/fr/dashboard.json @@ -22,5 +22,6 @@ "search-agent": "Agent de Recherche", "document-agent": "Agent de Documents", "multi-modal-agent": "Agent Multi Modal", - "social-media-agent": "Agent de Médias Sociaux" + "social-media-agent": "Agent de Médias Sociaux", + "no-projects-found": "Aucun projet trouvé." } diff --git a/src/i18n/locales/it/dashboard.json b/src/i18n/locales/it/dashboard.json index f2392c27b..73b0fff5b 100644 --- a/src/i18n/locales/it/dashboard.json +++ b/src/i18n/locales/it/dashboard.json @@ -22,5 +22,6 @@ "search-agent": "Agente di Ricerca", "document-agent": "Agente Documenti", "multi-modal-agent": "Agente Multi Modale", - "social-media-agent": "Agente Social Media" + "social-media-agent": "Agente Social Media", + "no-projects-found": "Nessun progetto trovato." } diff --git a/src/i18n/locales/ja/dashboard.json b/src/i18n/locales/ja/dashboard.json index 587ab3a39..aba32f28b 100644 --- a/src/i18n/locales/ja/dashboard.json +++ b/src/i18n/locales/ja/dashboard.json @@ -22,5 +22,6 @@ "search-agent": "検索エージェント", "document-agent": "ドキュメントエージェント", "multi-modal-agent": "マルチモーダルエージェント", - "social-media-agent": "ソーシャルメディアエージェント" + "social-media-agent": "ソーシャルメディアエージェント", + "no-projects-found": "プロジェクトが見つかりませんでした。" } diff --git a/src/i18n/locales/ko/dashboard.json b/src/i18n/locales/ko/dashboard.json index 1645b6ccf..05092ff6d 100644 --- a/src/i18n/locales/ko/dashboard.json +++ b/src/i18n/locales/ko/dashboard.json @@ -22,5 +22,6 @@ "search-agent": "검색 에이전트", "document-agent": "문서 에이전트", "multi-modal-agent": "멀티모달 에이전트", - "social-media-agent": "소셜미디어 에이전트" + "social-media-agent": "소셜미디어 에이전트", + "no-projects-found": "프로젝트를 찾을 수 없습니다." } diff --git a/src/i18n/locales/ru/dashboard.json b/src/i18n/locales/ru/dashboard.json index 1ea357ca5..af8730d56 100644 --- a/src/i18n/locales/ru/dashboard.json +++ b/src/i18n/locales/ru/dashboard.json @@ -22,5 +22,6 @@ "search-agent": "Агент поиска", "document-agent": "Агент документов", "multi-modal-agent": "Мультимодальный агент", - "social-media-agent": "Агент социальных сетей" + "social-media-agent": "Агент социальных сетей", + "no-projects-found": "Проекты не найдены" } diff --git a/src/i18n/locales/zh-Hans/dashboard.json b/src/i18n/locales/zh-Hans/dashboard.json index 7b99594ac..8ab137037 100644 --- a/src/i18n/locales/zh-Hans/dashboard.json +++ b/src/i18n/locales/zh-Hans/dashboard.json @@ -22,5 +22,6 @@ "search-agent": "搜索智能体", "document-agent": "文档智能体", "multi-modal-agent": "多模态智能体", - "social-media-agent": "社交媒体智能体" + "social-media-agent": "社交媒体智能体", + "no-projects-found": "没有找到项目" } diff --git a/src/i18n/locales/zh-Hant/dashboard.json b/src/i18n/locales/zh-Hant/dashboard.json index 3e63d8d0e..a29288a7f 100644 --- a/src/i18n/locales/zh-Hant/dashboard.json +++ b/src/i18n/locales/zh-Hant/dashboard.json @@ -22,5 +22,6 @@ "search-agent": "搜尋智能體", "document-agent": "文件智能體", "multi-modal-agent": "多模態智能體", - "social-media-agent": "社群媒體智能體" + "social-media-agent": "社群媒體智能體", + "no-projects-found": "沒有找到專案" } diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx index 3bd0e87f1..1a04220b3 100644 --- a/src/pages/Login.tsx +++ b/src/pages/Login.tsx @@ -20,7 +20,7 @@ const HAS_STACK_KEYS = hasStackKeys(); let lock = false; export default function Login() { const app = HAS_STACK_KEYS ? useStackApp() : null; - const { setAuth,setModelType } = useAuthStore(); + const { setAuth, setModelType, setLocalProxyValue } = useAuthStore(); const navigate = useNavigate(); const location = useLocation(); const [hidePassword, setHidePassword] = useState(true); @@ -103,7 +103,10 @@ export default function Login() { } setAuth({ email: formData.email, ...data }); - setModelType('cloud') + setModelType('cloud'); + // Record VITE_USE_LOCAL_PROXY value at login + const localProxyValue = import.meta.env.VITE_USE_LOCAL_PROXY || null; + setLocalProxyValue(localProxyValue); navigate("/"); } catch (error: any) { console.error("Login failed:", error); @@ -124,8 +127,11 @@ export default function Login() { return; } console.log("data", data); - setModelType('cloud') + setModelType('cloud'); setAuth({ email: formData.email, ...data }); + // Record VITE_USE_LOCAL_PROXY value at login + const localProxyValue = import.meta.env.VITE_USE_LOCAL_PROXY || null; + setLocalProxyValue(localProxyValue); navigate("/"); } catch (error: any) { console.error("Login failed:", error); diff --git a/src/pages/Setting/General.tsx b/src/pages/Setting/General.tsx index 85fc89477..5afda8cb2 100644 --- a/src/pages/Setting/General.tsx +++ b/src/pages/Setting/General.tsx @@ -6,6 +6,7 @@ import light from "@/assets/light.png"; import dark from "@/assets/dark.png"; import transparent from "@/assets/transparent.png"; import { useAuthStore } from "@/store/authStore"; +import { useInstallationStore } from "@/store/installationStore"; import { useNavigate } from "react-router-dom"; import { proxyFetchPut, proxyFetchGet } from "@/api/http"; import { createRef, RefObject } from "react"; @@ -29,6 +30,10 @@ import useChatStoreAdapter from "@/hooks/useChatStoreAdapter"; export default function SettingGeneral() { const { t } = useTranslation(); const authStore = useAuthStore(); + + const resetInstallation = useInstallationStore(state => state.reset); + const setNeedsBackendRestart = useInstallationStore(state => state.setNeedsBackendRestart); + const navigate = useNavigate(); const [isLoading, setIsLoading] = useState(false); const setAppearance = authStore.setAppearance; @@ -159,6 +164,10 @@ export default function SettingGeneral() { size="xs" onClick={() => { chatStore.clearTasks(); + + resetInstallation(); // Reset installation state for new account + setNeedsBackendRestart(true); // Mark that backend is restarting + authStore.logout(); navigate("/login"); }} diff --git a/src/pages/Setting/Models.tsx b/src/pages/Setting/Models.tsx index 1fa2a1c79..0a9f58113 100644 --- a/src/pages/Setting/Models.tsx +++ b/src/pages/Setting/Models.tsx @@ -45,6 +45,12 @@ export default function SettingModels() { useAuthStore(); const navigate = useNavigate(); const { t } = useTranslation(); + const getValidateMessage = (res: any) => + res?.message ?? + res?.detail?.message ?? + res?.detail?.error?.message ?? + res?.error?.message ?? + t("setting.validate-failed"); const [items, setItems] = useState( INIT_PROVODERS.filter((p) => p.id !== "local") ); @@ -226,7 +232,7 @@ const [errors, setErrors] = useState< setErrors((prev) => { const next = [...prev]; if (!next[idx]) next[idx] = {} as any; - next[idx].apiKey = res?.message || t("setting.validate-failed"); + next[idx].apiKey = getValidateMessage(res); return next; }); return; @@ -238,9 +244,10 @@ const [errors, setErrors] = useState< setErrors((prev) => { const next = [...prev]; if (!next[idx]) next[idx] = {} as any; - next[idx].apiKey = t("setting.validate-failed"); + next[idx].apiKey = getValidateMessage(e); return next; }); + return; } finally { setLoading(null); } @@ -369,7 +376,7 @@ const [errors, setErrors] = useState< } else { console.log("failed", res.message); const toastId = toast(t("setting.validate-failed"), { - description: res.message, + description: getValidateMessage(res), action: { label: t("setting.close"), onClick: () => { @@ -383,6 +390,16 @@ const [errors, setErrors] = useState< console.log(res); } catch (e) { console.log(e); + const toastId = toast(t("setting.validate-failed"), { + description: getValidateMessage(e), + action: { + label: t("setting.close"), + onClick: () => { + toast.dismiss(toastId); + }, + }, + }); + return; } finally { setLoading(null); } @@ -803,7 +820,7 @@ const [errors, setErrors] = useState< size="sm" className="focus-none" disabled={!canSwitch || loading === idx} - onClick={() => handleVerify(idx)} + onClick={() => handleSwitch(idx, false)} > Default @@ -813,7 +830,7 @@ const [errors, setErrors] = useState< variant="ghost" size="sm" disabled={!canSwitch || loading === idx} - onClick={() => handleVerify(idx)} + onClick={() => handleSwitch(idx, true)} className={canSwitch ? "!text-text-label" : ""} > {!canSwitch ? "Not Configured" : "Set as Default"} diff --git a/src/routers/index.tsx b/src/routers/index.tsx index 193766bd5..223d0a52e 100644 --- a/src/routers/index.tsx +++ b/src/routers/index.tsx @@ -16,12 +16,28 @@ const ProtectedRoute = () => { const [isAuthenticated, setIsAuthenticated] = useState(false); const [initialized, setInitialized] = useState(false); - const authStore = useAuthStore(); + const { token, localProxyValue, logout } = useAuthStore(); useEffect(() => { - setIsAuthenticated(!!authStore.token); + // Check VITE_USE_LOCAL_PROXY value on app startup + if (token) { + const currentProxyValue = import.meta.env.VITE_USE_LOCAL_PROXY || null; + const storedProxyValue = localProxyValue; + + // If stored value exists and differs from current, logout + if (storedProxyValue !== null && storedProxyValue !== currentProxyValue) { + console.warn('VITE_USE_LOCAL_PROXY value changed, logging out user'); + logout(); + setIsAuthenticated(false); + setLoading(false); + setInitialized(true); + return; + } + } + + setIsAuthenticated(!!token); setLoading(false); setInitialized(true); - }, [authStore.token]); + }, [token, localProxyValue, logout]); if (loading || !initialized) { return ( diff --git a/src/store/authStore.ts b/src/store/authStore.ts index aa7d3ff57..3bdf08f5d 100644 --- a/src/store/authStore.ts +++ b/src/store/authStore.ts @@ -33,12 +33,16 @@ interface AuthState { // shared token share_token?: string | null; + // local proxy value recorded at login + localProxyValue?: string | null; + // worker list data workerListData: { [key: string]: Agent[] }; - // auth related methods - setAuth: (auth: AuthInfo) => void; - logout: () => void; + // auth related methods + setAuth: (auth: AuthInfo) => void; + logout: () => void; + setLocalProxyValue: (value: string | null) => void; // set related methods setAppearance: (appearance: string) => void; @@ -69,18 +73,21 @@ const authStore = create()( cloud_model_type: 'gpt-4.1', initState: 'permissions', share_token: null, + localProxyValue: null, workerListData: {}, // auth related methods setAuth: ({ token, username, email, user_id }) => set({ token, username, email, user_id }), - - logout: () => - set({ - token: null, - username: null, - email: null, - user_id: null + + logout: () => + set({ + token: null, + username: null, + email: null, + user_id: null, + initState: 'carousel', + localProxyValue: null }), // set related methods @@ -99,6 +106,8 @@ const authStore = create()( setIsFirstLaunch: (isFirstLaunch) => set({ isFirstLaunch }), + setLocalProxyValue: (value) => set({ localProxyValue: value }), + // worker related methods setWorkerList: (workerList) => { const { email } = get(); @@ -152,6 +161,7 @@ const authStore = create()( cloud_model_type: state.cloud_model_type, initState: state.initState, isFirstLaunch: state.isFirstLaunch, + localProxyValue: state.localProxyValue, workerListData: state.workerListData, }), } diff --git a/src/store/chatStore.ts b/src/store/chatStore.ts index 83fd79a8e..a26bc3a78 100644 --- a/src/store/chatStore.ts +++ b/src/store/chatStore.ts @@ -534,7 +534,7 @@ const chatStore = (initial?: Partial) => createStore()( api_key: apiModel.api_key, api_url: apiModel.api_url, extra_params: apiModel.extra_params, - installed_mcp: mcpLocal, + installed_mcp: { mcpServers: {} }, language: systemLanguage, allow_local_system: true, attaches: (messageAttaches || targetChatStore.getState().tasks[newTaskId]?.attaches || []).map(f => f.filePath), diff --git a/src/store/installationStore.ts b/src/store/installationStore.ts index d5e9e9d15..4015add41 100644 --- a/src/store/installationStore.ts +++ b/src/store/installationStore.ts @@ -27,6 +27,7 @@ interface InstallationStoreState { error?: string; backendError?: string; // Separate error for backend startup failures isVisible: boolean; + needsBackendRestart: boolean; // Flag to indicate backend is restarting after logout // Actions startInstallation: () => void; @@ -35,6 +36,7 @@ interface InstallationStoreState { setError: (error: string) => void; setBackendError: (error: string) => void; setWaitingBackend: () => void; + setNeedsBackendRestart: (needs: boolean) => void; retryInstallation: () => void; retryBackend: () => Promise; completeSetup: () => void; @@ -55,6 +57,7 @@ const initialState = { error: undefined, backendError: undefined, isVisible: false, + needsBackendRestart: false, }; // Create the installation store @@ -110,6 +113,11 @@ export const useInstallationStore = create()( isVisible: true, }), + setNeedsBackendRestart: (needs: boolean) => + set({ + needsBackendRestart: needs, + }), + setBackendError: (error: string) => set({ backendError: error, diff --git a/src/types/electron.d.ts b/src/types/electron.d.ts index 116a06a8f..56629f138 100644 --- a/src/types/electron.d.ts +++ b/src/types/electron.d.ts @@ -29,7 +29,7 @@ interface ElectronAPI { createWebView: (id: string, url: string) => Promise; hideWebView: (id: string) => Promise; changeViewSize: (id: string, size: any) => Promise; - onWebviewNavigated: (callback: (id: string, url: string) => void) => void; + onWebviewNavigated: (callback: (id: string, url: string) => void) => () => void; showWebview: (id: string) => Promise; getActiveWebview: () => Promise; setSize: (size: any) => Promise; @@ -44,7 +44,13 @@ interface ElectronAPI { envWrite: (email: string, kv: { key: string, value: string }) => Promise; envRemove: (email: string, key: string) => Promise; getEnvPath: (email: string) => Promise; - executeCommand: (command: string,email:string) => Promise<{ success: boolean; stdout?: string; stderr?: string; error?: string }>; + executeCommand: (command: string, email: string) => Promise<{ success: boolean; stdout?: string; stderr?: string; error?: string }>; + readFile: (filePath: string) => Promise; + readFileAsDataUrl: (path: string) => Promise; + deleteFolder: (email: string) => Promise; + getMcpConfigPath: (email: string) => Promise; + uploadLog: (email: string, taskId: string, baseUrl: string, token: string) => Promise; + startBrowserImport: (args?: any) => Promise; checkAndInstallDepsOnUpdate: () => Promise<{ success: boolean; error?: string }>; checkInstallBrowser: () => Promise<{ data:any[] }>; getInstallationStatus: () => Promise<{ @@ -55,16 +61,18 @@ interface ElectronAPI { timestamp?: number; error?: string }>; + getBackendPort: () => Promise; restartBackend: () => Promise<{ success: boolean; error?: string }>; onInstallDependenciesStart: (callback: () => void) => void; onInstallDependenciesLog: (callback: (data: { type: string; data: string }) => void) => void; onInstallDependenciesComplete: (callback: (data: { success: boolean; code?: number; error?: string }) => void) => void; - onUpdateNotification: (callback: (data: { - type: string; - currentVersion: string; - previousVersion: string; - reason: string; + onUpdateNotification: (callback: (data: { + type: string; + currentVersion: string; + previousVersion: string; + reason: string; }) => void) => void; + onBackendReady: (callback: (data: { success: boolean; port?: number; error?: string }) => void) => void; removeAllListeners: (channel: string) => void; getEmailFolderPath: (email: string) => Promise<{ MCP_REMOTE_CONFIG_DIR: string;