import { app } from 'electron'; import path from 'path'; import { existsSync } from 'fs'; import { getAllSettings } from '../utils/store'; import { getApiKey, getDefaultProvider, getProvider } from '../utils/secure-storage'; import { getProviderEnvVar, getKeyableProviderTypes } from '../utils/provider-registry'; import { getOpenClawDir, getOpenClawEntryPath, isOpenClawPresent } from '../utils/paths'; import { getUvMirrorEnv } from '../utils/uv-env'; import { listConfiguredChannels } from '../utils/channel-config'; import { syncGatewayTokenToConfig, syncBrowserConfigToOpenClaw, sanitizeOpenClawConfig } from '../utils/openclaw-auth'; import { buildProxyEnv, resolveProxySettings } from '../utils/proxy'; import { syncProxyConfigToOpenClaw } from '../utils/openclaw-proxy'; import { logger } from '../utils/logger'; export interface GatewayLaunchContext { appSettings: Awaited>; openclawDir: string; entryScript: string; gatewayArgs: string[]; forkEnv: Record; mode: 'dev' | 'packaged'; binPathExists: boolean; loadedProviderKeyCount: number; proxySummary: string; channelStartupSummary: string; } export async function syncGatewayConfigBeforeLaunch( appSettings: Awaited>, ): Promise { await syncProxyConfigToOpenClaw(appSettings); try { await sanitizeOpenClawConfig(); } catch (err) { logger.warn('Failed to sanitize openclaw.json:', err); } try { await syncGatewayTokenToConfig(appSettings.gatewayToken); } catch (err) { logger.warn('Failed to sync gateway token to openclaw.json:', err); } try { await syncBrowserConfigToOpenClaw(); } catch (err) { logger.warn('Failed to sync browser config to openclaw.json:', err); } } async function loadProviderEnv(): Promise<{ providerEnv: Record; loadedProviderKeyCount: number }> { const providerEnv: Record = {}; const providerTypes = getKeyableProviderTypes(); let loadedProviderKeyCount = 0; try { const defaultProviderId = await getDefaultProvider(); if (defaultProviderId) { const defaultProvider = await getProvider(defaultProviderId); const defaultProviderType = defaultProvider?.type; const defaultProviderKey = await getApiKey(defaultProviderId); if (defaultProviderType && defaultProviderKey) { const envVar = getProviderEnvVar(defaultProviderType); if (envVar) { providerEnv[envVar] = defaultProviderKey; loadedProviderKeyCount++; } } } } catch (err) { logger.warn('Failed to load default provider key for environment injection:', err); } for (const providerType of providerTypes) { try { const key = await getApiKey(providerType); if (key) { const envVar = getProviderEnvVar(providerType); if (envVar) { providerEnv[envVar] = key; loadedProviderKeyCount++; } } } catch (err) { logger.warn(`Failed to load API key for ${providerType}:`, err); } } return { providerEnv, loadedProviderKeyCount }; } async function resolveChannelStartupPolicy(): Promise<{ skipChannels: boolean; channelStartupSummary: string; }> { try { const configuredChannels = await listConfiguredChannels(); if (configuredChannels.length === 0) { return { skipChannels: true, channelStartupSummary: 'skipped(no configured channels)', }; } return { skipChannels: false, channelStartupSummary: `enabled(${configuredChannels.join(',')})`, }; } catch (error) { logger.warn('Failed to determine configured channels for gateway launch:', error); return { skipChannels: false, channelStartupSummary: 'enabled(unknown)', }; } } export async function prepareGatewayLaunchContext(port: number): Promise { const openclawDir = getOpenClawDir(); const entryScript = getOpenClawEntryPath(); if (!isOpenClawPresent()) { throw new Error(`OpenClaw package not found at: ${openclawDir}`); } const appSettings = await getAllSettings(); await syncGatewayConfigBeforeLaunch(appSettings); if (!existsSync(entryScript)) { throw new Error(`OpenClaw entry script not found at: ${entryScript}`); } const gatewayArgs = ['gateway', '--port', String(port), '--token', appSettings.gatewayToken, '--allow-unconfigured']; const mode = app.isPackaged ? 'packaged' : 'dev'; const platform = process.platform; const arch = process.arch; const target = `${platform}-${arch}`; const binPath = app.isPackaged ? path.join(process.resourcesPath, 'bin') : path.join(process.cwd(), 'resources', 'bin', target); const binPathExists = existsSync(binPath); const finalPath = binPathExists ? `${binPath}${path.delimiter}${process.env.PATH || ''}` : process.env.PATH || ''; const { providerEnv, loadedProviderKeyCount } = await loadProviderEnv(); const { skipChannels, channelStartupSummary } = await resolveChannelStartupPolicy(); const uvEnv = await getUvMirrorEnv(); const proxyEnv = buildProxyEnv(appSettings); const resolvedProxy = resolveProxySettings(appSettings); const proxySummary = appSettings.proxyEnabled ? `http=${resolvedProxy.httpProxy || '-'}, https=${resolvedProxy.httpsProxy || '-'}, all=${resolvedProxy.allProxy || '-'}` : 'disabled'; const { NODE_OPTIONS: _nodeOptions, ...baseEnv } = process.env; const forkEnv: Record = { ...baseEnv, PATH: finalPath, ...providerEnv, ...uvEnv, ...proxyEnv, OPENCLAW_GATEWAY_TOKEN: appSettings.gatewayToken, OPENCLAW_SKIP_CHANNELS: skipChannels ? '1' : '', CLAWDBOT_SKIP_CHANNELS: skipChannels ? '1' : '', OPENCLAW_NO_RESPAWN: '1', }; return { appSettings, openclawDir, entryScript, gatewayArgs, forkEnv, mode, binPathExists, loadedProviderKeyCount, proxySummary, channelStartupSummary, }; }