Initial commit
Dieser Commit ist enthalten in:
264
terminal_launcher.py
Normale Datei
264
terminal_launcher.py
Normale Datei
@ -0,0 +1,264 @@
|
||||
"""
|
||||
Terminal Launcher Module
|
||||
Handles launching Claude in WSL terminal with specified directory
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import os
|
||||
import tempfile
|
||||
from datetime import datetime
|
||||
from typing import Optional, Union
|
||||
from utils.logger import logger
|
||||
|
||||
class TerminalLauncher:
|
||||
def __init__(self):
|
||||
self.claude_path = self.find_claude_path()
|
||||
|
||||
def find_claude_path(self) -> str:
|
||||
"""Find Claude path dynamically in WSL"""
|
||||
# Try to find claude using 'which' command in WSL with login shell
|
||||
try:
|
||||
# Hide console window on Windows
|
||||
startupinfo = None
|
||||
if hasattr(subprocess, 'STARTUPINFO'):
|
||||
startupinfo = subprocess.STARTUPINFO()
|
||||
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||
startupinfo.wShowWindow = subprocess.SW_HIDE
|
||||
|
||||
# Use login shell to ensure all PATH and aliases are loaded
|
||||
command = ["wsl", "bash", "-l", "-c", "which claude 2>/dev/null || command -v claude 2>/dev/null || type -p claude 2>/dev/null"]
|
||||
logger.debug(f"Executing command to find Claude: {' '.join(command)}")
|
||||
result = subprocess.run(
|
||||
command,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=5,
|
||||
startupinfo=startupinfo
|
||||
)
|
||||
|
||||
if result.returncode == 0 and result.stdout.strip():
|
||||
claude_path = result.stdout.strip()
|
||||
# Clean up any extra output from 'type' command
|
||||
if " is " in claude_path:
|
||||
claude_path = claude_path.split(" is ")[-1].strip()
|
||||
logger.info(f"Found Claude at: {claude_path}")
|
||||
print(f"Found Claude at: {claude_path}")
|
||||
return claude_path
|
||||
except Exception as e:
|
||||
logger.error(f"Error finding Claude path: {e}")
|
||||
print(f"Error finding Claude path: {e}")
|
||||
|
||||
# Fallback paths to try
|
||||
fallback_paths = [
|
||||
"claude", # Try just 'claude' - let WSL PATH handle it
|
||||
"/usr/local/bin/claude",
|
||||
"/usr/bin/claude",
|
||||
"~/.local/bin/claude",
|
||||
"/home/$USER/.nvm/versions/node/*/bin/claude" # Wildcard for any node version
|
||||
]
|
||||
|
||||
# Test each fallback path
|
||||
for path in fallback_paths:
|
||||
try:
|
||||
# Hide console window on Windows
|
||||
startupinfo = None
|
||||
if hasattr(subprocess, 'STARTUPINFO'):
|
||||
startupinfo = subprocess.STARTUPINFO()
|
||||
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||
startupinfo.wShowWindow = subprocess.SW_HIDE
|
||||
|
||||
result = subprocess.run(
|
||||
["wsl", "bash", "-c", f"test -f {path} && echo 'exists'"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=2,
|
||||
startupinfo=startupinfo
|
||||
)
|
||||
if result.stdout.strip() == 'exists':
|
||||
print(f"Found Claude at fallback: {path}")
|
||||
return path
|
||||
except:
|
||||
continue
|
||||
|
||||
# If nothing found, default to 'claude' and hope it's in PATH
|
||||
print("Claude path not found, using 'claude' (hoping it's in PATH)")
|
||||
return "claude"
|
||||
|
||||
def convert_to_wsl_path(self, windows_path: str) -> str:
|
||||
"""Convert Windows path to WSL format"""
|
||||
# Handle different path formats
|
||||
windows_path = windows_path.replace('/', '\\')
|
||||
|
||||
# Extract drive letter and path
|
||||
if ':' in windows_path:
|
||||
drive = windows_path[0].lower()
|
||||
path = windows_path[2:].replace('\\', '/')
|
||||
return f'/mnt/{drive}{path}'
|
||||
return windows_path
|
||||
|
||||
def launch_claude_wsl(self, project_path: str, project_name: str = "Claude Session") -> Union[subprocess.Popen, None]:
|
||||
"""Launch Claude in WSL terminal for specified directory"""
|
||||
try:
|
||||
wsl_path = self.convert_to_wsl_path(project_path)
|
||||
logger.info(f"Launching Claude for project: {project_name} at path: {project_path}")
|
||||
|
||||
# Check if Windows Terminal is available
|
||||
if os.path.exists(r"C:\Windows\System32\wt.exe"):
|
||||
# Use Windows Terminal
|
||||
cmd = [
|
||||
"wt.exe",
|
||||
"-w", "0",
|
||||
"new-tab",
|
||||
"--title", f"Claude - {project_name}",
|
||||
"--suppressApplicationTitle",
|
||||
"--",
|
||||
"wsl", "bash", "-l", "-i", "-c",
|
||||
f"cd '{wsl_path}' && echo 'Working directory: {wsl_path}' && echo '' && claude || (echo 'Error: Claude not found. Please install Claude in WSL.' && exec bash)"
|
||||
]
|
||||
|
||||
logger.info(f"Executing Windows Terminal command: {' '.join(cmd)}")
|
||||
process = subprocess.Popen(cmd, shell=False)
|
||||
logger.info(f"Successfully launched Claude in Windows Terminal for: {project_path}")
|
||||
print(f"Launched Claude in Windows Terminal for: {project_path}")
|
||||
return process
|
||||
|
||||
else:
|
||||
# Fallback to cmd window
|
||||
batch_content = f'''@echo off
|
||||
title Claude - {project_name}
|
||||
echo Starting Claude for project: {project_name}
|
||||
echo Working directory: {project_path}
|
||||
echo.
|
||||
wsl bash -l -i -c "cd '{wsl_path}' && claude || (echo 'Error: Claude not found. Please install Claude in WSL.' && exec bash)"
|
||||
pause
|
||||
'''
|
||||
|
||||
# Create temporary batch file
|
||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f:
|
||||
f.write(batch_content)
|
||||
batch_path = f.name
|
||||
|
||||
# Launch in new cmd window
|
||||
cmd = ['cmd', '/c', 'start', '', batch_path]
|
||||
logger.info(f"Executing CMD command: {' '.join(cmd)}")
|
||||
logger.debug(f"Batch file content:\n{batch_content}")
|
||||
process = subprocess.Popen(cmd, shell=False)
|
||||
logger.info(f"Successfully launched Claude in CMD for: {project_path}")
|
||||
print(f"Launched Claude in CMD for: {project_path}")
|
||||
return process
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error launching Claude: {str(e)}", extra={"project_path": project_path, "project_name": project_name})
|
||||
print(f"Error launching Claude: {str(e)}")
|
||||
return None
|
||||
|
||||
def launch_claude_vps(self, server: str, username: str, password: str) -> bool:
|
||||
"""Launch Claude on VPS via SSH"""
|
||||
try:
|
||||
# Create SSH command with password
|
||||
ssh_command = f'ssh {username}@{server}'
|
||||
|
||||
# Create batch file for SSH connection
|
||||
batch_content = f'''@echo off
|
||||
title Claude - VPS Server
|
||||
echo Connecting to VPS Server...
|
||||
echo Server: {server}
|
||||
echo Username: {username}
|
||||
echo.
|
||||
echo Please enter password when prompted: {password}
|
||||
echo.
|
||||
echo After connection, type 'claude' to start Claude
|
||||
echo.
|
||||
{ssh_command}
|
||||
pause
|
||||
'''
|
||||
|
||||
# Create temporary batch file
|
||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f:
|
||||
f.write(batch_content)
|
||||
batch_path = f.name
|
||||
|
||||
# Launch in new window
|
||||
if os.path.exists(r"C:\Windows\System32\wt.exe"):
|
||||
# Windows Terminal
|
||||
cmd = [
|
||||
"wt.exe",
|
||||
"-w", "0",
|
||||
"new-tab",
|
||||
"--title", "Claude - VPS",
|
||||
"--",
|
||||
"cmd", "/c", batch_path
|
||||
]
|
||||
subprocess.Popen(cmd)
|
||||
else:
|
||||
# CMD fallback
|
||||
subprocess.Popen(['cmd', '/c', 'start', '', batch_path])
|
||||
|
||||
print(f"Launched VPS connection to: {server}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error launching VPS connection: {str(e)}")
|
||||
return False
|
||||
|
||||
def launch_claude_admin_panel(self, project_name: str = "Admin Panel") -> Union[subprocess.Popen, None]:
|
||||
"""Launch Claude for Admin Panel with directory change to /opt/v2-Docker"""
|
||||
try:
|
||||
# Check if Windows Terminal is available
|
||||
if os.path.exists(r"C:\Windows\System32\wt.exe"):
|
||||
# Use Windows Terminal
|
||||
cmd = [
|
||||
"wt.exe",
|
||||
"-w", "0",
|
||||
"new-tab",
|
||||
"--title", f"Claude - {project_name}",
|
||||
"--suppressApplicationTitle",
|
||||
"--",
|
||||
"wsl", "bash", "-l", "-i", "-c",
|
||||
f"cd /opt/v2-Docker && echo 'Working directory: /opt/v2-Docker' && echo '' && claude || (echo 'Error: Claude not found. Please install Claude in WSL.' && exec bash)"
|
||||
]
|
||||
|
||||
process = subprocess.Popen(cmd, shell=False)
|
||||
print(f"Launched Claude in Windows Terminal for Admin Panel")
|
||||
return process
|
||||
|
||||
else:
|
||||
# Fallback to cmd window
|
||||
batch_content = f'''@echo off
|
||||
title Claude - {project_name}
|
||||
echo Starting Claude for: {project_name}
|
||||
echo Working directory: /opt/v2-Docker
|
||||
echo.
|
||||
wsl bash -l -i -c "cd /opt/v2-Docker && claude || (echo 'Error: Claude not found. Please install Claude in WSL.' && exec bash)"
|
||||
pause
|
||||
'''
|
||||
|
||||
# Create temporary batch file
|
||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.bat', delete=False) as f:
|
||||
f.write(batch_content)
|
||||
batch_path = f.name
|
||||
|
||||
# Launch in new cmd window
|
||||
process = subprocess.Popen(['cmd', '/c', 'start', '', batch_path], shell=False)
|
||||
print(f"Launched Claude in CMD for Admin Panel")
|
||||
return process
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error launching Claude for Admin Panel: {str(e)}")
|
||||
return None
|
||||
|
||||
# Test functions
|
||||
if __name__ == "__main__":
|
||||
launcher = TerminalLauncher()
|
||||
|
||||
# Test WSL path conversion
|
||||
test_paths = [
|
||||
"C:\\Users\\hendr\\Desktop\\test",
|
||||
"D:/Projects/MyApp",
|
||||
"C:\\Program Files\\test"
|
||||
]
|
||||
|
||||
print("Testing WSL path conversion:")
|
||||
for path in test_paths:
|
||||
wsl_path = launcher.convert_to_wsl_path(path)
|
||||
print(f"{path} -> {wsl_path}")
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren