mirror of
https://github.com/lone-cloud/gerbil
synced 2026-06-03 19:54:44 -07:00
further separate gui and cli for slightly less ram usage in CLI mode
This commit is contained in:
parent
458e083da4
commit
09e6155476
4 changed files with 334 additions and 178 deletions
127
src/main/cli.ts
Normal file
127
src/main/cli.ts
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
/* eslint-disable no-console */
|
||||
import { spawn } from 'child_process';
|
||||
import { existsSync, readFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { homedir } from 'os';
|
||||
|
||||
const CONFIG_FILE_NAME = 'config.json';
|
||||
|
||||
export class LightweightCliHandler {
|
||||
private getConfigPath(): string {
|
||||
const platform = process.platform;
|
||||
const home = homedir();
|
||||
|
||||
switch (platform) {
|
||||
case 'win32':
|
||||
return join(
|
||||
home,
|
||||
'AppData',
|
||||
'Roaming',
|
||||
'Friendly Kobold',
|
||||
CONFIG_FILE_NAME
|
||||
);
|
||||
case 'darwin':
|
||||
return join(
|
||||
home,
|
||||
'Library',
|
||||
'Application Support',
|
||||
'Friendly Kobold',
|
||||
CONFIG_FILE_NAME
|
||||
);
|
||||
default:
|
||||
return join(home, '.config', 'Friendly Kobold', CONFIG_FILE_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
private getCurrentKoboldBinary(): string | null {
|
||||
try {
|
||||
const configPath = this.getConfigPath();
|
||||
if (!existsSync(configPath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const config = JSON.parse(readFileSync(configPath, 'utf8'));
|
||||
return config.currentKoboldBinary || null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async handleCliMode(args: string[]): Promise<void> {
|
||||
const currentBinary = this.getCurrentKoboldBinary();
|
||||
|
||||
if (!currentBinary) {
|
||||
console.error(
|
||||
'Error: No KoboldCpp binary found. Please run the GUI first to download KoboldCpp.'
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!existsSync(currentBinary)) {
|
||||
console.error(`Error: KoboldCpp binary not found at: ${currentBinary}`);
|
||||
console.error('Please run the GUI to download or reconfigure KoboldCpp.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const isWindows = process.platform === 'win32';
|
||||
|
||||
const child = spawn(currentBinary, args, {
|
||||
stdio: isWindows ? 'pipe' : 'inherit',
|
||||
detached: false,
|
||||
});
|
||||
|
||||
if (isWindows) {
|
||||
child.stdout?.setEncoding('utf8');
|
||||
child.stderr?.setEncoding('utf8');
|
||||
|
||||
child.stdout?.on('data', (data) => {
|
||||
process.stdout.write(data.toString());
|
||||
});
|
||||
|
||||
child.stderr?.on('data', (data) => {
|
||||
process.stderr.write(data.toString());
|
||||
});
|
||||
|
||||
if (child.stdin && process.stdin.readable) {
|
||||
process.stdin.pipe(child.stdin);
|
||||
}
|
||||
}
|
||||
|
||||
child.on('exit', (code, signal) => {
|
||||
if (signal) {
|
||||
console.log(`\nProcess terminated with signal: ${signal}`);
|
||||
process.exit(128 + (signal === 'SIGTERM' ? 15 : 2));
|
||||
} else if (code !== null) {
|
||||
process.exit(code);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
child.on('error', (error) => {
|
||||
console.error(`Failed to start KoboldCpp: ${error.message}`);
|
||||
reject(error);
|
||||
});
|
||||
|
||||
const handleSignal = () => {
|
||||
console.log('\nReceived termination signal, terminating KoboldCpp...');
|
||||
if (!child.killed) {
|
||||
child.kill('SIGTERM');
|
||||
|
||||
setTimeout(() => {
|
||||
if (!child.killed) {
|
||||
child.kill('SIGKILL');
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
};
|
||||
|
||||
process.on('SIGINT', handleSignal);
|
||||
process.on('SIGTERM', handleSignal);
|
||||
if (process.platform === 'win32') {
|
||||
process.on('SIGBREAK', handleSignal);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
147
src/main/gui.ts
Normal file
147
src/main/gui.ts
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
import { app } from 'electron';
|
||||
import { join } from 'path';
|
||||
import { existsSync, mkdirSync } from 'fs';
|
||||
import { homedir } from 'os';
|
||||
|
||||
import { WindowManager } from '@/main/managers/WindowManager';
|
||||
import { ConfigManager } from '@/main/managers/ConfigManager';
|
||||
import { LogManager } from '@/main/managers/LogManager';
|
||||
import { KoboldCppManager } from '@/main/managers/KoboldCppManager';
|
||||
import { GitHubService } from '@/main/services/GitHubService';
|
||||
import { HardwareService } from '@/main/services/HardwareService';
|
||||
import { BinaryService } from '@/main/services/BinaryService';
|
||||
import { IPCHandlers } from '@/main/utils/IPCHandlers';
|
||||
import { APP_NAME, CONFIG_FILE_NAME } from '@/constants';
|
||||
|
||||
export class FriendlyKoboldApp {
|
||||
private windowManager: WindowManager;
|
||||
private configManager: ConfigManager;
|
||||
private logManager: LogManager;
|
||||
private koboldManager: KoboldCppManager;
|
||||
private githubService: GitHubService;
|
||||
private hardwareService: HardwareService;
|
||||
private binaryService: BinaryService;
|
||||
private ipcHandlers: IPCHandlers;
|
||||
|
||||
constructor() {
|
||||
this.logManager = new LogManager();
|
||||
this.logManager.setupGlobalErrorHandlers();
|
||||
|
||||
this.configManager = new ConfigManager(
|
||||
this.getConfigPath(),
|
||||
this.logManager
|
||||
);
|
||||
this.ensureInstallDirectory();
|
||||
this.windowManager = new WindowManager();
|
||||
this.githubService = new GitHubService(this.logManager);
|
||||
this.hardwareService = new HardwareService(this.logManager);
|
||||
|
||||
this.koboldManager = new KoboldCppManager(
|
||||
this.configManager,
|
||||
this.githubService,
|
||||
this.windowManager,
|
||||
this.logManager
|
||||
);
|
||||
|
||||
this.binaryService = new BinaryService(
|
||||
this.logManager,
|
||||
this.koboldManager,
|
||||
this.hardwareService
|
||||
);
|
||||
|
||||
this.ipcHandlers = new IPCHandlers(
|
||||
this.koboldManager,
|
||||
this.configManager,
|
||||
this.githubService,
|
||||
this.hardwareService,
|
||||
this.binaryService,
|
||||
this.logManager
|
||||
);
|
||||
}
|
||||
|
||||
private getConfigPath() {
|
||||
return join(app.getPath('userData'), CONFIG_FILE_NAME);
|
||||
}
|
||||
|
||||
private getDefaultInstallPath() {
|
||||
const platform = process.platform;
|
||||
const home = homedir();
|
||||
|
||||
switch (platform) {
|
||||
case 'win32':
|
||||
return join(home, APP_NAME);
|
||||
case 'darwin':
|
||||
return join(home, 'Applications', APP_NAME);
|
||||
default:
|
||||
return join(home, '.local', 'share', APP_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
private ensureInstallDirectory() {
|
||||
const installDir =
|
||||
this.configManager.getInstallDir() || this.getDefaultInstallPath();
|
||||
|
||||
if (!this.configManager.getInstallDir()) {
|
||||
this.configManager.setInstallDir(installDir);
|
||||
}
|
||||
|
||||
if (!existsSync(installDir)) {
|
||||
mkdirSync(installDir, { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
await app.whenReady();
|
||||
|
||||
if (process.platform === 'linux') {
|
||||
app.setAppUserModelId('com.friendly-kobold.app');
|
||||
}
|
||||
|
||||
this.windowManager.setupApplicationMenu();
|
||||
this.windowManager.createMainWindow();
|
||||
this.ipcHandlers.setupHandlers();
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform === 'darwin') {
|
||||
return;
|
||||
}
|
||||
|
||||
app.quit();
|
||||
});
|
||||
|
||||
app.on('before-quit', async (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
try {
|
||||
const cleanupPromise = this.koboldManager.cleanup();
|
||||
const timeoutPromise = new Promise<void>((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
}, 10000);
|
||||
});
|
||||
|
||||
await Promise.race([cleanupPromise, timeoutPromise]);
|
||||
} catch (error) {
|
||||
this.logManager.logError(
|
||||
'Error during KoboldCpp cleanup:',
|
||||
error as Error
|
||||
);
|
||||
}
|
||||
|
||||
this.windowManager.cleanup();
|
||||
|
||||
app.exit(0);
|
||||
});
|
||||
|
||||
app.on('will-quit', async (event) => {
|
||||
event.preventDefault();
|
||||
app.exit(0);
|
||||
});
|
||||
|
||||
app.on('activate', () => {
|
||||
if (!this.windowManager.getMainWindow()) {
|
||||
this.windowManager.createMainWindow();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,170 +1,29 @@
|
|||
import { app } from 'electron';
|
||||
import { join } from 'path';
|
||||
import { existsSync, mkdirSync } from 'fs';
|
||||
import { homedir } from 'os';
|
||||
|
||||
import { WindowManager } from '@/main/managers/WindowManager';
|
||||
import { ConfigManager } from '@/main/managers/ConfigManager';
|
||||
import { LogManager } from '@/main/managers/LogManager';
|
||||
import { KoboldCppManager } from '@/main/managers/KoboldCppManager';
|
||||
import { GitHubService } from '@/main/services/GitHubService';
|
||||
import { HardwareService } from '@/main/services/HardwareService';
|
||||
import { BinaryService } from '@/main/services/BinaryService';
|
||||
import { IPCHandlers } from '@/main/utils/IPCHandlers';
|
||||
import { CliHandler } from '@/main/utils/CliHandler';
|
||||
import { APP_NAME, CONFIG_FILE_NAME } from '@/constants';
|
||||
|
||||
class FriendlyKoboldApp {
|
||||
private windowManager: WindowManager;
|
||||
private configManager: ConfigManager;
|
||||
private logManager: LogManager;
|
||||
private koboldManager: KoboldCppManager;
|
||||
private githubService: GitHubService;
|
||||
private hardwareService: HardwareService;
|
||||
private binaryService: BinaryService;
|
||||
private ipcHandlers: IPCHandlers;
|
||||
|
||||
constructor() {
|
||||
this.logManager = new LogManager();
|
||||
this.logManager.setupGlobalErrorHandlers();
|
||||
|
||||
this.configManager = new ConfigManager(
|
||||
this.getConfigPath(),
|
||||
this.logManager
|
||||
);
|
||||
this.ensureInstallDirectory();
|
||||
this.windowManager = new WindowManager();
|
||||
this.githubService = new GitHubService(this.logManager);
|
||||
this.hardwareService = new HardwareService(this.logManager);
|
||||
|
||||
this.koboldManager = new KoboldCppManager(
|
||||
this.configManager,
|
||||
this.githubService,
|
||||
this.windowManager,
|
||||
this.logManager
|
||||
);
|
||||
|
||||
this.binaryService = new BinaryService(
|
||||
this.logManager,
|
||||
this.koboldManager,
|
||||
this.hardwareService
|
||||
);
|
||||
|
||||
this.ipcHandlers = new IPCHandlers(
|
||||
this.koboldManager,
|
||||
this.configManager,
|
||||
this.githubService,
|
||||
this.hardwareService,
|
||||
this.binaryService,
|
||||
this.logManager
|
||||
);
|
||||
}
|
||||
|
||||
private getConfigPath() {
|
||||
return join(app.getPath('userData'), CONFIG_FILE_NAME);
|
||||
}
|
||||
|
||||
private getDefaultInstallPath() {
|
||||
const platform = process.platform;
|
||||
const home = homedir();
|
||||
|
||||
switch (platform) {
|
||||
case 'win32':
|
||||
return join(home, APP_NAME);
|
||||
case 'darwin':
|
||||
return join(home, 'Applications', APP_NAME);
|
||||
default:
|
||||
return join(home, '.local', 'share', APP_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
private ensureInstallDirectory() {
|
||||
const installDir =
|
||||
this.configManager.getInstallDir() || this.getDefaultInstallPath();
|
||||
|
||||
if (!this.configManager.getInstallDir()) {
|
||||
this.configManager.setInstallDir(installDir);
|
||||
}
|
||||
|
||||
if (!existsSync(installDir)) {
|
||||
mkdirSync(installDir, { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
await app.whenReady();
|
||||
|
||||
if (process.platform === 'linux') {
|
||||
app.setAppUserModelId('com.friendly-kobold.app');
|
||||
}
|
||||
|
||||
this.windowManager.setupApplicationMenu();
|
||||
this.windowManager.createMainWindow();
|
||||
this.ipcHandlers.setupHandlers();
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform === 'darwin') {
|
||||
return;
|
||||
}
|
||||
|
||||
app.quit();
|
||||
});
|
||||
|
||||
app.on('before-quit', async (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
try {
|
||||
const cleanupPromise = this.koboldManager.cleanup();
|
||||
const timeoutPromise = new Promise<void>((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
}, 10000);
|
||||
});
|
||||
|
||||
await Promise.race([cleanupPromise, timeoutPromise]);
|
||||
} catch (error) {
|
||||
this.logManager.logError(
|
||||
'Error during KoboldCpp cleanup:',
|
||||
error as Error
|
||||
);
|
||||
}
|
||||
|
||||
this.windowManager.cleanup();
|
||||
|
||||
app.exit(0);
|
||||
});
|
||||
|
||||
app.on('will-quit', async (event) => {
|
||||
event.preventDefault();
|
||||
app.exit(0);
|
||||
});
|
||||
|
||||
app.on('activate', () => {
|
||||
if (!this.windowManager.getMainWindow()) {
|
||||
this.windowManager.createMainWindow();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const { isCliMode, args } = CliHandler.parseArguments(process.argv);
|
||||
const isCliMode = process.argv.includes('--cli');
|
||||
|
||||
if (isCliMode) {
|
||||
async function runCliMode() {
|
||||
try {
|
||||
const cliHandler = new CliHandler();
|
||||
await cliHandler.handleCliMode(args);
|
||||
} catch (error) {
|
||||
import('./cli')
|
||||
.then(async (cliModule) => {
|
||||
const args = process.argv.slice(process.argv.indexOf('--cli') + 1);
|
||||
const handler = new cliModule.LightweightCliHandler();
|
||||
try {
|
||||
await handler.handleCliMode(args);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('CLI mode error:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('CLI mode error:', error);
|
||||
console.error('Failed to load CLI module:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
runCliMode();
|
||||
});
|
||||
} else {
|
||||
const friendlyKoboldApp = new FriendlyKoboldApp();
|
||||
friendlyKoboldApp.initialize().catch((error) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Failed to initialize FriendlyKobold:', error);
|
||||
import('./gui').then((guiModule) => {
|
||||
const app = new guiModule.FriendlyKoboldApp();
|
||||
app.initialize().catch((error: unknown) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Failed to initialize FriendlyKobold:', error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,31 +1,54 @@
|
|||
/* eslint-disable no-console */
|
||||
import { spawn } from 'child_process';
|
||||
import { existsSync } from 'fs';
|
||||
import { app } from 'electron';
|
||||
import { existsSync, readFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { homedir } from 'os';
|
||||
|
||||
import { ConfigManager } from '@/main/managers/ConfigManager';
|
||||
import { LogManager } from '@/main/managers/LogManager';
|
||||
import { CONFIG_FILE_NAME } from '@/constants';
|
||||
|
||||
export class CliHandler {
|
||||
private configManager: ConfigManager;
|
||||
private logManager: LogManager;
|
||||
private getConfigPath(): string {
|
||||
const platform = process.platform;
|
||||
const home = homedir();
|
||||
|
||||
constructor() {
|
||||
this.logManager = new LogManager();
|
||||
this.configManager = new ConfigManager(
|
||||
this.getConfigPath(),
|
||||
this.logManager
|
||||
);
|
||||
switch (platform) {
|
||||
case 'win32':
|
||||
return join(
|
||||
home,
|
||||
'AppData',
|
||||
'Roaming',
|
||||
'Friendly Kobold',
|
||||
CONFIG_FILE_NAME
|
||||
);
|
||||
case 'darwin':
|
||||
return join(
|
||||
home,
|
||||
'Library',
|
||||
'Application Support',
|
||||
'Friendly Kobold',
|
||||
CONFIG_FILE_NAME
|
||||
);
|
||||
default:
|
||||
return join(home, '.config', 'Friendly Kobold', CONFIG_FILE_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
private getConfigPath(): string {
|
||||
return join(app.getPath('userData'), CONFIG_FILE_NAME);
|
||||
private getCurrentKoboldBinary(): string | null {
|
||||
try {
|
||||
const configPath = this.getConfigPath();
|
||||
if (!existsSync(configPath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const config = JSON.parse(readFileSync(configPath, 'utf8'));
|
||||
return config.currentKoboldBinary || null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async handleCliMode(args: string[]): Promise<void> {
|
||||
const currentBinary = this.configManager.getCurrentKoboldBinary();
|
||||
const currentBinary = this.getCurrentKoboldBinary();
|
||||
|
||||
if (!currentBinary) {
|
||||
console.error(
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue