mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-12 02:37:45 +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') {
|
if (pathname === '/api/codexlens/uninstall' && req.method === 'POST') {
|
||||||
handlePostRequest(req, res, async () => {
|
handlePostRequest(req, res, async () => {
|
||||||
try {
|
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();
|
const result = await uninstallCodexLens();
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
// Broadcast uninstallation event
|
// Broadcast uninstallation event
|
||||||
|
|||||||
@@ -1360,11 +1360,57 @@ async function uninstallCodexLens(): Promise<BootstrapResult> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log('[CodexLens] Uninstalling CodexLens...');
|
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}`);
|
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');
|
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
|
// Reset bootstrap cache
|
||||||
bootstrapChecked = false;
|
bootstrapChecked = false;
|
||||||
@@ -1374,7 +1420,15 @@ async function uninstallCodexLens(): Promise<BootstrapResult> {
|
|||||||
console.log('[CodexLens] CodexLens uninstalled successfully');
|
console.log('[CodexLens] CodexLens uninstalled successfully');
|
||||||
return { success: true, message: 'CodexLens uninstalled successfully' };
|
return { success: true, message: 'CodexLens uninstalled successfully' };
|
||||||
} catch (err) {
|
} 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/tokenizer.py
|
||||||
src/codexlens/parsers/treesitter_parser.py
|
src/codexlens/parsers/treesitter_parser.py
|
||||||
src/codexlens/search/__init__.py
|
src/codexlens/search/__init__.py
|
||||||
|
src/codexlens/search/binary_searcher.py
|
||||||
src/codexlens/search/chain_search.py
|
src/codexlens/search/chain_search.py
|
||||||
src/codexlens/search/enrichment.py
|
src/codexlens/search/enrichment.py
|
||||||
src/codexlens/search/graph_expander.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/api_reranker.py
|
||||||
src/codexlens/semantic/reranker/base.py
|
src/codexlens/semantic/reranker/base.py
|
||||||
src/codexlens/semantic/reranker/factory.py
|
src/codexlens/semantic/reranker/factory.py
|
||||||
|
src/codexlens/semantic/reranker/fastembed_reranker.py
|
||||||
src/codexlens/semantic/reranker/legacy.py
|
src/codexlens/semantic/reranker/legacy.py
|
||||||
src/codexlens/semantic/reranker/litellm_reranker.py
|
src/codexlens/semantic/reranker/litellm_reranker.py
|
||||||
src/codexlens/semantic/reranker/onnx_reranker.py
|
src/codexlens/semantic/reranker/onnx_reranker.py
|
||||||
|
|||||||
Reference in New Issue
Block a user