Merge branch 'main' into revert-chat-delete

This commit is contained in:
Wendong-Fan 2025-11-21 22:28:47 +08:00 committed by GitHub
commit 9edd0c2b4f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 270 additions and 73 deletions

View file

@ -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>

View file

@ -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>

View file

@ -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,

View file

@ -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

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

View file

@ -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

View file

@ -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>

View file

@ -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 = {

View file

@ -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') {

View file

@ -22,5 +22,6 @@
"search-agent": "وكيل البحث",
"document-agent": "وكيل المستندات",
"multi-modal-agent": "وكيل متعدد الوسائط",
"social-media-agent": "وكيل وسائل التواصل الاجتماعي"
"social-media-agent": "وكيل وسائل التواصل الاجتماعي",
"no-projects-found": "لا توجد مشاريع"
}

View file

@ -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."
}

View file

@ -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."
}

View file

@ -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."
}

View file

@ -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é."
}

View file

@ -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."
}

View file

@ -22,5 +22,6 @@
"search-agent": "検索エージェント",
"document-agent": "ドキュメントエージェント",
"multi-modal-agent": "マルチモーダルエージェント",
"social-media-agent": "ソーシャルメディアエージェント"
"social-media-agent": "ソーシャルメディアエージェント",
"no-projects-found": "プロジェクトが見つかりませんでした。"
}

View file

@ -22,5 +22,6 @@
"search-agent": "검색 에이전트",
"document-agent": "문서 에이전트",
"multi-modal-agent": "멀티모달 에이전트",
"social-media-agent": "소셜미디어 에이전트"
"social-media-agent": "소셜미디어 에이전트",
"no-projects-found": "프로젝트를 찾을 수 없습니다."
}

View file

@ -22,5 +22,6 @@
"search-agent": "Агент поиска",
"document-agent": "Агент документов",
"multi-modal-agent": "Мультимодальный агент",
"social-media-agent": "Агент социальных сетей"
"social-media-agent": "Агент социальных сетей",
"no-projects-found": "Проекты не найдены"
}

View file

@ -22,5 +22,6 @@
"search-agent": "搜索智能体",
"document-agent": "文档智能体",
"multi-modal-agent": "多模态智能体",
"social-media-agent": "社交媒体智能体"
"social-media-agent": "社交媒体智能体",
"no-projects-found": "没有找到项目"
}

View file

@ -22,5 +22,6 @@
"search-agent": "搜尋智能體",
"document-agent": "文件智能體",
"multi-modal-agent": "多模態智能體",
"social-media-agent": "社群媒體智能體"
"social-media-agent": "社群媒體智能體",
"no-projects-found": "沒有找到專案"
}

View file

@ -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);

View file

@ -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");
}}

View file

@ -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"}

View file

@ -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 (

View file

@ -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,
}),
}

View file

@ -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),

View file

@ -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,

View file

@ -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;