mirror of
https://github.com/eigent-ai/eigent.git
synced 2026-05-20 01:09:26 +00:00
Fix/multi bug 11 21 (#751)
This commit is contained in:
commit
8d01497530
22 changed files with 209 additions and 43 deletions
|
|
@ -68,6 +68,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,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>
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -820,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 />
|
||||
|
|
@ -830,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: [],
|
||||
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,
|
||||
|
|
|
|||
20
src/types/electron.d.ts
vendored
20
src/types/electron.d.ts
vendored
|
|
@ -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