diff --git a/ccw/src/templates/dashboard-js/components/notifications.js b/ccw/src/templates/dashboard-js/components/notifications.js index 7a460b28..eb5a6bbc 100644 --- a/ccw/src/templates/dashboard-js/components/notifications.js +++ b/ccw/src/templates/dashboard-js/components/notifications.js @@ -415,6 +415,11 @@ function handleNotification(data) { 'CodexLens' ); } + // Invalidate CodexLens page cache to ensure fresh data on next visit + if (typeof window.invalidateCodexLensCache === 'function') { + window.invalidateCodexLensCache(); + console.log('[CodexLens] Cache invalidated after installation'); + } // Refresh CLI status if active if (typeof loadCodexLensStatus === 'function') { loadCodexLensStatus().then(() => { @@ -438,6 +443,11 @@ function handleNotification(data) { 'CodexLens' ); } + // Invalidate CodexLens page cache to ensure fresh data on next visit + if (typeof window.invalidateCodexLensCache === 'function') { + window.invalidateCodexLensCache(); + console.log('[CodexLens] Cache invalidated after uninstallation'); + } // Refresh CLI status if active if (typeof loadCodexLensStatus === 'function') { loadCodexLensStatus().then(() => { @@ -531,6 +541,48 @@ function handleNotification(data) { console.log('[CodexLens] Index complete:', payload.result?.files_indexed, 'files indexed'); break; + case 'CCW_LITELLM_INSTALLED': + // Handle ccw-litellm installation completion + if (typeof addGlobalNotification === 'function') { + addGlobalNotification( + 'success', + `ccw-litellm installed successfully`, + { + 'Time': new Date(payload.timestamp).toLocaleTimeString() + }, + 'LiteLLM' + ); + } + // Invalidate ccw-litellm status cache + if (typeof window.checkCcwLitellmStatus === 'function') { + window.checkCcwLitellmStatus(true); + } + console.log('[ccw-litellm] Installation completed:', payload); + break; + + case 'CCW_LITELLM_UNINSTALLED': + // Handle ccw-litellm uninstallation completion + if (typeof addGlobalNotification === 'function') { + addGlobalNotification( + 'success', + `ccw-litellm uninstalled successfully`, + { + 'Time': new Date(payload.timestamp).toLocaleTimeString() + }, + 'LiteLLM' + ); + } + // Invalidate ccw-litellm status cache and refresh UI + if (typeof window.checkCcwLitellmStatus === 'function') { + window.checkCcwLitellmStatus(true).then(function() { + if (typeof window.renderCcwLitellmStatusCard === 'function') { + window.renderCcwLitellmStatusCard(); + } + }); + } + console.log('[ccw-litellm] Uninstallation completed:', payload); + break; + default: console.log('[WS] Unknown notification type:', type); } diff --git a/ccw/src/tools/codex-lens.ts b/ccw/src/tools/codex-lens.ts index 55fa4740..38b4ec90 100644 --- a/ccw/src/tools/codex-lens.ts +++ b/ccw/src/tools/codex-lens.ts @@ -329,32 +329,70 @@ async function ensureLiteLLMEmbedderReady(): Promise { return { success: true }; } + console.log('[CodexLens] Installing ccw-litellm for LiteLLM embedding backend...'); + + // Find local ccw-litellm package path + const possiblePaths = [ + join(process.cwd(), 'ccw-litellm'), + join(__dirname, '..', '..', '..', 'ccw-litellm'), // ccw/src/tools -> project root + join(homedir(), 'ccw-litellm'), + ]; + + let localPath: string | null = null; + for (const p of possiblePaths) { + if (existsSync(join(p, 'pyproject.toml'))) { + localPath = p; + break; + } + } + + // Priority: Use UV if available (faster, better dependency resolution) + if (await isUvAvailable()) { + console.log('[CodexLens] Using UV for ccw-litellm installation...'); + try { + const uv = createCodexLensUvManager(); + + // Ensure venv exists + if (!uv.isVenvValid()) { + const venvResult = await uv.createVenv(); + if (!venvResult.success) { + console.log('[CodexLens] UV venv creation failed, falling back to pip:', venvResult.error); + // Fall through to pip fallback + } + } + + if (uv.isVenvValid()) { + let uvResult; + if (localPath) { + console.log(`[CodexLens] Installing ccw-litellm from local path with UV: ${localPath}`); + uvResult = await uv.installFromProject(localPath); + } else { + console.log('[CodexLens] Installing ccw-litellm from PyPI with UV...'); + uvResult = await uv.install(['ccw-litellm']); + } + + if (uvResult.success) { + return { success: true }; + } + console.log('[CodexLens] UV install failed, falling back to pip:', uvResult.error); + } + } catch (uvErr) { + console.log('[CodexLens] UV error, falling back to pip:', (uvErr as Error).message); + } + } + + // Fallback: Use pip for installation const pipPath = process.platform === 'win32' ? join(CODEXLENS_VENV, 'Scripts', 'pip.exe') : join(CODEXLENS_VENV, 'bin', 'pip'); try { - console.log('[CodexLens] Installing ccw-litellm for LiteLLM embedding backend...'); - - const possiblePaths = [ - join(process.cwd(), 'ccw-litellm'), - join(__dirname, '..', '..', '..', 'ccw-litellm'), // ccw/src/tools -> project root - join(homedir(), 'ccw-litellm'), - ]; - - let installed = false; - for (const localPath of possiblePaths) { - if (existsSync(join(localPath, 'pyproject.toml'))) { - console.log(`[CodexLens] Installing ccw-litellm from local path: ${localPath}`); - execSync(`"${pipPath}" install -e "${localPath}"`, { stdio: 'inherit', timeout: EXEC_TIMEOUTS.PACKAGE_INSTALL }); - installed = true; - break; - } - } - - if (!installed) { - console.log('[CodexLens] Installing ccw-litellm from PyPI...'); + if (localPath) { + console.log(`[CodexLens] Installing ccw-litellm from local path with pip: ${localPath}`); + execSync(`"${pipPath}" install -e "${localPath}"`, { stdio: 'inherit', timeout: EXEC_TIMEOUTS.PACKAGE_INSTALL }); + } else { + console.log('[CodexLens] Installing ccw-litellm from PyPI with pip...'); execSync(`"${pipPath}" install ccw-litellm`, { stdio: 'inherit', timeout: EXEC_TIMEOUTS.PACKAGE_INSTALL }); }