refactor IPC (#341)

This commit is contained in:
Lingxuan Zuo
2026-03-08 11:54:49 +08:00
committed by GitHub
Unverified
parent c03d92e9a2
commit 3d804a9f5e
52 changed files with 3121 additions and 336 deletions

View File

@@ -4,6 +4,7 @@
*/
import { create } from 'zustand';
import type { Channel, ChannelType } from '../types/channel';
import { invokeIpc } from '@/lib/api-client';
interface AddChannelParams {
type: ChannelType;
@@ -17,7 +18,7 @@ interface ChannelsState {
error: string | null;
// Actions
fetchChannels: () => Promise<void>;
fetchChannels: (options?: { probe?: boolean; silent?: boolean }) => Promise<void>;
addChannel: (params: AddChannelParams) => Promise<Channel>;
deleteChannel: (channelId: string) => Promise<void>;
connectChannel: (channelId: string) => Promise<void>;
@@ -33,13 +34,17 @@ export const useChannelsStore = create<ChannelsState>((set, get) => ({
loading: false,
error: null,
fetchChannels: async () => {
set({ loading: true, error: null });
fetchChannels: async (options) => {
const probe = options?.probe ?? false;
const silent = options?.silent ?? false;
if (!silent) {
set({ loading: true, error: null });
}
try {
const result = await window.electron.ipcRenderer.invoke(
const result = await invokeIpc(
'gateway:rpc',
'channels.status',
{ probe: true }
{ probe }
) as {
success: boolean;
result?: {
@@ -126,20 +131,20 @@ export const useChannelsStore = create<ChannelsState>((set, get) => ({
});
}
set({ channels, loading: false });
set((state) => ({ channels, loading: silent ? state.loading : false }));
} else {
// Gateway not available - try to show channels from local config
set({ channels: [], loading: false });
set((state) => ({ channels: [], loading: silent ? state.loading : false }));
}
} catch {
// Gateway not connected, show empty
set({ channels: [], loading: false });
set((state) => ({ channels: [], loading: silent ? state.loading : false }));
}
},
addChannel: async (params) => {
try {
const result = await window.electron.ipcRenderer.invoke(
const result = await invokeIpc(
'gateway:rpc',
'channels.add',
params
@@ -184,13 +189,13 @@ export const useChannelsStore = create<ChannelsState>((set, get) => ({
try {
// Delete the channel configuration from openclaw.json
await window.electron.ipcRenderer.invoke('channel:deleteConfig', channelType);
await invokeIpc('channel:deleteConfig', channelType);
} catch (error) {
console.error('Failed to delete channel config:', error);
}
try {
await window.electron.ipcRenderer.invoke(
await invokeIpc(
'gateway:rpc',
'channels.delete',
{ channelId: channelType }
@@ -211,7 +216,7 @@ export const useChannelsStore = create<ChannelsState>((set, get) => ({
updateChannel(channelId, { status: 'connecting', error: undefined });
try {
const result = await window.electron.ipcRenderer.invoke(
const result = await invokeIpc(
'gateway:rpc',
'channels.connect',
{ channelId }
@@ -231,7 +236,7 @@ export const useChannelsStore = create<ChannelsState>((set, get) => ({
const { updateChannel } = get();
try {
await window.electron.ipcRenderer.invoke(
await invokeIpc(
'gateway:rpc',
'channels.disconnect',
{ channelId }
@@ -244,7 +249,7 @@ export const useChannelsStore = create<ChannelsState>((set, get) => ({
},
requestQrCode: async (channelType) => {
const result = await window.electron.ipcRenderer.invoke(
const result = await invokeIpc(
'gateway:rpc',
'channels.requestQr',
{ type: channelType }

View File

@@ -4,6 +4,7 @@
* Communicates with OpenClaw Gateway via gateway:rpc IPC.
*/
import { create } from 'zustand';
import { invokeIpc } from '@/lib/api-client';
// ── Types ────────────────────────────────────────────────────────
@@ -596,7 +597,7 @@ async function loadMissingPreviews(messages: RawMessage[]): Promise<boolean> {
if (needPreview.length === 0) return false;
try {
const thumbnails = await window.electron.ipcRenderer.invoke(
const thumbnails = await invokeIpc(
'media:getThumbnails',
needPreview,
) as Record<string, { preview: string | null; fileSize: number }>;
@@ -928,7 +929,7 @@ export const useChatStore = create<ChatState>((set, get) => ({
loadSessions: async () => {
try {
const result = await window.electron.ipcRenderer.invoke(
const result = await invokeIpc(
'gateway:rpc',
'sessions.list',
{}
@@ -1001,7 +1002,7 @@ export const useChatStore = create<ChatState>((set, get) => ({
void Promise.all(
sessionsToLabel.map(async (session) => {
try {
const r = await window.electron.ipcRenderer.invoke(
const r = await invokeIpc(
'gateway:rpc',
'chat.history',
{ sessionKey: session.key, limit: 1000 },
@@ -1077,7 +1078,7 @@ export const useChatStore = create<ChatState>((set, get) => ({
// The main process renames <suffix>.jsonl → <suffix>.deleted.jsonl so that
// sessions.list and token-usage queries both skip it automatically.
try {
const result = await window.electron.ipcRenderer.invoke('session:delete', key) as {
const result = await invokeIpc('session:delete', key) as {
success: boolean;
error?: string;
};
@@ -1185,7 +1186,7 @@ export const useChatStore = create<ChatState>((set, get) => ({
if (!quiet) set({ loading: true, error: null });
try {
const result = await window.electron.ipcRenderer.invoke(
const result = await invokeIpc(
'gateway:rpc',
'chat.history',
{ sessionKey: currentSessionKey, limit: 200 }
@@ -1425,7 +1426,7 @@ export const useChatStore = create<ChatState>((set, get) => ({
const CHAT_SEND_TIMEOUT_MS = 120_000;
if (hasMedia) {
result = await window.electron.ipcRenderer.invoke(
result = await invokeIpc(
'chat:sendWithMedia',
{
sessionKey: currentSessionKey,
@@ -1440,7 +1441,7 @@ export const useChatStore = create<ChatState>((set, get) => ({
},
) as { success: boolean; result?: { runId?: string }; error?: string };
} else {
result = await window.electron.ipcRenderer.invoke(
result = await invokeIpc(
'gateway:rpc',
'chat.send',
{
@@ -1477,7 +1478,7 @@ export const useChatStore = create<ChatState>((set, get) => ({
set({ streamingTools: [] });
try {
await window.electron.ipcRenderer.invoke(
await invokeIpc(
'gateway:rpc',
'chat.abort',
{ sessionKey: currentSessionKey },

View File

@@ -4,6 +4,7 @@
*/
import { create } from 'zustand';
import type { CronJob, CronJobCreateInput, CronJobUpdateInput } from '../types/cron';
import { invokeIpc } from '@/lib/api-client';
interface CronState {
jobs: CronJob[];
@@ -29,7 +30,7 @@ export const useCronStore = create<CronState>((set) => ({
set({ loading: true, error: null });
try {
const result = await window.electron.ipcRenderer.invoke('cron:list') as CronJob[];
const result = await invokeIpc<CronJob[]>('cron:list');
set({ jobs: result, loading: false });
} catch (error) {
set({ error: String(error), loading: false });
@@ -38,7 +39,7 @@ export const useCronStore = create<CronState>((set) => ({
createJob: async (input) => {
try {
const job = await window.electron.ipcRenderer.invoke('cron:create', input) as CronJob;
const job = await invokeIpc<CronJob>('cron:create', input);
set((state) => ({ jobs: [...state.jobs, job] }));
return job;
} catch (error) {
@@ -49,7 +50,7 @@ export const useCronStore = create<CronState>((set) => ({
updateJob: async (id, input) => {
try {
await window.electron.ipcRenderer.invoke('cron:update', id, input);
await invokeIpc('cron:update', id, input);
set((state) => ({
jobs: state.jobs.map((job) =>
job.id === id ? { ...job, ...input, updatedAt: new Date().toISOString() } : job
@@ -63,7 +64,7 @@ export const useCronStore = create<CronState>((set) => ({
deleteJob: async (id) => {
try {
await window.electron.ipcRenderer.invoke('cron:delete', id);
await invokeIpc('cron:delete', id);
set((state) => ({
jobs: state.jobs.filter((job) => job.id !== id),
}));
@@ -75,7 +76,7 @@ export const useCronStore = create<CronState>((set) => ({
toggleJob: async (id, enabled) => {
try {
await window.electron.ipcRenderer.invoke('cron:toggle', id, enabled);
await invokeIpc('cron:toggle', id, enabled);
set((state) => ({
jobs: state.jobs.map((job) =>
job.id === id ? { ...job, enabled } : job
@@ -89,11 +90,11 @@ export const useCronStore = create<CronState>((set) => ({
triggerJob: async (id) => {
try {
const result = await window.electron.ipcRenderer.invoke('cron:trigger', id);
const result = await invokeIpc<unknown>('cron:trigger', id);
console.log('Cron trigger result:', result);
// Refresh jobs after trigger to update lastRun/nextRun state
try {
const jobs = await window.electron.ipcRenderer.invoke('cron:list') as CronJob[];
const jobs = await invokeIpc<CronJob[]>('cron:list');
set({ jobs });
} catch {
// Ignore refresh error

View File

@@ -4,6 +4,7 @@
*/
import { create } from 'zustand';
import type { GatewayStatus } from '../types/gateway';
import { invokeIpc } from '@/lib/api-client';
let gatewayInitPromise: Promise<void> | null = null;
@@ -49,7 +50,7 @@ export const useGatewayStore = create<GatewayState>((set, get) => ({
gatewayInitPromise = (async () => {
try {
// Get initial status first
const status = await window.electron.ipcRenderer.invoke('gateway:status') as GatewayStatus;
const status = await invokeIpc('gateway:status') as GatewayStatus;
set({ status, isInitialized: true });
// Listen for status changes
@@ -197,7 +198,7 @@ export const useGatewayStore = create<GatewayState>((set, get) => ({
start: async () => {
try {
set({ status: { ...get().status, state: 'starting' }, lastError: null });
const result = await window.electron.ipcRenderer.invoke('gateway:start') as { success: boolean; error?: string };
const result = await invokeIpc('gateway:start') as { success: boolean; error?: string };
if (!result.success) {
set({
@@ -215,7 +216,7 @@ export const useGatewayStore = create<GatewayState>((set, get) => ({
stop: async () => {
try {
await window.electron.ipcRenderer.invoke('gateway:stop');
await invokeIpc('gateway:stop');
set({ status: { ...get().status, state: 'stopped' }, lastError: null });
} catch (error) {
console.error('Failed to stop Gateway:', error);
@@ -226,7 +227,7 @@ export const useGatewayStore = create<GatewayState>((set, get) => ({
restart: async () => {
try {
set({ status: { ...get().status, state: 'starting' }, lastError: null });
const result = await window.electron.ipcRenderer.invoke('gateway:restart') as { success: boolean; error?: string };
const result = await invokeIpc('gateway:restart') as { success: boolean; error?: string };
if (!result.success) {
set({
@@ -244,7 +245,7 @@ export const useGatewayStore = create<GatewayState>((set, get) => ({
checkHealth: async () => {
try {
const result = await window.electron.ipcRenderer.invoke('gateway:health') as {
const result = await invokeIpc('gateway:health') as {
success: boolean;
ok: boolean;
error?: string;
@@ -267,7 +268,7 @@ export const useGatewayStore = create<GatewayState>((set, get) => ({
},
rpc: async <T>(method: string, params?: unknown, timeoutMs?: number): Promise<T> => {
const result = await window.electron.ipcRenderer.invoke('gateway:rpc', method, params, timeoutMs) as {
const result = await invokeIpc('gateway:rpc', method, params, timeoutMs) as {
success: boolean;
result?: T;
error?: string;

View File

@@ -4,6 +4,7 @@
*/
import { create } from 'zustand';
import type { ProviderConfig, ProviderWithKeyInfo } from '@/lib/providers';
import { invokeIpc } from '@/lib/api-client';
// Re-export types for consumers that imported from here
export type { ProviderConfig, ProviderWithKeyInfo } from '@/lib/providers';
@@ -45,8 +46,8 @@ export const useProviderStore = create<ProviderState>((set, get) => ({
set({ loading: true, error: null });
try {
const providers = await window.electron.ipcRenderer.invoke('provider:list') as ProviderWithKeyInfo[];
const defaultId = await window.electron.ipcRenderer.invoke('provider:getDefault') as string | null;
const providers = await invokeIpc<ProviderWithKeyInfo[]>('provider:list');
const defaultId = await invokeIpc<string | null>('provider:getDefault');
set({
providers,
@@ -66,7 +67,7 @@ export const useProviderStore = create<ProviderState>((set, get) => ({
updatedAt: new Date().toISOString(),
};
const result = await window.electron.ipcRenderer.invoke('provider:save', fullConfig, apiKey) as { success: boolean; error?: string };
const result = await invokeIpc<{ success: boolean; error?: string }>('provider:save', fullConfig, apiKey);
if (!result.success) {
throw new Error(result.error || 'Failed to save provider');
@@ -95,7 +96,7 @@ export const useProviderStore = create<ProviderState>((set, get) => ({
updatedAt: new Date().toISOString(),
};
const result = await window.electron.ipcRenderer.invoke('provider:save', updatedConfig, apiKey) as { success: boolean; error?: string };
const result = await invokeIpc<{ success: boolean; error?: string }>('provider:save', updatedConfig, apiKey);
if (!result.success) {
throw new Error(result.error || 'Failed to update provider');
@@ -111,7 +112,7 @@ export const useProviderStore = create<ProviderState>((set, get) => ({
deleteProvider: async (providerId) => {
try {
const result = await window.electron.ipcRenderer.invoke('provider:delete', providerId) as { success: boolean; error?: string };
const result = await invokeIpc<{ success: boolean; error?: string }>('provider:delete', providerId);
if (!result.success) {
throw new Error(result.error || 'Failed to delete provider');
@@ -127,7 +128,7 @@ export const useProviderStore = create<ProviderState>((set, get) => ({
setApiKey: async (providerId, apiKey) => {
try {
const result = await window.electron.ipcRenderer.invoke('provider:setApiKey', providerId, apiKey) as { success: boolean; error?: string };
const result = await invokeIpc<{ success: boolean; error?: string }>('provider:setApiKey', providerId, apiKey);
if (!result.success) {
throw new Error(result.error || 'Failed to set API key');
@@ -143,12 +144,12 @@ export const useProviderStore = create<ProviderState>((set, get) => ({
updateProviderWithKey: async (providerId, updates, apiKey) => {
try {
const result = await window.electron.ipcRenderer.invoke(
const result = await invokeIpc<{ success: boolean; error?: string }>(
'provider:updateWithKey',
providerId,
updates,
apiKey
) as { success: boolean; error?: string };
);
if (!result.success) {
throw new Error(result.error || 'Failed to update provider');
@@ -163,7 +164,7 @@ export const useProviderStore = create<ProviderState>((set, get) => ({
deleteApiKey: async (providerId) => {
try {
const result = await window.electron.ipcRenderer.invoke('provider:deleteApiKey', providerId) as { success: boolean; error?: string };
const result = await invokeIpc<{ success: boolean; error?: string }>('provider:deleteApiKey', providerId);
if (!result.success) {
throw new Error(result.error || 'Failed to delete API key');
@@ -179,7 +180,7 @@ export const useProviderStore = create<ProviderState>((set, get) => ({
setDefaultProvider: async (providerId) => {
try {
const result = await window.electron.ipcRenderer.invoke('provider:setDefault', providerId) as { success: boolean; error?: string };
const result = await invokeIpc<{ success: boolean; error?: string }>('provider:setDefault', providerId);
if (!result.success) {
throw new Error(result.error || 'Failed to set default provider');
@@ -194,12 +195,12 @@ export const useProviderStore = create<ProviderState>((set, get) => ({
validateApiKey: async (providerId, apiKey, options) => {
try {
const result = await window.electron.ipcRenderer.invoke(
const result = await invokeIpc<{ valid: boolean; error?: string }>(
'provider:validateKey',
providerId,
apiKey,
options
) as { valid: boolean; error?: string };
);
return result;
} catch (error) {
return { valid: false, error: String(error) };
@@ -208,7 +209,7 @@ export const useProviderStore = create<ProviderState>((set, get) => ({
getApiKey: async (providerId) => {
try {
return await window.electron.ipcRenderer.invoke('provider:getApiKey', providerId) as string | null;
return await invokeIpc<string | null>('provider:getApiKey', providerId);
} catch {
return null;
}

View File

@@ -5,9 +5,11 @@
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import i18n from '@/i18n';
import { invokeIpc } from '@/lib/api-client';
type Theme = 'light' | 'dark' | 'system';
type UpdateChannel = 'stable' | 'beta' | 'dev';
type GatewayTransportPreference = 'ws-first' | 'http-first' | 'ws-only' | 'http-only' | 'ipc-only';
interface SettingsState {
// General
@@ -25,6 +27,7 @@ interface SettingsState {
proxyHttpsServer: string;
proxyAllServer: string;
proxyBypassRules: string;
gatewayTransportPreference: GatewayTransportPreference;
// Update
updateChannel: UpdateChannel;
@@ -52,6 +55,7 @@ interface SettingsState {
setProxyHttpsServer: (value: string) => void;
setProxyAllServer: (value: string) => void;
setProxyBypassRules: (value: string) => void;
setGatewayTransportPreference: (value: GatewayTransportPreference) => void;
setUpdateChannel: (channel: UpdateChannel) => void;
setAutoCheckUpdate: (value: boolean) => void;
setAutoDownloadUpdate: (value: boolean) => void;
@@ -79,6 +83,7 @@ const defaultSettings = {
proxyHttpsServer: '',
proxyAllServer: '',
proxyBypassRules: '<local>;localhost;127.0.0.1;::1',
gatewayTransportPreference: 'ws-first' as GatewayTransportPreference,
updateChannel: 'stable' as UpdateChannel,
autoCheckUpdate: true,
autoDownloadUpdate: false,
@@ -94,7 +99,7 @@ export const useSettingsStore = create<SettingsState>()(
init: async () => {
try {
const settings = await window.electron.ipcRenderer.invoke('settings:getAll') as Partial<typeof defaultSettings>;
const settings = await invokeIpc<Partial<typeof defaultSettings>>('settings:getAll');
set((state) => ({ ...state, ...settings }));
if (settings.language) {
i18n.changeLanguage(settings.language);
@@ -106,17 +111,21 @@ export const useSettingsStore = create<SettingsState>()(
},
setTheme: (theme) => set({ theme }),
setLanguage: (language) => { i18n.changeLanguage(language); set({ language }); void window.electron.ipcRenderer.invoke('settings:set', 'language', language).catch(() => {}); },
setLanguage: (language) => { i18n.changeLanguage(language); set({ language }); void invokeIpc('settings:set', 'language', language).catch(() => {}); },
setStartMinimized: (startMinimized) => set({ startMinimized }),
setLaunchAtStartup: (launchAtStartup) => set({ launchAtStartup }),
setGatewayAutoStart: (gatewayAutoStart) => { set({ gatewayAutoStart }); void window.electron.ipcRenderer.invoke('settings:set', 'gatewayAutoStart', gatewayAutoStart).catch(() => {}); },
setGatewayPort: (gatewayPort) => { set({ gatewayPort }); void window.electron.ipcRenderer.invoke('settings:set', 'gatewayPort', gatewayPort).catch(() => {}); },
setGatewayAutoStart: (gatewayAutoStart) => { set({ gatewayAutoStart }); void invokeIpc('settings:set', 'gatewayAutoStart', gatewayAutoStart).catch(() => {}); },
setGatewayPort: (gatewayPort) => { set({ gatewayPort }); void invokeIpc('settings:set', 'gatewayPort', gatewayPort).catch(() => {}); },
setProxyEnabled: (proxyEnabled) => set({ proxyEnabled }),
setProxyServer: (proxyServer) => set({ proxyServer }),
setProxyHttpServer: (proxyHttpServer) => set({ proxyHttpServer }),
setProxyHttpsServer: (proxyHttpsServer) => set({ proxyHttpsServer }),
setProxyAllServer: (proxyAllServer) => set({ proxyAllServer }),
setProxyBypassRules: (proxyBypassRules) => set({ proxyBypassRules }),
setGatewayTransportPreference: (gatewayTransportPreference) => {
set({ gatewayTransportPreference });
void invokeIpc('settings:set', 'gatewayTransportPreference', gatewayTransportPreference).catch(() => {});
},
setUpdateChannel: (updateChannel) => set({ updateChannel }),
setAutoCheckUpdate: (autoCheckUpdate) => set({ autoCheckUpdate }),
setAutoDownloadUpdate: (autoDownloadUpdate) => set({ autoDownloadUpdate }),

View File

@@ -4,6 +4,7 @@
*/
import { create } from 'zustand';
import type { Skill, MarketplaceSkill } from '../types/skill';
import { invokeIpc } from '@/lib/api-client';
type GatewaySkillStatus = {
skillKey: string;
@@ -70,20 +71,20 @@ export const useSkillsStore = create<SkillsState>((set, get) => ({
}
try {
// 1. Fetch from Gateway (running skills)
const gatewayResult = await window.electron.ipcRenderer.invoke(
const gatewayResult = await invokeIpc<GatewayRpcResponse<GatewaySkillsStatusResult>>(
'gateway:rpc',
'skills.status'
) as GatewayRpcResponse<GatewaySkillsStatusResult>;
);
// 2. Fetch from ClawHub (installed on disk)
const clawhubResult = await window.electron.ipcRenderer.invoke(
const clawhubResult = await invokeIpc<{ success: boolean; results?: ClawHubListResult[]; error?: string }>(
'clawhub:list'
) as { success: boolean; results?: ClawHubListResult[]; error?: string };
);
// 3. Fetch configurations directly from Electron (since Gateway doesn't return them)
const configResult = await window.electron.ipcRenderer.invoke(
const configResult = await invokeIpc<Record<string, { apiKey?: string; env?: Record<string, string> }>>(
'skill:getAllConfigs'
) as Record<string, { apiKey?: string; env?: Record<string, string> }>;
);
let combinedSkills: Skill[] = [];
const currentSkills = get().skills;
@@ -155,7 +156,7 @@ export const useSkillsStore = create<SkillsState>((set, get) => ({
searchSkills: async (query: string) => {
set({ searching: true, searchError: null });
try {
const result = await window.electron.ipcRenderer.invoke('clawhub:search', { query }) as { success: boolean; results?: MarketplaceSkill[]; error?: string };
const result = await invokeIpc<{ success: boolean; results?: MarketplaceSkill[]; error?: string }>('clawhub:search', { query });
if (result.success) {
set({ searchResults: result.results || [] });
} else {
@@ -177,7 +178,7 @@ export const useSkillsStore = create<SkillsState>((set, get) => ({
installSkill: async (slug: string, version?: string) => {
set((state) => ({ installing: { ...state.installing, [slug]: true } }));
try {
const result = await window.electron.ipcRenderer.invoke('clawhub:install', { slug, version }) as { success: boolean; error?: string };
const result = await invokeIpc<{ success: boolean; error?: string }>('clawhub:install', { slug, version });
if (!result.success) {
if (result.error?.includes('Timeout')) {
throw new Error('installTimeoutError');
@@ -204,7 +205,7 @@ export const useSkillsStore = create<SkillsState>((set, get) => ({
uninstallSkill: async (slug: string) => {
set((state) => ({ installing: { ...state.installing, [slug]: true } }));
try {
const result = await window.electron.ipcRenderer.invoke('clawhub:uninstall', { slug }) as { success: boolean; error?: string };
const result = await invokeIpc<{ success: boolean; error?: string }>('clawhub:uninstall', { slug });
if (!result.success) {
throw new Error(result.error || 'Uninstall failed');
}
@@ -226,11 +227,11 @@ export const useSkillsStore = create<SkillsState>((set, get) => ({
const { updateSkill } = get();
try {
const result = await window.electron.ipcRenderer.invoke(
const result = await invokeIpc<GatewayRpcResponse<unknown>>(
'gateway:rpc',
'skills.update',
{ skillKey: skillId, enabled: true }
) as GatewayRpcResponse<unknown>;
);
if (result.success) {
updateSkill(skillId, { enabled: true });
@@ -252,11 +253,11 @@ export const useSkillsStore = create<SkillsState>((set, get) => ({
}
try {
const result = await window.electron.ipcRenderer.invoke(
const result = await invokeIpc<GatewayRpcResponse<unknown>>(
'gateway:rpc',
'skills.update',
{ skillKey: skillId, enabled: false }
) as GatewayRpcResponse<unknown>;
);
if (result.success) {
updateSkill(skillId, { enabled: false });

View File

@@ -4,6 +4,7 @@
*/
import { create } from 'zustand';
import { useSettingsStore } from './settings';
import { invokeIpc } from '@/lib/api-client';
export interface UpdateInfo {
version: string;
@@ -63,7 +64,7 @@ export const useUpdateStore = create<UpdateState>((set, get) => ({
// Get current version
try {
const version = await window.electron.ipcRenderer.invoke('update:version');
const version = await invokeIpc<string>('update:version');
set({ currentVersion: version as string });
} catch (error) {
console.error('Failed to get version:', error);
@@ -71,12 +72,12 @@ export const useUpdateStore = create<UpdateState>((set, get) => ({
// Get current status
try {
const status = await window.electron.ipcRenderer.invoke('update:status') as {
const status = await invokeIpc<{
status: UpdateStatus;
info?: UpdateInfo;
progress?: ProgressInfo;
error?: string;
};
}>('update:status');
set({
status: status.status,
updateInfo: status.info || null,
@@ -117,7 +118,7 @@ export const useUpdateStore = create<UpdateState>((set, get) => ({
// Sync auto-download preference to the main process
if (autoDownloadUpdate) {
window.electron.ipcRenderer.invoke('update:setAutoDownload', true).catch(() => {});
invokeIpc('update:setAutoDownload', true).catch(() => {});
}
// Auto-check for updates on startup (respects user toggle)
@@ -133,7 +134,7 @@ export const useUpdateStore = create<UpdateState>((set, get) => ({
try {
const result = await Promise.race([
window.electron.ipcRenderer.invoke('update:check'),
invokeIpc('update:check'),
new Promise((_, reject) => setTimeout(() => reject(new Error('Update check timed out')), 30000))
]) as {
success: boolean;
@@ -172,10 +173,10 @@ export const useUpdateStore = create<UpdateState>((set, get) => ({
set({ status: 'downloading', error: null });
try {
const result = await window.electron.ipcRenderer.invoke('update:download') as {
const result = await invokeIpc<{
success: boolean;
error?: string;
};
}>('update:download');
if (!result.success) {
set({ status: 'error', error: result.error || 'Failed to download update' });
@@ -186,12 +187,12 @@ export const useUpdateStore = create<UpdateState>((set, get) => ({
},
installUpdate: () => {
window.electron.ipcRenderer.invoke('update:install');
void invokeIpc('update:install');
},
cancelAutoInstall: async () => {
try {
await window.electron.ipcRenderer.invoke('update:cancelAutoInstall');
await invokeIpc('update:cancelAutoInstall');
} catch (error) {
console.error('Failed to cancel auto-install:', error);
}
@@ -199,7 +200,7 @@ export const useUpdateStore = create<UpdateState>((set, get) => ({
setChannel: async (channel) => {
try {
await window.electron.ipcRenderer.invoke('update:setChannel', channel);
await invokeIpc('update:setChannel', channel);
} catch (error) {
console.error('Failed to set update channel:', error);
}
@@ -207,7 +208,7 @@ export const useUpdateStore = create<UpdateState>((set, get) => ({
setAutoDownload: async (enable) => {
try {
await window.electron.ipcRenderer.invoke('update:setAutoDownload', enable);
await invokeIpc('update:setAutoDownload', enable);
} catch (error) {
console.error('Failed to set auto-download:', error);
}