mirror of
https://github.com/eigent-ai/eigent.git
synced 2026-05-05 07:10:19 +00:00
chroe: prebuilt
This commit is contained in:
parent
48dbae3f1a
commit
ed9d0bfdb6
7 changed files with 1440 additions and 354 deletions
|
|
@ -1,146 +1,227 @@
|
|||
import { spawn } from 'child_process'
|
||||
import log from 'electron-log'
|
||||
import fs from 'fs'
|
||||
import os from 'os'
|
||||
import path from 'path'
|
||||
import { app } from 'electron'
|
||||
|
||||
import { spawn } from 'child_process';
|
||||
import log from 'electron-log';
|
||||
import fs from 'fs';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
import { app } from 'electron';
|
||||
|
||||
export function getResourcePath() {
|
||||
return path.join(app.getAppPath(), 'resources')
|
||||
return path.join(app.getAppPath(), 'resources');
|
||||
}
|
||||
|
||||
export function getBackendPath() {
|
||||
if (app.isPackaged) {
|
||||
// after packaging, backend is in extraResources
|
||||
return path.join(process.resourcesPath, 'backend')
|
||||
return path.join(process.resourcesPath, 'backend');
|
||||
} else {
|
||||
// development environment
|
||||
return path.join(app.getAppPath(), 'backend')
|
||||
return path.join(app.getAppPath(), 'backend');
|
||||
}
|
||||
}
|
||||
|
||||
export function runInstallScript(scriptPath: string): Promise<boolean> {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
const installScriptPath = path.join(getResourcePath(), 'scripts', scriptPath)
|
||||
log.info(`Running script at: ${installScriptPath}`)
|
||||
const installScriptPath = path.join(
|
||||
getResourcePath(),
|
||||
'scripts',
|
||||
scriptPath
|
||||
);
|
||||
log.info(`Running script at: ${installScriptPath}`);
|
||||
|
||||
const nodeProcess = spawn(process.execPath, [installScriptPath], {
|
||||
env: { ...process.env, ELECTRON_RUN_AS_NODE: '1' }
|
||||
})
|
||||
env: { ...process.env, ELECTRON_RUN_AS_NODE: '1' },
|
||||
});
|
||||
|
||||
let stderrOutput = '';
|
||||
|
||||
nodeProcess.stdout.on('data', (data) => {
|
||||
log.info(`Script output: ${data}`)
|
||||
})
|
||||
log.info(`Script output: ${data}`);
|
||||
});
|
||||
|
||||
nodeProcess.stderr.on('data', (data) => {
|
||||
const errorMsg = data.toString();
|
||||
stderrOutput += errorMsg;
|
||||
log.error(`Script error: ${errorMsg}`)
|
||||
})
|
||||
log.error(`Script error: ${errorMsg}`);
|
||||
});
|
||||
|
||||
nodeProcess.on('close', (code) => {
|
||||
if (code === 0) {
|
||||
log.info('Script completed successfully')
|
||||
resolve(true)
|
||||
log.info('Script completed successfully');
|
||||
resolve(true);
|
||||
} else {
|
||||
log.error(`Script exited with code ${code}`)
|
||||
const errorMessage = stderrOutput.trim() || `Script exited with code ${code}`;
|
||||
reject(new Error(errorMessage))
|
||||
log.error(`Script exited with code ${code}`);
|
||||
const errorMessage =
|
||||
stderrOutput.trim() || `Script exited with code ${code}`;
|
||||
reject(new Error(errorMessage));
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export async function getBinaryName(name: string): Promise<string> {
|
||||
if (process.platform === 'win32') {
|
||||
return `${name}.exe`
|
||||
return `${name}.exe`;
|
||||
}
|
||||
return name
|
||||
return name;
|
||||
}
|
||||
|
||||
export async function getBinaryPath(name?: string): Promise<string> {
|
||||
const binariesDir = path.join(os.homedir(), '.eigent', 'bin')
|
||||
/**
|
||||
* Get path to prebuilt binary (if available in packaged app)
|
||||
*/
|
||||
export function getPrebuiltBinaryPath(name?: string): string | null {
|
||||
if (!app.isPackaged) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Ensure .eigent/bin directory exists
|
||||
if (!fs.existsSync(binariesDir)) {
|
||||
fs.mkdirSync(binariesDir, { recursive: true })
|
||||
const prebuiltBinDir = path.join(process.resourcesPath, 'prebuilt', 'bin');
|
||||
if (!fs.existsSync(prebuiltBinDir)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
return binariesDir
|
||||
return prebuiltBinDir;
|
||||
}
|
||||
|
||||
const binaryName = await getBinaryName(name)
|
||||
return path.join(binariesDir, binaryName)
|
||||
const binaryName = process.platform === 'win32' ? `${name}.exe` : name;
|
||||
const binaryPath = path.join(prebuiltBinDir, binaryName);
|
||||
return fs.existsSync(binaryPath) ? binaryPath : null;
|
||||
}
|
||||
|
||||
export async function getBinaryPath(name?: string): Promise<string> {
|
||||
// First check for prebuilt binary in packaged app
|
||||
if (app.isPackaged) {
|
||||
const prebuiltPath = getPrebuiltBinaryPath(name);
|
||||
if (prebuiltPath) {
|
||||
log.info(`Using prebuilt binary: ${prebuiltPath}`);
|
||||
return prebuiltPath;
|
||||
}
|
||||
}
|
||||
|
||||
const binariesDir = path.join(os.homedir(), '.eigent', 'bin');
|
||||
|
||||
// Ensure .eigent/bin directory exists
|
||||
if (!fs.existsSync(binariesDir)) {
|
||||
fs.mkdirSync(binariesDir, { recursive: true });
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
return binariesDir;
|
||||
}
|
||||
|
||||
const binaryName = await getBinaryName(name);
|
||||
return path.join(binariesDir, binaryName);
|
||||
}
|
||||
|
||||
export function getCachePath(folder: string): string {
|
||||
const cacheDir = path.join(os.homedir(), '.eigent', 'cache', folder)
|
||||
// For packaged app, try to use prebuilt cache first
|
||||
if (app.isPackaged) {
|
||||
const prebuiltCachePath = path.join(
|
||||
process.resourcesPath,
|
||||
'prebuilt',
|
||||
'cache',
|
||||
folder
|
||||
);
|
||||
if (fs.existsSync(prebuiltCachePath)) {
|
||||
log.info(`Using prebuilt cache: ${prebuiltCachePath}`);
|
||||
return prebuiltCachePath;
|
||||
}
|
||||
}
|
||||
|
||||
const cacheDir = path.join(os.homedir(), '.eigent', 'cache', folder);
|
||||
|
||||
// Ensure cache directory exists
|
||||
if (!fs.existsSync(cacheDir)) {
|
||||
fs.mkdirSync(cacheDir, { recursive: true })
|
||||
fs.mkdirSync(cacheDir, { recursive: true });
|
||||
}
|
||||
|
||||
return cacheDir
|
||||
return cacheDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get path to prebuilt venv (if available in packaged app)
|
||||
*/
|
||||
export function getPrebuiltVenvPath(): string | null {
|
||||
if (!app.isPackaged) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const prebuiltVenvPath = path.join(process.resourcesPath, 'prebuilt', 'venv');
|
||||
if (fs.existsSync(prebuiltVenvPath)) {
|
||||
const pyvenvCfg = path.join(prebuiltVenvPath, 'pyvenv.cfg');
|
||||
if (fs.existsSync(pyvenvCfg)) {
|
||||
log.info(`Using prebuilt venv: ${prebuiltVenvPath}`);
|
||||
return prebuiltVenvPath;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function getVenvPath(version: string): string {
|
||||
const venvDir = path.join(os.homedir(), '.eigent', 'venvs', `backend-${version}`)
|
||||
|
||||
// Ensure venvs directory exists (parent of the actual venv)
|
||||
const venvsBaseDir = path.dirname(venvDir)
|
||||
if (!fs.existsSync(venvsBaseDir)) {
|
||||
fs.mkdirSync(venvsBaseDir, { recursive: true })
|
||||
// First check for prebuilt venv in packaged app
|
||||
if (app.isPackaged) {
|
||||
const prebuiltVenv = getPrebuiltVenvPath();
|
||||
if (prebuiltVenv) {
|
||||
return prebuiltVenv;
|
||||
}
|
||||
}
|
||||
|
||||
return venvDir
|
||||
const venvDir = path.join(
|
||||
os.homedir(),
|
||||
'.eigent',
|
||||
'venvs',
|
||||
`backend-${version}`
|
||||
);
|
||||
|
||||
// Ensure venvs directory exists (parent of the actual venv)
|
||||
const venvsBaseDir = path.dirname(venvDir);
|
||||
if (!fs.existsSync(venvsBaseDir)) {
|
||||
fs.mkdirSync(venvsBaseDir, { recursive: true });
|
||||
}
|
||||
|
||||
return venvDir;
|
||||
}
|
||||
|
||||
export function getVenvsBaseDir(): string {
|
||||
return path.join(os.homedir(), '.eigent', 'venvs')
|
||||
return path.join(os.homedir(), '.eigent', 'venvs');
|
||||
}
|
||||
|
||||
export async function cleanupOldVenvs(currentVersion: string): Promise<void> {
|
||||
const venvsBaseDir = getVenvsBaseDir()
|
||||
const venvsBaseDir = getVenvsBaseDir();
|
||||
|
||||
// Check if venvs directory exists
|
||||
if (!fs.existsSync(venvsBaseDir)) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const entries = fs.readdirSync(venvsBaseDir, { withFileTypes: true })
|
||||
const entries = fs.readdirSync(venvsBaseDir, { withFileTypes: true });
|
||||
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory() && entry.name.startsWith('backend-')) {
|
||||
const versionMatch = entry.name.match(/^backend-(.+)$/)
|
||||
const versionMatch = entry.name.match(/^backend-(.+)$/);
|
||||
if (versionMatch && versionMatch[1] !== currentVersion) {
|
||||
const oldVenvPath = path.join(venvsBaseDir, entry.name)
|
||||
console.log(`Cleaning up old venv: ${oldVenvPath}`)
|
||||
const oldVenvPath = path.join(venvsBaseDir, entry.name);
|
||||
console.log(`Cleaning up old venv: ${oldVenvPath}`);
|
||||
|
||||
try {
|
||||
// Remove old venv directory recursively
|
||||
fs.rmSync(oldVenvPath, { recursive: true, force: true })
|
||||
console.log(`Successfully removed old venv: ${entry.name}`)
|
||||
fs.rmSync(oldVenvPath, { recursive: true, force: true });
|
||||
console.log(`Successfully removed old venv: ${entry.name}`);
|
||||
} catch (err) {
|
||||
console.error(`Failed to remove old venv ${entry.name}:`, err)
|
||||
console.error(`Failed to remove old venv ${entry.name}:`, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error during venv cleanup:', err)
|
||||
console.error('Error during venv cleanup:', err);
|
||||
}
|
||||
}
|
||||
|
||||
export async function isBinaryExists(name: string): Promise<boolean> {
|
||||
const cmd = await getBinaryPath(name)
|
||||
const cmd = await getBinaryPath(name);
|
||||
|
||||
return await fs.existsSync(cmd)
|
||||
return await fs.existsSync(cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -155,36 +236,36 @@ export function getUvEnv(version: string): Record<string, string> {
|
|||
UV_TOOL_DIR: getCachePath('uv_tool'),
|
||||
UV_PROJECT_ENVIRONMENT: getVenvPath(version),
|
||||
UV_HTTP_TIMEOUT: '300',
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export async function killProcessByName(name: string): Promise<void> {
|
||||
const platform = process.platform
|
||||
const platform = process.platform;
|
||||
try {
|
||||
if (platform === 'win32') {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
// /F = force, /IM = image name
|
||||
const cmd = spawn('taskkill', ['/F', '/IM', `${name}.exe`])
|
||||
const cmd = spawn('taskkill', ['/F', '/IM', `${name}.exe`]);
|
||||
cmd.on('close', (code) => {
|
||||
// code 0 = success, code 128 = process not found (which is fine)
|
||||
if (code === 0 || code === 128) resolve()
|
||||
else reject(new Error(`taskkill exited with code ${code}`))
|
||||
})
|
||||
cmd.on('error', reject)
|
||||
})
|
||||
if (code === 0 || code === 128) resolve();
|
||||
else reject(new Error(`taskkill exited with code ${code}`));
|
||||
});
|
||||
cmd.on('error', reject);
|
||||
});
|
||||
} else {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const cmd = spawn('pkill', ['-9', name])
|
||||
const cmd = spawn('pkill', ['-9', name]);
|
||||
cmd.on('close', (code) => {
|
||||
// code 0 = success, code 1 = no process found (which is fine)
|
||||
if (code === 0 || code === 1) resolve()
|
||||
else reject(new Error(`pkill exited with code ${code}`))
|
||||
})
|
||||
cmd.on('error', reject)
|
||||
})
|
||||
if (code === 0 || code === 1) resolve();
|
||||
else reject(new Error(`pkill exited with code ${code}`));
|
||||
});
|
||||
cmd.on('error', reject);
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
// Ignore errors, just best effort
|
||||
log.warn(`Failed to kill process ${name}:`, err)
|
||||
log.warn(`Failed to kill process ${name}:`, err);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue