UNPKG

cliseo

Version:

Instant AI-Powered SEO Optimization CLI for Developers

167 lines • 6.44 kB
import axios from 'axios'; import { loadConfig, saveConfig } from './config.js'; import chalk from 'chalk'; const DEFAULT_API_URL = process.env.API_URL || 'https://your-api-gateway-url.amazonaws.com'; /** * Get the authenticated axios instance with JWT token */ async function getAuthenticatedAxios() { const config = await loadConfig(); const apiUrl = config.apiUrl || DEFAULT_API_URL; if (!config.auth?.token) { throw new Error('No authentication token found. Please run "cliseo auth" to login.'); } // Check if token is expired if (config.auth.expiresAt && Date.now() > config.auth.expiresAt) { throw new Error('Authentication token expired. Please run "cliseo auth" to login again.'); } return axios.create({ baseURL: apiUrl, headers: { 'Authorization': `Bearer ${config.auth.token}`, 'Content-Type': 'application/json' } }); } /** * Simple device code authentication flow for CLI */ export async function login() { try { const config = await loadConfig(); const apiUrl = config.apiUrl || process.env.API_URL || DEFAULT_API_URL; console.log(chalk.cyan('šŸ” Starting device authentication...')); // Step 1: Get a device code const deviceResponse = await axios.post(`${apiUrl}/cli-auth/device`); if (deviceResponse.status !== 200) { throw new Error('Failed to get device code'); } const { device_code, user_code, verification_url, expires_in } = deviceResponse.data; console.log(chalk.bold('\nšŸ“± To authenticate your CLI:')); console.log(chalk.cyan(`1. Visit: ${verification_url}`)); console.log(chalk.cyan(`2. Enter code: ${chalk.bold(user_code)}`)); console.log(chalk.gray(`\nCode expires in ${Math.floor(expires_in / 60)} minutes`)); console.log(chalk.gray('Waiting for authentication...\n')); // Open browser automatically try { const { default: open } = await import('open'); await open(verification_url); console.log(chalk.green('āœ“ Browser opened automatically')); } catch (error) { console.log(chalk.yellow('āš ļø Could not open browser automatically')); } // Step 2: Poll for authentication const pollInterval = 5000; // 5 seconds const maxAttempts = Math.floor((expires_in * 1000) / pollInterval); let attempts = 0; while (attempts < maxAttempts) { try { await new Promise(resolve => setTimeout(resolve, pollInterval)); attempts++; const tokenResponse = await axios.post(`${apiUrl}/cli-auth/token`, { device_code }); if (tokenResponse.status === 200) { const { access_token, user } = tokenResponse.data; // Store the authentication data await saveConfig({ auth: { token: access_token, expiresAt: Date.now() + (24 * 60 * 60 * 1000) // 24 hours } }); console.log(chalk.green('\nāœ… Authentication successful!')); console.log(chalk.cyan(`Welcome, ${user?.name || user?.email}!`)); return true; } } catch (error) { if (error.response?.status === 400) { // Still pending, continue polling continue; } else if (error.response?.status === 404 || error.response?.status === 410) { console.log(chalk.red('\nāŒ Authentication code expired or invalid')); return false; } // For other errors, continue polling } // Show progress every 30 seconds if (attempts % 6 === 0) { const remaining = Math.floor((maxAttempts - attempts) * pollInterval / 1000); console.log(chalk.gray(`Still waiting for authentication... (${remaining}s remaining)`)); } } console.log(chalk.red('\nāŒ Authentication timeout')); return false; } catch (error) { console.error(chalk.red('Authentication failed:'), error.response?.data?.error || error.message); return false; } } /** * Check if user is authenticated */ export async function isAuthenticated() { try { const config = await loadConfig(); if (!config.auth?.token) { return false; } // Check if token is expired if (config.auth.expiresAt && Date.now() > config.auth.expiresAt) { return false; } return true; } catch (error) { return false; } } /** * Call the AI optimization endpoint */ export async function getAIOptimizations(projectContext, files, currentIssues = [], optimizationType = 'full') { try { const axiosInstance = await getAuthenticatedAxios(); const requestData = { project_context: projectContext, files: files, current_issues: currentIssues, optimization_type: optimizationType }; const response = await axiosInstance.post('/ask-openai', requestData); if (response.status === 200) { return response.data; } else { throw new Error(`API request failed with status ${response.status}`); } } catch (error) { if (error.response?.status === 401) { throw new Error('Authentication failed. Please run "cliseo auth" to login again.'); } else if (error.response?.status === 429) { const errorData = error.response.data; throw new Error(`AI usage limit exceeded. Used: ${errorData.used}/${errorData.limit} calls for your subscription tier.`); } else if (error.response?.data?.error) { throw new Error(error.response.data.error); } else { throw new Error(`Failed to get AI optimizations: ${error.message}`); } } } /** * Logout and clear stored tokens */ export async function logout() { await saveConfig({ auth: undefined }); } //# sourceMappingURL=api.js.map