UNPKG

capsule-ai-cli

Version:

The AI Model Orchestrator - Intelligent multi-model workflows with device-locked licensing

157 lines 5.77 kB
import { createHash, randomBytes } from 'crypto'; import { networkInterfaces, hostname, platform, cpus } from 'os'; import keytar from 'keytar'; export class LocalLicenseService { SERVICE_NAME = 'capsule-cli'; ACCOUNT_NAME = 'license'; MACHINE_ID_ACCOUNT = 'machine_id'; async activate(licenseKey) { try { if (!this.isValidLicenseFormat(licenseKey)) { return { success: false, error: 'Invalid license key format' }; } const machineId = await this.getMachineId(); const licenseInfo = this.decodeLicense(licenseKey, machineId); if (!licenseInfo) { return { success: false, error: 'Invalid license key for this machine' }; } await keytar.setPassword(this.SERVICE_NAME, this.ACCOUNT_NAME, licenseKey); await keytar.setPassword(this.SERVICE_NAME, this.MACHINE_ID_ACCOUNT, machineId); await keytar.setPassword(this.SERVICE_NAME, 'activation_date', new Date().toISOString()); return { success: true }; } catch (error) { return { success: false, error: `Failed to activate license: ${error.message}` }; } } async validate() { try { const licenseKey = await keytar.getPassword(this.SERVICE_NAME, this.ACCOUNT_NAME); if (!licenseKey) { return { valid: false }; } const storedMachineId = await keytar.getPassword(this.SERVICE_NAME, this.MACHINE_ID_ACCOUNT); const currentMachineId = await this.getMachineId(); if (storedMachineId !== currentMachineId) { return { valid: false }; } const licenseInfo = this.decodeLicense(licenseKey, currentMachineId); if (!licenseInfo) { return { valid: false }; } const activationDate = await keytar.getPassword(this.SERVICE_NAME, 'activation_date'); if (activationDate) { licenseInfo.activatedAt = new Date(activationDate); } return { valid: true, license: licenseInfo }; } catch { return { valid: false }; } } async deactivate() { await keytar.deletePassword(this.SERVICE_NAME, this.ACCOUNT_NAME); await keytar.deletePassword(this.SERVICE_NAME, 'activation_date'); } isValidLicenseFormat(key) { const pattern = /^[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/; return pattern.test(key); } decodeLicense(licenseKey, machineId) { try { const parts = licenseKey.split('-'); if (parts.length !== 4) return null; const tierCode = parts[0]; const payload = parts.slice(0, 3).join(''); const checksum = parts[3]; const hash = createHash('sha256') .update(payload + machineId + 'capsule-secret-v2') .digest('hex') .substring(0, 4) .toUpperCase(); if (hash !== checksum) { const demoHash = createHash('sha256') .update(payload + 'capsule-demo-v2') .digest('hex') .substring(0, 4) .toUpperCase(); if (demoHash !== checksum) { return null; } } const tier = tierCode === 'PRO' ? 'pro' : tierCode === 'CLOUD' ? 'cloud' : 'free'; const features = tier === 'pro' || tier === 'cloud' ? [ 'model_fusion', 'smart_router', 'advanced_tools', 'local_analytics', 'priority_support' ] : [ 'basic_chat', 'single_model' ]; if (tier === 'cloud') { features.push('cloud_analytics', 'hosted_models', 'team_collaboration', 'api_access'); } return { key: licenseKey, tier, features, activatedAt: new Date() }; } catch { return null; } } async getMachineId() { const interfaces = networkInterfaces(); const macs = []; for (const [name, nets] of Object.entries(interfaces)) { if (nets) { for (const net of nets) { if (!net.internal && net.mac && net.mac !== '00:00:00:00:00:00') { macs.push(net.mac); } } } } const hardwareInfo = [ hostname(), platform(), cpus()[0]?.model || 'unknown', macs.sort().join(',') || 'no-mac' ].join('|'); return createHash('sha256') .update(hardwareInfo) .digest('hex') .substring(0, 16); } generateDemoLicenseKey(tier = 'pro') { const tierCode = tier === 'cloud' ? 'CLOUD' : 'PRO'; const randomPart = randomBytes(4).toString('hex').toUpperCase(); const payload = `${tierCode}-${randomPart.substring(0, 4)}-${randomPart.substring(4, 8)}`; const hash = createHash('sha256') .update(payload + 'capsule-demo-v2') .digest('hex') .substring(0, 4) .toUpperCase(); return `${payload}-${hash}`; } } //# sourceMappingURL=license-service.js.map