UNPKG

tinyagent

Version:

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

165 lines • 6.99 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AuthClient = void 0; const chalk_1 = __importDefault(require("chalk")); const crypto_1 = __importDefault(require("crypto")); 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 AuthClient { authUrl; configPath; token; constructor(authUrl = 'https://auth.tinyagent.app') { this.authUrl = process.env.AUTH_URL || authUrl; this.configPath = path_1.default.join(os_1.default.homedir(), '.tinyagent', 'auth.json'); this.loadToken(); } 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?.accessToken : undefined; } async authenticate() { console.log(chalk_1.default.cyan('\nšŸ” Authentication required')); // Generate PKCE challenge const codeVerifier = crypto_1.default.randomBytes(32).toString('base64url'); const codeChallenge = crypto_1.default .createHash('sha256') .update(codeVerifier) .digest('base64url'); try { // Request device code const deviceResponse = await fetch(`${this.authUrl}/api/auth/device`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ codeChallenge }), }); if (!deviceResponse.ok) { throw new Error('Failed to get device code'); } const deviceData = await deviceResponse.json(); console.log(chalk_1.default.white('\nTo authenticate, visit:')); console.log(chalk_1.default.green.bold(deviceData.verificationUrl)); console.log(chalk_1.default.white('\nVerification code: ') + chalk_1.default.yellow.bold(deviceData.userCode)); // Open browser try { await (0, open_1.default)(deviceData.verificationUrl); console.log(chalk_1.default.gray('(Browser should open automatically)')); } catch { console.log(chalk_1.default.gray('(Please open the URL manually in your browser)')); } console.log(chalk_1.default.gray('\nWaiting for authentication...')); // Poll for authentication const startTime = Date.now(); const expiresIn = deviceData.expiresIn * 1000; // Convert to ms const interval = deviceData.interval * 1000; // Convert to ms while (Date.now() - startTime < expiresIn) { await new Promise(resolve => setTimeout(resolve, interval)); const pollResponse = await fetch(`${this.authUrl}/api/auth/cli?device_code=${deviceData.deviceCode}`); if (!pollResponse.ok) continue; const pollData = await pollResponse.json(); if (pollData.status === 'authenticated') { // Exchange for tokens const tokenResponse = await fetch(`${this.authUrl}/api/auth/token`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ grantType: 'authorization_code', code: pollData.accessToken, // Using token as code for simplicity codeVerifier, }), }); if (!tokenResponse.ok) { throw new Error('Failed to exchange code for tokens'); } const tokenData = await tokenResponse.json(); // Save token this.saveToken({ accessToken: tokenData.accessToken, refreshToken: tokenData.refreshToken, user: tokenData.user, expiresAt: Date.now() + (tokenData.expiresIn * 1000), }); console.log(chalk_1.default.green('\nāœ“ Authentication successful!')); if (tokenData.user?.email) { console.log(chalk_1.default.gray(`Logged in as: ${tokenData.user.email}`)); } return true; } } console.log(chalk_1.default.red('\nāœ— Authentication timed out')); return false; } 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; } if (this.token?.user) { console.log(chalk_1.default.cyan('Current user:')); console.log(` ID: ${this.token.user.id}`); if (this.token.user.email) { console.log(` Email: ${this.token.user.email}`); } if (this.token.user.name) { console.log(` Name: ${this.token.user.name}`); } } } } exports.AuthClient = AuthClient; //# sourceMappingURL=auth-client.js.map