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

287 lines
11 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.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
/**
* TaskPing 无人值守远程控制设置助手
*/
const { exec, spawn } = require('child_process');
const fs = require('fs');
const path = require('path');
class RemoteControlSetup {
constructor(sessionName = null) {
this.sessionName = sessionName || 'claude-taskping';
this.taskpingHome = this.findTaskPingHome();
}
findTaskPingHome() {
// If TASKPING_HOME environment variable is set, use it
if (process.env.TASKPING_HOME) {
return process.env.TASKPING_HOME;
}
// If running from the TaskPing 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.includes('taskping')) {
return __dirname;
}
} catch (e) {
// Continue searching
}
}
// Search for TaskPing in common locations
const commonPaths = [
path.join(process.env.HOME, 'dev', 'TaskPing'),
path.join(process.env.HOME, 'Projects', 'TaskPing'),
path.join(process.env.HOME, 'taskping'),
__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('taskping')) {
return searchPath;
}
} catch (e) {
// Continue searching
}
}
}
// If not found, use current directory as fallback
return __dirname;
}
async setup() {
console.log('🚀 TaskPing 无人值守远程控制设置\n');
console.log('🎯 目标: 人在外面用手机→家中电脑Claude Code自动执行命令\n');
try {
// 1. 检查tmux
await this.checkAndInstallTmux();
// 2. 检查Claude CLI
await this.checkClaudeCLI();
// 3. 设置Claude tmux会话
await this.setupClaudeSession();
// 4. 会话创建完成
console.log('\n4⃣ 会话创建完成');
// 5. 提供使用指南
this.showUsageGuide();
} catch (error) {
console.error('❌ 设置过程中发生错误:', error.message);
}
}
async checkAndInstallTmux() {
console.log('1⃣ 检查tmux安装状态...');
return new Promise((resolve) => {
exec('which tmux', (error, stdout) => {
if (error) {
console.log('❌ tmux未安装');
console.log('📦 正在安装tmux...');
exec('brew install tmux', (installError, installStdout, installStderr) => {
if (installError) {
console.log('❌ tmux安装失败请手动安装:');
console.log(' brew install tmux');
console.log(' 或从 https://github.com/tmux/tmux 下载');
} else {
console.log('✅ tmux安装成功');
}
resolve();
});
} else {
console.log(`✅ tmux已安装: ${stdout.trim()}`);
resolve();
}
});
});
}
async checkClaudeCLI() {
console.log('\n2⃣ 检查Claude CLI状态...');
return new Promise((resolve) => {
exec('which claude', (error, stdout) => {
if (error) {
console.log('❌ Claude CLI未找到');
console.log('📦 请安装Claude CLI:');
console.log(' npm install -g @anthropic-ai/claude-code');
} else {
console.log(`✅ Claude CLI已安装: ${stdout.trim()}`);
// 检查版本
exec('claude --version', (versionError, versionStdout) => {
if (!versionError) {
console.log(`📋 版本: ${versionStdout.trim()}`);
}
});
}
resolve();
});
});
}
async setupClaudeSession() {
console.log('\n3⃣ 设置Claude tmux会话...');
return new Promise((resolve) => {
// 检查是否已有会话
exec(`tmux has-session -t ${this.sessionName} 2>/dev/null`, (checkError) => {
if (!checkError) {
console.log('⚠️ Claude tmux会话已存在');
console.log('🔄 是否重新创建会话? (会杀死现有会话)');
// 简单起见,直接重建
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) {
// 使用TaskPing主目录作为工作目录
const workingDir = this.taskpingHome;
const command = `tmux new-session -d -s ${this.sessionName} -c "${workingDir}" clauderun`;
console.log(`🚀 创建Claude tmux会话: ${this.sessionName}`);
console.log(`📁 工作目录: ${workingDir}`);
console.log(`💡 使用便捷命令: clauderun (等同于 claude --dangerously-skip-permissions)`);
exec(command, (error, stdout, stderr) => {
if (error) {
console.log(`❌ 会话创建失败: ${error.message}`);
if (stderr) {
console.log(`错误详情: ${stderr}`);
}
// 如果clauderun失败尝试使用完整路径命令
console.log('🔄 尝试使用完整路径命令...');
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(`❌ 完整路径命令也失败: ${fallbackError.message}`);
} else {
console.log('✅ Claude tmux会话创建成功 (使用完整路径)');
console.log(`📺 查看会话: tmux attach -t ${this.sessionName}`);
console.log(`🔚 退出会话: Ctrl+B, D (不会关闭Claude)`);
}
resolve();
});
} else {
console.log('✅ Claude tmux会话创建成功');
console.log(`📺 查看会话: tmux attach -t ${this.sessionName}`);
console.log(`🔚 退出会话: Ctrl+B, D (不会关闭Claude)`);
resolve();
}
});
}
async testRemoteInjection() {
console.log('\n💡 会话已就绪,可以开始使用');
console.log('📋 Claude Code正在等待您的指令');
console.log('🔧 如需测试注入功能,请使用单独的测试脚本');
return Promise.resolve();
}
showUsageGuide() {
console.log('\n🎉 设置完成!无人值守远程控制已就绪\n');
console.log('🎯 新功能: clauderun 便捷命令');
console.log(' 现在可以使用 clauderun 代替 claude --dangerously-skip-permissions');
console.log(' 更清晰的Claude Code启动方式\n');
console.log('📋 使用流程:');
console.log('1. 🏠 在家启动邮件监听: npm run relay:pty');
console.log('2. 🚪 出门时Claude继续在tmux中运行');
console.log('3. 📱 手机收到TaskPing邮件通知');
console.log('4. 💬 手机回复邮件输入命令');
console.log('5. 🤖 家中Claude自动接收并执行命令');
console.log('6. 🔄 循环上述过程,完全无人值守\n');
console.log('🔧 管理命令:');
console.log(` 查看Claude会话: tmux attach -t ${this.sessionName}`);
console.log(` 退出会话(不关闭): Ctrl+B, D`);
console.log(` 杀死会话: tmux kill-session -t ${this.sessionName}`);
console.log(` 查看所有会话: tmux list-sessions\n`);
console.log('🎛️ 多会话支持:');
console.log(' 创建自定义会话: node claude-control.js --session my-project');
console.log(' 创建多个会话: node claude-control.js --session frontend');
console.log(' node claude-control.js --session backend');
console.log(' 邮件回复会自动路由到对应的会话\n');
console.log('📱 邮件测试:');
console.log(' Token将包含会话信息自动路由到正确的tmux会话');
console.log(' 收件邮箱: jiaxicui446@gmail.com');
console.log(' 回复邮件输入: echo "远程控制测试"\n');
console.log('🚨 重要提醒:');
console.log('- Claude会话在tmux中持续运行断网重连也不会中断');
console.log('- 邮件监听服务需要保持运行状态');
console.log('- 家中电脑需要保持开机和网络连接');
console.log('- 手机可以从任何地方发送邮件命令');
console.log('- 支持同时运行多个不同项目的Claude会话\n');
console.log('✅ 现在你可以实现真正的无人值守远程控制了!🎯');
}
// 快速重建会话的方法
async quickRestart() {
console.log('🔄 快速重启Claude会话...');
return new Promise((resolve) => {
this.killAndCreateSession(() => {
console.log('✅ Claude会话已重启');
resolve();
});
});
}
}
// 命令行参数处理
if (require.main === module) {
const args = process.argv.slice(2);
// 解析会话名称参数
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(`🎛️ 使用自定义会话名称: ${sessionName}`);
}
if (args.includes('--restart')) {
setup.quickRestart();
} else {
setup.setup();
}
}
module.exports = RemoteControlSetup;