1- import { getBackendPath , getBinaryPath , getCachePath , getVenvPath , isBinaryExists , runInstallScript } from "./utils/process" ;
1+ import { getBackendPath , getBinaryPath , getCachePath , getVenvPath , getUvEnv , isBinaryExists , runInstallScript , killProcessByName } from "./utils/process" ;
22import { spawn , exec } from 'child_process'
33import log from 'electron-log'
44import fs from 'fs'
@@ -21,16 +21,16 @@ export function getMainWindow(): BrowserWindow | null {
2121export async function checkToolInstalled ( ) {
2222 return new Promise < PromiseReturnType > ( async ( resolve , reject ) => {
2323 if ( ! ( await isBinaryExists ( 'uv' ) ) ) {
24- resolve ( { success : false , message : "uv doesn't exist" } )
24+ resolve ( { success : false , message : "uv doesn't exist" } )
2525 return
2626 }
2727
2828 if ( ! ( await isBinaryExists ( 'bun' ) ) ) {
29- resolve ( { success : false , message : "Bun doesn't exist" } )
29+ resolve ( { success : false , message : "Bun doesn't exist" } )
3030 return
3131 }
3232
33- resolve ( { success : true , message : "Tools exist already" } )
33+ resolve ( { success : true , message : "Tools exist already" } )
3434 } )
3535
3636}
@@ -159,12 +159,13 @@ export async function startBackend(setPort?: (port: number) => void): Promise<an
159159 fs . mkdirSync ( npmCacheDir , { recursive : true } ) ;
160160 }
161161
162+ const uvEnv = getUvEnv ( currentVersion ) ;
162163 const env = {
163164 ...process . env ,
165+ ...uvEnv ,
164166 SERVER_URL : "https://dev.eigent.ai/api" ,
165167 PYTHONIOENCODING : 'utf-8' ,
166168 PYTHONUNBUFFERED : '1' ,
167- UV_PROJECT_ENVIRONMENT : venvPath ,
168169 npm_config_cache : npmCacheDir ,
169170 }
170171
@@ -202,10 +203,79 @@ export async function startBackend(setPort?: (port: number) => void): Promise<an
202203 { cwd : backendPath , env : env }
203204 ) ;
204205 log . info ( `Python test output: ${ pythonTest . trim ( ) } ` ) ;
205- } catch ( testErr ) {
206- log . error ( `Pre-flight check failed: ${ testErr } ` ) ;
207- reject ( new Error ( `Backend environment check failed: ${ testErr } ` ) ) ;
208- return ;
206+ } catch ( testErr : any ) {
207+ log . warn ( `Pre-flight check failed, attempting repair: ${ testErr } ` ) ;
208+
209+ try {
210+ // Attempt to repair the environment
211+ log . info ( "Attempting to repair environment..." ) ;
212+
213+ // Cleanup stale processes and locks
214+ log . info ( "Cleaning up stale processes and locks..." ) ;
215+ await killProcessByName ( 'uv' ) ;
216+ await killProcessByName ( 'python' ) ;
217+
218+ // Try to remove the lock file explicitly if it exists
219+ try {
220+ const lockFile = path . join ( getCachePath ( 'uv_python' ) , '.lock' ) ;
221+ if ( fs . existsSync ( lockFile ) ) {
222+ fs . unlinkSync ( lockFile ) ;
223+ }
224+ } catch ( e ) {
225+ log . warn ( `Failed to remove lock file: ${ e } ` ) ;
226+ }
227+
228+ // Cleanup corrupted python cache
229+ try {
230+ const pythonCacheDir = getCachePath ( 'uv_python' ) ;
231+ if ( fs . existsSync ( pythonCacheDir ) ) {
232+ log . info ( `Removing potentially corrupted Python cache: ${ pythonCacheDir } ` ) ;
233+ fs . rmSync ( pythonCacheDir , { recursive : true , force : true } ) ;
234+ }
235+ } catch ( e ) {
236+ log . warn ( `Failed to remove Python cache: ${ e } ` ) ;
237+ }
238+
239+ // Cleanup corrupted venv (pyvenv.cfg may reference non-existent Python version)
240+ try {
241+ if ( fs . existsSync ( venvPath ) ) {
242+ log . info ( `Removing potentially corrupted venv: ${ venvPath } ` ) ;
243+ fs . rmSync ( venvPath , { recursive : true , force : true } ) ;
244+ }
245+ } catch ( e ) {
246+ log . warn ( `Failed to remove venv: ${ e } ` ) ;
247+ }
248+
249+ // Use proxy if in China (simple check based on timezone)
250+ // Add official PyPI as fallback for packages not available on mirror
251+ const timezone = Intl . DateTimeFormat ( ) . resolvedOptions ( ) . timeZone ;
252+ const proxyArgs = timezone === 'Asia/Shanghai'
253+ ? [
254+ '--default-index' , 'https://mirrors.aliyun.com/pypi/simple/' ,
255+ '--index' , 'https://pypi.org/simple/'
256+ ]
257+ : [ ] ;
258+
259+ // Step 1: Ensure Python is installed (fixes corrupted/missing Python)
260+ log . info ( "Step 1: Ensuring Python is installed..." ) ;
261+ await execAsync ( `${ uv_path } python install 3.10` , { cwd : backendPath , env : env } ) ;
262+
263+ // Step 2: Sync dependencies
264+ log . info ( "Step 2: Syncing dependencies..." ) ;
265+ const syncArgs = [ 'sync' , '--no-dev' , ...proxyArgs ] ;
266+ await execAsync ( `${ uv_path } ${ syncArgs . join ( ' ' ) } ` , { cwd : backendPath , env : env } ) ;
267+
268+ // Retry the check
269+ const { stdout : pythonTest } = await execAsync (
270+ `${ uv_path } run python -c "print('Python OK')"` ,
271+ { cwd : backendPath , env : env }
272+ ) ;
273+ log . info ( `Python test output after repair: ${ pythonTest . trim ( ) } ` ) ;
274+ } catch ( repairErr ) {
275+ log . error ( `Repair failed: ${ repairErr } ` ) ;
276+ reject ( new Error ( `Backend environment check failed: ${ testErr } \nRepair failed: ${ repairErr } ` ) ) ;
277+ return ;
278+ }
209279 }
210280
211281 const node_process = spawn (
@@ -265,7 +335,7 @@ export async function startBackend(setPort?: (port: number) => void): Promise<an
265335 setTimeout ( ( ) => {
266336 try {
267337 process . kill ( - proc . pid , 'SIGKILL' ) ;
268- } catch ( e ) { }
338+ } catch ( e ) { }
269339 } , 1000 ) ;
270340 } catch ( e ) {
271341 log . error ( `Failed to kill process group: ${ e } ` ) ;
0 commit comments