fix terminal env issue

This commit is contained in:
Wendong-Fan 2026-01-21 02:11:19 +08:00
parent 2397d5e463
commit e839212c5e
2 changed files with 77 additions and 36 deletions

View file

@ -152,13 +152,16 @@ class TerminalToolkit(BaseTerminalToolkit, AbstractToolkit):
logger.warning("Falling back to system Python")
def _get_venv_path(self):
"""Return the cloned venv path for shell activation."""
cloned_env_path = getattr(self, 'cloned_env_path', None)
if cloned_env_path and os.path.exists(cloned_env_path):
return cloned_env_path
return None
def _clone_venv_with_symlinks(self, source_venv: str, target_venv: str):
"""Clone a venv using symlinks for efficiency.
Only creates the minimum structure needed: pyvenv.cfg, bin/python, and lib symlink.
Activation scripts are not needed since we use python_executable directly.
Creates the structure needed: pyvenv.cfg, bin/python, lib symlink, and activate scripts.
"""
is_windows = platform.system() == 'Windows'
@ -187,6 +190,16 @@ class TerminalToolkit(BaseTerminalToolkit, AbstractToolkit):
src = os.path.join(source_scripts, exe)
if os.path.exists(src):
shutil.copy2(src, os.path.join(target_bin, exe))
# Copy activate scripts (need to modify VIRTUAL_ENV path)
for script in ["activate.bat", "activate.ps1", "deactivate.bat"]:
src = os.path.join(source_scripts, script)
if os.path.exists(src):
with open(src, 'r', encoding='utf-8') as f:
content = f.read()
content = content.replace(source_venv, target_venv)
dst = os.path.join(target_bin, script)
with open(dst, 'w', encoding='utf-8') as f:
f.write(content)
# Use directory junction for Lib (no admin rights needed, unlike symlink)
source_lib = os.path.join(source_venv, "Lib")
target_lib = os.path.join(target_venv, "Lib")
@ -204,6 +217,19 @@ class TerminalToolkit(BaseTerminalToolkit, AbstractToolkit):
os.symlink(python_exe, os.path.join(target_bin, "python"))
os.symlink("python", os.path.join(target_bin, "python3"))
# Copy activate scripts (need to modify VIRTUAL_ENV path)
source_bin = os.path.join(source_venv, "bin")
for script in ["activate", "activate.csh", "activate.fish"]:
src = os.path.join(source_bin, script)
if os.path.exists(src):
with open(src, 'r') as f:
content = f.read()
# Replace source venv path with target venv path
content = content.replace(source_venv, target_venv)
dst = os.path.join(target_bin, script)
with open(dst, 'w') as f:
f.write(content)
# Symlink lib directory
source_lib = os.path.join(source_venv, "lib")
os.symlink(source_lib, os.path.join(target_venv, "lib"))

View file

@ -493,51 +493,64 @@ async function installTerminalBaseVenv(version: string): Promise<PromiseReturnTy
const pythonPath = process.platform === 'win32'
? path.join(terminalVenvPath, 'Scripts', 'python.exe')
: path.join(terminalVenvPath, 'bin', 'python');
// Marker file to indicate packages were installed successfully
const installedMarker = path.join(terminalVenvPath, '.packages_installed');
// Check if terminal base venv already exists and is valid
if (fs.existsSync(pythonPath)) {
log.info('[DEPS INSTALL] Terminal base venv already exists, skipping creation');
// Check if terminal base venv already exists and packages are installed
if (fs.existsSync(pythonPath) && fs.existsSync(installedMarker)) {
log.info('[DEPS INSTALL] Terminal base venv already exists with packages, skipping creation');
return { message: 'Terminal base venv already exists', success: true };
}
log.info('[DEPS INSTALL] Creating terminal base venv...');
// If python exists but marker doesn't, packages may not be installed - need to reinstall
const needsPackageInstall = fs.existsSync(pythonPath) && !fs.existsSync(installedMarker);
if (needsPackageInstall) {
log.info('[DEPS INSTALL] Terminal venv exists but packages not installed, installing packages...');
} else {
log.info('[DEPS INSTALL] Creating terminal base venv...');
}
safeMainWindowSend('install-dependencies-log', {
type: 'stdout',
data: 'Creating terminal base environment...\n',
data: needsPackageInstall
? 'Installing missing packages in terminal environment...\n'
: 'Creating terminal base environment...\n',
});
try {
// Create the venv using uv
await new Promise<void>((resolve, reject) => {
const createVenv = spawn(
uv_path,
['venv', '--python', '3.10', terminalVenvPath],
{
env: {
...process.env,
UV_PYTHON_INSTALL_DIR: getCachePath('uv_python'),
},
}
);
// Create the venv using uv (skip if only need package install)
if (!needsPackageInstall) {
await new Promise<void>((resolve, reject) => {
const createVenv = spawn(
uv_path,
['venv', '--python', '3.10', terminalVenvPath],
{
env: {
...process.env,
UV_PYTHON_INSTALL_DIR: getCachePath('uv_python'),
},
}
);
createVenv.stdout.on('data', (data) => {
log.info(`[DEPS INSTALL] terminal venv: ${data}`);
createVenv.stdout.on('data', (data) => {
log.info(`[DEPS INSTALL] terminal venv: ${data}`);
});
createVenv.stderr.on('data', (data) => {
log.info(`[DEPS INSTALL] terminal venv: ${data}`);
});
createVenv.on('close', (code) => {
if (code === 0) {
resolve();
} else {
reject(new Error(`Failed to create terminal venv, exit code: ${code}`));
}
});
createVenv.on('error', reject);
});
createVenv.stderr.on('data', (data) => {
log.info(`[DEPS INSTALL] terminal venv: ${data}`);
});
createVenv.on('close', (code) => {
if (code === 0) {
resolve();
} else {
reject(new Error(`Failed to create terminal venv, exit code: ${code}`));
}
});
createVenv.on('error', reject);
});
}
// Install base packages
log.info('[DEPS INSTALL] Installing terminal base packages...');
@ -591,6 +604,8 @@ async function installTerminalBaseVenv(version: string): Promise<PromiseReturnTy
installPkgs.on('error', reject);
});
// Create marker file to indicate successful installation
fs.writeFileSync(installedMarker, new Date().toISOString());
log.info('[DEPS INSTALL] Terminal base venv created successfully');
return { message: 'Terminal base venv created successfully', success: true };
} catch (error) {