Add built-in proxy settings for Electron and Gateway (#239)

Co-authored-by: zuolingxuan <zuolingxuan@bytedance.com>
This commit is contained in:
Lingxuan Zuo
2026-03-02 17:33:06 +08:00
committed by GitHub
Unverified
parent c09b45832b
commit e40f4b2163
20 changed files with 758 additions and 25 deletions

View File

@@ -17,6 +17,8 @@ import { ClawHubService } from '../gateway/clawhub';
import { ensureClawXContext, repairClawXOnlyBootstrapFiles } from '../utils/openclaw-workspace';
import { autoInstallCliIfNeeded, generateCompletionCache, installCompletionToProfile } from '../utils/openclaw-cli';
import { isQuitting, setQuitting } from './app-state';
import { applyProxySettings } from './proxy';
import { getSetting } from '../utils/store';
import { ensureBuiltinSkillsInstalled } from '../utils/skill-config';
// Disable GPU hardware acceleration globally for maximum stability across
@@ -128,6 +130,9 @@ async function initialize(): Promise<void> {
// Warm up network optimization (non-blocking)
void warmupNetworkOptimization();
// Apply persisted proxy settings before creating windows or network requests.
await applyProxySettings();
// Set application menu
createMenu();
@@ -195,13 +200,18 @@ async function initialize(): Promise<void> {
});
// Start Gateway automatically (this seeds missing bootstrap files with full templates)
try {
logger.debug('Auto-starting Gateway...');
await gatewayManager.start();
logger.info('Gateway auto-start succeeded');
} catch (error) {
logger.error('Gateway auto-start failed:', error);
mainWindow?.webContents.send('gateway:error', String(error));
const gatewayAutoStart = await getSetting('gatewayAutoStart');
if (gatewayAutoStart) {
try {
logger.debug('Auto-starting Gateway...');
await gatewayManager.start();
logger.info('Gateway auto-start succeeded');
} catch (error) {
logger.error('Gateway auto-start failed:', error);
mainWindow?.webContents.send('gateway:error', String(error));
}
} else {
logger.info('Gateway auto-start disabled in settings');
}
// Merge ClawX context snippets into the workspace bootstrap files.

View File

@@ -24,7 +24,7 @@ import {
} from '../utils/secure-storage';
import { getOpenClawStatus, getOpenClawDir, getOpenClawConfigDir, getOpenClawSkillsDir, ensureDir } from '../utils/paths';
import { getOpenClawCliCommand } from '../utils/openclaw-cli';
import { getSetting } from '../utils/store';
import { getAllSettings, getSetting, resetSettings, setSetting, type AppSettings } from '../utils/store';
import {
saveProviderKeyToOpenClaw,
removeProviderFromOpenClaw,
@@ -49,6 +49,8 @@ import { updateSkillConfig, getSkillConfig, getAllSkillConfigs } from '../utils/
import { whatsAppLoginManager } from '../utils/whatsapp-login';
import { getProviderConfig } from '../utils/provider-registry';
import { deviceOAuthManager, OAuthProviderType } from '../utils/device-oauth';
import { applyProxySettings } from './proxy';
import { proxyAwareFetch } from '../utils/proxy-fetch';
import { getRecentTokenUsageHistory } from '../utils/token-usage';
/**
@@ -100,6 +102,9 @@ export function registerIpcHandlers(
// App handlers
registerAppHandlers();
// Settings handlers
registerSettingsHandlers(gatewayManager);
// UV handlers
registerUvHandlers();
@@ -1478,7 +1483,7 @@ async function performProviderValidationRequest(
): Promise<{ valid: boolean; error?: string }> {
try {
logValidationRequest(providerLabel, 'GET', url, headers);
const response = await fetch(url, { headers });
const response = await proxyAwareFetch(url, { headers });
logValidationStatus(providerLabel, response.status);
const data = await response.json().catch(() => ({}));
return classifyAuthResponse(response.status, data);
@@ -1553,7 +1558,7 @@ async function performChatCompletionsProbe(
): Promise<{ valid: boolean; error?: string }> {
try {
logValidationRequest(providerLabel, 'POST', url, headers);
const response = await fetch(url, {
const response = await proxyAwareFetch(url, {
method: 'POST',
headers: { ...headers, 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -1755,6 +1760,67 @@ function registerAppHandlers(): void {
});
}
function registerSettingsHandlers(gatewayManager: GatewayManager): void {
const handleProxySettingsChange = async () => {
const settings = await getAllSettings();
await applyProxySettings(settings);
if (gatewayManager.getStatus().state === 'running') {
await gatewayManager.restart();
}
};
ipcMain.handle('settings:get', async (_, key: keyof AppSettings) => {
return await getSetting(key);
});
ipcMain.handle('settings:getAll', async () => {
return await getAllSettings();
});
ipcMain.handle('settings:set', async (_, key: keyof AppSettings, value: AppSettings[keyof AppSettings]) => {
await setSetting(key, value as never);
if (
key === 'proxyEnabled' ||
key === 'proxyServer' ||
key === 'proxyHttpServer' ||
key === 'proxyHttpsServer' ||
key === 'proxyAllServer' ||
key === 'proxyBypassRules'
) {
await handleProxySettingsChange();
}
return { success: true };
});
ipcMain.handle('settings:setMany', async (_, patch: Partial<AppSettings>) => {
const entries = Object.entries(patch) as Array<[keyof AppSettings, AppSettings[keyof AppSettings]]>;
for (const [key, value] of entries) {
await setSetting(key, value as never);
}
if (entries.some(([key]) =>
key === 'proxyEnabled' ||
key === 'proxyServer' ||
key === 'proxyHttpServer' ||
key === 'proxyHttpsServer' ||
key === 'proxyAllServer' ||
key === 'proxyBypassRules'
)) {
await handleProxySettingsChange();
}
return { success: true };
});
ipcMain.handle('settings:reset', async () => {
await resetSettings();
const settings = await getAllSettings();
await handleProxySettingsChange();
return { success: true, settings };
});
}
function registerUsageHandlers(): void {
ipcMain.handle('usage:recentTokenHistory', async (_, limit?: number) => {
const safeLimit = typeof limit === 'number' && Number.isFinite(limit)

22
electron/main/proxy.ts Normal file
View File

@@ -0,0 +1,22 @@
import { session } from 'electron';
import { getAllSettings, type AppSettings } from '../utils/store';
import { buildElectronProxyConfig } from '../utils/proxy';
import { logger } from '../utils/logger';
export async function applyProxySettings(
partialSettings?: Pick<AppSettings, 'proxyEnabled' | 'proxyServer' | 'proxyBypassRules'>
): Promise<void> {
const settings = partialSettings ?? await getAllSettings();
const config = buildElectronProxyConfig(settings);
await session.defaultSession.setProxy(config);
try {
await session.defaultSession.closeAllConnections();
} catch (error) {
logger.debug('Failed to close existing connections after proxy update:', error);
}
logger.info(
`Applied Electron proxy (${config.mode}${config.proxyRules ? `, server=${config.proxyRules}` : ''}${config.proxyBypassRules ? `, bypass=${config.proxyBypassRules}` : ''})`
);
}