import { app, BrowserWindow, ipcMain } from 'electron' import { CONFIG_KEYS } from '@runtime/lib/constants' import { setupMainWindow } from './wins'; import started from 'electron-squirrel-startup' import configManager from '@electron/service/config-service' import themeManager from '@electron/service/theme-service' import { runTaskOperationService } from '@electron/process/runTaskOperationService' import { initScriptStoreService } from '@electron/service/script-store-service' import log from 'electron-log'; import 'bytenode'; // Ensure bytenode is bundled/externalized correctly import { appUpdater } from '@electron/service/updater'; import axios from 'axios'; import { onProviderChange } from '@electron/service/provider-api-service'; import { gatewayManager } from '@electron/gateway/manager'; import { dispatchLocalHostApi } from '@electron/api/router'; import { syncProviderRuntimeSnapshot } from '@electron/service/provider-runtime-sync'; import { ensureBuiltinSkillsInstalled, ensurePreinstalledSkillsInstalled } from '@electron/utils/skill-config'; // 初始化 updater,确保在 app ready 之前或者之中注册好 IPC appUpdater.init(); // 注册 hostapi:fetch IPC 代理 // 模型管理相关接口在本地处理(对齐 ClawX),其余接口代理到远端后端 const HOST_API_BASE_URL = process.env['ZN_AI_HOST_API_BASE_URL'] || process.env['VITE_SERVICE_URL'] || 'http://8.138.234.141/ingress'; function refreshProviderRuntime(): { warnings: string[] } { try { return syncProviderRuntimeSnapshot(); } catch (error) { log.error('provider runtime sync failed', error); return { warnings: [error instanceof Error ? error.message : String(error)], }; } } async function requestUpstreamHostApi(path: string, method: string, headers: Record | undefined, body: unknown) { const url = `${HOST_API_BASE_URL}${path}`; try { const response = await axios({ url, method, headers: { 'Content-Type': 'application/json', ...headers, }, data: body ?? undefined, timeout: 30000, }); return { success: true, ok: true, json: response.data, data: response.data, }; } catch (error: any) { if (error.response) { return { success: false, ok: false, status: error.response.status, error: error.response.data?.message || error.message, text: error.response.statusText, data: error.response.data, }; } return { success: false, ok: false, error: error.message || 'Unknown error', }; } } ipcMain.handle('hostapi:fetch', async (_event, { path, method, headers, body }) => { const normalizedMethod = method || 'GET'; // 1. 优先本地处理 Host API 路由(逐步对齐 ClawX) const localResult = await dispatchLocalHostApi({ path, method: normalizedMethod, headers, body, }); if (localResult) return localResult; // 2. 其余接口代理到远端后端 return await requestUpstreamHostApi(path, normalizedMethod, headers, body); }); // Gateway RPC IPC handler ipcMain.handle('gateway:rpc', async (_event, method: string, params: any) => { return gatewayManager.rpc(method, params); }); // import logManager from '@electron/service/logger' // Handle creating/removing shortcuts on Windows when installing/uninstalling. if (started) { app.quit(); } // process.on('uncaughtException', (err) => { // logManager.error('uncaughtException', err); // }); // process.on('unhandledRejection', (reason, promise) => { // logManager.error('unhandledRejection', reason, promise); // }); app.whenReady().then(async () => { await configManager.init(); await themeManager.init(); void ensureBuiltinSkillsInstalled().catch((error) => { log.warn('Failed to install built-in skills:', error); }); void ensurePreinstalledSkillsInstalled().catch((error) => { log.warn('Failed to install preinstalled skills:', error); }); gatewayManager.init(); refreshProviderRuntime(); onProviderChange(() => { const runtimeSync = refreshProviderRuntime(); gatewayManager.reloadProviders({ topics: ['providers', 'models', 'agents'], reason: 'providers:changed', warnings: runtimeSync.warnings, }); }); setupMainWindow(); // 初始化脚本存储服务 initScriptStoreService() // 开启任务操作子进程 runTaskOperationService() // 开启subagent子进程 }); // Quit when all windows are closed, except on macOS. There, it's common // for applications and their menu bar to stay active until the user quits // explicitly with Cmd + Q. app.on('window-all-closed', () => { if (process.platform !== 'darwin' && !configManager.get(CONFIG_KEYS.MINIMIZE_TO_TRAY)) { log.info('app closing due to all windows being closed'); app.quit(); } }); // On OS X it's common to re-create a window in the app when the // dock icon is clicked and there are no other windows open. app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) { setupMainWindow(); } });