mirror of
https://github.com/siteboon/claudecodeui.git
synced 2025-12-11 22:29:38 +00:00
162 lines
5.0 KiB
Plaintext
162 lines
5.0 KiB
Plaintext
const { spawn } = require('child_process');
|
|
|
|
async function spawnClaude(command, options = {}, ws) {
|
|
return new Promise(async (resolve, reject) => {
|
|
const { sessionId, projectPath, cwd, resume, toolsSettings } = options;
|
|
let capturedSessionId = sessionId; // Track session ID throughout the process
|
|
let sessionCreatedSent = false; // Track if we've already sent session-created event
|
|
|
|
// Use tools settings passed from frontend, or defaults
|
|
const settings = toolsSettings || {
|
|
allowedTools: [],
|
|
disallowedTools: [],
|
|
skipPermissions: false
|
|
};
|
|
|
|
console.log('🔧 Using tools settings:', settings);
|
|
|
|
// Build Claude CLI command - start with basic flags
|
|
const args = ['--output-format', 'stream-json', '--verbose'];
|
|
|
|
// Add tools settings flags
|
|
if (settings.skipPermissions) {
|
|
args.push('--dangerously-skip-permissions');
|
|
console.log('⚠️ Using --dangerously-skip-permissions');
|
|
}
|
|
|
|
// Add print flag if we have a command
|
|
if (command && command.trim()) {
|
|
args.push('--print');
|
|
}
|
|
|
|
// Add resume flag if resuming (after --print)
|
|
if (resume && sessionId) {
|
|
args.push('--resume', sessionId);
|
|
}
|
|
|
|
// Add allowed tools
|
|
if (settings.allowedTools && settings.allowedTools.length > 0) {
|
|
for (const tool of settings.allowedTools) {
|
|
args.push('--allowedTools', tool);
|
|
console.log('✅ Allowing tool:', tool);
|
|
}
|
|
}
|
|
|
|
// Add disallowed tools
|
|
if (settings.disallowedTools && settings.disallowedTools.length > 0) {
|
|
for (const tool of settings.disallowedTools) {
|
|
args.push('--disallowedTools', tool);
|
|
console.log('❌ Disallowing tool:', tool);
|
|
}
|
|
}
|
|
|
|
// Add the command as the final argument
|
|
if (command && command.trim()) {
|
|
args.push(command);
|
|
}
|
|
|
|
const workingDir = projectPath || cwd || process.cwd();
|
|
console.log('Spawning Claude CLI:', 'claude', args.join(' '));
|
|
console.log('Working directory:', workingDir);
|
|
console.log('Session info - Input sessionId:', sessionId, 'Resume:', resume);
|
|
|
|
const claudeProcess = spawn('claude', args, {
|
|
cwd: workingDir,
|
|
stdio: ['pipe', 'pipe', 'pipe']
|
|
});
|
|
|
|
// Handle stdout (streaming JSON responses)
|
|
claudeProcess.stdout.on('data', (data) => {
|
|
const lines = data.toString().split('\n').filter(line => line.trim());
|
|
|
|
for (const line of lines) {
|
|
try {
|
|
const response = JSON.parse(line);
|
|
|
|
// Capture session ID if it's in the response
|
|
if (response.session_id && !capturedSessionId) {
|
|
capturedSessionId = response.session_id;
|
|
console.log('📝 Captured session ID:', capturedSessionId);
|
|
|
|
// Send session-created event only once for new sessions
|
|
if (!sessionId && !sessionCreatedSent) {
|
|
sessionCreatedSent = true;
|
|
ws.send(JSON.stringify({
|
|
type: 'session-created',
|
|
sessionId: capturedSessionId
|
|
}));
|
|
}
|
|
}
|
|
|
|
// Send parsed response to WebSocket
|
|
ws.send(JSON.stringify({
|
|
type: 'claude-response',
|
|
data: response
|
|
}));
|
|
} catch (parseError) {
|
|
// If not JSON, send as raw text
|
|
ws.send(JSON.stringify({
|
|
type: 'claude-output',
|
|
data: line
|
|
}));
|
|
}
|
|
}
|
|
});
|
|
|
|
// Handle stderr
|
|
claudeProcess.stderr.on('data', (data) => {
|
|
console.error('Claude CLI stderr:', data.toString());
|
|
ws.send(JSON.stringify({
|
|
type: 'claude-error',
|
|
error: data.toString()
|
|
}));
|
|
});
|
|
|
|
// Handle process completion
|
|
claudeProcess.on('close', (code) => {
|
|
console.log(`Claude CLI process exited with code ${code}`);
|
|
|
|
ws.send(JSON.stringify({
|
|
type: 'claude-complete',
|
|
exitCode: code,
|
|
isNewSession: !sessionId && !!command // Flag to indicate this was a new session
|
|
}));
|
|
|
|
if (code === 0) {
|
|
resolve();
|
|
} else {
|
|
reject(new Error(`Claude CLI exited with code ${code}`));
|
|
}
|
|
});
|
|
|
|
// Handle process errors
|
|
claudeProcess.on('error', (error) => {
|
|
console.error('Claude CLI process error:', error);
|
|
|
|
ws.send(JSON.stringify({
|
|
type: 'claude-error',
|
|
error: error.message
|
|
}));
|
|
|
|
reject(error);
|
|
});
|
|
|
|
// Handle stdin for interactive mode
|
|
if (command) {
|
|
// For --print mode with arguments, we don't need to write to stdin
|
|
claudeProcess.stdin.end();
|
|
} else {
|
|
// For interactive mode, we need to write the command to stdin if provided later
|
|
// Keep stdin open for interactive session
|
|
if (command !== undefined) {
|
|
claudeProcess.stdin.write(command + '\n');
|
|
claudeProcess.stdin.end();
|
|
}
|
|
// If no command provided, stdin stays open for interactive use
|
|
}
|
|
});
|
|
}
|
|
|
|
module.exports = {
|
|
spawnClaude
|
|
}; |