2025-07-27 02:34:32 +08:00
|
|
|
|
#!/usr/bin/env node
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2025-07-28 02:21:38 +08:00
|
|
|
|
* Claude-Code-Remote PTY Relay Startup Script
|
2025-07-27 17:17:15 +08:00
|
|
|
|
* Start node-pty based email command relay service
|
2025-07-27 02:34:32 +08:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
const { spawn } = require('child_process');
|
|
|
|
|
|
const path = require('path');
|
|
|
|
|
|
const fs = require('fs');
|
|
|
|
|
|
|
2025-07-27 17:17:15 +08:00
|
|
|
|
// Check environment configuration
|
2025-07-27 02:34:32 +08:00
|
|
|
|
function checkConfig() {
|
|
|
|
|
|
const envPath = path.join(__dirname, '.env');
|
|
|
|
|
|
|
|
|
|
|
|
if (!fs.existsSync(envPath)) {
|
2025-07-27 17:17:15 +08:00
|
|
|
|
console.error('❌ Error: .env configuration file not found');
|
|
|
|
|
|
console.log('\nPlease first copy .env.example to .env and configure your email information:');
|
2025-07-27 02:34:32 +08:00
|
|
|
|
console.log(' cp .env.example .env');
|
2025-07-27 17:17:15 +08:00
|
|
|
|
console.log(' Then edit .env file to fill in your email configuration\n');
|
2025-07-27 02:34:32 +08:00
|
|
|
|
process.exit(1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-27 17:17:15 +08:00
|
|
|
|
// Load environment variables
|
2025-07-27 02:34:32 +08:00
|
|
|
|
require('dotenv').config();
|
|
|
|
|
|
|
2025-07-27 17:17:15 +08:00
|
|
|
|
// Check required configuration
|
2025-07-27 02:34:32 +08:00
|
|
|
|
const required = ['IMAP_HOST', 'IMAP_USER', 'IMAP_PASS'];
|
|
|
|
|
|
const missing = required.filter(key => !process.env[key]);
|
|
|
|
|
|
|
|
|
|
|
|
if (missing.length > 0) {
|
2025-07-27 17:17:15 +08:00
|
|
|
|
console.error('❌ Error: Missing required environment variables:');
|
2025-07-27 02:34:32 +08:00
|
|
|
|
missing.forEach(key => console.log(` - ${key}`));
|
2025-07-27 17:17:15 +08:00
|
|
|
|
console.log('\nPlease edit .env file and fill in all required configurations\n');
|
2025-07-27 02:34:32 +08:00
|
|
|
|
process.exit(1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-27 17:17:15 +08:00
|
|
|
|
console.log('✅ Configuration check passed');
|
|
|
|
|
|
console.log(`📧 IMAP server: ${process.env.IMAP_HOST}`);
|
|
|
|
|
|
console.log(`👤 Email account: ${process.env.IMAP_USER}`);
|
|
|
|
|
|
console.log(`🔒 Whitelist senders: ${process.env.ALLOWED_SENDERS || '(Not set, will accept all emails)'}`);
|
|
|
|
|
|
console.log(`💾 Session storage path: ${process.env.SESSION_MAP_PATH || '(Using default path)'}`);
|
2025-07-27 02:34:32 +08:00
|
|
|
|
console.log('');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-27 17:17:15 +08:00
|
|
|
|
// Create example session
|
2025-07-27 02:34:32 +08:00
|
|
|
|
function createExampleSession() {
|
|
|
|
|
|
const sessionMapPath = process.env.SESSION_MAP_PATH || path.join(__dirname, 'src/data/session-map.json');
|
|
|
|
|
|
const sessionDir = path.dirname(sessionMapPath);
|
|
|
|
|
|
|
2025-07-27 17:17:15 +08:00
|
|
|
|
// Ensure directory exists
|
2025-07-27 02:34:32 +08:00
|
|
|
|
if (!fs.existsSync(sessionDir)) {
|
|
|
|
|
|
fs.mkdirSync(sessionDir, { recursive: true });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-27 17:17:15 +08:00
|
|
|
|
// If session file doesn't exist, create an example
|
2025-07-27 02:34:32 +08:00
|
|
|
|
if (!fs.existsSync(sessionMapPath)) {
|
|
|
|
|
|
const exampleToken = 'TEST123';
|
|
|
|
|
|
const exampleSession = {
|
|
|
|
|
|
[exampleToken]: {
|
|
|
|
|
|
type: 'pty',
|
|
|
|
|
|
createdAt: Math.floor(Date.now() / 1000),
|
2025-07-27 17:17:15 +08:00
|
|
|
|
expiresAt: Math.floor((Date.now() + 24 * 60 * 60 * 1000) / 1000), // Expires after 24 hours
|
2025-07-27 02:34:32 +08:00
|
|
|
|
cwd: process.cwd(),
|
2025-07-28 02:21:38 +08:00
|
|
|
|
description: 'Test session - Include [Claude-Code-Remote #TEST123] in email subject when sending'
|
2025-07-27 02:34:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
fs.writeFileSync(sessionMapPath, JSON.stringify(exampleSession, null, 2));
|
2025-07-27 17:17:15 +08:00
|
|
|
|
console.log(`📝 Created example session file: ${sessionMapPath}`);
|
|
|
|
|
|
console.log(`🔑 Test Token: ${exampleToken}`);
|
2025-07-28 02:21:38 +08:00
|
|
|
|
console.log(' When sending test email, include in subject: [Claude-Code-Remote #TEST123]');
|
2025-07-27 02:34:32 +08:00
|
|
|
|
console.log('');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-27 17:17:15 +08:00
|
|
|
|
// PID file path
|
2025-07-27 15:27:24 +08:00
|
|
|
|
const PID_FILE = path.join(__dirname, 'relay-pty.pid');
|
|
|
|
|
|
|
2025-07-27 17:17:15 +08:00
|
|
|
|
// Check if an instance is already running
|
2025-07-27 15:27:24 +08:00
|
|
|
|
function checkSingleInstance() {
|
|
|
|
|
|
if (fs.existsSync(PID_FILE)) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const oldPid = parseInt(fs.readFileSync(PID_FILE, 'utf8'));
|
2025-07-27 17:17:15 +08:00
|
|
|
|
// Check if process is actually running
|
2025-07-27 15:27:24 +08:00
|
|
|
|
process.kill(oldPid, 0);
|
2025-07-27 17:17:15 +08:00
|
|
|
|
// If no error thrown, process is still running
|
|
|
|
|
|
console.error('❌ Error: relay-pty service is already running (PID: ' + oldPid + ')');
|
|
|
|
|
|
console.log('\nIf you\'re sure the service is not running, you can delete the PID file:');
|
2025-07-27 15:27:24 +08:00
|
|
|
|
console.log(' rm ' + PID_FILE);
|
2025-07-27 17:17:15 +08:00
|
|
|
|
console.log('\nOr stop existing service:');
|
2025-07-27 15:27:24 +08:00
|
|
|
|
console.log(' kill ' + oldPid);
|
|
|
|
|
|
process.exit(1);
|
|
|
|
|
|
} catch (err) {
|
2025-07-27 17:17:15 +08:00
|
|
|
|
// Process doesn't exist, delete old PID file
|
2025-07-27 15:27:24 +08:00
|
|
|
|
fs.unlinkSync(PID_FILE);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-27 17:17:15 +08:00
|
|
|
|
// Write current process PID
|
2025-07-27 15:27:24 +08:00
|
|
|
|
fs.writeFileSync(PID_FILE, process.pid.toString());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-27 17:17:15 +08:00
|
|
|
|
// Clean up PID file
|
2025-07-27 15:27:24 +08:00
|
|
|
|
function cleanupPidFile() {
|
|
|
|
|
|
if (fs.existsSync(PID_FILE)) {
|
|
|
|
|
|
fs.unlinkSync(PID_FILE);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-27 17:17:15 +08:00
|
|
|
|
// Start service
|
2025-07-27 02:34:32 +08:00
|
|
|
|
function startService() {
|
2025-07-27 17:17:15 +08:00
|
|
|
|
// Check single instance
|
2025-07-27 15:27:24 +08:00
|
|
|
|
checkSingleInstance();
|
|
|
|
|
|
|
2025-07-28 02:21:38 +08:00
|
|
|
|
console.log('🚀 Starting Claude-Code-Remote PTY Relay service...\n');
|
2025-07-27 02:34:32 +08:00
|
|
|
|
|
|
|
|
|
|
const relayPath = path.join(__dirname, 'src/relay/relay-pty.js');
|
|
|
|
|
|
|
2025-07-27 17:17:15 +08:00
|
|
|
|
// Use node to run directly, so we can see complete log output
|
2025-07-27 02:34:32 +08:00
|
|
|
|
const relay = spawn('node', [relayPath], {
|
|
|
|
|
|
stdio: 'inherit',
|
|
|
|
|
|
env: {
|
|
|
|
|
|
...process.env,
|
|
|
|
|
|
INJECTION_MODE: 'pty'
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-07-27 17:17:15 +08:00
|
|
|
|
// Handle exit
|
2025-07-27 02:34:32 +08:00
|
|
|
|
process.on('SIGINT', () => {
|
2025-07-27 17:17:15 +08:00
|
|
|
|
console.log('\n⏹️ Stopping service...');
|
2025-07-27 02:34:32 +08:00
|
|
|
|
relay.kill('SIGINT');
|
2025-07-27 15:27:24 +08:00
|
|
|
|
cleanupPidFile();
|
2025-07-27 02:34:32 +08:00
|
|
|
|
process.exit(0);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-07-27 15:27:24 +08:00
|
|
|
|
process.on('exit', cleanupPidFile);
|
|
|
|
|
|
process.on('SIGTERM', cleanupPidFile);
|
|
|
|
|
|
|
2025-07-27 02:34:32 +08:00
|
|
|
|
relay.on('error', (error) => {
|
2025-07-27 17:17:15 +08:00
|
|
|
|
console.error('❌ Startup failed:', error.message);
|
2025-07-27 15:27:24 +08:00
|
|
|
|
cleanupPidFile();
|
2025-07-27 02:34:32 +08:00
|
|
|
|
process.exit(1);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
relay.on('exit', (code, signal) => {
|
2025-07-27 15:27:24 +08:00
|
|
|
|
cleanupPidFile();
|
2025-07-27 02:34:32 +08:00
|
|
|
|
if (signal) {
|
2025-07-27 17:17:15 +08:00
|
|
|
|
console.log(`\nService stopped (signal: ${signal})`);
|
2025-07-27 02:34:32 +08:00
|
|
|
|
} else if (code !== 0) {
|
2025-07-27 17:17:15 +08:00
|
|
|
|
console.error(`\nService exited abnormally (code: ${code})`);
|
2025-07-27 02:34:32 +08:00
|
|
|
|
process.exit(code);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-27 17:17:15 +08:00
|
|
|
|
// Show usage instructions
|
2025-07-27 02:34:32 +08:00
|
|
|
|
function showInstructions() {
|
2025-07-27 17:17:15 +08:00
|
|
|
|
console.log('📖 Usage instructions:');
|
|
|
|
|
|
console.log('1. When executing tasks in Claude Code, reminder emails containing Token will be sent');
|
|
|
|
|
|
console.log('2. Reply to that email with the commands to execute');
|
|
|
|
|
|
console.log('3. Supported command formats:');
|
|
|
|
|
|
console.log(' - Enter command text directly');
|
|
|
|
|
|
console.log(' - Use CMD: prefix, like "CMD: continue"');
|
|
|
|
|
|
console.log(' - Use code block wrapping, like:');
|
2025-07-27 02:34:32 +08:00
|
|
|
|
console.log(' ```');
|
2025-07-27 17:17:15 +08:00
|
|
|
|
console.log(' your command');
|
2025-07-27 02:34:32 +08:00
|
|
|
|
console.log(' ```');
|
2025-07-27 17:17:15 +08:00
|
|
|
|
console.log('4. System will automatically extract commands and inject them into corresponding Claude Code session');
|
|
|
|
|
|
console.log('\n⌨️ Press Ctrl+C to stop service\n');
|
2025-07-27 02:34:32 +08:00
|
|
|
|
console.log('━'.repeat(60) + '\n');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-27 17:17:15 +08:00
|
|
|
|
// Main function
|
2025-07-27 02:34:32 +08:00
|
|
|
|
function main() {
|
|
|
|
|
|
console.log('╔══════════════════════════════════════════════════════════╗');
|
2025-07-28 02:21:38 +08:00
|
|
|
|
console.log('║ Claude-Code-Remote PTY Relay Service ║');
|
2025-07-27 17:17:15 +08:00
|
|
|
|
console.log('║ Email Command Relay Service - node-pty based PTY mode ║');
|
2025-07-27 02:34:32 +08:00
|
|
|
|
console.log('╚══════════════════════════════════════════════════════════╝\n');
|
|
|
|
|
|
|
2025-07-27 17:17:15 +08:00
|
|
|
|
// Check configuration
|
2025-07-27 02:34:32 +08:00
|
|
|
|
checkConfig();
|
|
|
|
|
|
|
2025-07-27 17:17:15 +08:00
|
|
|
|
// Create example session
|
2025-07-27 02:34:32 +08:00
|
|
|
|
createExampleSession();
|
|
|
|
|
|
|
2025-07-27 17:17:15 +08:00
|
|
|
|
// Show usage instructions
|
2025-07-27 02:34:32 +08:00
|
|
|
|
showInstructions();
|
|
|
|
|
|
|
2025-07-27 17:17:15 +08:00
|
|
|
|
// Start service
|
2025-07-27 02:34:32 +08:00
|
|
|
|
startService();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-27 17:17:15 +08:00
|
|
|
|
// Run
|
2025-07-27 02:34:32 +08:00
|
|
|
|
main();
|