feat: Electron auto-starts gateway + always loads web app

- createWindow() no longer loads static onboarding HTML on missing gateway
- Auto-starts 'openclaw gateway start' if not running + OpenClaw installed
- Always loads the React web app (wizard handles gateway state)
- Added gateway:restart IPC handler for renderer to restart gateway
- Preload exposes gateway.restart() via contextBridge
- before-quit kills spawned gateway process
This commit is contained in:
outsourc-e
2026-03-08 13:30:37 -04:00
parent 0c202af2e4
commit aebd34818d
2 changed files with 49 additions and 14 deletions

View File

@@ -220,23 +220,33 @@ async function createWindow() {
});
const gatewayUrl = getGatewayUrl();
if (gatewayUrl) {
// Gateway found — start local server to serve UI + proxy API
if (process.env.NODE_ENV !== 'development') {
try {
await startLocalServer(gatewayUrl);
} catch (err) {
console.error('[ClawSuite] Failed to start local server:', err);
}
if (!gatewayUrl && isOpenClawInstalled()) {
console.log('[ClawSuite] Gateway not running, auto-starting...');
try {
gatewayProcess = (0, child_process_1.spawn)('openclaw', ['gateway', 'start'], {
shell: true,
stdio: 'ignore',
detached: true,
});
gatewayProcess.unref();
await new Promise((resolve) => setTimeout(resolve, 3000));
} catch (err) {
console.error('[ClawSuite] Failed to auto-start gateway:', err);
}
const appUrl = getAppUrl();
console.log(`[ClawSuite] Loading: ${appUrl}`);
mainWindow.loadURL(appUrl);
} else {
// No gateway — show onboarding wizard
mainWindow.loadFile((0, path_1.join)(__dirname, '..', 'electron', 'onboarding', 'index.html'));
}
if (process.env.NODE_ENV !== 'development') {
try {
await startLocalServer(getGatewayUrl());
} catch (err) {
console.error('[ClawSuite] Failed to start local server:', err);
}
}
const appUrl = getAppUrl();
console.log(`[ClawSuite] Loading: ${appUrl}`);
mainWindow.loadURL(appUrl);
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
if (url.startsWith('http')) {
electron_1.shell.openExternal(url);
@@ -356,6 +366,26 @@ electron_1.ipcMain.handle('gateway:start', async () => {
stdio: 'pipe',
detached: true,
});
gatewayProcess.unref();
setTimeout(() => {
const url = getGatewayUrl();
resolve({ success: !!url, url });
}, 5000);
});
});
electron_1.ipcMain.handle('gateway:restart', async () => {
try {
(0, child_process_1.execSync)('openclaw gateway stop', { timeout: 5000 });
} catch { /* may not be running */ }
return new Promise((resolve) => {
gatewayProcess = (0, child_process_1.spawn)('openclaw', ['gateway', 'start'], {
shell: true,
stdio: 'pipe',
detached: true,
});
gatewayProcess.unref();
setTimeout(() => {
const url = getGatewayUrl();
resolve({ success: !!url, url });
@@ -413,6 +443,10 @@ electron_1.app.on('before-quit', () => {
appProcess.kill();
appProcess = null;
}
if (gatewayProcess) {
gatewayProcess.kill();
gatewayProcess = null;
}
});
electron_1.app.setName('ClawSuite');

View File

@@ -11,6 +11,7 @@ electron_1.contextBridge.exposeInMainWorld('clawsuite', {
check: () => electron_1.ipcRenderer.invoke('gateway:check'),
install: () => electron_1.ipcRenderer.invoke('gateway:install'),
start: () => electron_1.ipcRenderer.invoke('gateway:start'),
restart: () => electron_1.ipcRenderer.invoke('gateway:restart'),
connect: (url) => electron_1.ipcRenderer.invoke('gateway:connect', url),
},
// Onboarding