diff --git a/src/main/main.ts b/src/main/main.ts index 7e666a1..6ad7347 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -1730,6 +1730,32 @@ if (!gotTheLock) { } }); + let restartGatewayPromise: Promise | null = null; + ipcMain.handle('openclaw:engine:restartGateway', async () => { + if (restartGatewayPromise) { + const status = await restartGatewayPromise; + return { success: status.phase === 'running' || status.phase === 'ready', status }; + } + try { + const manager = getOpenClawEngineManager(); + restartGatewayPromise = manager.restartGateway(); + const status = await restartGatewayPromise; + return { + success: status.phase === 'running' || status.phase === 'ready', + status, + }; + } catch (error) { + const manager = getOpenClawEngineManager(); + return { + success: false, + status: manager.getStatus(), + error: error instanceof Error ? error.message : 'Failed to restart OpenClaw gateway', + }; + } finally { + restartGatewayPromise = null; + } + }); + // MCP Server IPC handlers ipcMain.handle('mcp:list', () => { try { diff --git a/src/main/preload.ts b/src/main/preload.ts index d8ee958..f28e75b 100644 --- a/src/main/preload.ts +++ b/src/main/preload.ts @@ -123,6 +123,7 @@ contextBridge.exposeInMainWorld('electron', { getStatus: () => ipcRenderer.invoke('openclaw:engine:getStatus'), install: () => ipcRenderer.invoke('openclaw:engine:install'), retryInstall: () => ipcRenderer.invoke('openclaw:engine:retryInstall'), + restartGateway: () => ipcRenderer.invoke('openclaw:engine:restartGateway'), onProgress: (callback: (status: any) => void) => { const handler = (_event: any, status: any) => callback(status); ipcRenderer.on('openclaw:engine:onProgress', handler); diff --git a/src/renderer/components/cowork/CoworkView.tsx b/src/renderer/components/cowork/CoworkView.tsx index ecc5c4e..dd783d5 100644 --- a/src/renderer/components/cowork/CoworkView.tsx +++ b/src/renderer/components/cowork/CoworkView.tsx @@ -33,6 +33,7 @@ const CoworkView: React.FC = ({ onRequestAppSettings, onShowSki const isMac = window.electron.platform === 'darwin'; const [isInitialized, setIsInitialized] = useState(false); const [openClawStatus, setOpenClawStatus] = useState(null); + const [isRestartingGateway, setIsRestartingGateway] = useState(false); // Track if we're starting a session to prevent duplicate submissions const isStartingRef = useRef(false); // Track pending start request so stop can cancel delayed startup. @@ -69,9 +70,6 @@ const CoworkView: React.FC = ({ onRequestAppSettings, onShowSki }; const resolveEngineStatusText = (status: OpenClawEngineStatus): string => { - if (status.message?.trim()) { - return status.message.trim(); - } switch (status.phase) { case 'not_installed': return i18nService.t('coworkOpenClawNotInstalledNotice'); @@ -94,6 +92,18 @@ const CoworkView: React.FC = ({ onRequestAppSettings, onShowSki return status.phase === 'running' || status.phase === 'ready'; }; + const handleRestartGateway = async () => { + if (isRestartingGateway) return; + setIsRestartingGateway(true); + try { + await coworkService.restartOpenClawGateway(); + } catch (error) { + console.error('[CoworkView] Failed to restart gateway:', error); + } finally { + setIsRestartingGateway(false); + } + }; + useEffect(() => { const init = async () => { await coworkService.init(); @@ -390,11 +400,6 @@ const CoworkView: React.FC = ({ onRequestAppSettings, onShowSki const shouldShowEngineStatus = Boolean(isOpenClawEngine && openClawStatus && openClawStatus.phase !== 'running'); const isEngineError = openClawStatus?.phase === 'error'; - const shouldShowGoToSettingsInstall = openClawStatus - ? (openClawStatus.phase === 'not_installed' - || openClawStatus.phase === 'installing' - || openClawStatus.phase === 'error') - : false; const isEngineReady = isOpenClawEngine ? isOpenClawReadyForSession(openClawStatus) : true; @@ -480,10 +485,11 @@ const CoworkView: React.FC = ({ onRequestAppSettings, onShowSki )} @@ -528,17 +534,16 @@ const CoworkView: React.FC = ({ onRequestAppSettings, onShowSki )} - {shouldShowGoToSettingsInstall && ( - - )} )} diff --git a/src/renderer/services/cowork.ts b/src/renderer/services/cowork.ts index 23e06ca..896a170 100644 --- a/src/renderer/services/cowork.ts +++ b/src/renderer/services/cowork.ts @@ -582,6 +582,19 @@ class CoworkService { return this.openClawStatus; } + async restartOpenClawGateway(): Promise { + const engineApi = window.electron?.openclaw?.engine; + if (!engineApi?.restartGateway) { + return null; + } + const result = await engineApi.restartGateway(); + if (result?.status) { + this.notifyOpenClawStatus(result.status); + return result.status; + } + return this.openClawStatus; + } + async generateSessionTitle(prompt: string | null): Promise { if (!window.electron?.generateSessionTitle) { return null; diff --git a/src/renderer/services/i18n.ts b/src/renderer/services/i18n.ts index ad88f36..1ced470 100644 --- a/src/renderer/services/i18n.ts +++ b/src/renderer/services/i18n.ts @@ -253,11 +253,12 @@ const translations: Record> = { coworkOpenClawInstalling: '正在启动 OpenClaw...', coworkOpenClawInstallHint: 'OpenClaw 运行时已内置。切换到该引擎或启动任务时会自动拉起网关。', coworkOpenClawGoToSettingsInstall: '查看引擎设置', + coworkOpenClawRestartGateway: '重新启动网关', coworkOpenClawNotInstalledNotice: '未检测到内置 OpenClaw runtime(cfmind),请先执行打包前构建脚本。', coworkOpenClawReadyNotice: 'OpenClaw runtime 已就绪。开始任务时会自动启动网关。', coworkOpenClawStarting: 'AI 引擎正在启动网关...', coworkOpenClawRunning: 'AI 引擎已就绪。', - coworkOpenClawError: 'AI 引擎初始化失败,请前往设置页检查运行时配置。', + coworkOpenClawError: 'OpenClaw 网关未能在规定时间内启动成功。', coworkMemoryTitle: '记忆', coworkAgentTab: 'Agent', coworkBootstrapAgentSectionTitle: 'Agent 设定', @@ -1167,11 +1168,12 @@ const translations: Record> = { coworkOpenClawInstalling: 'Starting OpenClaw...', coworkOpenClawInstallHint: 'OpenClaw runtime is bundled. Switching to this engine or starting a task will auto-start the gateway.', coworkOpenClawGoToSettingsInstall: 'View Engine Settings', + coworkOpenClawRestartGateway: 'Restart Gateway', coworkOpenClawNotInstalledNotice: 'Bundled OpenClaw runtime (cfmind) was not found. Build the runtime before packaging.', coworkOpenClawReadyNotice: 'OpenClaw runtime is ready. The gateway will auto-start when you run a task.', coworkOpenClawStarting: 'AI engine is starting the gateway...', coworkOpenClawRunning: 'AI engine is ready.', - coworkOpenClawError: 'AI engine initialization failed. Please check the runtime configuration in Settings.', + coworkOpenClawError: 'OpenClaw gateway failed to become healthy in time.', coworkMemoryTitle: 'Memory', coworkAgentTab: 'Agent', coworkBootstrapAgentSectionTitle: 'Agent Settings', diff --git a/src/renderer/types/electron.d.ts b/src/renderer/types/electron.d.ts index 7c7bcac..0e33530 100644 --- a/src/renderer/types/electron.d.ts +++ b/src/renderer/types/electron.d.ts @@ -276,6 +276,7 @@ interface IElectronAPI { getStatus: () => Promise<{ success: boolean; status?: OpenClawEngineStatus; error?: string }>; install: () => Promise<{ success: boolean; status?: OpenClawEngineStatus; error?: string }>; retryInstall: () => Promise<{ success: boolean; status?: OpenClawEngineStatus; error?: string }>; + restartGateway: () => Promise<{ success: boolean; status?: OpenClawEngineStatus; error?: string }>; onProgress: (callback: (status: OpenClawEngineStatus) => void) => () => void; }; };