mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-05 01:50:27 +08:00
fix(codexlens): 修复 npm install 后 CodexLens 配置被重置的问题
问题分析: - npm install 时,`__dirname` 指向 node_modules 内的路径 - 使用 `pip install -e`(editable mode)会保存源码路径引用 - npm 升级后旧路径失效,导致需要删除虚拟环境才能重新安装 修复内容: - 添加 isInsideNodeModules() 检测函数 - 添加 isDevEnvironment() 判断是否在开发环境 - 添加 findLocalPackagePath() 统一的本地包路径查找函数 - 当运行在 node_modules 中时,跳过本地路径,直接使用 PyPI 安装 影响的函数: - bootstrapWithUv() - installSemanticWithUv() - bootstrapVenv() - ensureLiteLLMEmbedderReady() 行为变化: - 开发环境(不在 node_modules 中):使用本地路径安装(editable mode) - 生产环境(npm install 安装):使用 PyPI 安装(稳定的包引用)
This commit is contained in:
@@ -29,6 +29,71 @@ import {
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
/**
|
||||
* Check if a path is inside node_modules (unstable for editable installs)
|
||||
* Paths inside node_modules will change when npm reinstalls packages,
|
||||
* breaking editable (-e) pip installs that reference them.
|
||||
*/
|
||||
function isInsideNodeModules(pathToCheck: string): boolean {
|
||||
const normalizedPath = pathToCheck.replace(/\\/g, '/').toLowerCase();
|
||||
return normalizedPath.includes('/node_modules/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we're running in a development environment (not from node_modules)
|
||||
*/
|
||||
function isDevEnvironment(): boolean {
|
||||
return !isInsideNodeModules(__dirname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find valid local package path for development installs.
|
||||
* Returns null if running from node_modules (should use PyPI instead).
|
||||
*
|
||||
* IMPORTANT: When running from node_modules, local paths are unstable
|
||||
* because npm reinstall will delete and recreate the node_modules directory,
|
||||
* breaking any editable (-e) pip installs that reference them.
|
||||
*/
|
||||
function findLocalPackagePath(packageName: string): string | null {
|
||||
// If running from node_modules, skip local paths entirely - use PyPI
|
||||
if (!isDevEnvironment()) {
|
||||
console.log(`[CodexLens] Running from node_modules - will use PyPI for ${packageName}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const possiblePaths = [
|
||||
join(process.cwd(), packageName),
|
||||
join(__dirname, '..', '..', '..', packageName), // ccw/src/tools -> project root
|
||||
join(homedir(), packageName),
|
||||
];
|
||||
|
||||
for (const localPath of possiblePaths) {
|
||||
// Skip paths inside node_modules
|
||||
if (isInsideNodeModules(localPath)) {
|
||||
continue;
|
||||
}
|
||||
if (existsSync(join(localPath, 'pyproject.toml'))) {
|
||||
return localPath;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find valid local codex-lens package path for development installs.
|
||||
*/
|
||||
function findLocalCodexLensPath(): string | null {
|
||||
return findLocalPackagePath('codex-lens');
|
||||
}
|
||||
|
||||
/**
|
||||
* Find valid local ccw-litellm package path for development installs.
|
||||
*/
|
||||
function findLocalCcwLitellmPath(): string | null {
|
||||
return findLocalPackagePath('ccw-litellm');
|
||||
}
|
||||
|
||||
// CodexLens configuration
|
||||
const CODEXLENS_DATA_DIR = join(homedir(), '.codexlens');
|
||||
const CODEXLENS_VENV = join(CODEXLENS_DATA_DIR, 'venv');
|
||||
@@ -383,20 +448,8 @@ async function ensureLiteLLMEmbedderReady(): Promise<BootstrapResult> {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
// Find local ccw-litellm package path (only in development, not from node_modules)
|
||||
const localPath = findLocalCcwLitellmPath();
|
||||
|
||||
// Priority: Use UV if available (faster, better dependency resolution)
|
||||
if (await isUvAvailable()) {
|
||||
@@ -598,20 +651,8 @@ async function bootstrapWithUv(gpuMode: GpuMode = 'cpu'): Promise<BootstrapResul
|
||||
}
|
||||
}
|
||||
|
||||
// Find local codex-lens package
|
||||
const possiblePaths = [
|
||||
join(process.cwd(), 'codex-lens'),
|
||||
join(__dirname, '..', '..', '..', 'codex-lens'), // ccw/src/tools -> project root
|
||||
join(homedir(), 'codex-lens'),
|
||||
];
|
||||
|
||||
let codexLensPath: string | null = null;
|
||||
for (const localPath of possiblePaths) {
|
||||
if (existsSync(join(localPath, 'pyproject.toml'))) {
|
||||
codexLensPath = localPath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Find local codex-lens package (only in development, not from node_modules)
|
||||
const codexLensPath = findLocalCodexLensPath();
|
||||
|
||||
// Determine extras based on GPU mode
|
||||
const extras = GPU_MODE_EXTRAS[gpuMode];
|
||||
@@ -671,20 +712,8 @@ async function installSemanticWithUv(gpuMode: GpuMode = 'cpu'): Promise<Bootstra
|
||||
// Create UV manager
|
||||
const uv = createCodexLensUvManager();
|
||||
|
||||
// Find local codex-lens package
|
||||
const possiblePaths = [
|
||||
join(process.cwd(), 'codex-lens'),
|
||||
join(__dirname, '..', '..', '..', 'codex-lens'),
|
||||
join(homedir(), 'codex-lens'),
|
||||
];
|
||||
|
||||
let codexLensPath: string | null = null;
|
||||
for (const localPath of possiblePaths) {
|
||||
if (existsSync(join(localPath, 'pyproject.toml'))) {
|
||||
codexLensPath = localPath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Find local codex-lens package (only in development, not from node_modules)
|
||||
const codexLensPath = findLocalCodexLensPath();
|
||||
|
||||
// Determine extras based on GPU mode
|
||||
const extras = GPU_MODE_EXTRAS[gpuMode];
|
||||
@@ -907,24 +936,13 @@ async function bootstrapVenv(): Promise<BootstrapResult> {
|
||||
? join(CODEXLENS_VENV, 'Scripts', 'pip.exe')
|
||||
: join(CODEXLENS_VENV, 'bin', 'pip');
|
||||
|
||||
// Try multiple local paths, then fall back to PyPI
|
||||
const possiblePaths = [
|
||||
join(process.cwd(), 'codex-lens'),
|
||||
join(__dirname, '..', '..', '..', 'codex-lens'), // ccw/src/tools -> project root
|
||||
join(homedir(), 'codex-lens'),
|
||||
];
|
||||
// Try local path if in development (not from node_modules), then fall back to PyPI
|
||||
const codexLensPath = findLocalCodexLensPath();
|
||||
|
||||
let installed = false;
|
||||
for (const localPath of possiblePaths) {
|
||||
if (existsSync(join(localPath, 'pyproject.toml'))) {
|
||||
console.log(`[CodexLens] Installing from local path: ${localPath}`);
|
||||
execSync(`"${pipPath}" install -e "${localPath}"`, { stdio: 'inherit', timeout: EXEC_TIMEOUTS.PACKAGE_INSTALL });
|
||||
installed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!installed) {
|
||||
if (codexLensPath) {
|
||||
console.log(`[CodexLens] Installing from local path: ${codexLensPath}`);
|
||||
execSync(`"${pipPath}" install -e "${codexLensPath}"`, { stdio: 'inherit', timeout: EXEC_TIMEOUTS.PACKAGE_INSTALL });
|
||||
} else {
|
||||
console.log('[CodexLens] Installing from PyPI...');
|
||||
execSync(`"${pipPath}" install codexlens`, { stdio: 'inherit', timeout: EXEC_TIMEOUTS.PACKAGE_INSTALL });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user