UNPKG

shell-mirror

Version:

Access your Mac shell from any device securely. Perfect for mobile coding with Claude Code CLI, Gemini CLI, and any shell tool.

183 lines (150 loc) • 6.55 kB
#!/usr/bin/env node /** * Interactive Setup for Terminal Mirror Mac Agent * Handles Google OAuth token retrieval and agent configuration */ const { execSync } = require('child_process'); const fs = require('fs'); const path = require('path'); const readline = require('readline'); const http = require('http'); const url = require('url'); const { spawn } = require('child_process'); const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); function question(prompt) { return new Promise(resolve => { rl.question(prompt, resolve); }); } async function setup() { console.log('šŸš€ Terminal Mirror Mac Agent Interactive Setup'); console.log('===============================================\n'); try { // Install dependencies console.log('šŸ“¦ Installing dependencies...'); execSync('npm install', { stdio: 'inherit' }); console.log('āœ… Dependencies installed\n'); // Get user email const userEmail = await question('šŸ“§ Enter your email address (Google account): '); if (!userEmail || !userEmail.includes('@')) { console.log('āŒ Invalid email address'); process.exit(1); } console.log('\nšŸ” Google OAuth Setup'); console.log('======================'); console.log('To connect your Mac to Terminal Mirror, you need to authenticate with Google.'); console.log('This will open a browser window for you to sign in.\n'); const proceed = await question('Continue with Google authentication? (y/n): '); if (proceed.toLowerCase() !== 'y' && proceed.toLowerCase() !== 'yes') { console.log('Setup cancelled.'); process.exit(0); } // Start OAuth flow console.log('\n🌐 Starting Google OAuth flow...'); const token = await getGoogleOAuthToken(); if (!token) { console.log('āŒ Failed to get Google authentication token'); process.exit(1); } console.log('āœ… Google authentication successful\n'); // Generate agent configuration const os = require('os'); const agentId = generateAgentId(); // Update .env file updateEnvFile({ WEB_SERVER_HTTP: 'https://shellmirror.app', AGENT_SECRET: 'mac-agent-secret-2024', AGENT_ID: agentId, OWNER_EMAIL: userEmail, OWNER_TOKEN: token, POLL_INTERVAL: '2000', COMMAND_TIMEOUT: '30000', MAX_CONCURRENT_COMMANDS: '3', LOG_LEVEL: 'info' }); // Create logs directory const logsDir = path.join(__dirname, 'logs'); if (!fs.existsSync(logsDir)) { fs.mkdirSync(logsDir, { recursive: true }); console.log('šŸ“ Created logs directory'); } console.log('šŸŽ‰ Setup completed successfully!\n'); console.log('šŸ“‹ Configuration:'); console.log(` Agent ID: ${agentId}`); console.log(` Owner Email: ${userEmail}`); console.log(` Machine: ${os.hostname()}`); console.log(` User: ${os.userInfo().username}\n`); console.log('šŸ“‹ Next steps:'); console.log('1. Deploy the updated backend: npm run deploy (in the main project directory)'); console.log('2. Start the Mac agent: npm start'); console.log('3. Visit https://shellmirror.app and test terminal commands\n'); const startNow = await question('šŸš€ Start the Mac agent now? (y/n): '); if (startNow.toLowerCase() === 'y' || startNow.toLowerCase() === 'yes') { console.log('\nšŸ”„ Starting Mac agent...'); execSync('npm start', { stdio: 'inherit' }); } } catch (error) { console.error('āŒ Setup failed:', error.message); process.exit(1); } finally { rl.close(); } } function generateAgentId() { const os = require('os'); const hostname = os.hostname(); const username = os.userInfo().username; // No timestamp - consistent ID per machine to avoid duplicate agent registrations return `mac-${username}-${hostname}`.replace(/[^a-zA-Z0-9-]/g, '-'); } function updateEnvFile(config) { const envPath = path.join(__dirname, '.env'); const envContent = Object.entries(config) .map(([key, value]) => `${key}=${value}`) .join('\n'); fs.writeFileSync(envPath, envContent + '\n'); console.log('āœ… Configuration saved to .env file'); } async function getGoogleOAuthToken() { const CLIENT_ID = '804759223392-i5nv5csn1o6siqr760c99l2a9k4sammp.apps.googleusercontent.com'; const REDIRECT_URI = 'https://shellmirror.app/php-backend/api/mac-agent-oauth.php'; const SCOPE = 'openid email profile'; const authUrl = `https://accounts.google.com/o/oauth2/auth?` + `client_id=${CLIENT_ID}&` + `redirect_uri=${encodeURIComponent(REDIRECT_URI)}&` + `scope=${encodeURIComponent(SCOPE)}&` + `response_type=code&` + `access_type=offline&` + `prompt=consent`; console.log('🌐 Opening browser for Google authentication...'); console.log('\nšŸ“‹ Instructions:'); console.log('1. A browser window will open for Google authentication'); console.log('2. Sign in with your Google account'); console.log('3. After authentication, you\'ll see a page with an access token'); console.log('4. Copy the access token and return here'); console.log('\nIf the browser doesn\'t open automatically, please visit:'); console.log(authUrl); console.log(''); // Try to open browser try { const open = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open'; spawn(open, [authUrl], { detached: true, stdio: 'ignore' }); } catch (error) { console.log('Please manually open the URL above in your browser.'); } // Wait a moment for browser to open await new Promise(resolve => setTimeout(resolve, 3000)); console.log('šŸ”‘ Please paste the access token from the browser page:'); const token = await question('Access Token: '); if (!token || token.trim().length < 20) { console.log('āŒ Invalid or empty token'); return null; } return token.trim(); } // Run setup setup();