mirror of
https://github.com/eigent-ai/eigent.git
synced 2026-05-20 17:53:54 +00:00
Merge branch 'main' into revert-chat-delete
This commit is contained in:
commit
9edd0c2b4f
32 changed files with 270 additions and 73 deletions
|
|
@ -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.
|
||||
|
||||
<div align="center">
|
||||
<img src="./src/assets/wechat_qr_1.jpg" width="200" style="display: inline-block; margin: 10px;">
|
||||
<img src="./src/assets/wechat_qr_2.jpg" width="200" style="display: inline-block; margin: 10px;">
|
||||
<img src="./src/assets/wechat_qr_3.jpg" width="200" style="display: inline-block; margin: 10px;">
|
||||
<img src="./src/assets/wechat_qr_4.jpg" width="200" style="display: inline-block; margin: 10px;">
|
||||
<img src="./src/assets/wechat_qr.jpg" width="200" style="display: inline-block; margin: 10px;">
|
||||
</div>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -310,13 +310,10 @@ Eigent 基于 [CAMEL-AI.org][camel-ai-org-github] 的研究和基础设施构建
|
|||
|
||||
- **X (Twitter):** 关注更新、AI 见解和重要公告。[关注我们][social-x-link]
|
||||
|
||||
- **微信社区:** 扫描下方二维码加入我们的微信社区群。
|
||||
- **微信社区:** 扫描下方二维码添加我们的微信助手,加入我们的微信社区群。
|
||||
|
||||
<div align="center">
|
||||
<img src="./src/assets/wechat_qr_1.jpg" width="200" style="display: inline-block; margin: 10px;">
|
||||
<img src="./src/assets/wechat_qr_2.jpg" width="200" style="display: inline-block; margin: 10px;">
|
||||
<img src="./src/assets/wechat_qr_3.jpg" width="200" style="display: inline-block; margin: 10px;">
|
||||
<img src="./src/assets/wechat_qr_4.jpg" width="200" style="display: inline-block; margin: 10px;">
|
||||
<img src="./src/assets/wechat_qr.jpg" width="200" style="display: inline-block; margin: 10px;">
|
||||
</div>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
setTimeout(removeLoading, 4999)
|
||||
|
|
|
|||
BIN
src/assets/wechat_qr.jpg
Normal file
BIN
src/assets/wechat_qr.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 121 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 162 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 161 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 160 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 161 KiB |
|
|
@ -124,17 +124,25 @@ export const MarkDown = memo(
|
|||
em: ({ children }) => (
|
||||
<em className="italic text-primary">{children}</em>
|
||||
),
|
||||
a: ({ children, href }) => (
|
||||
<a
|
||||
href={href}
|
||||
className="hover:text-blue-800 underline break-all"
|
||||
style={{ wordBreak: 'break-all' }}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
),
|
||||
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 (
|
||||
<a
|
||||
href={cleanHref}
|
||||
className="text-blue-600 hover:text-blue-800 underline break-words inline"
|
||||
style={{ wordBreak: 'break-word', overflowWrap: 'break-word' }}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{cleanChildren}
|
||||
</a>
|
||||
);
|
||||
},
|
||||
table: ({ children }) => (
|
||||
<div className="overflow-x-auto w-full max-w-full">
|
||||
<table
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ export default function ProjectGroup({
|
|||
</motion.div>
|
||||
)} */}
|
||||
|
||||
{!isOngoing && hasIssue && (
|
||||
{/* {!isOngoing && hasIssue && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.8 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
|
|
@ -202,7 +202,7 @@ export default function ProjectGroup({
|
|||
{t("layout.issue") || "Issue"}
|
||||
</Tag>
|
||||
</motion.div>
|
||||
)}
|
||||
)} */}
|
||||
</div>
|
||||
</div>
|
||||
<TooltipSimple
|
||||
|
|
@ -353,12 +353,12 @@ export default function ProjectGroup({
|
|||
</Tag>
|
||||
)} */}
|
||||
|
||||
{!isOngoing && hasIssue && (
|
||||
{/* {!isOngoing && hasIssue && (
|
||||
<Tag variant="warning" size="sm">
|
||||
{t("layout.issue") || "Issue"}
|
||||
</Tag>
|
||||
)}
|
||||
|
||||
)} */}
|
||||
|
||||
{/* Menu button */}
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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') {
|
||||
|
|
|
|||
|
|
@ -22,5 +22,6 @@
|
|||
"search-agent": "وكيل البحث",
|
||||
"document-agent": "وكيل المستندات",
|
||||
"multi-modal-agent": "وكيل متعدد الوسائط",
|
||||
"social-media-agent": "وكيل وسائل التواصل الاجتماعي"
|
||||
"social-media-agent": "وكيل وسائل التواصل الاجتماعي",
|
||||
"no-projects-found": "لا توجد مشاريع"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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é."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,5 +22,6 @@
|
|||
"search-agent": "検索エージェント",
|
||||
"document-agent": "ドキュメントエージェント",
|
||||
"multi-modal-agent": "マルチモーダルエージェント",
|
||||
"social-media-agent": "ソーシャルメディアエージェント"
|
||||
"social-media-agent": "ソーシャルメディアエージェント",
|
||||
"no-projects-found": "プロジェクトが見つかりませんでした。"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,5 +22,6 @@
|
|||
"search-agent": "검색 에이전트",
|
||||
"document-agent": "문서 에이전트",
|
||||
"multi-modal-agent": "멀티모달 에이전트",
|
||||
"social-media-agent": "소셜미디어 에이전트"
|
||||
"social-media-agent": "소셜미디어 에이전트",
|
||||
"no-projects-found": "프로젝트를 찾을 수 없습니다."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,5 +22,6 @@
|
|||
"search-agent": "Агент поиска",
|
||||
"document-agent": "Агент документов",
|
||||
"multi-modal-agent": "Мультимодальный агент",
|
||||
"social-media-agent": "Агент социальных сетей"
|
||||
"social-media-agent": "Агент социальных сетей",
|
||||
"no-projects-found": "Проекты не найдены"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,5 +22,6 @@
|
|||
"search-agent": "搜索智能体",
|
||||
"document-agent": "文档智能体",
|
||||
"multi-modal-agent": "多模态智能体",
|
||||
"social-media-agent": "社交媒体智能体"
|
||||
"social-media-agent": "社交媒体智能体",
|
||||
"no-projects-found": "没有找到项目"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,5 +22,6 @@
|
|||
"search-agent": "搜尋智能體",
|
||||
"document-agent": "文件智能體",
|
||||
"multi-modal-agent": "多模態智能體",
|
||||
"social-media-agent": "社群媒體智能體"
|
||||
"social-media-agent": "社群媒體智能體",
|
||||
"no-projects-found": "沒有找到專案"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -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<Provider[]>(
|
||||
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
|
||||
<Check />
|
||||
|
|
@ -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"}
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
|
|
|
|||
|
|
@ -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<AuthState>()(
|
|||
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<AuthState>()(
|
|||
|
||||
setIsFirstLaunch: (isFirstLaunch) => set({ isFirstLaunch }),
|
||||
|
||||
setLocalProxyValue: (value) => set({ localProxyValue: value }),
|
||||
|
||||
// worker related methods
|
||||
setWorkerList: (workerList) => {
|
||||
const { email } = get();
|
||||
|
|
@ -152,6 +161,7 @@ const authStore = create<AuthState>()(
|
|||
cloud_model_type: state.cloud_model_type,
|
||||
initState: state.initState,
|
||||
isFirstLaunch: state.isFirstLaunch,
|
||||
localProxyValue: state.localProxyValue,
|
||||
workerListData: state.workerListData,
|
||||
}),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -534,7 +534,7 @@ const chatStore = (initial?: Partial<ChatStore>) => createStore<ChatStore>()(
|
|||
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),
|
||||
|
|
|
|||
|
|
@ -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<void>;
|
||||
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<InstallationStoreState>()(
|
|||
isVisible: true,
|
||||
}),
|
||||
|
||||
setNeedsBackendRestart: (needs: boolean) =>
|
||||
set({
|
||||
needsBackendRestart: needs,
|
||||
}),
|
||||
|
||||
setBackendError: (error: string) =>
|
||||
set({
|
||||
backendError: error,
|
||||
|
|
|
|||
22
src/types/electron.d.ts
vendored
22
src/types/electron.d.ts
vendored
|
|
@ -29,7 +29,7 @@ interface ElectronAPI {
|
|||
createWebView: (id: string, url: string) => Promise<any>;
|
||||
hideWebView: (id: string) => Promise<any>;
|
||||
changeViewSize: (id: string, size: any) => Promise<any>;
|
||||
onWebviewNavigated: (callback: (id: string, url: string) => void) => void;
|
||||
onWebviewNavigated: (callback: (id: string, url: string) => void) => () => void;
|
||||
showWebview: (id: string) => Promise<any>;
|
||||
getActiveWebview: () => Promise<any>;
|
||||
setSize: (size: any) => Promise<any>;
|
||||
|
|
@ -44,7 +44,13 @@ interface ElectronAPI {
|
|||
envWrite: (email: string, kv: { key: string, value: string }) => Promise<any>;
|
||||
envRemove: (email: string, key: string) => Promise<any>;
|
||||
getEnvPath: (email: string) => Promise<string>;
|
||||
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<any>;
|
||||
readFileAsDataUrl: (path: string) => Promise<string>;
|
||||
deleteFolder: (email: string) => Promise<any>;
|
||||
getMcpConfigPath: (email: string) => Promise<string>;
|
||||
uploadLog: (email: string, taskId: string, baseUrl: string, token: string) => Promise<any>;
|
||||
startBrowserImport: (args?: any) => Promise<any>;
|
||||
checkAndInstallDepsOnUpdate: () => Promise<{ success: boolean; error?: string }>;
|
||||
checkInstallBrowser: () => Promise<{ data:any[] }>;
|
||||
getInstallationStatus: () => Promise<{
|
||||
|
|
@ -55,16 +61,18 @@ interface ElectronAPI {
|
|||
timestamp?: number;
|
||||
error?: string
|
||||
}>;
|
||||
getBackendPort: () => Promise<number | null>;
|
||||
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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue