UNPKG

@snapcommit/cli

Version:

Instant AI commits. Beautiful progress tracking. Never write commit messages again.

239 lines (238 loc) 11.2 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.isAuthenticated = isAuthenticated; exports.getAuthConfig = getAuthConfig; exports.clearAuth = clearAuth; exports.promptAuth = promptAuth; exports.ensureAuth = ensureAuth; exports.getToken = getToken; exports.logout = logout; const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const os_1 = __importDefault(require("os")); const chalk_1 = __importDefault(require("chalk")); const readline_1 = __importDefault(require("readline")); const CONFIG_DIR = path_1.default.join(os_1.default.homedir(), '.snapcommit'); const AUTH_FILE = path_1.default.join(CONFIG_DIR, 'auth.json'); // API URL - defaults to production, can be overridden for development const API_BASE_URL = process.env.SNAPCOMMIT_API_URL || 'https://www.snapcommit.dev'; // Token validation cache let lastValidationTime = 0; const VALIDATION_INTERVAL = 60 * 60 * 1000; // 1 hour in milliseconds /** * Ensure config directory exists */ function ensureConfigDir() { if (!fs_1.default.existsSync(CONFIG_DIR)) { fs_1.default.mkdirSync(CONFIG_DIR, { recursive: true }); } } /** * Check if user is authenticated */ function isAuthenticated() { return fs_1.default.existsSync(AUTH_FILE); } /** * Get stored authentication config */ function getAuthConfig() { if (!fs_1.default.existsSync(AUTH_FILE)) { return null; } try { const data = fs_1.default.readFileSync(AUTH_FILE, 'utf-8'); return JSON.parse(data); } catch (error) { return null; } } /** * Save authentication config */ function saveAuthConfig(config) { ensureConfigDir(); fs_1.default.writeFileSync(AUTH_FILE, JSON.stringify(config, null, 2)); } /** * Clear authentication */ function clearAuth() { if (fs_1.default.existsSync(AUTH_FILE)) { fs_1.default.unlinkSync(AUTH_FILE); } } /** * Ask a question and return the answer */ function askQuestion(query) { const rl = readline_1.default.createInterface({ input: process.stdin, output: process.stdout, }); return new Promise((resolve) => rl.question(query, (ans) => { rl.close(); resolve(ans); })); } /** * Verify token with backend */ async function verifyToken(token) { try { const response = await fetch(`${API_BASE_URL}/api/auth/token?token=${encodeURIComponent(token)}`); const data = await response.json(); if (response.ok && data.valid) { return { valid: true, user: data.user }; } return { valid: false }; } catch (error) { throw new Error('Failed to verify token. Please check your internet connection.'); } } /** * Prompt user to authenticate */ async function promptAuth() { console.log(chalk_1.default.bold.cyan('\n╔════════════════════════════════════════╗')); console.log(chalk_1.default.bold.cyan('║ 🔐 Authentication Required 🔐 ║')); console.log(chalk_1.default.bold.cyan('╚════════════════════════════════════════╝\n')); console.log(chalk_1.default.gray('SnapCommit needs authentication to provide AI-powered features.\n')); console.log(chalk_1.default.yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n')); console.log(chalk_1.default.bold.white('Step 1: ') + chalk_1.default.gray('Sign in with GitHub (one-click!)')); console.log(chalk_1.default.cyan(' 👉 https://www.snapcommit.dev/login\n')); console.log(chalk_1.default.bold.white('Step 2: ') + chalk_1.default.gray('Subscribe ($9.99/mo or $100/year)')); console.log(chalk_1.default.cyan(' 👉 Choose your plan on the dashboard\n')); console.log(chalk_1.default.bold.white('Step 3: ') + chalk_1.default.gray('Generate & paste your CLI token')); console.log(chalk_1.default.cyan(' 👉 https://www.snapcommit.dev/dashboard\n')); console.log(chalk_1.default.yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n')); const token = await askQuestion(chalk_1.default.yellow('🔑 Paste your authentication token: ')); if (!token || token.trim().length === 0) { console.log(chalk_1.default.red('\n❌ No token provided. Authentication cancelled.\n')); console.log(chalk_1.default.gray('Run "snap" again when you\'re ready to authenticate.\n')); return false; } console.log(chalk_1.default.gray('\n🔄 Verifying token with SnapCommit servers...\n')); try { const result = await verifyToken(token.trim()); if (result.valid && result.user) { const config = { token: token.trim(), userId: result.user.id, email: result.user.email, name: result.user.name || 'Developer', }; saveAuthConfig(config); console.log(chalk_1.default.green.bold('✅ AUTHENTICATION SUCCESSFUL! 🎉\n')); console.log(chalk_1.default.white(` Welcome back, ${chalk_1.default.bold(config.name)}!`)); console.log(chalk_1.default.gray(` Email: ${config.email}`)); console.log(chalk_1.default.gray(` Plan: SnapCommit Pro (Active)\n`)); console.log(chalk_1.default.yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n')); console.log(chalk_1.default.bold('🚀 Quick Start:\n')); console.log(chalk_1.default.gray(' • ') + chalk_1.default.cyan('snap quick') + chalk_1.default.gray(' - AI commit in any repo')); console.log(chalk_1.default.gray(' • ') + chalk_1.default.cyan('snap') + chalk_1.default.gray(' - Natural language Git commands')); console.log(chalk_1.default.gray(' • Type ') + chalk_1.default.cyan('exit') + chalk_1.default.gray(' to leave SnapCommit\n')); console.log(chalk_1.default.yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n')); console.log(chalk_1.default.bold('💡 Want GitHub features too?\n')); console.log(chalk_1.default.gray(' Create PRs, check CI, and more with natural language!')); console.log(chalk_1.default.gray(' Run: ') + chalk_1.default.cyan('github connect') + chalk_1.default.gray(' (inside snap)')); console.log(chalk_1.default.gray(' (You\'ll need a GitHub Personal Access Token - Classic)\n')); console.log(chalk_1.default.gray(' Get one at: ') + chalk_1.default.cyan('https://github.com/settings/tokens')); console.log(chalk_1.default.gray(' Scopes needed: ') + chalk_1.default.white('repo, workflow, read:user')); console.log(chalk_1.default.gray(' 💡 Tip: Set no expiration or remember to renew!\n')); return true; } else { console.log(chalk_1.default.red('\n❌ Invalid token. Please try again.\n')); console.log(chalk_1.default.yellow('Troubleshooting:')); console.log(chalk_1.default.gray(' • Make sure you copied the ENTIRE token')); console.log(chalk_1.default.gray(' • Generate a new token at: https://www.snapcommit.dev/dashboard')); console.log(chalk_1.default.gray(' • Ensure you have an active SnapCommit subscription\n')); return false; } } catch (error) { console.log(chalk_1.default.red(`\n❌ ${error.message}\n`)); console.log(chalk_1.default.gray('Please check your internet connection and try again.\n')); return false; } } /** * Ensure user is authenticated (prompt if not) */ async function ensureAuth() { // Check if we have a locally stored token if (isAuthenticated()) { const config = getAuthConfig(); if (config) { const now = Date.now(); const timeSinceLastValidation = now - lastValidationTime; // Only validate if more than 1 hour has passed since last validation if (timeSinceLastValidation > VALIDATION_INTERVAL) { // CRITICAL: Verify token with server every hour try { const result = await verifyToken(config.token); if (result.valid) { // Token is still valid! lastValidationTime = now; // Update last validation time return config; } else { // Token is invalid (user deleted, subscription expired, etc.) console.log(chalk_1.default.yellow('\n⚠️ Your authentication token is no longer valid.')); console.log(chalk_1.default.gray('This could mean:')); console.log(chalk_1.default.gray(' • Your subscription has expired')); console.log(chalk_1.default.gray(' • Your account was deleted')); console.log(chalk_1.default.gray(' • The token was revoked\n')); clearAuth(); lastValidationTime = 0; // Reset validation timer // Fall through to re-prompt } } catch (error) { // Network error - allow offline usage with cached token console.log(chalk_1.default.yellow('\n⚠️ Could not verify token (offline mode)')); console.log(chalk_1.default.gray('Using cached credentials. Some features may be limited.\n')); return config; } } else { // Token was validated recently (within the last hour) - skip validation return config; } } } // No valid token - prompt for authentication const success = await promptAuth(); if (success) { lastValidationTime = Date.now(); // Set validation time after successful auth return getAuthConfig(); } return null; } /** * Get authentication token for API calls */ function getToken() { const config = getAuthConfig(); return config?.token || null; } /** * Logout command */ async function logout() { if (!isAuthenticated()) { console.log(chalk_1.default.gray('\n Not currently logged in.\n')); return; } const config = getAuthConfig(); console.log(chalk_1.default.yellow(`\n Logging out ${config?.email}...\n`)); clearAuth(); console.log(chalk_1.default.green(' ✅ Logged out successfully!\n')); console.log(chalk_1.default.gray(' Run "snapcommit" again to log back in.\n')); }