mirror of
https://github.com/siteboon/claudecodeui.git
synced 2025-12-11 20:59:38 +00:00
feat(platform): improve cross-platform compatibility with Windows support
Enhance application to work seamlessly across operating systems by implementing platform-specific adaptations: - Update node-pty to version 1.1.0-beta34 for Windows support - Implement PowerShell terminal support for Windows environments - Integrate cross-spawn library for reliable process execution on all platforms - Standardize development ports to industry defaults (3001/5173) - Add dynamic shell detection based on operating system BREAKING CHANGE: Development server ports have been changed from 3008/3009 to 3001/5173 to align with standard Vite/Express defaults
This commit is contained in:
@@ -7,6 +7,6 @@
|
|||||||
|
|
||||||
# Backend server port (Express API + WebSocket server)
|
# Backend server port (Express API + WebSocket server)
|
||||||
#API server
|
#API server
|
||||||
PORT=3008
|
PORT=3001
|
||||||
#Frontend port
|
#Frontend port
|
||||||
VITE_PORT=3009
|
VITE_PORT=5173
|
||||||
1462
package-lock.json
generated
1462
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -39,13 +39,14 @@
|
|||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
|
"cross-spawn": "^7.0.3",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"lucide-react": "^0.515.0",
|
"lucide-react": "^0.515.0",
|
||||||
"mime-types": "^3.0.1",
|
"mime-types": "^3.0.1",
|
||||||
"multer": "^2.0.1",
|
"multer": "^2.0.1",
|
||||||
"node-fetch": "^2.7.0",
|
"node-fetch": "^2.7.0",
|
||||||
"node-pty": "^1.0.0",
|
"node-pty": "^1.1.0-beta34",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-dropzone": "^14.2.3",
|
"react-dropzone": "^14.2.3",
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
import { spawn } from 'child_process';
|
import { spawn } from 'child_process';
|
||||||
|
import crossSpawn from 'cross-spawn';
|
||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
|
|
||||||
|
// Use cross-spawn on Windows for better command execution
|
||||||
|
const spawnFunction = process.platform === 'win32' ? crossSpawn : spawn;
|
||||||
|
|
||||||
let activeClaudeProcesses = new Map(); // Track active processes by session ID
|
let activeClaudeProcesses = new Map(); // Track active processes by session ID
|
||||||
|
|
||||||
async function spawnClaude(command, options = {}, ws) {
|
async function spawnClaude(command, options = {}, ws) {
|
||||||
@@ -23,7 +27,10 @@ async function spawnClaude(command, options = {}, ws) {
|
|||||||
|
|
||||||
// Add print flag with command if we have a command
|
// Add print flag with command if we have a command
|
||||||
if (command && command.trim()) {
|
if (command && command.trim()) {
|
||||||
args.push('--print', command);
|
// Separate arguments for better cross-platform compatibility
|
||||||
|
// This prevents issues with spaces and quotes on Windows
|
||||||
|
args.push('--print');
|
||||||
|
args.push(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use cwd (actual project directory) instead of projectPath (Claude's metadata directory)
|
// Use cwd (actual project directory) instead of projectPath (Claude's metadata directory)
|
||||||
@@ -63,9 +70,9 @@ async function spawnClaude(command, options = {}, ws) {
|
|||||||
const imageNote = `\n\n[Images provided at the following paths:]\n${tempImagePaths.map((p, i) => `${i + 1}. ${p}`).join('\n')}`;
|
const imageNote = `\n\n[Images provided at the following paths:]\n${tempImagePaths.map((p, i) => `${i + 1}. ${p}`).join('\n')}`;
|
||||||
const modifiedCommand = command + imageNote;
|
const modifiedCommand = command + imageNote;
|
||||||
|
|
||||||
// Update the command in args
|
// Update the command in args - now that --print and command are separate
|
||||||
const printIndex = args.indexOf('--print');
|
const printIndex = args.indexOf('--print');
|
||||||
if (printIndex !== -1 && args[printIndex + 1] === command) {
|
if (printIndex !== -1 && printIndex + 1 < args.length && args[printIndex + 1] === command) {
|
||||||
args[printIndex + 1] = modifiedCommand;
|
args[printIndex + 1] = modifiedCommand;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -227,7 +234,7 @@ async function spawnClaude(command, options = {}, ws) {
|
|||||||
console.log('🔍 Full command args:', JSON.stringify(args, null, 2));
|
console.log('🔍 Full command args:', JSON.stringify(args, null, 2));
|
||||||
console.log('🔍 Final Claude command will be: claude ' + args.join(' '));
|
console.log('🔍 Final Claude command will be: claude ' + args.join(' '));
|
||||||
|
|
||||||
const claudeProcess = spawn('claude', args, {
|
const claudeProcess = spawnFunction('claude', args, {
|
||||||
cwd: workingDir,
|
cwd: workingDir,
|
||||||
stdio: ['pipe', 'pipe', 'pipe'],
|
stdio: ['pipe', 'pipe', 'pipe'],
|
||||||
env: { ...process.env } // Inherit all environment variables
|
env: { ...process.env } // Inherit all environment variables
|
||||||
|
|||||||
@@ -515,32 +515,41 @@ function handleShellConnection(ws) {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Build shell command that changes to project directory first, then runs claude
|
// Prepare the shell command adapted to the platform
|
||||||
let claudeCommand = 'claude';
|
let shellCommand;
|
||||||
|
if (os.platform() === 'win32') {
|
||||||
if (hasSession && sessionId) {
|
if (hasSession && sessionId) {
|
||||||
// Try to resume session, but with fallback to new session if it fails
|
// Try to resume session, but with fallback to new session if it fails
|
||||||
claudeCommand = `claude --resume ${sessionId} || claude`;
|
shellCommand = `Set-Location -Path "${projectPath}"; claude --resume ${sessionId}; if ($LASTEXITCODE -ne 0) { claude }`;
|
||||||
|
} else {
|
||||||
|
shellCommand = `Set-Location -Path "${projectPath}"; claude`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (hasSession && sessionId) {
|
||||||
|
shellCommand = `cd "${projectPath}" && claude --resume ${sessionId} || claude`;
|
||||||
|
} else {
|
||||||
|
shellCommand = `cd "${projectPath}" && claude`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create shell command that cds to the project directory first
|
|
||||||
const shellCommand = `cd "${projectPath}" && ${claudeCommand}`;
|
|
||||||
|
|
||||||
console.log('🔧 Executing shell command:', shellCommand);
|
console.log('🔧 Executing shell command:', shellCommand);
|
||||||
|
|
||||||
// Start shell using PTY for proper terminal emulation
|
// Use appropriate shell based on platform
|
||||||
shellProcess = pty.spawn('bash', ['-c', shellCommand], {
|
const shell = os.platform() === 'win32' ? 'powershell.exe' : 'bash';
|
||||||
|
const shellArgs = os.platform() === 'win32' ? ['-Command', shellCommand] : ['-c', shellCommand];
|
||||||
|
|
||||||
|
shellProcess = pty.spawn(shell, shellArgs, {
|
||||||
name: 'xterm-256color',
|
name: 'xterm-256color',
|
||||||
cols: 80,
|
cols: 80,
|
||||||
rows: 24,
|
rows: 24,
|
||||||
cwd: process.env.HOME || '/', // Start from home directory
|
cwd: process.env.HOME || (os.platform() === 'win32' ? process.env.USERPROFILE : '/'),
|
||||||
env: {
|
env: {
|
||||||
...process.env,
|
...process.env,
|
||||||
TERM: 'xterm-256color',
|
TERM: 'xterm-256color',
|
||||||
COLORTERM: 'truecolor',
|
COLORTERM: 'truecolor',
|
||||||
FORCE_COLOR: '3',
|
FORCE_COLOR: '3',
|
||||||
// Override browser opening commands to echo URL for detection
|
// Override browser opening commands to echo URL for detection
|
||||||
BROWSER: 'echo "OPEN_URL:"'
|
BROWSER: os.platform() === 'win32' ? 'echo "OPEN_URL:"' : 'echo "OPEN_URL:"'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -889,12 +898,7 @@ app.post('/api/projects/:projectName/upload-images', authenticateToken, async (r
|
|||||||
|
|
||||||
// Serve React app for all other routes
|
// Serve React app for all other routes
|
||||||
app.get('*', (req, res) => {
|
app.get('*', (req, res) => {
|
||||||
if (process.env.NODE_ENV === 'production') {
|
|
||||||
res.sendFile(path.join(__dirname, '../dist/index.html'));
|
res.sendFile(path.join(__dirname, '../dist/index.html'));
|
||||||
} else {
|
|
||||||
// In development, redirect to Vite dev server
|
|
||||||
res.redirect(`http://localhost:${process.env.VITE_PORT || 3001}`);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Helper function to convert permissions to rwx format
|
// Helper function to convert permissions to rwx format
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ export default defineConfig(({ command, mode }) => {
|
|||||||
return {
|
return {
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
server: {
|
server: {
|
||||||
port: parseInt(env.VITE_PORT) || 3001,
|
port: parseInt(env.VITE_PORT) || 5173,
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': `http://localhost:${env.PORT || 3002}`,
|
'/api': `http://localhost:${env.PORT || 3001}`,
|
||||||
'/ws': {
|
'/ws': {
|
||||||
target: `ws://localhost:${env.PORT || 3002}`,
|
target: `ws://localhost:${env.PORT || 3001}`,
|
||||||
ws: true
|
ws: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user