claude-code-remote-remake/claude-control.js

287 lines
12 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
/**
* Claude-Code-Remote Unattended Remote Control Setup Assistant
*/
const { exec, spawn } = require('child_process');
const fs = require('fs');
const path = require('path');
class RemoteControlSetup {
constructor(sessionName = null) {
this.sessionName = sessionName || 'claude-code-remote';
this.claudeCodeRemoteHome = this.findClaudeCodeRemoteHome();
}
findClaudeCodeRemoteHome() {
// If CLAUDE_CODE_REMOTE_HOME environment variable is set, use it
if (process.env.CLAUDE_CODE_REMOTE_HOME) {
return process.env.CLAUDE_CODE_REMOTE_HOME;
}
// If running from the Claude-Code-Remote directory, use current directory
if (fs.existsSync(path.join(__dirname, 'package.json'))) {
const packagePath = path.join(__dirname, 'package.json');
try {
const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
if (packageJson.name && packageJson.name.toLowerCase().includes('claude-code-remote')) {
return __dirname;
}
} catch (e) {
// Continue searching
}
}
// Search for Claude-Code-Remote in common locations
const commonPaths = [
path.join(process.env.HOME, 'dev', 'Claude-Code-Remote'),
path.join(process.env.HOME, 'Projects', 'Claude-Code-Remote'),
path.join(process.env.HOME, 'claude-code-remote'),
__dirname // fallback to current script directory
];
for (const searchPath of commonPaths) {
if (fs.existsSync(searchPath) && fs.existsSync(path.join(searchPath, 'package.json'))) {
try {
const packageJson = JSON.parse(fs.readFileSync(path.join(searchPath, 'package.json'), 'utf8'));
if (packageJson.name && packageJson.name.toLowerCase().includes('claude-code-remote')) {
return searchPath;
}
} catch (e) {
// Continue searching
}
}
}
// If not found, use current directory as fallback
return __dirname;
}
async setup() {
console.log('🚀 Claude-Code-Remote Unattended Remote Control Setup\n');
console.log('🎯 Goal: Remote access via mobile phone → Home computer Claude Code automatically executes commands\n');
try {
// 1. Check tmux
await this.checkAndInstallTmux();
// 2. Check Claude CLI
await this.checkClaudeCLI();
// 3. Setup Claude tmux session
await this.setupClaudeSession();
// 4. Session creation complete
console.log('\n4⃣ Session creation complete');
// 5. Provide usage guide
this.showUsageGuide();
} catch (error) {
console.error('❌ Error occurred during setup:', error.message);
}
}
async checkAndInstallTmux() {
console.log('1⃣ Checking tmux installation status...');
return new Promise((resolve) => {
exec('which tmux', (error, stdout) => {
if (error) {
console.log('❌ tmux not installed');
console.log('📦 Installing tmux...');
exec('brew install tmux', (installError, installStdout, installStderr) => {
if (installError) {
console.log('❌ tmux installation failed, please install manually:');
console.log(' brew install tmux');
console.log(' or download from https://github.com/tmux/tmux');
} else {
console.log('✅ tmux installation successful');
}
resolve();
});
} else {
console.log(`✅ tmux already installed: ${stdout.trim()}`);
resolve();
}
});
});
}
async checkClaudeCLI() {
console.log('\n2⃣ Checking Claude CLI status...');
return new Promise((resolve) => {
exec('which claude', (error, stdout) => {
if (error) {
console.log('❌ Claude CLI not found');
console.log('📦 Please install Claude CLI:');
console.log(' npm install -g @anthropic-ai/claude-code');
} else {
console.log(`✅ Claude CLI installed: ${stdout.trim()}`);
// Check version
exec('claude --version', (versionError, versionStdout) => {
if (!versionError) {
console.log(`📋 Version: ${versionStdout.trim()}`);
}
});
}
resolve();
});
});
}
async setupClaudeSession() {
console.log('\n3⃣ Setting up Claude tmux session...');
return new Promise((resolve) => {
// Check if session already exists
exec(`tmux has-session -t ${this.sessionName} 2>/dev/null`, (checkError) => {
if (!checkError) {
console.log('⚠️ Claude tmux session already exists');
console.log('🔄 Recreating session? (will kill existing session)');
// For simplicity, recreate directly
this.killAndCreateSession(resolve);
} else {
this.createNewSession(resolve);
}
});
});
}
killAndCreateSession(resolve) {
exec(`tmux kill-session -t ${this.sessionName} 2>/dev/null`, () => {
setTimeout(() => {
this.createNewSession(resolve);
}, 1000);
});
}
createNewSession(resolve) {
// Use Claude-Code-Remote home directory as working directory
const workingDir = this.claudeCodeRemoteHome;
const command = `tmux new-session -d -s ${this.sessionName} -c "${workingDir}" clauderun`;
console.log(`🚀 Creating Claude tmux session: ${this.sessionName}`);
console.log(`📁 Working directory: ${workingDir}`);
console.log(`💡 Using convenience command: clauderun (equivalent to claude --dangerously-skip-permissions)`);
exec(command, (error, stdout, stderr) => {
if (error) {
console.log(`❌ Session creation failed: ${error.message}`);
if (stderr) {
console.log(`Error details: ${stderr}`);
}
// If clauderun fails, try using full path command
console.log('🔄 Trying full path command...');
const fallbackCommand = `tmux new-session -d -s ${this.sessionName} -c "${workingDir}" /Users/jessytsui/.nvm/versions/node/v18.17.0/bin/claude --dangerously-skip-permissions`;
exec(fallbackCommand, (fallbackError) => {
if (fallbackError) {
console.log(`❌ Full path command also failed: ${fallbackError.message}`);
} else {
console.log('✅ Claude tmux session created successfully (using full path)');
console.log(`📺 View session: tmux attach -t ${this.sessionName}`);
console.log(`🔚 Exit session: Ctrl+B, D (won't close Claude)`);
}
resolve();
});
} else {
console.log('✅ Claude tmux session created successfully');
console.log(`📺 View session: tmux attach -t ${this.sessionName}`);
console.log(`🔚 Exit session: Ctrl+B, D (won't close Claude)`);
resolve();
}
});
}
async testRemoteInjection() {
console.log('\n💡 Session is ready, you can start using it');
console.log('📋 Claude Code is waiting for your instructions');
console.log('🔧 To test injection functionality, please use separate test script');
return Promise.resolve();
}
showUsageGuide() {
console.log('\n🎉 Setup complete! Unattended remote control is ready\n');
console.log('🎯 New feature: clauderun convenience command');
console.log(' You can now use clauderun instead of claude --dangerously-skip-permissions');
console.log(' Clearer Claude Code startup method\n');
console.log('📋 Usage workflow:');
console.log('1. 🏠 Start email monitoring at home: npm run relay:pty');
console.log('2. 🚪 When going out, Claude continues running in tmux');
console.log('3. 📱 Receive Claude-Code-Remote email notifications on mobile');
console.log('4. 💬 Reply to email with commands on mobile');
console.log('5. 🤖 Claude at home automatically receives and executes commands');
console.log('6. 🔄 Repeat above process, completely unattended\n');
console.log('🔧 Management commands:');
console.log(` View Claude session: tmux attach -t ${this.sessionName}`);
console.log(` Exit session (without closing): Ctrl+B, D`);
console.log(` Kill session: tmux kill-session -t ${this.sessionName}`);
console.log(` View all sessions: tmux list-sessions\n`);
console.log('🎛️ Multi-session support:');
console.log(' Create custom session: node claude-control.js --session my-project');
console.log(' Create multiple sessions: node claude-control.js --session frontend');
console.log(' node claude-control.js --session backend');
console.log(' Email replies will automatically route to corresponding session\n');
console.log('📱 Email testing:');
console.log(' Token will include session information, automatically routing to correct tmux session');
console.log(' Recipient email: jiaxicui446@gmail.com');
console.log(' Reply with command: echo "Remote control test"\n');
console.log('🚨 Important reminders:');
console.log('- Claude session runs continuously in tmux, won\'t be interrupted by network disconnection/reconnection');
console.log('- Email monitoring service needs to remain running');
console.log('- Home computer needs to stay powered on with network connection');
console.log('- Mobile can send email commands from anywhere');
console.log('- Supports running multiple Claude sessions for different projects simultaneously\n');
console.log('✅ Now you can achieve true unattended remote control! 🎯');
}
// Quick session restart method
async quickRestart() {
console.log('🔄 Quick restart Claude session...');
return new Promise((resolve) => {
this.killAndCreateSession(() => {
console.log('✅ Claude session restarted');
resolve();
});
});
}
}
// Command line parameter processing
if (require.main === module) {
const args = process.argv.slice(2);
// Parse session name parameter
let sessionName = null;
const sessionIndex = args.indexOf('--session');
if (sessionIndex !== -1 && args[sessionIndex + 1]) {
sessionName = args[sessionIndex + 1];
}
const setup = new RemoteControlSetup(sessionName);
if (sessionName) {
console.log(`🎛️ Using custom session name: ${sessionName}`);
}
if (args.includes('--restart')) {
setup.quickRestart();
} else {
setup.setup();
}
}
module.exports = RemoteControlSetup;