mirror of
https://github.com/lone-cloud/gerbil
synced 2026-06-03 19:54:44 -07:00
refactor to use a util to try/catch->logError, hover states on badges
This commit is contained in:
parent
2723b79c6e
commit
38c504be8b
23 changed files with 264 additions and 276 deletions
1
.github/copilot-instructions.md
vendored
1
.github/copilot-instructions.md
vendored
|
|
@ -10,6 +10,7 @@
|
||||||
- Stop asking me to run the "dev" script to test changes
|
- Stop asking me to run the "dev" script to test changes
|
||||||
- Try to move helper functions from component code to their own separate files to help minimize clutter
|
- Try to move helper functions from component code to their own separate files to help minimize clutter
|
||||||
- Always use absolute imports (e.g. `import { MyComponent } from '@/components/MyComponent'`)
|
- Always use absolute imports (e.g. `import { MyComponent } from '@/components/MyComponent'`)
|
||||||
|
- Never add explicit return types to functions. We want to rely on implicit types as much as possible
|
||||||
|
|
||||||
### Logging and Error Handling
|
### Logging and Error Handling
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "gerbil",
|
"name": "gerbil",
|
||||||
"productName": "Gerbil",
|
"productName": "Gerbil",
|
||||||
"version": "1.4.0-beta.2",
|
"version": "1.4.0-beta.3",
|
||||||
"description": "Run Large Language Models locally",
|
"description": "Run Large Language Models locally",
|
||||||
"main": "out/main/index.js",
|
"main": "out/main/index.js",
|
||||||
"homepage": "./",
|
"homepage": "./",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
import { Badge, Tooltip } from '@mantine/core';
|
import { Badge, Tooltip } from '@mantine/core';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { safeExecute } from '@/utils/logger';
|
||||||
|
|
||||||
interface PerformanceBadgeProps {
|
interface PerformanceBadgeProps {
|
||||||
label: string;
|
label: string;
|
||||||
|
|
@ -11,20 +13,19 @@ export const PerformanceBadge = ({
|
||||||
value,
|
value,
|
||||||
tooltipLabel,
|
tooltipLabel,
|
||||||
}: PerformanceBadgeProps) => {
|
}: PerformanceBadgeProps) => {
|
||||||
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
|
|
||||||
const handlePerformanceClick = async () => {
|
const handlePerformanceClick = async () => {
|
||||||
try {
|
const result = await safeExecute(
|
||||||
const result = await window.electronAPI.app.openPerformanceManager();
|
() => window.electronAPI.app.openPerformanceManager(),
|
||||||
if (!result.success) {
|
'Failed to open performance manager'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result && !result.success) {
|
||||||
window.electronAPI.logs.logError(
|
window.electronAPI.logs.logError(
|
||||||
`Failed to open performance manager: ${result.error}`
|
`Failed to open performance manager: ${result.error}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
window.electronAPI.logs.logError(
|
|
||||||
'Error opening performance manager',
|
|
||||||
error as Error
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -36,7 +37,13 @@ export const PerformanceBadge = ({
|
||||||
minWidth: '5rem',
|
minWidth: '5rem',
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
|
transition: 'background-color 0.2s ease',
|
||||||
|
backgroundColor: isHovered
|
||||||
|
? 'var(--mantine-color-blue-1)'
|
||||||
|
: undefined,
|
||||||
}}
|
}}
|
||||||
|
onMouseEnter={() => setIsHovered(true)}
|
||||||
|
onMouseLeave={() => setIsHovered(false)}
|
||||||
onClick={handlePerformanceClick}
|
onClick={handlePerformanceClick}
|
||||||
>
|
>
|
||||||
{label}: {value}
|
{label}: {value}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useState, useCallback, useEffect } from 'react';
|
import { useState, useCallback, useEffect } from 'react';
|
||||||
import { logError, safeExecute } from '@/utils/logger';
|
import { safeExecute, tryExecute } from '@/utils/logger';
|
||||||
import { compareVersions } from '@/utils/version';
|
import { compareVersions } from '@/utils/version';
|
||||||
import { GITHUB_API } from '@/constants';
|
import { GITHUB_API } from '@/constants';
|
||||||
|
|
||||||
|
|
@ -40,16 +40,15 @@ export const useAppUpdateChecker = () => {
|
||||||
if (!canAutoUpdate) return;
|
if (!canAutoUpdate) return;
|
||||||
|
|
||||||
setIsDownloading(true);
|
setIsDownloading(true);
|
||||||
try {
|
|
||||||
|
await tryExecute(async () => {
|
||||||
const success = await window.electronAPI.updater.downloadUpdate();
|
const success = await window.electronAPI.updater.downloadUpdate();
|
||||||
if (success) {
|
if (success) {
|
||||||
setIsUpdateDownloaded(true);
|
setIsUpdateDownloaded(true);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
}, 'Failed to download update');
|
||||||
logError('Failed to download update:', err as Error);
|
|
||||||
} finally {
|
|
||||||
setIsDownloading(false);
|
setIsDownloading(false);
|
||||||
}
|
|
||||||
}, [canAutoUpdate]);
|
}, [canAutoUpdate]);
|
||||||
|
|
||||||
const installUpdate = useCallback(() => {
|
const installUpdate = useCallback(() => {
|
||||||
|
|
@ -59,7 +58,7 @@ export const useAppUpdateChecker = () => {
|
||||||
}, [isUpdateDownloaded]);
|
}, [isUpdateDownloaded]);
|
||||||
|
|
||||||
const checkForAppUpdates = useCallback(async () => {
|
const checkForAppUpdates = useCallback(async () => {
|
||||||
try {
|
await tryExecute(async () => {
|
||||||
const currentVersion = await window.electronAPI.app.getVersion();
|
const currentVersion = await window.electronAPI.app.getVersion();
|
||||||
|
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
|
|
@ -87,9 +86,7 @@ export const useAppUpdateChecker = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
setUpdateInfo(updateInfo);
|
setUpdateInfo(updateInfo);
|
||||||
} catch (err) {
|
}, 'Failed to check for app updates');
|
||||||
logError('Failed to check for app updates:', err as Error);
|
|
||||||
}
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
import { logError } from '@/utils/logger';
|
import { logError, tryExecuteImmediate } from '@/utils/logger';
|
||||||
import { getROCmDownload } from '@/utils/rocm';
|
import { getROCmDownload } from '@/utils/rocm';
|
||||||
import { GITHUB_API } from '@/constants';
|
import { GITHUB_API } from '@/constants';
|
||||||
import { filterAssetsByPlatform } from '@/utils/platform';
|
import { filterAssetsByPlatform } from '@/utils/platform';
|
||||||
|
|
@ -41,15 +41,13 @@ const loadFromCache = (): CachedReleaseData | null => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const saveToCache = (releases: DownloadItem[]) => {
|
const saveToCache = (releases: DownloadItem[]) => {
|
||||||
try {
|
tryExecuteImmediate(() => {
|
||||||
const data: CachedReleaseData = {
|
const data: CachedReleaseData = {
|
||||||
releases,
|
releases,
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
};
|
};
|
||||||
localStorage.setItem(CACHE_KEY, JSON.stringify(data));
|
localStorage.setItem(CACHE_KEY, JSON.stringify(data));
|
||||||
} catch (err) {
|
}, 'Failed to save releases to cache');
|
||||||
logError('Failed to save releases to cache', err as Error);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const transformReleaseToDownloadItems = (
|
const transformReleaseToDownloadItems = (
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
import { logError } from '@/utils/logger';
|
import { tryExecute } from '@/utils/logger';
|
||||||
import type { SdConvDirectMode } from '@/types';
|
import type { SdConvDirectMode } from '@/types';
|
||||||
|
|
||||||
interface UseLaunchLogicProps {
|
interface UseLaunchLogicProps {
|
||||||
|
|
@ -258,7 +258,7 @@ export const useLaunchLogic = ({
|
||||||
|
|
||||||
onLaunch();
|
onLaunch();
|
||||||
|
|
||||||
try {
|
await tryExecute(async () => {
|
||||||
const args: string[] = [
|
const args: string[] = [
|
||||||
...buildModelArgs(model, sdmodel, launchArgs),
|
...buildModelArgs(model, sdmodel, launchArgs),
|
||||||
...buildConfigArgs(hasImageModel, launchArgs),
|
...buildConfigArgs(hasImageModel, launchArgs),
|
||||||
|
|
@ -283,11 +283,9 @@ export const useLaunchLogic = ({
|
||||||
new Error(errorMessage)
|
new Error(errorMessage)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
}, 'Error launching');
|
||||||
logError('Error launching:', err as Error);
|
|
||||||
} finally {
|
|
||||||
setIsLaunching(false);
|
setIsLaunching(false);
|
||||||
}
|
|
||||||
},
|
},
|
||||||
[model, sdmodel, isLaunching, onLaunch]
|
[model, sdmodel, isLaunching, onLaunch]
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useState, useCallback, useEffect } from 'react';
|
import { useState, useCallback, useEffect } from 'react';
|
||||||
import { logError } from '@/utils/logger';
|
import { tryExecute, safeExecute } from '@/utils/logger';
|
||||||
import {
|
import {
|
||||||
getDisplayNameFromPath,
|
getDisplayNameFromPath,
|
||||||
compareVersions,
|
compareVersions,
|
||||||
|
|
@ -27,32 +27,30 @@ export const useUpdateChecker = () => {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadDismissedUpdates = async () => {
|
const loadDismissedUpdates = async () => {
|
||||||
try {
|
const dismissed = await safeExecute(
|
||||||
const dismissed = (await window.electronAPI.config.get(
|
async () =>
|
||||||
'dismissedUpdates'
|
(await window.electronAPI.config.get('dismissedUpdates')) as
|
||||||
)) as string[] | undefined;
|
| string[]
|
||||||
|
| undefined,
|
||||||
|
'Failed to load dismissed updates'
|
||||||
|
);
|
||||||
|
|
||||||
if (dismissed) {
|
if (dismissed) {
|
||||||
setDismissedUpdates(new Set(dismissed));
|
setDismissedUpdates(new Set(dismissed));
|
||||||
}
|
}
|
||||||
setDismissedUpdatesLoaded(true);
|
setDismissedUpdatesLoaded(true);
|
||||||
} catch (err) {
|
|
||||||
logError('Failed to load dismissed updates:', err as Error);
|
|
||||||
setDismissedUpdatesLoaded(true);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
loadDismissedUpdates();
|
loadDismissedUpdates();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const saveDismissedUpdates = useCallback(async (updates: Set<string>) => {
|
const saveDismissedUpdates = useCallback(async (updates: Set<string>) => {
|
||||||
try {
|
await tryExecute(async () => {
|
||||||
await window.electronAPI.config.set(
|
await window.electronAPI.config.set(
|
||||||
'dismissedUpdates',
|
'dismissedUpdates',
|
||||||
Array.from(updates)
|
Array.from(updates)
|
||||||
);
|
);
|
||||||
} catch (err) {
|
}, 'Failed to save dismissed updates');
|
||||||
logError('Failed to save dismissed updates:', err as Error);
|
|
||||||
}
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const checkForUpdates = useCallback(async () => {
|
const checkForUpdates = useCallback(async () => {
|
||||||
|
|
@ -62,7 +60,7 @@ export const useUpdateChecker = () => {
|
||||||
|
|
||||||
setIsChecking(true);
|
setIsChecking(true);
|
||||||
|
|
||||||
try {
|
await tryExecute(async () => {
|
||||||
const [currentVersion, rocmDownload] = await Promise.all([
|
const [currentVersion, rocmDownload] = await Promise.all([
|
||||||
window.electronAPI.kobold.getCurrentVersion(),
|
window.electronAPI.kobold.getCurrentVersion(),
|
||||||
getROCmDownload(),
|
getROCmDownload(),
|
||||||
|
|
@ -105,11 +103,9 @@ export const useUpdateChecker = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
}, 'Failed to check for updates');
|
||||||
logError('Failed to check for updates:', err as Error);
|
|
||||||
} finally {
|
|
||||||
setIsChecking(false);
|
setIsChecking(false);
|
||||||
}
|
|
||||||
}, [dismissedUpdates, dismissedUpdatesLoaded, releases]);
|
}, [dismissedUpdates, dismissedUpdatesLoaded, releases]);
|
||||||
|
|
||||||
const dismissUpdate = useCallback(async () => {
|
const dismissUpdate = useCallback(async () => {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import {
|
||||||
initialize as initializeConfig,
|
initialize as initializeConfig,
|
||||||
getInstallDir,
|
getInstallDir,
|
||||||
} from '@/main/modules/config';
|
} from '@/main/modules/config';
|
||||||
import { logError } from '@/main/modules/logging';
|
import { safeExecute } from '@/utils/node/logger';
|
||||||
import { cleanup } from '@/main/modules/koboldcpp';
|
import { cleanup } from '@/main/modules/koboldcpp';
|
||||||
import { getSillyTavernManager } from '@/main/modules/sillytavern';
|
import { getSillyTavernManager } from '@/main/modules/sillytavern';
|
||||||
import { cleanup as cleanupOpenWebUI } from '@/main/modules/openwebui';
|
import { cleanup as cleanupOpenWebUI } from '@/main/modules/openwebui';
|
||||||
|
|
@ -38,7 +38,7 @@ export async function initializeApp() {
|
||||||
app.on('before-quit', async (event) => {
|
app.on('before-quit', async (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
try {
|
await safeExecute(async () => {
|
||||||
const cleanupPromises = [
|
const cleanupPromises = [
|
||||||
cleanup(),
|
cleanup(),
|
||||||
getSillyTavernManager().cleanup(),
|
getSillyTavernManager().cleanup(),
|
||||||
|
|
@ -53,9 +53,7 @@ export async function initializeApp() {
|
||||||
});
|
});
|
||||||
|
|
||||||
await Promise.race([Promise.all(cleanupPromises), timeoutPromise]);
|
await Promise.race([Promise.all(cleanupPromises), timeoutPromise]);
|
||||||
} catch (error) {
|
}, 'Error during cleanup');
|
||||||
logError('Error during cleanup:', error as Error);
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanupWindow();
|
cleanupWindow();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import {
|
||||||
setColorScheme,
|
setColorScheme,
|
||||||
} from '@/main/modules/config';
|
} from '@/main/modules/config';
|
||||||
import { logError } from '@/main/modules/logging';
|
import { logError } from '@/main/modules/logging';
|
||||||
|
import { safeExecute } from '@/utils/node/logger';
|
||||||
import { getSillyTavernManager } from '@/main/modules/sillytavern';
|
import { getSillyTavernManager } from '@/main/modules/sillytavern';
|
||||||
import {
|
import {
|
||||||
startFrontend as startOpenWebUIFrontend,
|
startFrontend as startOpenWebUIFrontend,
|
||||||
|
|
@ -64,8 +65,8 @@ import {
|
||||||
import type { FrontendPreference } from '@/types';
|
import type { FrontendPreference } from '@/types';
|
||||||
import { getAppVersion } from '@/utils/node/fs';
|
import { getAppVersion } from '@/utils/node/fs';
|
||||||
|
|
||||||
async function launchKoboldCppWithCustomFrontends(args: string[] = []) {
|
const launchKoboldCppWithCustomFrontends = async (args: string[] = []) =>
|
||||||
try {
|
(await safeExecute(async () => {
|
||||||
const frontendPreference = (await getConfig(
|
const frontendPreference = (await getConfig(
|
||||||
'frontendPreference'
|
'frontendPreference'
|
||||||
)) as FrontendPreference;
|
)) as FrontendPreference;
|
||||||
|
|
@ -83,14 +84,10 @@ async function launchKoboldCppWithCustomFrontends(args: string[] = []) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
} catch (error) {
|
}, 'Error in enhanced launch')) || {
|
||||||
logError('Error in enhanced launch:', error as Error);
|
|
||||||
return {
|
|
||||||
success: false,
|
success: false,
|
||||||
error: (error as Error).message,
|
error: 'Launch failed',
|
||||||
};
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setupIPCHandlers() {
|
export function setupIPCHandlers() {
|
||||||
ipcMain.handle('kobold:downloadRelease', async (_, asset) => ({
|
ipcMain.handle('kobold:downloadRelease', async (_, asset) => ({
|
||||||
|
|
@ -188,18 +185,18 @@ export function setupIPCHandlers() {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('app:showLogsFolder', async () => {
|
ipcMain.handle(
|
||||||
try {
|
'app:showLogsFolder',
|
||||||
|
async () =>
|
||||||
|
(await safeExecute(async () => {
|
||||||
const logsDir = join(app.getPath('userData'), 'logs');
|
const logsDir = join(app.getPath('userData'), 'logs');
|
||||||
await shell.openPath(logsDir);
|
await shell.openPath(logsDir);
|
||||||
return { success: true };
|
return { success: true };
|
||||||
} catch (error) {
|
}, 'Failed to open logs folder')) || {
|
||||||
logError('Failed to open logs folder:', error as Error);
|
success: false,
|
||||||
throw new Error(
|
error: 'Failed to open logs folder',
|
||||||
`Failed to open logs folder: ${(error as Error).message}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
ipcMain.handle('app:minimizeWindow', () => getMainWindow()?.minimize());
|
ipcMain.handle('app:minimizeWindow', () => getMainWindow()?.minimize());
|
||||||
|
|
||||||
|
|
@ -239,17 +236,17 @@ export function setupIPCHandlers() {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
ipcMain.handle('app:openExternal', async (_, url: string) => {
|
ipcMain.handle(
|
||||||
try {
|
'app:openExternal',
|
||||||
|
async (_, url: string) =>
|
||||||
|
(await safeExecute(async () => {
|
||||||
await shell.openExternal(url);
|
await shell.openExternal(url);
|
||||||
return { success: true };
|
return { success: true };
|
||||||
} catch (error) {
|
}, 'Failed to open external URL')) || {
|
||||||
logError('Failed to open external URL:', error as Error);
|
success: false,
|
||||||
throw new Error(
|
error: 'Failed to open external URL',
|
||||||
`Failed to open external URL: ${(error as Error).message}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
const mainWindow = getMainWindow();
|
const mainWindow = getMainWindow();
|
||||||
if (mainWindow) {
|
if (mainWindow) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { autoUpdater } from 'electron-updater';
|
import { autoUpdater } from 'electron-updater';
|
||||||
import { app } from 'electron';
|
import { app } from 'electron';
|
||||||
import { logError } from '@/main/modules/logging';
|
import { logError } from '@/main/modules/logging';
|
||||||
|
import { safeExecute } from '@/utils/node/logger';
|
||||||
|
|
||||||
export interface UpdateInfo {
|
export interface UpdateInfo {
|
||||||
version: string;
|
version: string;
|
||||||
|
|
@ -33,25 +34,17 @@ function setupAutoUpdater() {
|
||||||
|
|
||||||
setupAutoUpdater();
|
setupAutoUpdater();
|
||||||
|
|
||||||
export async function checkForUpdates() {
|
export const checkForUpdates = async () =>
|
||||||
try {
|
(await safeExecute(
|
||||||
const result = await autoUpdater.checkForUpdates();
|
() => autoUpdater.checkForUpdates(),
|
||||||
return result !== null;
|
'Failed to check for updates'
|
||||||
} catch (error) {
|
)) !== null;
|
||||||
logError('Failed to check for updates:', error as Error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function downloadUpdate() {
|
export const downloadUpdate = async () =>
|
||||||
try {
|
(await safeExecute(
|
||||||
await autoUpdater.downloadUpdate();
|
() => autoUpdater.downloadUpdate(),
|
||||||
return true;
|
'Failed to download update'
|
||||||
} catch (error) {
|
)) !== null;
|
||||||
logError('Failed to download update:', error as Error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function quitAndInstall() {
|
export function quitAndInstall() {
|
||||||
if (updateDownloaded) {
|
if (updateDownloaded) {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { join, dirname } from 'path';
|
import { join, dirname } from 'path';
|
||||||
import { pathExists } from '@/utils/node/fs';
|
import { pathExists } from '@/utils/node/fs';
|
||||||
import { logError } from './logging';
|
|
||||||
import { getCurrentBinaryInfo } from './koboldcpp';
|
import { getCurrentBinaryInfo } from './koboldcpp';
|
||||||
import { detectGPUCapabilities, detectCPU } from './hardware';
|
import { detectGPUCapabilities, detectCPU } from './hardware';
|
||||||
|
import { tryExecute, safeExecute } from '@/utils/node/logger';
|
||||||
import type { BackendOption, BackendSupport } from '@/types';
|
import type { BackendOption, BackendSupport } from '@/types';
|
||||||
|
|
||||||
const backendSupportCache = new Map<string, BackendSupport>();
|
const backendSupportCache = new Map<string, BackendSupport>();
|
||||||
|
|
@ -22,7 +22,7 @@ async function detectBackendSupportFromPath(koboldBinaryPath: string) {
|
||||||
cuda: false,
|
cuda: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
await tryExecute(async () => {
|
||||||
const binaryDir = dirname(koboldBinaryPath);
|
const binaryDir = dirname(koboldBinaryPath);
|
||||||
const internalDir = join(binaryDir, '_internal');
|
const internalDir = join(binaryDir, '_internal');
|
||||||
|
|
||||||
|
|
@ -60,16 +60,14 @@ async function detectBackendSupportFromPath(koboldBinaryPath: string) {
|
||||||
support.noavx2 = noavx2;
|
support.noavx2 = noavx2;
|
||||||
support.failsafe = failsafe;
|
support.failsafe = failsafe;
|
||||||
support.cuda = cuda;
|
support.cuda = cuda;
|
||||||
} catch (error) {
|
}, 'Error detecting backend support');
|
||||||
logError('Error detecting backend support:', error as Error);
|
|
||||||
}
|
|
||||||
|
|
||||||
backendSupportCache.set(koboldBinaryPath, support);
|
backendSupportCache.set(koboldBinaryPath, support);
|
||||||
return support;
|
return support;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function detectBackendSupport() {
|
export const detectBackendSupport = async () =>
|
||||||
try {
|
(await safeExecute(async () => {
|
||||||
const currentBinaryInfo = await getCurrentBinaryInfo();
|
const currentBinaryInfo = await getCurrentBinaryInfo();
|
||||||
|
|
||||||
if (!currentBinaryInfo?.path) {
|
if (!currentBinaryInfo?.path) {
|
||||||
|
|
@ -77,17 +75,11 @@ export async function detectBackendSupport() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return detectBackendSupportFromPath(currentBinaryInfo.path);
|
return detectBackendSupportFromPath(currentBinaryInfo.path);
|
||||||
} catch (error) {
|
}, 'Error detecting current binary backend support')) || null;
|
||||||
logError('Error detecting current binary backend support:', error as Error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
export async function getAvailableBackends(includeDisabled = false) {
|
||||||
export async function getAvailableBackends(
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||||
includeDisabled = false
|
const result = await safeExecute(async () => {
|
||||||
): Promise<BackendOption[]> {
|
|
||||||
try {
|
|
||||||
const [currentBinaryInfo, hardwareCapabilities, cpuCapabilities] =
|
const [currentBinaryInfo, hardwareCapabilities, cpuCapabilities] =
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
getCurrentBinaryInfo(),
|
getCurrentBinaryInfo(),
|
||||||
|
|
@ -177,8 +169,7 @@ export async function getAvailableBackends(
|
||||||
|
|
||||||
availableBackendsCache.set(cacheKey, backends);
|
availableBackendsCache.set(cacheKey, backends);
|
||||||
return backends;
|
return backends;
|
||||||
} catch (error) {
|
}, 'Failed to get available backends');
|
||||||
logError('Failed to get available backends:', error as Error);
|
|
||||||
return [{ value: 'cpu', label: 'CPU' }];
|
return result || [{ value: 'cpu', label: 'CPU' }];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import type { ChildProcess } from 'child_process';
|
||||||
import yauzl from 'yauzl';
|
import yauzl from 'yauzl';
|
||||||
|
|
||||||
import { logError } from './logging';
|
import { logError } from './logging';
|
||||||
|
import { safeExecute } from '@/utils/node/logger';
|
||||||
import { sendKoboldOutput } from './window';
|
import { sendKoboldOutput } from './window';
|
||||||
import { getInstallDir } from './config';
|
import { getInstallDir } from './config';
|
||||||
import { COMFYUI, SERVER_READY_SIGNALS, GITHUB_API } from '@/constants';
|
import { COMFYUI, SERVER_READY_SIGNALS, GITHUB_API } from '@/constants';
|
||||||
|
|
@ -20,7 +21,7 @@ interface ComfyUIVersionInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getLatestComfyUIVersion(): Promise<ComfyUIVersionInfo | null> {
|
async function getLatestComfyUIVersion(): Promise<ComfyUIVersionInfo | null> {
|
||||||
try {
|
return safeExecute(async () => {
|
||||||
const response = await fetch(GITHUB_API.COMFYUI_LATEST_COMMIT_URL);
|
const response = await fetch(GITHUB_API.COMFYUI_LATEST_COMMIT_URL);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|
@ -33,13 +34,7 @@ async function getLatestComfyUIVersion(): Promise<ComfyUIVersionInfo | null> {
|
||||||
sha: data.sha,
|
sha: data.sha,
|
||||||
date: data.commit.committer.date,
|
date: data.commit.committer.date,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
}, 'Failed to fetch latest ComfyUI version');
|
||||||
logError(
|
|
||||||
'Failed to fetch latest ComfyUI version',
|
|
||||||
error instanceof Error ? error : undefined
|
|
||||||
);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getCurrentComfyUIVersion(
|
async function getCurrentComfyUIVersion(
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { logError } from '@/main/modules/logging';
|
|
||||||
import { readJsonFile, writeJsonFile } from '@/utils/node/fs';
|
import { readJsonFile, writeJsonFile } from '@/utils/node/fs';
|
||||||
|
import { safeExecute } from '@/utils/node/logger';
|
||||||
import { getConfigDir } from '@/utils/node/path';
|
import { getConfigDir } from '@/utils/node/path';
|
||||||
import { homedir } from 'os';
|
import { homedir } from 'os';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
|
@ -23,21 +23,19 @@ let config: AppConfig = {};
|
||||||
let configPath: string;
|
let configPath: string;
|
||||||
|
|
||||||
async function loadConfig() {
|
async function loadConfig() {
|
||||||
try {
|
const config = await safeExecute(
|
||||||
const loadedConfig = await readJsonFile<AppConfig>(configPath);
|
() => readJsonFile<AppConfig>(configPath),
|
||||||
return loadedConfig || {};
|
'Error loading config'
|
||||||
} catch (error) {
|
);
|
||||||
logError('Error loading config:', error as Error);
|
return config || {};
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveConfig() {
|
async function saveConfig() {
|
||||||
try {
|
const success = await safeExecute(
|
||||||
await writeJsonFile(configPath, config);
|
() => writeJsonFile(configPath, config),
|
||||||
} catch (error) {
|
'Error saving config'
|
||||||
logError('Error saving config:', error as Error);
|
);
|
||||||
}
|
return success !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function initialize() {
|
export async function initialize() {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
/* eslint-disable no-comments/disallowComments */
|
/* eslint-disable no-comments/disallowComments */
|
||||||
import si from 'systeminformation';
|
import si from 'systeminformation';
|
||||||
import { logError } from '@/main/modules/logging';
|
import { safeExecute } from '@/utils/node/logger';
|
||||||
import { terminateProcess } from '@/utils/node/process';
|
import { terminateProcess } from '@/utils/node/process';
|
||||||
import { getGPUData } from '@/utils/node/gpu';
|
import { getGPUData } from '@/utils/node/gpu';
|
||||||
import type {
|
import type {
|
||||||
|
|
@ -23,7 +23,7 @@ export async function detectCPU() {
|
||||||
return cpuCapabilitiesCache;
|
return cpuCapabilitiesCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
const result = await safeExecute(async () => {
|
||||||
const [cpu, flags] = await Promise.all([si.cpu(), si.cpuFlags()]);
|
const [cpu, flags] = await Promise.all([si.cpu(), si.cpuFlags()]);
|
||||||
|
|
||||||
const devices: string[] = [];
|
const devices: string[] = [];
|
||||||
|
|
@ -34,23 +34,24 @@ export async function detectCPU() {
|
||||||
const avx = flags.includes('avx') || flags.includes('AVX');
|
const avx = flags.includes('avx') || flags.includes('AVX');
|
||||||
const avx2 = flags.includes('avx2') || flags.includes('AVX2');
|
const avx2 = flags.includes('avx2') || flags.includes('AVX2');
|
||||||
|
|
||||||
cpuCapabilitiesCache = {
|
const capabilities = {
|
||||||
avx,
|
avx,
|
||||||
avx2,
|
avx2,
|
||||||
devices,
|
devices,
|
||||||
};
|
};
|
||||||
|
|
||||||
return cpuCapabilitiesCache;
|
cpuCapabilitiesCache = capabilities;
|
||||||
} catch (error) {
|
return capabilities;
|
||||||
logError('CPU detection failed:', error as Error);
|
}, 'CPU detection failed');
|
||||||
|
|
||||||
const fallbackCapabilities = {
|
const fallbackCapabilities = {
|
||||||
avx: false,
|
avx: false,
|
||||||
avx2: false,
|
avx2: false,
|
||||||
devices: [],
|
devices: [],
|
||||||
};
|
};
|
||||||
cpuCapabilitiesCache = fallbackCapabilities;
|
|
||||||
return fallbackCapabilities;
|
cpuCapabilitiesCache = result || fallbackCapabilities;
|
||||||
}
|
return cpuCapabilitiesCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function detectGPU() {
|
export async function detectGPU() {
|
||||||
|
|
@ -58,7 +59,7 @@ export async function detectGPU() {
|
||||||
return basicGPUInfoCache;
|
return basicGPUInfoCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
const result = await safeExecute(async () => {
|
||||||
const gpuData = await getGPUData();
|
const gpuData = await getGPUData();
|
||||||
|
|
||||||
let hasAMD = false;
|
let hasAMD = false;
|
||||||
|
|
@ -90,23 +91,24 @@ export async function detectGPU() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
basicGPUInfoCache = {
|
const basicInfo = {
|
||||||
hasAMD,
|
hasAMD,
|
||||||
hasNVIDIA,
|
hasNVIDIA,
|
||||||
gpuInfo: gpuInfo.length > 0 ? gpuInfo : ['No GPU information available'],
|
gpuInfo: gpuInfo.length > 0 ? gpuInfo : ['No GPU information available'],
|
||||||
};
|
};
|
||||||
|
|
||||||
return basicGPUInfoCache;
|
basicGPUInfoCache = basicInfo;
|
||||||
} catch (error) {
|
return basicInfo;
|
||||||
logError('GPU detection failed:', error as Error);
|
}, 'GPU detection failed');
|
||||||
|
|
||||||
const fallbackGPUInfo = {
|
const fallbackGPUInfo = {
|
||||||
hasAMD: false,
|
hasAMD: false,
|
||||||
hasNVIDIA: false,
|
hasNVIDIA: false,
|
||||||
gpuInfo: ['GPU detection failed'],
|
gpuInfo: ['GPU detection failed'],
|
||||||
};
|
};
|
||||||
basicGPUInfoCache = fallbackGPUInfo;
|
|
||||||
return fallbackGPUInfo;
|
basicGPUInfoCache = result || fallbackGPUInfo;
|
||||||
}
|
return basicGPUInfoCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function detectGPUCapabilities() {
|
export async function detectGPUCapabilities() {
|
||||||
|
|
@ -428,10 +430,9 @@ export async function detectGPUMemory() {
|
||||||
return gpuMemoryInfoCache;
|
return gpuMemoryInfoCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
const memoryInfo: GPUMemoryInfo[] = [];
|
const result = await safeExecute(async () => {
|
||||||
|
|
||||||
try {
|
|
||||||
const gpuData = await getGPUData();
|
const gpuData = await getGPUData();
|
||||||
|
const memoryInfo: GPUMemoryInfo[] = [];
|
||||||
|
|
||||||
for (const gpu of gpuData) {
|
for (const gpu of gpuData) {
|
||||||
if (gpu.deviceName) {
|
if (gpu.deviceName) {
|
||||||
|
|
@ -448,11 +449,9 @@ export async function detectGPUMemory() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gpuMemoryInfoCache = memoryInfo;
|
return memoryInfo;
|
||||||
} catch (error) {
|
}, 'GPU memory detection failed');
|
||||||
logError('GPU memory detection failed:', error as Error);
|
|
||||||
gpuMemoryInfoCache = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
gpuMemoryInfoCache = result || [];
|
||||||
return gpuMemoryInfoCache;
|
return gpuMemoryInfoCache;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import {
|
||||||
setCurrentKoboldBinary,
|
setCurrentKoboldBinary,
|
||||||
} from './config';
|
} from './config';
|
||||||
import { logError } from './logging';
|
import { logError } from './logging';
|
||||||
|
import { tryExecute } from '@/utils/node/logger';
|
||||||
import { sendKoboldOutput, getMainWindow, sendToRenderer } from './window';
|
import { sendKoboldOutput, getMainWindow, sendToRenderer } from './window';
|
||||||
import { PRODUCT_NAME, SERVER_READY_SIGNALS } from '@/constants';
|
import { PRODUCT_NAME, SERVER_READY_SIGNALS } from '@/constants';
|
||||||
import {
|
import {
|
||||||
|
|
@ -207,9 +208,7 @@ export async function downloadRelease(asset: GitHubAsset) {
|
||||||
return launcherPath;
|
return launcherPath;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logError('Failed to download or unpack binary:', error as Error);
|
logError('Failed to download or unpack binary:', error as Error);
|
||||||
throw new Error(
|
throw new Error('Failed to download or unpack binary');
|
||||||
`Failed to download or unpack binary: ${(error as Error).message}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -232,7 +231,7 @@ async function unpackKoboldCpp(packedPath: string, unpackDir: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function patchKliteEmbd(unpackedDir: string) {
|
async function patchKliteEmbd(unpackedDir: string) {
|
||||||
try {
|
await tryExecute(async () => {
|
||||||
const possiblePaths = [
|
const possiblePaths = [
|
||||||
join(unpackedDir, '_internal', 'klite.embd'),
|
join(unpackedDir, '_internal', 'klite.embd'),
|
||||||
join(unpackedDir, 'klite.embd'),
|
join(unpackedDir, 'klite.embd'),
|
||||||
|
|
@ -276,13 +275,11 @@ async function patchKliteEmbd(unpackedDir: string) {
|
||||||
|
|
||||||
await writeFile(kliteEmbdPath, patchedContent, 'utf8');
|
await writeFile(kliteEmbdPath, patchedContent, 'utf8');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
}, 'Failed to patch klite.embd');
|
||||||
logError('Failed to patch klite.embd:', error as Error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function patchKcppSduiEmbd(unpackedDir: string) {
|
async function patchKcppSduiEmbd(unpackedDir: string) {
|
||||||
try {
|
await tryExecute(async () => {
|
||||||
const possiblePaths = [
|
const possiblePaths = [
|
||||||
join(unpackedDir, '_internal', 'kcpp_sdui.embd'),
|
join(unpackedDir, '_internal', 'kcpp_sdui.embd'),
|
||||||
join(unpackedDir, 'kcpp_sdui.embd'),
|
join(unpackedDir, 'kcpp_sdui.embd'),
|
||||||
|
|
@ -296,9 +293,7 @@ async function patchKcppSduiEmbd(unpackedDir: string) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
}, 'Failed to patch kcpp_sdui.embd');
|
||||||
logError('Failed to patch kcpp_sdui.embd:', error as Error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getLauncherPath(unpackedDir: string) {
|
async function getLauncherPath(unpackedDir: string) {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import si from 'systeminformation';
|
import si from 'systeminformation';
|
||||||
import { BrowserWindow } from 'electron';
|
import { BrowserWindow } from 'electron';
|
||||||
import { platform } from 'process';
|
import { platform } from 'process';
|
||||||
import { logError } from '@/main/modules/logging';
|
|
||||||
import { getGPUData } from '@/utils/node/gpu';
|
import { getGPUData } from '@/utils/node/gpu';
|
||||||
|
import { tryExecute } from '@/utils/node/logger';
|
||||||
|
|
||||||
export interface CpuMetrics {
|
export interface CpuMetrics {
|
||||||
usage: number;
|
usage: number;
|
||||||
|
|
@ -90,7 +90,7 @@ export function stopMonitoring() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function collectAndSendCpuMetrics() {
|
async function collectAndSendCpuMetrics() {
|
||||||
try {
|
await tryExecute(async () => {
|
||||||
const cpuData = await si.currentLoad();
|
const cpuData = await si.currentLoad();
|
||||||
const metrics: CpuMetrics = {
|
const metrics: CpuMetrics = {
|
||||||
usage: Math.round(cpuData.currentLoad),
|
usage: Math.round(cpuData.currentLoad),
|
||||||
|
|
@ -99,13 +99,11 @@ async function collectAndSendCpuMetrics() {
|
||||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||||
mainWindow.webContents.send('cpu-metrics', metrics);
|
mainWindow.webContents.send('cpu-metrics', metrics);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
}, 'Failed to collect CPU metrics');
|
||||||
logError('Failed to collect CPU metrics:', error as Error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function collectAndSendMemoryMetrics() {
|
async function collectAndSendMemoryMetrics() {
|
||||||
try {
|
await tryExecute(async () => {
|
||||||
const memData = await si.mem();
|
const memData = await si.mem();
|
||||||
const usedBytes = memData.active || memData.used;
|
const usedBytes = memData.active || memData.used;
|
||||||
const totalBytes = memData.total;
|
const totalBytes = memData.total;
|
||||||
|
|
@ -118,13 +116,11 @@ async function collectAndSendMemoryMetrics() {
|
||||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||||
mainWindow.webContents.send('memory-metrics', metrics);
|
mainWindow.webContents.send('memory-metrics', metrics);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
}, 'Failed to collect memory metrics');
|
||||||
logError('Failed to collect memory metrics:', error as Error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function collectAndSendGpuMetrics() {
|
async function collectAndSendGpuMetrics() {
|
||||||
try {
|
await tryExecute(async () => {
|
||||||
const gpuData = await getGPUData();
|
const gpuData = await getGPUData();
|
||||||
const metrics: GpuMetrics = {
|
const metrics: GpuMetrics = {
|
||||||
gpus: gpuData.map((gpuInfo) => ({
|
gpus: gpuData.map((gpuInfo) => ({
|
||||||
|
|
@ -142,7 +138,5 @@ async function collectAndSendGpuMetrics() {
|
||||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||||
mainWindow.webContents.send('gpu-metrics', metrics);
|
mainWindow.webContents.send('gpu-metrics', metrics);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
}, 'Failed to collect GPU metrics');
|
||||||
logError('Failed to collect GPU metrics:', error as Error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import type { ChildProcess } from 'child_process';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
|
||||||
import { logError } from './logging';
|
import { logError } from './logging';
|
||||||
|
import { safeTryExecute, tryExecute } from '@/utils/node/logger';
|
||||||
import { sendKoboldOutput } from './window';
|
import { sendKoboldOutput } from './window';
|
||||||
import { getInstallDir } from './config';
|
import { getInstallDir } from './config';
|
||||||
import { OPENWEBUI, SERVER_READY_SIGNALS } from '@/constants';
|
import { OPENWEBUI, SERVER_READY_SIGNALS } from '@/constants';
|
||||||
|
|
@ -113,23 +114,19 @@ export async function startFrontend(args: string[]) {
|
||||||
|
|
||||||
if (openWebUIProcess.stdout) {
|
if (openWebUIProcess.stdout) {
|
||||||
openWebUIProcess.stdout.on('data', (data: Buffer) => {
|
openWebUIProcess.stdout.on('data', (data: Buffer) => {
|
||||||
try {
|
safeTryExecute(() => {
|
||||||
const output = data.toString('utf8');
|
const output = data.toString('utf8');
|
||||||
sendKoboldOutput(output, true);
|
sendKoboldOutput(output, true);
|
||||||
} catch (error) {
|
}, 'Error processing stdout data');
|
||||||
logError('Error processing stdout data:', error as Error);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (openWebUIProcess.stderr) {
|
if (openWebUIProcess.stderr) {
|
||||||
openWebUIProcess.stderr.on('data', (data: Buffer) => {
|
openWebUIProcess.stderr.on('data', (data: Buffer) => {
|
||||||
try {
|
safeTryExecute(() => {
|
||||||
const output = data.toString('utf8');
|
const output = data.toString('utf8');
|
||||||
sendKoboldOutput(output, true);
|
sendKoboldOutput(output, true);
|
||||||
} catch (error) {
|
}, 'Error processing stderr data');
|
||||||
logError('Error processing stderr data:', error as Error);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -170,14 +167,12 @@ export async function stopFrontend() {
|
||||||
if (openWebUIProcess) {
|
if (openWebUIProcess) {
|
||||||
sendKoboldOutput('Stopping Open WebUI...');
|
sendKoboldOutput('Stopping Open WebUI...');
|
||||||
|
|
||||||
try {
|
await tryExecute(async () => {
|
||||||
await terminateProcess(openWebUIProcess, {
|
await terminateProcess(openWebUIProcess!, {
|
||||||
logError: (message, error) => logError(message, error),
|
logError: (message, error) => logError(message, error),
|
||||||
});
|
});
|
||||||
sendKoboldOutput('Open WebUI stopped');
|
sendKoboldOutput('Open WebUI stopped');
|
||||||
} catch (error) {
|
}, 'Error stopping Open WebUI');
|
||||||
logError('Error stopping Open WebUI:', error as Error);
|
|
||||||
}
|
|
||||||
|
|
||||||
openWebUIProcess = null;
|
openWebUIProcess = null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { spawn } from 'child_process';
|
import { spawn } from 'child_process';
|
||||||
import { platform } from 'process';
|
import { platform } from 'process';
|
||||||
import { logError } from './logging';
|
import { safeExecute } from '@/utils/node/logger';
|
||||||
|
|
||||||
const LINUX_PERFORMANCE_APPS = [
|
const LINUX_PERFORMANCE_APPS = [
|
||||||
'resources',
|
'resources',
|
||||||
|
|
@ -45,8 +45,8 @@ async function tryLaunchCommand(command: string, args: string[] = []) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function openPerformanceManager() {
|
export const openPerformanceManager = async () =>
|
||||||
try {
|
(await safeExecute(async () => {
|
||||||
switch (platform) {
|
switch (platform) {
|
||||||
case 'darwin': {
|
case 'darwin': {
|
||||||
const success = await tryLaunchCommand('open', [
|
const success = await tryLaunchCommand('open', [
|
||||||
|
|
@ -87,9 +87,7 @@ export async function openPerformanceManager() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
}, 'Failed to open performance manager')) || {
|
||||||
const errorMessage = `Failed to open performance manager: ${(error as Error).message}`;
|
success: false,
|
||||||
logError(errorMessage, error as Error);
|
error: 'Failed to open performance manager',
|
||||||
return { success: false, error: errorMessage };
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { join } from 'path';
|
||||||
import type { ChildProcess } from 'child_process';
|
import type { ChildProcess } from 'child_process';
|
||||||
|
|
||||||
import { logError } from './logging';
|
import { logError } from './logging';
|
||||||
|
import { tryExecute } from '@/utils/node/logger';
|
||||||
import { sendKoboldOutput } from './window';
|
import { sendKoboldOutput } from './window';
|
||||||
import { SILLYTAVERN, SERVER_READY_SIGNALS } from '@/constants';
|
import { SILLYTAVERN, SERVER_READY_SIGNALS } from '@/constants';
|
||||||
import { terminateProcess } from '@/utils/node/process';
|
import { terminateProcess } from '@/utils/node/process';
|
||||||
|
|
@ -166,7 +167,7 @@ async function setupSillyTavernConfig(
|
||||||
koboldPort: number,
|
koboldPort: number,
|
||||||
isImageMode: boolean
|
isImageMode: boolean
|
||||||
) {
|
) {
|
||||||
try {
|
const success = await tryExecute(async () => {
|
||||||
const configPath = getSillyTavernSettingsPath();
|
const configPath = getSillyTavernSettingsPath();
|
||||||
let settings: Record<string, unknown> = {};
|
let settings: Record<string, unknown> = {};
|
||||||
|
|
||||||
|
|
@ -221,10 +222,11 @@ async function setupSillyTavernConfig(
|
||||||
await writeJsonFile(configPath, settings);
|
await writeJsonFile(configPath, settings);
|
||||||
|
|
||||||
sendKoboldOutput(`SillyTavern configuration updated successfully!`);
|
sendKoboldOutput(`SillyTavern configuration updated successfully!`);
|
||||||
} catch (error) {
|
}, 'Failed to setup SillyTavern config');
|
||||||
logError('Failed to setup SillyTavern config:', error as Error);
|
|
||||||
|
if (!success) {
|
||||||
sendKoboldOutput(
|
sendKoboldOutput(
|
||||||
`Failed to configure SillyTavern: ${error instanceof Error ? error.message : String(error)}`
|
`Failed to configure SillyTavern. Check logs for details.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -396,11 +398,9 @@ export async function stopFrontend() {
|
||||||
|
|
||||||
export async function cleanup() {
|
export async function cleanup() {
|
||||||
if (sillyTavernProcess) {
|
if (sillyTavernProcess) {
|
||||||
try {
|
await tryExecute(async () => {
|
||||||
await stopFrontend();
|
await stopFrontend();
|
||||||
} catch (error) {
|
}, 'Error during SillyTavernManager cleanup');
|
||||||
logError('Error during SillyTavernManager cleanup:', error as Error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,13 @@
|
||||||
|
import {
|
||||||
|
createSafeExecute,
|
||||||
|
createTryExecute,
|
||||||
|
createTryExecuteImmediate,
|
||||||
|
} from '@/utils/shared/logger-core';
|
||||||
|
|
||||||
export const logError = (message: string, error: Error) => {
|
export const logError = (message: string, error: Error) => {
|
||||||
if (window.electronAPI?.logs?.logError) {
|
|
||||||
window.electronAPI.logs.logError(message, error);
|
window.electronAPI.logs.logError(message, error);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const safeExecute = async <T>(
|
export const safeExecute = createSafeExecute(logError);
|
||||||
operation: () => Promise<T>,
|
export const tryExecute = createTryExecute(logError);
|
||||||
errorMessage: string
|
export const tryExecuteImmediate = createTryExecuteImmediate(logError);
|
||||||
): Promise<T | null> => {
|
|
||||||
try {
|
|
||||||
return operation();
|
|
||||||
} catch (error) {
|
|
||||||
logError(errorMessage, error as Error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const tryExecute = async (
|
|
||||||
operation: () => Promise<void>,
|
|
||||||
errorMessage: string
|
|
||||||
) => {
|
|
||||||
try {
|
|
||||||
await operation();
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
logError(errorMessage, error as Error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
|
||||||
10
src/utils/node/logger.ts
Normal file
10
src/utils/node/logger.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { logError } from '@/main/modules/logging';
|
||||||
|
import {
|
||||||
|
createSafeExecute,
|
||||||
|
createTryExecute,
|
||||||
|
createSafeTryExecute,
|
||||||
|
} from '@/utils/shared/logger-core';
|
||||||
|
|
||||||
|
export const safeExecute = createSafeExecute(logError);
|
||||||
|
export const tryExecute = createTryExecute(logError);
|
||||||
|
export const safeTryExecute = createSafeTryExecute(logError);
|
||||||
46
src/utils/shared/logger-core.ts
Normal file
46
src/utils/shared/logger-core.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
type LoggerFunction = (message: string, error: Error) => void;
|
||||||
|
|
||||||
|
export const createSafeExecute =
|
||||||
|
(logger: LoggerFunction) =>
|
||||||
|
async <T>(operation: () => Promise<T>, errorMessage: string) => {
|
||||||
|
try {
|
||||||
|
return operation();
|
||||||
|
} catch (error) {
|
||||||
|
logger(errorMessage, error as Error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createTryExecute =
|
||||||
|
(logger: LoggerFunction) =>
|
||||||
|
async (operation: () => Promise<void>, errorMessage: string) => {
|
||||||
|
try {
|
||||||
|
await operation();
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
logger(errorMessage, error as Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createSafeTryExecute =
|
||||||
|
(logger: LoggerFunction) =>
|
||||||
|
<T>(operation: () => T, errorMessage: string) => {
|
||||||
|
try {
|
||||||
|
return operation();
|
||||||
|
} catch (error) {
|
||||||
|
logger(errorMessage, error as Error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createTryExecuteImmediate =
|
||||||
|
(logger: LoggerFunction) => (operation: () => void, errorMessage: string) => {
|
||||||
|
try {
|
||||||
|
operation();
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
logger(errorMessage, error as Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { logError } from '@/utils/logger';
|
import { tryExecuteImmediate } from '@/utils/logger';
|
||||||
|
|
||||||
export const handleTerminalOutput = (prevContent: string, newData: string) => {
|
export const handleTerminalOutput = (prevContent: string, newData: string) => {
|
||||||
try {
|
const result = tryExecuteImmediate(() => {
|
||||||
if (newData.includes('\r')) {
|
if (newData.includes('\r')) {
|
||||||
const hasStandaloneCarriageReturns = /\r(?!\n)/g.test(newData);
|
const hasStandaloneCarriageReturns = /\r(?!\n)/g.test(newData);
|
||||||
|
|
||||||
|
|
@ -28,10 +28,9 @@ export const handleTerminalOutput = (prevContent: string, newData: string) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return prevContent + newData;
|
return prevContent + newData;
|
||||||
} catch (err) {
|
}, 'Terminal Basic Error');
|
||||||
logError('Terminal Basic Error', err as Error);
|
|
||||||
return prevContent + newData;
|
return result ?? prevContent + newData;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const URL_REGEX = /(https?:\/\/[^\s<>"{}|\\^`[\]]+)/gi;
|
const URL_REGEX = /(https?:\/\/[^\s<>"{}|\\^`[\]]+)/gi;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue