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
JavaScript
/**
* 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();