diff --git a/electron/utils/openclaw-auth.ts b/electron/utils/openclaw-auth.ts index 16b028a..cf4a857 100644 --- a/electron/utils/openclaw-auth.ts +++ b/electron/utils/openclaw-auth.ts @@ -715,6 +715,22 @@ export async function syncGatewayTokenToConfig(token: string): Promise { auth.mode = 'token'; auth.token = token; gateway.auth = auth; + + // Packaged ClawX loads the renderer from file://, so the gateway must allow + // that origin for the chat WebSocket handshake. + const controlUi = ( + gateway.controlUi && typeof gateway.controlUi === 'object' + ? { ...(gateway.controlUi as Record) } + : {} + ) as Record; + const allowedOrigins = Array.isArray(controlUi.allowedOrigins) + ? (controlUi.allowedOrigins as unknown[]).filter((value): value is string => typeof value === 'string') + : []; + if (!allowedOrigins.includes('file://')) { + controlUi.allowedOrigins = [...allowedOrigins, 'file://']; + } + gateway.controlUi = controlUi; + if (!gateway.mode) gateway.mode = 'local'; config.gateway = gateway; diff --git a/src/stores/gateway.ts b/src/stores/gateway.ts index a714ffe..bd172bd 100644 --- a/src/stores/gateway.ts +++ b/src/stores/gateway.ts @@ -4,7 +4,7 @@ */ import { create } from 'zustand'; import { createHostEventSource, hostApiFetch } from '@/lib/host-api'; -import { gatewayClient } from '@/lib/gateway-client'; +import { invokeIpc } from '@/lib/api-client'; import type { GatewayStatus } from '../types/gateway'; let gatewayInitPromise: Promise | null = null; @@ -174,24 +174,29 @@ export const useGatewayStore = create((set, get) => ({ const payload = JSON.parse((event as MessageEvent).data) as { message?: string }; set({ lastError: payload.message || 'Gateway error' }); }); + gatewayEventSource.addEventListener('gateway:notification', (event) => { + handleGatewayNotification(JSON.parse((event as MessageEvent).data) as { + method?: string; + params?: Record; + }); + }); + gatewayEventSource.addEventListener('gateway:chat-message', (event) => { + handleGatewayChatMessage(JSON.parse((event as MessageEvent).data)); + }); + gatewayEventSource.addEventListener('gateway:channel-status', (event) => { + import('./channels') + .then(({ useChannelsStore }) => { + const update = JSON.parse((event as MessageEvent).data) as { channelId?: string; status?: string }; + if (!update.channelId || !update.status) return; + const state = useChannelsStore.getState(); + const channel = state.channels.find((item) => item.type === update.channelId); + if (channel) { + state.updateChannel(channel.id, { status: mapChannelStatus(update.status) }); + } + }) + .catch(() => {}); + }); } - - gatewayClient.on('agent', (payload) => handleGatewayNotification({ method: 'agent', params: payload as Record })); - gatewayClient.on('chat', (payload) => handleGatewayChatMessage({ message: payload })); - gatewayClient.on('message', handleGatewayMessage); - gatewayClient.on('channel.status', (payload) => { - import('./channels') - .then(({ useChannelsStore }) => { - const update = payload as { channelId?: string; status?: string }; - if (!update.channelId || !update.status) return; - const state = useChannelsStore.getState(); - const channel = state.channels.find((item) => item.type === update.channelId); - if (channel) { - state.updateChannel(channel.id, { status: mapChannelStatus(update.status) }); - } - }) - .catch(() => {}); - }); } catch (error) { console.error('Failed to initialize Gateway:', error); set({ lastError: String(error) }); @@ -226,7 +231,6 @@ export const useGatewayStore = create((set, get) => ({ stop: async () => { try { await hostApiFetch('/api/gateway/stop', { method: 'POST' }); - gatewayClient.disconnect(); set({ status: { ...get().status, state: 'stopped' }, lastError: null }); } catch (error) { console.error('Failed to stop Gateway:', error); @@ -237,7 +241,6 @@ export const useGatewayStore = create((set, get) => ({ restart: async () => { try { set({ status: { ...get().status, state: 'starting' }, lastError: null }); - gatewayClient.disconnect(); const result = await hostApiFetch<{ success: boolean; error?: string }>('/api/gateway/restart', { method: 'POST', }); @@ -268,7 +271,15 @@ export const useGatewayStore = create((set, get) => ({ }, rpc: async (method: string, params?: unknown, timeoutMs?: number): Promise => { - return await gatewayClient.rpc(method, params, timeoutMs); + const response = await invokeIpc<{ + success: boolean; + result?: T; + error?: string; + }>('gateway:rpc', method, params, timeoutMs); + if (!response.success) { + throw new Error(response.error || `Gateway RPC failed: ${method}`); + } + return response.result as T; }, setStatus: (status) => set({ status }),