mirror of
https://github.com/lone-cloud/gerbil
synced 2026-06-03 09:33:10 -07:00
more sillytavern integration improvements, default configs to .json
This commit is contained in:
parent
07ebcb182e
commit
ae8ec47f51
25 changed files with 235 additions and 647 deletions
|
|
@ -83,7 +83,6 @@
|
|||
"@mantine/core": "^8.2.7",
|
||||
"@mantine/hooks": "^8.2.7",
|
||||
"execa": "^9.6.0",
|
||||
"fkill": "^9.0.0",
|
||||
"got": "^14.4.7",
|
||||
"lucide-react": "^0.542.0",
|
||||
"react": "^19.1.1",
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import {
|
|||
import { Download, X, ExternalLink } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import type { InstalledVersion, DownloadItem } from '@/types/electron';
|
||||
import { getDisplayNameFromPath } from '@/utils/versionUtils';
|
||||
import { getDisplayNameFromPath } from '@/utils/version';
|
||||
import { GITHUB_API } from '@/constants';
|
||||
|
||||
interface UpdateAvailableModalProps {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { useState, useCallback, useEffect, useRef } from 'react';
|
|||
import { Card, Text, Title, Loader, Stack, Container } from '@mantine/core';
|
||||
import { DownloadCard } from '@/components/DownloadCard';
|
||||
import { getPlatformDisplayName } from '@/utils/platform';
|
||||
import { formatDownloadSize } from '@/utils/downloadUtils';
|
||||
import { formatDownloadSize } from '@/utils/download';
|
||||
import { getAssetDescription, sortDownloadsByType } from '@/utils/assets';
|
||||
import { useKoboldVersions } from '@/hooks/useKoboldVersions';
|
||||
import type { DownloadItem } from '@/types/electron';
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ import {
|
|||
import { ChevronDown } from 'lucide-react';
|
||||
import styles from '@/styles/layout.module.css';
|
||||
import { UI } from '@/constants';
|
||||
import { handleTerminalOutput } from '@/utils/terminal';
|
||||
import { processTerminalContent } from '@/utils/linkifyTerminal';
|
||||
import { handleTerminalOutput, processTerminalContent } from '@/utils/terminal';
|
||||
import { useLaunchConfigStore } from '@/stores/launchConfigStore';
|
||||
import type { FrontendPreference } from '@/types';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Stack, Text, Group, Button, Select, Badge } from '@mantine/core';
|
||||
import { Stack, Text, Group, Button, Select } from '@mantine/core';
|
||||
import { useState, useCallback } from 'react';
|
||||
import { Save, File, Plus, Check } from 'lucide-react';
|
||||
import type { ConfigFile } from '@/types';
|
||||
|
|
@ -14,33 +14,6 @@ interface ConfigFileManagerProps {
|
|||
onLoadConfigFiles: () => Promise<void>;
|
||||
}
|
||||
|
||||
interface SelectItemProps {
|
||||
label: string;
|
||||
extension: string;
|
||||
}
|
||||
|
||||
const getBadgeColor = (extension: string) => {
|
||||
switch (extension.toLowerCase()) {
|
||||
case '.kcpps':
|
||||
return 'blue';
|
||||
case '.kcppt':
|
||||
return 'green';
|
||||
default:
|
||||
return 'gray';
|
||||
}
|
||||
};
|
||||
|
||||
const SelectItem = ({ label, extension }: SelectItemProps) => (
|
||||
<Group justify="space-between" wrap="nowrap">
|
||||
<Text size="sm" truncate>
|
||||
{label}
|
||||
</Text>
|
||||
<Badge size="xs" variant="light" color={getBadgeColor(extension)}>
|
||||
{extension}
|
||||
</Badge>
|
||||
</Group>
|
||||
);
|
||||
|
||||
export const ConfigFileManager = ({
|
||||
configFiles,
|
||||
selectedFile,
|
||||
|
|
@ -110,15 +83,6 @@ export const ConfigFileManager = ({
|
|||
leftSection={<File size={16} />}
|
||||
searchable
|
||||
clearable={false}
|
||||
renderOption={({ option }) => {
|
||||
const dataItem = selectData.find(
|
||||
(item) => item.value === option.value
|
||||
);
|
||||
const extension = dataItem?.extension || '';
|
||||
return (
|
||||
<SelectItem label={option.label} extension={extension} />
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -195,16 +195,17 @@ export const LaunchScreen = ({
|
|||
|
||||
const handleCreateNewConfig = async (configName: string) => {
|
||||
try {
|
||||
const fullConfigName = `${configName}.json`;
|
||||
const success = await window.electronAPI.kobold.saveConfigFile(
|
||||
configName,
|
||||
fullConfigName,
|
||||
buildConfigData()
|
||||
);
|
||||
|
||||
if (success) {
|
||||
await loadConfigFiles();
|
||||
const newFileName = `${configName}.kcpps`;
|
||||
setSelectedFile(newFileName);
|
||||
await window.electronAPI.kobold.setSelectedConfig(newFileName);
|
||||
|
||||
setSelectedFile(fullConfigName);
|
||||
await window.electronAPI.kobold.setSelectedConfig(fullConfigName);
|
||||
} else {
|
||||
window.electronAPI.logs.logError(
|
||||
'Failed to create new configuration',
|
||||
|
|
@ -229,10 +230,8 @@ export const LaunchScreen = ({
|
|||
}
|
||||
|
||||
try {
|
||||
const configName = selectedFile.replace(/\.(kcpps|kcppt)$/, '');
|
||||
|
||||
const success = await window.electronAPI.kobold.saveConfigFile(
|
||||
configName,
|
||||
selectedFile,
|
||||
buildConfigData()
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -16,8 +16,10 @@ import { getAssetDescription, sortDownloadsByType } from '@/utils/assets';
|
|||
import {
|
||||
getDisplayNameFromPath,
|
||||
stripAssetExtensions,
|
||||
} from '@/utils/versionUtils';
|
||||
import { formatDownloadSize, compareVersions } from '@/utils/downloadUtils';
|
||||
compareVersions,
|
||||
} from '@/utils/version';
|
||||
import { formatDownloadSize } from '@/utils/download';
|
||||
|
||||
import { useKoboldVersions } from '@/hooks/useKoboldVersions';
|
||||
import type { InstalledVersion, ReleaseWithStatus } from '@/types/electron';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useState, useCallback, useEffect } from 'react';
|
||||
import { compareVersions } from '@/utils/downloadUtils';
|
||||
import { compareVersions } from '@/utils/version';
|
||||
import { GITHUB_API } from '@/constants';
|
||||
|
||||
interface AppUpdateInfo {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { GITHUB_API, ROCM } from '@/constants';
|
||||
import { getROCmDownload } from '@/utils/rocm';
|
||||
import { GITHUB_API } from '@/constants';
|
||||
import { filterAssetsByPlatform } from '@/utils/platform';
|
||||
import type {
|
||||
DownloadItem,
|
||||
|
|
@ -91,44 +92,6 @@ const fetchLatestReleaseFromAPI = async (
|
|||
return transformReleaseToDownloadItems(release, platform);
|
||||
};
|
||||
|
||||
const getROCmDownload = async (
|
||||
platform: string
|
||||
): Promise<DownloadItem | null> => {
|
||||
if (platform !== 'linux') {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(GITHUB_API.LATEST_RELEASE_URL);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const latestRelease = await response.json();
|
||||
const version = latestRelease?.tag_name?.replace(/^v/, '') || 'unknown';
|
||||
|
||||
return {
|
||||
name: ROCM.BINARY_NAME,
|
||||
url: ROCM.DOWNLOAD_URL,
|
||||
size: ROCM.SIZE_BYTES_APPROX,
|
||||
version,
|
||||
type: 'rocm',
|
||||
};
|
||||
} catch (error) {
|
||||
window.electronAPI.logs.logError(
|
||||
'Failed to fetch ROCm version info:',
|
||||
error as Error
|
||||
);
|
||||
return {
|
||||
name: ROCM.BINARY_NAME,
|
||||
url: ROCM.DOWNLOAD_URL,
|
||||
size: ROCM.SIZE_BYTES_APPROX,
|
||||
version: 'unknown',
|
||||
type: 'rocm',
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const getLatestReleaseWithDownloadStatus =
|
||||
async (): Promise<ReleaseWithStatus | null> => {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { useState, useCallback, useEffect } from 'react';
|
||||
import { getDisplayNameFromPath } from '@/utils/versionUtils';
|
||||
import { compareVersions } from '@/utils/downloadUtils';
|
||||
import { getDisplayNameFromPath, compareVersions } from '@/utils/version';
|
||||
import { useKoboldVersions } from '@/hooks/useKoboldVersions';
|
||||
import type { InstalledVersion, DownloadItem } from '@/types/electron';
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@
|
|||
import { spawn } from 'child_process';
|
||||
import { existsSync, readFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { PRODUCT_NAME, CONFIG_FILE_NAME } from '@/constants';
|
||||
import { homedir } from 'os';
|
||||
|
||||
import { PRODUCT_NAME, CONFIG_FILE_NAME } from '@/constants';
|
||||
import { terminateProcess } from '@/utils/process';
|
||||
|
||||
export class LightweightCliHandler {
|
||||
private getConfigDir(appName: string): string {
|
||||
const platform = process.platform;
|
||||
|
|
@ -95,16 +97,15 @@ export class LightweightCliHandler {
|
|||
reject(error);
|
||||
});
|
||||
|
||||
const handleSignal = () => {
|
||||
const handleSignal = async () => {
|
||||
console.log('\nReceived termination signal, terminating KoboldCpp...');
|
||||
if (!child.killed) {
|
||||
child.kill('SIGTERM');
|
||||
|
||||
setTimeout(() => {
|
||||
if (!child.killed) {
|
||||
child.kill('SIGKILL');
|
||||
}
|
||||
}, 5000);
|
||||
await terminateProcess(child, {
|
||||
timeoutMs: 5000,
|
||||
logError: (message, error) => {
|
||||
console.error(`${message} ${error.message}`);
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -30,22 +30,16 @@ export class IPCHandlers {
|
|||
this.binaryService = binaryService;
|
||||
}
|
||||
|
||||
private async launchKoboldCppWithCustomFrontends(
|
||||
args: string[] = []
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
pid?: number;
|
||||
error?: string;
|
||||
}> {
|
||||
private async launchKoboldCppWithCustomFrontends(args: string[] = []) {
|
||||
try {
|
||||
const frontendPreference =
|
||||
await this.configManager.get('frontendPreference');
|
||||
|
||||
if (frontendPreference === 'sillytavern') {
|
||||
await this.sillyTavernManager.startFrontend(args);
|
||||
}
|
||||
this.koboldManager.launchKoboldCpp(args);
|
||||
|
||||
return await this.koboldManager.launchKoboldCpp(args);
|
||||
if (frontendPreference === 'sillytavern') {
|
||||
this.sillyTavernManager.startFrontend(args);
|
||||
}
|
||||
} catch (error) {
|
||||
this.logManager.logError('Error in enhanced launch:', error as Error);
|
||||
return {
|
||||
|
|
@ -164,11 +158,10 @@ export class IPCHandlers {
|
|||
|
||||
ipcMain.handle('kobold:stopKoboldCpp', async () => {
|
||||
try {
|
||||
this.koboldManager.stopKoboldCpp();
|
||||
await this.sillyTavernManager.cleanup();
|
||||
await this.koboldManager.stopKoboldCpp();
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
this.logManager.logError('Error during stop/cleanup:', error as Error);
|
||||
throw error;
|
||||
return { success: false, error: (error as Error).message };
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable no-comments/disallowComments */
|
||||
import { spawn, ChildProcess } from 'child_process';
|
||||
import { join } from 'path';
|
||||
import {
|
||||
|
|
@ -14,17 +13,17 @@ import {
|
|||
import { rm } from 'fs/promises';
|
||||
import { dialog } from 'electron';
|
||||
|
||||
import { terminateProcess } from '@/utils/processUtils';
|
||||
import { execa } from 'execa';
|
||||
import { terminateProcess } from '@/utils/process';
|
||||
import { getROCmDownload } from '@/utils/rocm';
|
||||
import { got } from 'got';
|
||||
import { pipeline } from 'stream/promises';
|
||||
import { ConfigManager } from '@/main/managers/ConfigManager';
|
||||
import { LogManager } from '@/main/managers/LogManager';
|
||||
import { WindowManager } from '@/main/managers/WindowManager';
|
||||
import { ROCM, PRODUCT_NAME, GITHUB_API } from '@/constants';
|
||||
import { stripAssetExtensions } from '@/utils/versionUtils';
|
||||
import { PRODUCT_NAME } from '@/constants';
|
||||
import { stripAssetExtensions } from '@/utils/version';
|
||||
import type {
|
||||
DownloadItem,
|
||||
GitHubAsset,
|
||||
InstalledVersion,
|
||||
KoboldConfig,
|
||||
|
|
@ -285,7 +284,9 @@ export class KoboldCppManager {
|
|||
|
||||
if (
|
||||
statSync(filePath).isFile() &&
|
||||
(file.endsWith('.kcpps') || file.endsWith('.kcppt'))
|
||||
(file.endsWith('.kcpps') ||
|
||||
file.endsWith('.kcppt') ||
|
||||
file.endsWith('.json'))
|
||||
) {
|
||||
const stats = statSync(filePath);
|
||||
configFiles.push({
|
||||
|
|
@ -323,7 +324,7 @@ export class KoboldCppManager {
|
|||
}
|
||||
|
||||
async saveConfigFile(
|
||||
configName: string,
|
||||
configFileName: string,
|
||||
configData: KoboldConfig
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
|
|
@ -332,15 +333,7 @@ export class KoboldCppManager {
|
|||
return false;
|
||||
}
|
||||
|
||||
let configFileName = `${configName}.kcpps`;
|
||||
let configPath = join(this.installDir, configFileName);
|
||||
|
||||
const kcpptPath = join(this.installDir, `${configName}.kcppt`);
|
||||
if (existsSync(kcpptPath)) {
|
||||
configFileName = `${configName}.kcppt`;
|
||||
configPath = kcpptPath;
|
||||
}
|
||||
|
||||
const configPath = join(this.installDir, configFileName);
|
||||
writeFileSync(configPath, JSON.stringify(configData, null, 2), 'utf-8');
|
||||
return true;
|
||||
} catch (error) {
|
||||
|
|
@ -555,81 +548,13 @@ export class KoboldCppManager {
|
|||
return this.koboldProcess !== null && !this.koboldProcess.killed;
|
||||
}
|
||||
|
||||
async getROCmDownload(): Promise<DownloadItem | null> {
|
||||
const platform = process.platform;
|
||||
|
||||
if (platform === 'linux') {
|
||||
try {
|
||||
const response = await fetch(GITHUB_API.LATEST_RELEASE_URL);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const latestRelease = await response.json();
|
||||
const version = latestRelease?.tag_name?.replace(/^v/, '') || 'unknown';
|
||||
|
||||
return {
|
||||
name: ROCM.BINARY_NAME,
|
||||
url: ROCM.DOWNLOAD_URL,
|
||||
size: ROCM.SIZE_BYTES_APPROX,
|
||||
version,
|
||||
type: 'rocm',
|
||||
};
|
||||
} catch (error) {
|
||||
this.logManager.logError(
|
||||
'Failed to fetch ROCm version info:',
|
||||
error as Error
|
||||
);
|
||||
return {
|
||||
name: ROCM.BINARY_NAME,
|
||||
url: ROCM.DOWNLOAD_URL,
|
||||
size: ROCM.SIZE_BYTES_APPROX,
|
||||
version: 'unknown',
|
||||
type: 'rocm',
|
||||
};
|
||||
}
|
||||
} else if (platform === 'win32') {
|
||||
return null;
|
||||
// The launcher doesn't exist in unpacked state yet.
|
||||
// Enable when it's ready.
|
||||
// try {
|
||||
// const response = await fetch(GITHUB_API.ROCM_LATEST_RELEASE_URL);
|
||||
// if (!response.ok) {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// const release = await response.json();
|
||||
// const rocmAsset = release.assets?.find((asset: GitHubAsset) =>
|
||||
// asset.name.endsWith('rocm.exe')
|
||||
// );
|
||||
|
||||
// if (rocmAsset) {
|
||||
// return {
|
||||
// name: rocmAsset.name,
|
||||
// url: rocmAsset.browser_download_url,
|
||||
// size: rocmAsset.size,
|
||||
// version: release.tag_name?.replace(/^v/, '') || 'unknown',
|
||||
// type: 'rocm',
|
||||
// };
|
||||
// }
|
||||
// } catch (error) {
|
||||
// this.logManager.logError(
|
||||
// 'Failed to fetch Windows ROCm release:',
|
||||
// error as Error
|
||||
// );
|
||||
// }
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async downloadROCm(onProgress?: (progress: number) => void): Promise<{
|
||||
success: boolean;
|
||||
path?: string;
|
||||
error?: string;
|
||||
}> {
|
||||
try {
|
||||
const rocmInfo = await this.getROCmDownload();
|
||||
const rocmInfo = await getROCmDownload(process.platform);
|
||||
if (!rocmInfo) {
|
||||
return {
|
||||
success: false,
|
||||
|
|
@ -740,7 +665,7 @@ export class KoboldCppManager {
|
|||
): Promise<{ success: boolean; pid?: number; error?: string }> {
|
||||
try {
|
||||
if (this.koboldProcess) {
|
||||
this.stopKoboldCpp();
|
||||
await this.stopKoboldCpp();
|
||||
}
|
||||
|
||||
const currentVersion = await this.getCurrentVersion();
|
||||
|
|
@ -820,34 +745,13 @@ export class KoboldCppManager {
|
|||
}
|
||||
}
|
||||
|
||||
stopKoboldCpp(): void {
|
||||
async stopKoboldCpp(): Promise<void> {
|
||||
if (this.koboldProcess) {
|
||||
const pid = this.koboldProcess.pid;
|
||||
|
||||
try {
|
||||
this.koboldProcess.kill('SIGTERM');
|
||||
|
||||
setTimeout(() => {
|
||||
if (this.koboldProcess && !this.koboldProcess.killed) {
|
||||
try {
|
||||
this.koboldProcess.kill('SIGKILL');
|
||||
} catch (error) {
|
||||
this.logManager.logError(
|
||||
'Error force-killing KoboldCpp process:',
|
||||
error as Error
|
||||
);
|
||||
}
|
||||
}
|
||||
}, 5000);
|
||||
|
||||
this.koboldProcess = null;
|
||||
} catch (error) {
|
||||
this.logManager.logError(
|
||||
`Error sending SIGTERM to KoboldCpp process (PID: ${pid}):`,
|
||||
error as Error
|
||||
);
|
||||
this.koboldProcess = null;
|
||||
}
|
||||
await terminateProcess(this.koboldProcess, {
|
||||
timeoutMs: 5000,
|
||||
logError: (message, error) => this.logManager.logError(message, error),
|
||||
});
|
||||
this.koboldProcess = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import type { ChildProcess } from 'child_process';
|
|||
import { LogManager } from './LogManager';
|
||||
import { WindowManager } from './WindowManager';
|
||||
import { SILLYTAVERN } from '@/constants';
|
||||
import { terminateProcess, killProcessOnPort } from '@/utils/processUtils';
|
||||
import { terminateProcess } from '@/utils/process';
|
||||
|
||||
export interface SillyTavernConfig {
|
||||
name: string;
|
||||
|
|
@ -109,15 +109,13 @@ export class SillyTavernManager {
|
|||
return join(this.getSillyTavernDataRoot(), 'default-user', 'settings.json');
|
||||
}
|
||||
|
||||
private getSillyTavernBaseArgs(): string[] {
|
||||
return [
|
||||
'sillytavern',
|
||||
'--listen',
|
||||
'--browserLaunchEnabled',
|
||||
'false',
|
||||
'--disableCsrf',
|
||||
];
|
||||
}
|
||||
private static readonly SILLYTAVERN_BASE_ARGS = [
|
||||
'sillytavern',
|
||||
'--listen',
|
||||
'--browserLaunchEnabled',
|
||||
'false',
|
||||
'--disableCsrf',
|
||||
];
|
||||
|
||||
async isNpxAvailable(): Promise<boolean> {
|
||||
try {
|
||||
|
|
@ -167,22 +165,19 @@ export class SillyTavernManager {
|
|||
'SillyTavern settings not found, starting SillyTavern briefly to generate config...'
|
||||
);
|
||||
|
||||
const spawnArgs = this.getSillyTavernBaseArgs();
|
||||
|
||||
this.windowManager.sendKoboldOutput(
|
||||
`Running command: npx ${spawnArgs.join(' ')}`
|
||||
);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const initProcess = this.createNpxProcess(spawnArgs);
|
||||
const initProcess = this.createNpxProcess(
|
||||
SillyTavernManager.SILLYTAVERN_BASE_ARGS
|
||||
);
|
||||
|
||||
let hasResolved = false;
|
||||
|
||||
initProcess.on('exit', (code: number | null, signal: string | null) => {
|
||||
const exitMessage = signal
|
||||
? `SillyTavern init process terminated with signal ${signal}`
|
||||
: `SillyTavern init process exited with code ${code}`;
|
||||
this.windowManager.sendKoboldOutput(exitMessage);
|
||||
this.windowManager.sendKoboldOutput(
|
||||
signal
|
||||
? `SillyTavern init process terminated with signal ${signal}`
|
||||
: `SillyTavern init process exited with code ${code}`
|
||||
);
|
||||
|
||||
if (!hasResolved) {
|
||||
hasResolved = true;
|
||||
|
|
@ -227,24 +222,29 @@ export class SillyTavernManager {
|
|||
const output = data.toString();
|
||||
|
||||
if (output.includes('SillyTavern is listening')) {
|
||||
setTimeout(() => {
|
||||
setTimeout(async () => {
|
||||
if (!initProcess.killed && !hasResolved) {
|
||||
hasResolved = true;
|
||||
initProcess.kill('SIGTERM');
|
||||
|
||||
await terminateProcess(initProcess, {
|
||||
logError: (message, error) =>
|
||||
this.logManager.logError(message, error),
|
||||
});
|
||||
|
||||
this.windowManager.sendKoboldOutput(
|
||||
'SillyTavern settings should now be generated'
|
||||
);
|
||||
|
||||
resolve();
|
||||
}
|
||||
}, 1000);
|
||||
}, 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (initProcess.stderr) {
|
||||
initProcess.stderr.on('data', (data: Buffer) => {
|
||||
const output = data.toString();
|
||||
this.windowManager.sendKoboldOutput(output.trim());
|
||||
this.windowManager.sendKoboldOutput(data.toString().trim());
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -296,7 +296,6 @@ export class SillyTavernManager {
|
|||
}
|
||||
|
||||
const koboldUrl = `http://${koboldHost}:${koboldPort}`;
|
||||
const koboldApiUrl = `${koboldUrl}/api`;
|
||||
|
||||
if (!settings.power_user) settings.power_user = {};
|
||||
const powerUser = settings.power_user as Record<string, unknown>;
|
||||
|
|
@ -310,7 +309,7 @@ export class SillyTavernManager {
|
|||
|
||||
if (!textgenSettings.server_urls) textgenSettings.server_urls = {};
|
||||
const serverUrls = textgenSettings.server_urls as Record<string, unknown>;
|
||||
serverUrls.koboldcpp = koboldApiUrl;
|
||||
serverUrls.koboldcpp = koboldUrl;
|
||||
|
||||
settings.main_api = 'textgenerationwebui';
|
||||
textgenSettings.type = 'koboldcpp';
|
||||
|
|
@ -341,8 +340,7 @@ export class SillyTavernManager {
|
|||
}, 30000);
|
||||
|
||||
const checkForOutput = (data: Buffer) => {
|
||||
const output = data.toString();
|
||||
if (output.includes('SillyTavern is listening')) {
|
||||
if (data.toString().includes('SillyTavern is listening')) {
|
||||
clearTimeout(timeout);
|
||||
this.windowManager.sendKoboldOutput('SillyTavern is now running!');
|
||||
resolve();
|
||||
|
|
@ -414,8 +412,6 @@ export class SillyTavernManager {
|
|||
|
||||
await this.stopFrontend();
|
||||
|
||||
await killProcessOnPort(config.port);
|
||||
|
||||
this.windowManager.sendKoboldOutput(
|
||||
`Preparing SillyTavern to connect to KoboldCpp at ${koboldHost}:${koboldPort}...`
|
||||
);
|
||||
|
|
@ -428,7 +424,7 @@ export class SillyTavernManager {
|
|||
);
|
||||
|
||||
const sillyTavernArgs = [
|
||||
...this.getSillyTavernBaseArgs(),
|
||||
...SillyTavernManager.SILLYTAVERN_BASE_ARGS,
|
||||
'--port',
|
||||
config.port.toString(),
|
||||
];
|
||||
|
|
@ -436,14 +432,12 @@ export class SillyTavernManager {
|
|||
this.windowManager.sendKoboldOutput(
|
||||
'Final port check before starting SillyTavern...'
|
||||
);
|
||||
await killProcessOnPort(config.port);
|
||||
|
||||
this.sillyTavernProcess = this.createNpxProcess(sillyTavernArgs);
|
||||
|
||||
if (this.sillyTavernProcess.stdout) {
|
||||
this.sillyTavernProcess.stdout.on('data', (data: Buffer) => {
|
||||
const output = data.toString();
|
||||
this.windowManager.sendKoboldOutput(output, true);
|
||||
this.windowManager.sendKoboldOutput(data.toString(), true);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -491,10 +485,6 @@ export class SillyTavernManager {
|
|||
this.windowManager.sendKoboldOutput(
|
||||
`Killing processes on port ${SILLYTAVERN.PORT}...`
|
||||
);
|
||||
await killProcessOnPort(SILLYTAVERN.PORT);
|
||||
this.windowManager.sendKoboldOutput(
|
||||
`Port ${SILLYTAVERN.PORT} is now free`
|
||||
);
|
||||
} catch (error) {
|
||||
this.logManager.logError(
|
||||
'Error killing processes on port:',
|
||||
|
|
@ -531,6 +521,7 @@ export class SillyTavernManager {
|
|||
await Promise.all(promises);
|
||||
this.windowManager.sendKoboldOutput('SillyTavern frontend stopped');
|
||||
}
|
||||
|
||||
async cleanup(): Promise<void> {
|
||||
if (this.sillyTavernProcess) {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import si from 'systeminformation';
|
||||
import { shortenDeviceName } from '@/utils/hardware';
|
||||
import { LogManager } from '@/main/managers/LogManager';
|
||||
import { terminateProcess } from '@/utils/process';
|
||||
import type {
|
||||
CPUCapabilities,
|
||||
GPUCapabilities,
|
||||
|
|
@ -184,12 +185,8 @@ export class HardwareService {
|
|||
resolve({ supported: false, devices: [] });
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
try {
|
||||
nvidia.kill('SIGTERM');
|
||||
} catch {
|
||||
void 0;
|
||||
}
|
||||
setTimeout(async () => {
|
||||
await terminateProcess(nvidia);
|
||||
resolve({ supported: false, devices: [] });
|
||||
}, 5000);
|
||||
});
|
||||
|
|
@ -266,12 +263,8 @@ export class HardwareService {
|
|||
resolve({ supported: false, devices: [] });
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
try {
|
||||
rocminfo.kill('SIGTERM');
|
||||
} catch {
|
||||
void 0;
|
||||
}
|
||||
setTimeout(async () => {
|
||||
await terminateProcess(rocminfo);
|
||||
resolve({ supported: false, devices: [] });
|
||||
}, 5000);
|
||||
});
|
||||
|
|
@ -325,12 +318,8 @@ export class HardwareService {
|
|||
resolve({ supported: false, devices: [] });
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
try {
|
||||
vulkaninfo.kill('SIGTERM');
|
||||
} catch {
|
||||
void 0;
|
||||
}
|
||||
setTimeout(async () => {
|
||||
await terminateProcess(vulkaninfo);
|
||||
resolve({ supported: false, devices: [] });
|
||||
}, 5000);
|
||||
});
|
||||
|
|
@ -426,12 +415,8 @@ export class HardwareService {
|
|||
resolve({ supported: false, devices: [] });
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
try {
|
||||
clinfo.kill('SIGTERM');
|
||||
} catch {
|
||||
void 0;
|
||||
}
|
||||
setTimeout(async () => {
|
||||
await terminateProcess(clinfo);
|
||||
resolve({ supported: false, devices: [] });
|
||||
}, 3000);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { ASSET_SUFFIXES } from '@/constants';
|
||||
import { stripAssetExtensions } from '@/utils/versionUtils';
|
||||
import { stripAssetExtensions } from '@/utils/version';
|
||||
|
||||
export const getAssetDescription = (assetName: string): string => {
|
||||
const name = stripAssetExtensions(assetName).toLowerCase();
|
||||
|
|
|
|||
17
src/utils/download.ts
Normal file
17
src/utils/download.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { ROCM } from '@/constants';
|
||||
|
||||
export const formatDownloadSize = (size: number, url?: string): string => {
|
||||
if (!size) return '';
|
||||
|
||||
const isApproximateSize = url?.includes(ROCM.DOWNLOAD_URL);
|
||||
|
||||
return isApproximateSize
|
||||
? `~${formatFileSizeInMB(size)}`
|
||||
: formatFileSizeInMB(size);
|
||||
};
|
||||
|
||||
const formatFileSizeInMB = (bytes: number) => {
|
||||
if (bytes === 0) return '0 MB';
|
||||
const mb = bytes / (1024 * 1024);
|
||||
return parseFloat(mb.toFixed(1)) + ' MB';
|
||||
};
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
const URL_REGEX = /(https?:\/\/[^\s<>"{}|\\^`[\]]+)/gi;
|
||||
|
||||
export const linkifyText = (text: string): string =>
|
||||
text.replace(URL_REGEX, (url) => {
|
||||
const cleanUrl = url.replace(/[.,;:!?]+$/, '');
|
||||
const trailingPunctuation = url.slice(cleanUrl.length);
|
||||
|
||||
return `<a href="${cleanUrl}" target="_blank" rel="noopener noreferrer" style="color: #339af0; text-decoration: underline; cursor: pointer;">${cleanUrl}</a>${trailingPunctuation}`;
|
||||
});
|
||||
|
||||
export const escapeHtmlExceptLinks = (text: string): string => {
|
||||
const escaped = text
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
|
||||
return linkifyText(escaped);
|
||||
};
|
||||
|
||||
export const processTerminalContent = (content: string): string => {
|
||||
if (!content) return '';
|
||||
|
||||
return escapeHtmlExceptLinks(content);
|
||||
};
|
||||
42
src/utils/process.ts
Normal file
42
src/utils/process.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import type { ChildProcess } from 'child_process';
|
||||
|
||||
export interface ProcessTerminationOptions {
|
||||
timeoutMs?: number;
|
||||
logError?: (message: string, error: Error) => void;
|
||||
}
|
||||
|
||||
export async function terminateProcess(
|
||||
childProcess: ChildProcess,
|
||||
options: ProcessTerminationOptions = {}
|
||||
): Promise<void> {
|
||||
const { timeoutMs = 3000, logError } = options;
|
||||
|
||||
if (!childProcess?.pid) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const signal = process.platform === 'win32' ? undefined : 'SIGTERM';
|
||||
childProcess.kill(signal);
|
||||
|
||||
await new Promise<void>((resolve) => {
|
||||
const timeout = setTimeout(() => {
|
||||
if (childProcess && !childProcess.killed) {
|
||||
try {
|
||||
childProcess.kill('SIGKILL');
|
||||
} catch (error) {
|
||||
logError?.('Error force-killing process:', error as Error);
|
||||
}
|
||||
}
|
||||
resolve();
|
||||
}, timeoutMs);
|
||||
|
||||
childProcess.once('exit', () => {
|
||||
clearTimeout(timeout);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
logError?.('Error terminating process:', error as Error);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
import type { ChildProcess } from 'child_process';
|
||||
import { exec } from 'child_process';
|
||||
import fkill from 'fkill';
|
||||
|
||||
export interface ProcessTerminationOptions {
|
||||
logError?: (message: string, error: Error) => void;
|
||||
}
|
||||
|
||||
async function findProcessesOnPort(port: number): Promise<number[]> {
|
||||
return new Promise((resolve) => {
|
||||
if (process.platform === 'win32') {
|
||||
exec(`netstat -ano | findstr :${port}`, (error, stdout) => {
|
||||
if (error || !stdout) {
|
||||
resolve([]);
|
||||
return;
|
||||
}
|
||||
|
||||
const pids: number[] = [];
|
||||
const lines = stdout.split('\n');
|
||||
|
||||
for (const line of lines) {
|
||||
const match = line.match(/\s+(\d+)$/);
|
||||
if (match) {
|
||||
const pid = parseInt(match[1], 10);
|
||||
if (!isNaN(pid) && pid > 0) {
|
||||
pids.push(pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resolve([...new Set(pids)]);
|
||||
});
|
||||
} else {
|
||||
exec(`lsof -ti:${port}`, (error, stdout) => {
|
||||
if (error || !stdout) {
|
||||
resolve([]);
|
||||
return;
|
||||
}
|
||||
|
||||
const pids = stdout
|
||||
.trim()
|
||||
.split('\n')
|
||||
.map((pid) => parseInt(pid, 10))
|
||||
.filter((pid) => !isNaN(pid) && pid > 0);
|
||||
|
||||
resolve(pids);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function isPortFree(port: number): Promise<boolean> {
|
||||
const pids = await findProcessesOnPort(port);
|
||||
return pids.length === 0;
|
||||
}
|
||||
|
||||
export async function killProcessOnPort(port: number): Promise<void> {
|
||||
try {
|
||||
await fkill(`:${port}`, { force: true, silent: true });
|
||||
} catch {
|
||||
void 0;
|
||||
}
|
||||
|
||||
const pids = await findProcessesOnPort(port);
|
||||
|
||||
if (pids.length > 0) {
|
||||
for (const pid of pids) {
|
||||
try {
|
||||
await fkill(pid, { force: true, silent: true });
|
||||
} catch {
|
||||
void 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let attempts = 0;
|
||||
const maxAttempts = 10;
|
||||
|
||||
while (attempts < maxAttempts && !(await isPortFree(port))) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
attempts++;
|
||||
}
|
||||
}
|
||||
|
||||
export async function terminateProcess(
|
||||
process: ChildProcess,
|
||||
options: ProcessTerminationOptions = {}
|
||||
): Promise<void> {
|
||||
const { logError } = options;
|
||||
|
||||
if (!process || !process.pid) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await fkill(process.pid, { force: true, silent: true });
|
||||
} catch (error) {
|
||||
logError?.('Error terminating process:', error as Error);
|
||||
}
|
||||
}
|
||||
36
src/utils/rocm.ts
Normal file
36
src/utils/rocm.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import { GITHUB_API, ROCM } from '@/constants';
|
||||
import type { DownloadItem } from '@/types/electron';
|
||||
|
||||
export async function getROCmDownload(
|
||||
platform: string
|
||||
): Promise<DownloadItem | null> {
|
||||
if (platform !== 'linux') {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(GITHUB_API.LATEST_RELEASE_URL);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const latestRelease = await response.json();
|
||||
const version = latestRelease?.tag_name?.replace(/^v/, '') || 'unknown';
|
||||
|
||||
return {
|
||||
name: ROCM.BINARY_NAME,
|
||||
url: ROCM.DOWNLOAD_URL,
|
||||
size: ROCM.SIZE_BYTES_APPROX,
|
||||
version,
|
||||
type: 'rocm',
|
||||
};
|
||||
} catch {
|
||||
return {
|
||||
name: ROCM.BINARY_NAME,
|
||||
url: ROCM.DOWNLOAD_URL,
|
||||
size: ROCM.SIZE_BYTES_APPROX,
|
||||
version: 'unknown',
|
||||
type: 'rocm',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -24,3 +24,30 @@ export const handleTerminalOutput = (
|
|||
return prevContent + newData;
|
||||
}
|
||||
};
|
||||
|
||||
const URL_REGEX = /(https?:\/\/[^\s<>"{}|\\^`[\]]+)/gi;
|
||||
|
||||
const linkifyText = (text: string): string =>
|
||||
text.replace(URL_REGEX, (url) => {
|
||||
const cleanUrl = url.replace(/[.,;:!?]+$/, '');
|
||||
const trailingPunctuation = url.slice(cleanUrl.length);
|
||||
|
||||
return `<a href="${cleanUrl}" target="_blank" rel="noopener noreferrer" style="color: #339af0; text-decoration: underline; cursor: pointer;">${cleanUrl}</a>${trailingPunctuation}`;
|
||||
});
|
||||
|
||||
const escapeHtmlExceptLinks = (text: string): string => {
|
||||
const escaped = text
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
|
||||
return linkifyText(escaped);
|
||||
};
|
||||
|
||||
export const processTerminalContent = (content: string): string => {
|
||||
if (!content) return '';
|
||||
|
||||
return escapeHtmlExceptLinks(content);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,15 +1,25 @@
|
|||
import { ROCM } from '@/constants';
|
||||
import type { InstalledVersion } from '@/types';
|
||||
|
||||
export const formatDownloadSize = (size: number, url?: string): string => {
|
||||
if (!size) return '';
|
||||
export const getDisplayNameFromPath = (
|
||||
installedVersion: InstalledVersion
|
||||
): string => {
|
||||
const pathParts = installedVersion.path.split(/[/\\]/);
|
||||
const launcherIndex = pathParts.findIndex(
|
||||
(part) => part === 'koboldcpp-launcher' || part === 'koboldcpp-launcher.exe'
|
||||
);
|
||||
|
||||
const isApproximateSize = url?.includes(ROCM.DOWNLOAD_URL);
|
||||
if (launcherIndex > 0) {
|
||||
return pathParts[launcherIndex - 1];
|
||||
}
|
||||
|
||||
return isApproximateSize
|
||||
? `~${formatFileSizeInMB(size)}`
|
||||
: formatFileSizeInMB(size);
|
||||
return installedVersion.filename;
|
||||
};
|
||||
|
||||
export const stripAssetExtensions = (assetName: string): string =>
|
||||
assetName
|
||||
.replace(/\.(tar\.gz|zip|exe|dmg|AppImage)$/i, '')
|
||||
.replace(/\.packed$/, '');
|
||||
|
||||
export const compareVersions = (versionA: string, versionB: string): number => {
|
||||
const cleanVersion = (version: string): string =>
|
||||
version.replace(/^v/, '').replace(/[^0-9.]/g, '');
|
||||
|
|
@ -34,9 +44,3 @@ export const compareVersions = (versionA: string, versionB: string): number => {
|
|||
|
||||
return 0;
|
||||
};
|
||||
|
||||
const formatFileSizeInMB = (bytes: number) => {
|
||||
if (bytes === 0) return '0 MB';
|
||||
const mb = bytes / (1024 * 1024);
|
||||
return parseFloat(mb.toFixed(1)) + ' MB';
|
||||
};
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
import type { InstalledVersion } from '@/types';
|
||||
|
||||
export const getDisplayNameFromPath = (
|
||||
installedVersion: InstalledVersion
|
||||
): string => {
|
||||
const pathParts = installedVersion.path.split(/[/\\]/);
|
||||
const launcherIndex = pathParts.findIndex(
|
||||
(part) => part === 'koboldcpp-launcher' || part === 'koboldcpp-launcher.exe'
|
||||
);
|
||||
|
||||
if (launcherIndex > 0) {
|
||||
return pathParts[launcherIndex - 1];
|
||||
}
|
||||
|
||||
return installedVersion.filename;
|
||||
};
|
||||
|
||||
export const stripAssetExtensions = (assetName: string): string =>
|
||||
assetName
|
||||
.replace(/\.(tar\.gz|zip|exe|dmg|AppImage)$/i, '')
|
||||
.replace(/\.packed$/, '');
|
||||
194
yarn.lock
194
yarn.lock
|
|
@ -1601,16 +1601,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"aggregate-error@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "aggregate-error@npm:5.0.0"
|
||||
dependencies:
|
||||
clean-stack: "npm:^5.2.0"
|
||||
indent-string: "npm:^5.0.0"
|
||||
checksum: 10c0/a5de7138571f514bad76290736f49a0db8809247082f2519037e0c37d03fc8d91d733e079d6b1674feda28a757b1932421ad205b8c0f8794a0c0e5bf1be2315e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ajv-keywords@npm:^3.4.1":
|
||||
version: 3.5.2
|
||||
resolution: "ajv-keywords@npm:3.5.2"
|
||||
|
|
@ -2237,15 +2227,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"clean-stack@npm:^5.2.0":
|
||||
version: 5.2.0
|
||||
resolution: "clean-stack@npm:5.2.0"
|
||||
dependencies:
|
||||
escape-string-regexp: "npm:5.0.0"
|
||||
checksum: 10c0/0de47a4152e49dcdeede5f47d7bb9a39a3ea748acb1cd2f0160dbee972d920be81390cb4c5566e6b795791b9efb12359e89fdd7c2e63b36025d59529558570f1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cli-cursor@npm:^3.1.0":
|
||||
version: 3.1.0
|
||||
resolution: "cli-cursor@npm:3.1.0"
|
||||
|
|
@ -2467,7 +2448,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cross-spawn@npm:^7.0.1, cross-spawn@npm:^7.0.3, cross-spawn@npm:^7.0.6":
|
||||
"cross-spawn@npm:^7.0.1, cross-spawn@npm:^7.0.6":
|
||||
version: 7.0.6
|
||||
resolution: "cross-spawn@npm:7.0.6"
|
||||
dependencies:
|
||||
|
|
@ -3117,13 +3098,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"escape-string-regexp@npm:5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "escape-string-regexp@npm:5.0.0"
|
||||
checksum: 10c0/6366f474c6f37a802800a435232395e04e9885919873e382b157ab7e8f0feb8fed71497f84a6f6a81a49aab41815522f5839112bd38026d203aea0c91622df95
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"escape-string-regexp@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "escape-string-regexp@npm:4.0.0"
|
||||
|
|
@ -3380,40 +3354,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"execa@npm:^6.1.0":
|
||||
version: 6.1.0
|
||||
resolution: "execa@npm:6.1.0"
|
||||
dependencies:
|
||||
cross-spawn: "npm:^7.0.3"
|
||||
get-stream: "npm:^6.0.1"
|
||||
human-signals: "npm:^3.0.1"
|
||||
is-stream: "npm:^3.0.0"
|
||||
merge-stream: "npm:^2.0.0"
|
||||
npm-run-path: "npm:^5.1.0"
|
||||
onetime: "npm:^6.0.0"
|
||||
signal-exit: "npm:^3.0.7"
|
||||
strip-final-newline: "npm:^3.0.0"
|
||||
checksum: 10c0/004ee32092af745766a1b0352fdba8701a4001bc3fe08e63101c04276d4c860bbe11bb8ab85f37acdff13d3da83d60e044041dcf24bd7e25e645a543828d9c41
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"execa@npm:^8.0.1":
|
||||
version: 8.0.1
|
||||
resolution: "execa@npm:8.0.1"
|
||||
dependencies:
|
||||
cross-spawn: "npm:^7.0.3"
|
||||
get-stream: "npm:^8.0.1"
|
||||
human-signals: "npm:^5.0.0"
|
||||
is-stream: "npm:^3.0.0"
|
||||
merge-stream: "npm:^2.0.0"
|
||||
npm-run-path: "npm:^5.1.0"
|
||||
onetime: "npm:^6.0.0"
|
||||
signal-exit: "npm:^4.1.0"
|
||||
strip-final-newline: "npm:^3.0.0"
|
||||
checksum: 10c0/2c52d8775f5bf103ce8eec9c7ab3059909ba350a5164744e9947ed14a53f51687c040a250bda833f906d1283aa8803975b84e6c8f7a7c42f99dc8ef80250d1af
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"execa@npm:^9.6.0":
|
||||
version: 9.6.0
|
||||
resolution: "execa@npm:9.6.0"
|
||||
|
|
@ -3591,20 +3531,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fkill@npm:^9.0.0":
|
||||
version: 9.0.0
|
||||
resolution: "fkill@npm:9.0.0"
|
||||
dependencies:
|
||||
aggregate-error: "npm:^5.0.0"
|
||||
execa: "npm:^8.0.1"
|
||||
pid-port: "npm:^1.0.0"
|
||||
process-exists: "npm:^5.0.0"
|
||||
ps-list: "npm:^8.1.1"
|
||||
taskkill: "npm:^5.0.0"
|
||||
checksum: 10c0/91090ef26287b833d810789ef828b0fb37458677ea9c7b41a294e20a1147692d80e8ba61fa5a943211d1a0fa9331503f634f30a0f931a794c5be6a635296e90b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"flat-cache@npm:^4.0.0":
|
||||
version: 4.0.1
|
||||
resolution: "flat-cache@npm:4.0.1"
|
||||
|
|
@ -3693,7 +3619,6 @@ __metadata:
|
|||
eslint-plugin-react-refresh: "npm:^0.4.20"
|
||||
eslint-plugin-sonarjs: "npm:^3.0.5"
|
||||
execa: "npm:^9.6.0"
|
||||
fkill: "npm:^9.0.0"
|
||||
globals: "npm:^16.3.0"
|
||||
got: "npm:^14.4.7"
|
||||
husky: "npm:^9.1.7"
|
||||
|
|
@ -3902,20 +3827,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"get-stream@npm:^6.0.1":
|
||||
version: 6.0.1
|
||||
resolution: "get-stream@npm:6.0.1"
|
||||
checksum: 10c0/49825d57d3fd6964228e6200a58169464b8e8970489b3acdc24906c782fb7f01f9f56f8e6653c4a50713771d6658f7cfe051e5eb8c12e334138c9c918b296341
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"get-stream@npm:^8.0.1":
|
||||
version: 8.0.1
|
||||
resolution: "get-stream@npm:8.0.1"
|
||||
checksum: 10c0/5c2181e98202b9dae0bb4a849979291043e5892eb40312b47f0c22b9414fc9b28a3b6063d2375705eb24abc41ecf97894d9a51f64ff021511b504477b27b4290
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"get-stream@npm:^9.0.0, get-stream@npm:^9.0.1":
|
||||
version: 9.0.1
|
||||
resolution: "get-stream@npm:9.0.1"
|
||||
|
|
@ -4229,20 +4140,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"human-signals@npm:^3.0.1":
|
||||
version: 3.0.1
|
||||
resolution: "human-signals@npm:3.0.1"
|
||||
checksum: 10c0/0bb27e72aea1666322f69ab9816e05df952ef2160346f2293f98f45d472edb1b62d0f1a596697b50d48d8f8222e6db3b9f9dc0b6bf6113866121001f0a8e48e9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"human-signals@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "human-signals@npm:5.0.0"
|
||||
checksum: 10c0/5a9359073fe17a8b58e5a085e9a39a950366d9f00217c4ff5878bd312e09d80f460536ea6a3f260b5943a01fe55c158d1cea3fc7bee3d0520aeef04f6d915c82
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"human-signals@npm:^8.0.1":
|
||||
version: 8.0.1
|
||||
resolution: "human-signals@npm:8.0.1"
|
||||
|
|
@ -4332,13 +4229,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"indent-string@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "indent-string@npm:5.0.0"
|
||||
checksum: 10c0/8ee77b57d92e71745e133f6f444d6fa3ed503ad0e1bcd7e80c8da08b42375c07117128d670589725ed07b1978065803fa86318c309ba45415b7fe13e7f170220
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"infer-owner@npm:^1.0.4":
|
||||
version: 1.0.4
|
||||
resolution: "infer-owner@npm:1.0.4"
|
||||
|
|
@ -4624,13 +4514,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-stream@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "is-stream@npm:3.0.0"
|
||||
checksum: 10c0/eb2f7127af02ee9aa2a0237b730e47ac2de0d4e76a4a905a50a11557f2339df5765eaea4ceb8029f1efa978586abe776908720bfcb1900c20c6ec5145f6f29d8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-stream@npm:^4.0.1":
|
||||
version: 4.0.1
|
||||
resolution: "is-stream@npm:4.0.1"
|
||||
|
|
@ -5188,13 +5071,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"merge-stream@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "merge-stream@npm:2.0.0"
|
||||
checksum: 10c0/867fdbb30a6d58b011449b8885601ec1690c3e41c759ecd5a9d609094f7aed0096c37823ff4a7190ef0b8f22cc86beb7049196ff68c016e3b3c671d0dac91ce5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"merge2@npm:^1.3.0":
|
||||
version: 1.4.1
|
||||
resolution: "merge2@npm:1.4.1"
|
||||
|
|
@ -5244,13 +5120,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mimic-fn@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "mimic-fn@npm:4.0.0"
|
||||
checksum: 10c0/de9cc32be9996fd941e512248338e43407f63f6d497abe8441fa33447d922e927de54d4cc3c1a3c6d652857acd770389d5a3823f311a744132760ce2be15ccbf
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mimic-function@npm:^5.0.0":
|
||||
version: 5.0.1
|
||||
resolution: "mimic-function@npm:5.0.1"
|
||||
|
|
@ -5598,15 +5467,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"npm-run-path@npm:^5.1.0":
|
||||
version: 5.3.0
|
||||
resolution: "npm-run-path@npm:5.3.0"
|
||||
dependencies:
|
||||
path-key: "npm:^4.0.0"
|
||||
checksum: 10c0/124df74820c40c2eb9a8612a254ea1d557ddfab1581c3e751f825e3e366d9f00b0d76a3c94ecd8398e7f3eee193018622677e95816e8491f0797b21e30b2deba
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"npm-run-path@npm:^6.0.0":
|
||||
version: 6.0.0
|
||||
resolution: "npm-run-path@npm:6.0.0"
|
||||
|
|
@ -5733,15 +5593,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"onetime@npm:^6.0.0":
|
||||
version: 6.0.0
|
||||
resolution: "onetime@npm:6.0.0"
|
||||
dependencies:
|
||||
mimic-fn: "npm:^4.0.0"
|
||||
checksum: 10c0/4eef7c6abfef697dd4479345a4100c382d73c149d2d56170a54a07418c50816937ad09500e1ed1e79d235989d073a9bade8557122aee24f0576ecde0f392bb6c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"onetime@npm:^7.0.0":
|
||||
version: 7.0.0
|
||||
resolution: "onetime@npm:7.0.0"
|
||||
|
|
@ -5955,15 +5806,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"pid-port@npm:^1.0.0":
|
||||
version: 1.0.2
|
||||
resolution: "pid-port@npm:1.0.2"
|
||||
dependencies:
|
||||
execa: "npm:^8.0.1"
|
||||
checksum: 10c0/b3a62fde1c73f896a0950f5446b014b55fff2bd84068bce3cba4a459561f695de76c11f74a6c54a64f395b0eca29c79f5c3d705061067d3d157ae8115b00e8a3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"pidtree@npm:^0.6.0":
|
||||
version: 0.6.0
|
||||
resolution: "pidtree@npm:0.6.0"
|
||||
|
|
@ -6041,15 +5883,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"process-exists@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "process-exists@npm:5.0.0"
|
||||
dependencies:
|
||||
ps-list: "npm:^8.0.0"
|
||||
checksum: 10c0/2d0edce88dbf22adc1d30aac08a996bd747786cb91c861b003127345f27217e89c528c2c661404e2ad3d50c2a598e5ae4bbb997ce3628e095a07596590a29b67
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"progress@npm:^2.0.3":
|
||||
version: 2.0.3
|
||||
resolution: "progress@npm:2.0.3"
|
||||
|
|
@ -6085,13 +5918,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ps-list@npm:^8.0.0, ps-list@npm:^8.1.1":
|
||||
version: 8.1.1
|
||||
resolution: "ps-list@npm:8.1.1"
|
||||
checksum: 10c0/5c0fa265f910e03eac14e0785f106d481c4bc3eb95f6fd77a055c231194a5accdea5e1e410050bdb5976cf1620a0bcf41923a10c9540e3ad1b9c8e0bd402116b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"pump@npm:^3.0.0":
|
||||
version: 3.0.3
|
||||
resolution: "pump@npm:3.0.3"
|
||||
|
|
@ -6805,7 +6631,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.7":
|
||||
"signal-exit@npm:^3.0.2":
|
||||
version: 3.0.7
|
||||
resolution: "signal-exit@npm:3.0.7"
|
||||
checksum: 10c0/25d272fa73e146048565e08f3309d5b942c1979a6f4a58a8c59d5fa299728e9c2fcd1a759ec870863b1fd38653670240cd420dad2ad9330c71f36608a6a1c912
|
||||
|
|
@ -7130,13 +6956,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"strip-final-newline@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "strip-final-newline@npm:3.0.0"
|
||||
checksum: 10c0/a771a17901427bac6293fd416db7577e2bc1c34a19d38351e9d5478c3c415f523f391003b42ed475f27e33a78233035df183525395f731d3bfb8cdcbd4da08ce
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"strip-final-newline@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "strip-final-newline@npm:4.0.0"
|
||||
|
|
@ -7221,15 +7040,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"taskkill@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "taskkill@npm:5.0.0"
|
||||
dependencies:
|
||||
execa: "npm:^6.1.0"
|
||||
checksum: 10c0/ba9684224806a3b0bbc1898a51125d56aa6fa16f4e9e5dfbb14bc00301151297414966067a28cb5078cb5e7db8161b9b1c95a4969d0846078f7f7582fe633e46
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"temp-file@npm:^3.4.0":
|
||||
version: 3.4.0
|
||||
resolution: "temp-file@npm:3.4.0"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue