capsule-ai-cli
Version:
The AI Model Orchestrator - Intelligent multi-model workflows with device-locked licensing
197 lines • 7.44 kB
JavaScript
import { configManager } from '../core/config.js';
import chalk from 'chalk';
import crypto from 'crypto';
import os from 'os';
export class AuthService {
baseUrl;
devMode = false;
deviceId;
constructor(baseUrl = 'https://www.capsulecli.dev') {
this.baseUrl = baseUrl;
const config = configManager.getConfig();
this.deviceId = config.auth?.deviceId || this.generateDeviceId();
}
setDevMode(enabled) {
this.devMode = enabled;
}
generateDeviceId() {
const fingerprint = [
os.hostname(),
os.platform(),
os.arch(),
os.homedir(),
os.cpus()[0]?.model || 'unknown-cpu'
].join('-');
return crypto.createHash('sha256').update(fingerprint).digest('hex');
}
getDeviceId() {
return this.deviceId;
}
async activate(activationCode) {
try {
const response = await fetch(`${this.baseUrl}/api/activate`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
code: activationCode,
deviceId: this.deviceId
}),
});
if (!response.ok) {
const error = await response.json();
if (response.status === 400) {
throw new Error(error.message || 'Invalid activation code');
}
if (response.status === 409) {
throw new Error('This activation code has already been used on another device');
}
throw new Error('Activation failed');
}
const data = await response.json();
const authData = {
...data,
tier: 'base'
};
configManager.setConfig('auth', {
token: authData.token,
email: authData.email,
tier: authData.tier,
deviceId: this.deviceId,
remainingRUs: authData.remainingRUs,
validUntil: authData.validUntil,
});
return authData;
}
catch (error) {
if (error.message.includes('fetch')) {
throw new Error('Unable to connect to Capsule servers. Please check your internet connection.');
}
throw error;
}
}
async authenticate(_email) {
throw new Error('Email authentication is deprecated. Please use "capsule activate <code>" instead.');
}
async getStatus() {
if (this.devMode) {
return {
isAuthenticated: true,
tier: 'super',
email: 'dev@capsule.local',
deviceId: this.deviceId,
remainingRUs: 999999,
validUntil: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000).toISOString()
};
}
const config = configManager.getConfig();
if (!config.auth?.token) {
return { isAuthenticated: false };
}
try {
const response = await fetch(`${this.baseUrl}/api/auth/verify`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${config.auth.token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
deviceId: this.deviceId
}),
});
if (!response.ok) {
configManager.setConfig('auth', undefined);
if (response.status === 403) {
const error = await response.json();
return {
isAuthenticated: false,
email: error.email,
message: 'License is activated on a different device. Please contact support@capsulecli.dev to reset your activation.'
};
}
else if (response.status === 401) {
return {
isAuthenticated: false,
message: 'License verification failed. Please run `capsule activate <code>` to reactivate.'
};
}
return {
isAuthenticated: false,
message: 'Unable to verify license. Please check your activation status.'
};
}
const data = await response.json();
configManager.setConfig('auth', {
token: config.auth.token,
email: data.email,
tier: data.tier,
deviceId: data.deviceId,
remainingRUs: data.remainingRUs,
validUntil: data.validUntil,
});
return {
isAuthenticated: true,
tier: data.tier,
email: data.email,
deviceId: data.deviceId,
remainingRUs: data.remainingRUs,
validUntil: data.validUntil,
};
}
catch (error) {
return {
isAuthenticated: false,
message: 'Unable to verify license. Please check your activation status.'
};
}
}
async deductRUs(usage) {
const config = configManager.getConfig();
if (!config.auth?.token || config.auth.tier !== 'super') {
return;
}
try {
await fetch(`${this.baseUrl}/api/usage/deduct`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${config.auth.token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ tokens: usage.totalTokens }),
});
}
catch {
}
}
async logout() {
configManager.setConfig('auth', undefined);
}
formatStatus(status) {
if (!status.isAuthenticated) {
if (status.message) {
return chalk.yellow(status.message);
}
if (status.email) {
return chalk.yellow(`License for ${status.email} is activated on a different device.\nPlease contact support@capsulecli.dev to reset your activation.`);
}
return chalk.yellow('Not activated. Run `capsule activate <code>` with your activation code.');
}
const tierColor = status.tier === 'super' ? chalk.cyan : chalk.green;
let output = `${tierColor(`${status.tier?.toUpperCase()} Tier`)} - ${status.email}`;
if (status.tier === 'super' && status.remainingRUs !== undefined) {
output += `\n${chalk.gray('Remaining RUs:')} ${status.remainingRUs.toLocaleString()}`;
}
if (status.validUntil) {
const validUntil = new Date(status.validUntil);
const daysLeft = Math.ceil((validUntil.getTime() - Date.now()) / (1000 * 60 * 60 * 24));
output += `\n${chalk.gray('Valid for:')} ${daysLeft} days`;
}
if (status.deviceId) {
output += `\n${chalk.gray('Device ID:')} ${status.deviceId.substring(0, 8)}...`;
}
return output;
}
}
export const authService = new AuthService();
//# sourceMappingURL=auth.js.map