mirror of
https://gh-proxy.org/https://github.com/klinsmaya/LobsterAI.git
synced 2026-11-08 01:06:44 +08:00
Merge pull request #484 from netease-youdao/liuzhq/gateway-restart
- 添加openclaw gateway 启动失败后重启功能
This commit is contained in:
@@ -1730,6 +1730,32 @@ if (!gotTheLock) {
|
||||
}
|
||||
});
|
||||
|
||||
let restartGatewayPromise: Promise<OpenClawEngineStatus> | 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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -33,6 +33,7 @@ const CoworkView: React.FC<CoworkViewProps> = ({ onRequestAppSettings, onShowSki
|
||||
const isMac = window.electron.platform === 'darwin';
|
||||
const [isInitialized, setIsInitialized] = useState(false);
|
||||
const [openClawStatus, setOpenClawStatus] = useState<OpenClawEngineStatus | null>(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<CoworkViewProps> = ({ 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<CoworkViewProps> = ({ 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<CoworkViewProps> = ({ 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<CoworkViewProps> = ({ onRequestAppSettings, onShowSki
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onRequestAppSettings?.({ initialTab: 'coworkAgentEngine' })}
|
||||
className="mt-1 rounded-lg px-3 py-1.5 text-xs font-medium bg-claude-accent text-white hover:bg-claude-accentHover transition-colors"
|
||||
onClick={handleRestartGateway}
|
||||
disabled={isRestartingGateway || openClawStatus.phase === 'starting'}
|
||||
className="mt-1 rounded-lg px-3 py-1.5 text-xs font-medium bg-claude-accent text-white hover:bg-claude-accentHover disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||
>
|
||||
{i18nService.t('coworkOpenClawGoToSettingsInstall')}
|
||||
{i18nService.t('coworkOpenClawRestartGateway')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -528,17 +534,16 @@ const CoworkView: React.FC<CoworkViewProps> = ({ onRequestAppSettings, onShowSki
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{shouldShowGoToSettingsInstall && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onRequestAppSettings?.({ initialTab: 'coworkAgentEngine' })}
|
||||
className={`rounded-lg px-3 py-1.5 text-xs font-medium transition-colors ${isEngineError
|
||||
? 'bg-red-600 text-white hover:bg-red-700'
|
||||
: 'bg-amber-600 text-white hover:bg-amber-700'}`}
|
||||
>
|
||||
{i18nService.t('coworkOpenClawGoToSettingsInstall')}
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleRestartGateway}
|
||||
disabled={isRestartingGateway}
|
||||
className={`shrink-0 rounded-lg px-3 py-1.5 text-xs font-medium transition-colors disabled:opacity-50 disabled:cursor-not-allowed ${isEngineError
|
||||
? 'bg-red-600 text-white hover:bg-red-700'
|
||||
: 'bg-amber-600 text-white hover:bg-amber-700'}`}
|
||||
>
|
||||
{i18nService.t('coworkOpenClawRestartGateway')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -582,6 +582,19 @@ class CoworkService {
|
||||
return this.openClawStatus;
|
||||
}
|
||||
|
||||
async restartOpenClawGateway(): Promise<OpenClawEngineStatus | null> {
|
||||
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<string | null> {
|
||||
if (!window.electron?.generateSessionTitle) {
|
||||
return null;
|
||||
|
||||
@@ -253,11 +253,12 @@ const translations: Record<LanguageType, Record<string, string>> = {
|
||||
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<LanguageType, Record<string, string>> = {
|
||||
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',
|
||||
|
||||
Vendored
+1
@@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user