cliseo
Version:
Instant AI-Powered SEO Optimization CLI for Developers
167 lines ⢠6.44 kB
JavaScript
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