UNPKG

tinyagent

Version:

Connect your local shell to any device - access your dev environment from anywhere

211 lines • 8.93 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.FirebaseAuthClient = void 0; const chalk_1 = __importDefault(require("chalk")); const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const os_1 = __importDefault(require("os")); const open_1 = __importDefault(require("open")); class FirebaseAuthClient { configPath; token; firebaseConfig; constructor() { this.configPath = path_1.default.join(os_1.default.homedir(), '.tinyagent', 'auth.json'); this.loadToken(); // These will be filled from environment variables this.firebaseConfig = { apiKey: process.env.FIREBASE_API_KEY, authDomain: process.env.FIREBASE_AUTH_DOMAIN, projectId: process.env.FIREBASE_PROJECT_ID, }; } loadToken() { try { if (fs_1.default.existsSync(this.configPath)) { const data = fs_1.default.readFileSync(this.configPath, 'utf-8'); this.token = JSON.parse(data); } } catch (error) { // Ignore errors, token will be undefined } } saveToken(token) { try { const dir = path_1.default.dirname(this.configPath); if (!fs_1.default.existsSync(dir)) { fs_1.default.mkdirSync(dir, { recursive: true }); } fs_1.default.writeFileSync(this.configPath, JSON.stringify(token, null, 2)); this.token = token; } catch (error) { console.error(chalk_1.default.yellow('Warning: Could not save auth token')); } } isAuthenticated() { if (!this.token) return false; if (this.token.expiresAt && this.token.expiresAt < Date.now()) { return false; } return true; } getToken() { return this.isAuthenticated() ? this.token?.idToken : undefined; } getUser() { return this.isAuthenticated() ? this.token?.user : undefined; } async authenticate() { console.log(chalk_1.default.cyan('\nšŸ” Authenticating with Firebase')); if (!this.firebaseConfig.apiKey) { console.error(chalk_1.default.red('Firebase configuration missing. Please set environment variables.')); return false; } try { // Generate random state for security const state = crypto.randomBytes(32).toString('base64url'); // Create local server to receive the callback const server = http.createServer(); const port = 9005; // Fixed port for Firebase redirect return new Promise((resolve) => { server.on('request', async (req, res) => { const url = new URL(req.url, `http://localhost:${port}`); if (url.pathname === '/auth/callback') { const token = url.searchParams.get('token'); const refreshToken = url.searchParams.get('refreshToken'); const user = url.searchParams.get('user'); if (token && user) { try { const userData = JSON.parse(decodeURIComponent(user)); // Save token this.saveToken({ idToken: token, refreshToken: refreshToken || '', user: userData, expiresAt: Date.now() + (60 * 60 * 1000), // 1 hour }); // Send success response res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(` <html> <head> <title>Authentication Successful</title> <style> body { font-family: -apple-system, system-ui, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #000; color: #0f0; } .success { text-align: center; } h1 { font-size: 48px; margin: 0; } p { color: #888; margin-top: 10px; } </style> </head> <body> <div class="success"> <h1>āœ“</h1> <p>Authentication successful! You can close this window.</p> </div> <script>setTimeout(() => window.close(), 2000)</script> </body> </html> `); console.log(chalk_1.default.green('\nāœ“ Authentication successful!')); if (userData.email) { console.log(chalk_1.default.gray(`Logged in as: ${userData.email}`)); } server.close(); resolve(true); } catch (error) { console.error(chalk_1.default.red('Failed to process authentication')); server.close(); resolve(false); } } else { res.writeHead(400); res.end('Missing authentication data'); server.close(); resolve(false); } } else { res.writeHead(404); res.end('Not found'); } }); server.listen(port, async () => { const authUrl = `https://${this.firebaseConfig.authDomain}/auth/cli?` + `redirect_uri=http://localhost:${port}/auth/callback&` + `state=${state}`; console.log(chalk_1.default.white('\nTo authenticate, visit:')); console.log(chalk_1.default.green.bold(authUrl)); // Open browser try { await (0, open_1.default)(authUrl); console.log(chalk_1.default.gray('\n(Browser should open automatically)')); } catch { console.log(chalk_1.default.gray('\n(Please open the URL manually in your browser)')); } console.log(chalk_1.default.gray('\nWaiting for authentication...')); }); // Timeout after 5 minutes setTimeout(() => { if (server.listening) { console.log(chalk_1.default.red('\nāœ— Authentication timed out')); server.close(); resolve(false); } }, 5 * 60 * 1000); }); } catch (error) { console.error(chalk_1.default.red('\nāœ— Authentication failed:'), error); return false; } } logout() { try { if (fs_1.default.existsSync(this.configPath)) { fs_1.default.unlinkSync(this.configPath); } this.token = undefined; console.log(chalk_1.default.green('āœ“ Logged out successfully')); } catch (error) { console.error(chalk_1.default.red('āœ— Logout failed:'), error); } } whoami() { if (!this.isAuthenticated()) { console.log(chalk_1.default.yellow('Not authenticated. Run "tinyagent login" to authenticate.')); return; } const user = this.getUser(); if (user) { console.log(chalk_1.default.cyan('Current user:')); console.log(` UID: ${user.uid}`); if (user.email) { console.log(` Email: ${user.email}`); } if (user.displayName) { console.log(` Name: ${user.displayName}`); } } } } exports.FirebaseAuthClient = FirebaseAuthClient; //# sourceMappingURL=firebase-auth.js.map