mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-05 01:50:27 +08:00
feat: Enhance CodexLens uninstallation process with improved error handling and process termination for locked files
This commit is contained in:
@@ -407,6 +407,41 @@ export async function handleCodexLensRoutes(ctx: RouteContext): Promise<boolean>
|
||||
if (pathname === '/api/codexlens/uninstall' && req.method === 'POST') {
|
||||
handlePostRequest(req, res, async () => {
|
||||
try {
|
||||
// Stop watcher if running (to release file handles)
|
||||
if (watcherStats.running && watcherProcess) {
|
||||
console.log('[CodexLens] Stopping watcher before uninstall...');
|
||||
try {
|
||||
watcherProcess.kill('SIGTERM');
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
if (watcherProcess && !watcherProcess.killed) {
|
||||
watcherProcess.kill('SIGKILL');
|
||||
}
|
||||
} catch {
|
||||
// Ignore errors stopping watcher
|
||||
}
|
||||
watcherStats.running = false;
|
||||
watcherProcess = null;
|
||||
}
|
||||
|
||||
// Cancel any running indexing process
|
||||
if (currentIndexingProcess) {
|
||||
console.log('[CodexLens] Cancelling indexing before uninstall...');
|
||||
try {
|
||||
if (process.platform === 'win32') {
|
||||
const { execSync } = require('child_process');
|
||||
execSync(`taskkill /pid ${currentIndexingProcess.pid} /T /F`, { stdio: 'ignore' });
|
||||
} else {
|
||||
currentIndexingProcess.kill('SIGKILL');
|
||||
}
|
||||
} catch {
|
||||
// Ignore errors
|
||||
}
|
||||
currentIndexingProcess = null;
|
||||
}
|
||||
|
||||
// Wait a moment for processes to fully exit and release handles
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
const result = await uninstallCodexLens();
|
||||
if (result.success) {
|
||||
// Broadcast uninstallation event
|
||||
|
||||
@@ -1360,11 +1360,57 @@ async function uninstallCodexLens(): Promise<BootstrapResult> {
|
||||
}
|
||||
|
||||
console.log('[CodexLens] Uninstalling CodexLens...');
|
||||
|
||||
// On Windows, kill any Python processes that might be holding locks on .db files
|
||||
if (process.platform === 'win32') {
|
||||
console.log('[CodexLens] Killing any CodexLens Python processes...');
|
||||
const { execSync } = await import('child_process');
|
||||
try {
|
||||
// Kill any python processes from our venv that might be holding file locks
|
||||
execSync(`taskkill /F /IM python.exe /FI "MODULES eq sqlite3" 2>nul`, { stdio: 'ignore' });
|
||||
} catch {
|
||||
// Ignore errors - no processes to kill
|
||||
}
|
||||
// Small delay to allow file handles to be released
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
}
|
||||
|
||||
console.log(`[CodexLens] Removing directory: ${CODEXLENS_DATA_DIR}`);
|
||||
|
||||
// Remove the entire .codexlens directory
|
||||
// Remove the entire .codexlens directory with retry logic for locked files
|
||||
const fs = await import('fs');
|
||||
fs.rmSync(CODEXLENS_DATA_DIR, { recursive: true, force: true });
|
||||
const path = await import('path');
|
||||
|
||||
// Helper function to remove directory with retries (Windows EBUSY workaround)
|
||||
const removeWithRetry = async (dirPath: string, maxRetries = 3, delay = 1000): Promise<void> => {
|
||||
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
||||
try {
|
||||
fs.rmSync(dirPath, { recursive: true, force: true, maxRetries: 3, retryDelay: 500 });
|
||||
return;
|
||||
} catch (err: any) {
|
||||
if (err.code === 'EBUSY' || err.code === 'EPERM' || err.code === 'ENOTEMPTY') {
|
||||
console.log(`[CodexLens] Retry ${attempt}/${maxRetries} - file locked, waiting...`);
|
||||
if (attempt < maxRetries) {
|
||||
// On Windows, try to forcefully release file handles
|
||||
if (process.platform === 'win32' && err.path) {
|
||||
try {
|
||||
const { execSync } = await import('child_process');
|
||||
// Try to close handles on the specific file
|
||||
execSync(`handle -c ${err.path} -y 2>nul`, { stdio: 'ignore' });
|
||||
} catch {
|
||||
// handle.exe may not be installed, ignore
|
||||
}
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, delay));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
await removeWithRetry(CODEXLENS_DATA_DIR);
|
||||
|
||||
// Reset bootstrap cache
|
||||
bootstrapChecked = false;
|
||||
@@ -1374,7 +1420,15 @@ async function uninstallCodexLens(): Promise<BootstrapResult> {
|
||||
console.log('[CodexLens] CodexLens uninstalled successfully');
|
||||
return { success: true, message: 'CodexLens uninstalled successfully' };
|
||||
} catch (err) {
|
||||
return { success: false, error: `Failed to uninstall CodexLens: ${(err as Error).message}` };
|
||||
const errorMsg = (err as Error).message;
|
||||
// Provide helpful message for Windows users with locked files
|
||||
if (errorMsg.includes('EBUSY') || errorMsg.includes('resource busy')) {
|
||||
return {
|
||||
success: false,
|
||||
error: `Failed to uninstall CodexLens: Files are locked. Please close any applications using CodexLens indexes (e.g., Claude Code, VS Code) and try again. Details: ${errorMsg}`
|
||||
};
|
||||
}
|
||||
return { success: false, error: `Failed to uninstall CodexLens: ${errorMsg}` };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ src/codexlens/parsers/factory.py
|
||||
src/codexlens/parsers/tokenizer.py
|
||||
src/codexlens/parsers/treesitter_parser.py
|
||||
src/codexlens/search/__init__.py
|
||||
src/codexlens/search/binary_searcher.py
|
||||
src/codexlens/search/chain_search.py
|
||||
src/codexlens/search/enrichment.py
|
||||
src/codexlens/search/graph_expander.py
|
||||
@@ -46,6 +47,7 @@ src/codexlens/semantic/reranker/__init__.py
|
||||
src/codexlens/semantic/reranker/api_reranker.py
|
||||
src/codexlens/semantic/reranker/base.py
|
||||
src/codexlens/semantic/reranker/factory.py
|
||||
src/codexlens/semantic/reranker/fastembed_reranker.py
|
||||
src/codexlens/semantic/reranker/legacy.py
|
||||
src/codexlens/semantic/reranker/litellm_reranker.py
|
||||
src/codexlens/semantic/reranker/onnx_reranker.py
|
||||
|
||||
Reference in New Issue
Block a user