Files
zn-ai/electron/main.ts
duanshuwen ef46c73c3e Add unit tests for channel utilities and configure testing environment
- Created a new test file `channels.test.ts` to cover utilities related to channel configurations and targets.
- Implemented tests for normalizing and grouping selected channels by type, as well as building channel targets from account data and cron history.
- Mocked necessary dependencies to isolate tests and ensure accurate results.
- Updated `vite.config.ts` to set up the testing environment with jsdom and enable global variables for tests.
2026-04-18 16:12:49 +08:00

157 lines
4.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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';
// 初始化 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<string, string> | 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();
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();
}
});