UNPKG

lanonasis-memory

Version:

Memory as a Service integration - AI-powered memory management with semantic search (Compatible with CLI v3.0.6+)

245 lines (202 loc) 8.26 kB
import * as vscode from 'vscode'; import { SecureApiKeyService, StoredCredential } from './SecureApiKeyService'; export interface ApiKey { id: string; name: string; keyType: string; environment: string; accessLevel: string; projectId: string; createdAt: string; expiresAt?: string; tags: string[]; metadata: Record<string, any>; } export interface Project { id: string; name: string; description?: string; organizationId: string; createdAt: string; teamMembers: string[]; settings: Record<string, any>; } export interface CreateApiKeyRequest { name: string; value: string; keyType: 'api_key' | 'database_url' | 'oauth_token' | 'certificate' | 'ssh_key' | 'webhook_secret' | 'encryption_key'; environment: 'development' | 'staging' | 'production'; accessLevel: 'public' | 'authenticated' | 'team' | 'admin' | 'enterprise'; projectId: string; tags?: string[]; expiresAt?: string; rotationFrequency?: number; metadata?: Record<string, any>; } export interface CreateProjectRequest { name: string; description?: string; organizationId: string; teamMembers?: string[]; settings?: Record<string, any>; } export class ApiKeyService { private config: vscode.WorkspaceConfiguration; private baseUrl: string = 'https://mcp.lanonasis.com'; private secureApiKeyService: SecureApiKeyService; constructor(secureApiKeyService: SecureApiKeyService) { this.secureApiKeyService = secureApiKeyService; this.config = vscode.workspace.getConfiguration('lanonasis'); this.updateConfig(); } private updateConfig(): void { const useGateway = this.config.get<boolean>('useGateway', true); const apiUrl = this.config.get<string>('apiUrl', 'https://mcp.lanonasis.com'); const gatewayUrl = this.config.get<string>('gatewayUrl', 'https://mcp.lanonasis.com'); this.baseUrl = this.sanitizeBaseUrl(useGateway ? gatewayUrl : apiUrl); } refreshConfig(): void { this.config = vscode.workspace.getConfiguration('lanonasis'); this.updateConfig(); } private async makeRequest<T>(endpoint: string, options: RequestInit = {}): Promise<T> { const credentials = await this.resolveCredentials(); const normalizedEndpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`; const url = `${this.baseUrl}${normalizedEndpoint}`; const authHeaders: Record<string, string> = credentials.type === 'oauth' ? { 'Authorization': `Bearer ${credentials.token}` } : { 'X-API-Key': credentials.token }; const response = await fetch(url, { ...options, headers: { 'Content-Type': 'application/json', ...authHeaders, ...options.headers } }); if (!response.ok) { const errorText = await response.text(); throw new Error(`API request failed: ${response.status} ${response.statusText} - ${errorText}`); } return response.json(); } private sanitizeBaseUrl(url: string): string { if (!url) { return 'https://mcp.lanonasis.com'; } let clean = url.trim(); // remove trailing slashes clean = clean.replace(/\/+$/, ''); // remove duplicate /api or /api/v1 suffixes clean = clean.replace(/\/api\/v1$/i, '').replace(/\/api$/i, ''); return clean || 'https://mcp.lanonasis.com'; } private async resolveCredentials(): Promise<StoredCredential> { let credentials = await this.secureApiKeyService.getStoredCredentials(); if (!credentials) { const value = await this.secureApiKeyService.getApiKeyOrPrompt(); if (!value) { throw new Error('API key not configured. Please configure your API key to use Lanonasis services.'); } credentials = await this.secureApiKeyService.getStoredCredentials(); if (!credentials) { credentials = { type: this.looksLikeJwt(value) ? 'oauth' : 'apiKey', token: value }; } } return credentials; } private looksLikeJwt(token: string): boolean { const parts = token.split('.'); if (parts.length !== 3) { return false; } const jwtSegment = /^[A-Za-z0-9-_]+$/; return parts.every(segment => jwtSegment.test(segment)); } // ============================================================================ // PROJECT MANAGEMENT // ============================================================================ async getProjects(): Promise<Project[]> { return this.makeRequest<Project[]>('/api/v1/projects'); } async getProject(projectId: string): Promise<Project> { return this.makeRequest<Project>(`/api/v1/projects/${projectId}`); } async createProject(request: CreateProjectRequest): Promise<Project> { return this.makeRequest<Project>('/api/v1/projects', { method: 'POST', body: JSON.stringify(request) }); } async updateProject(projectId: string, updates: Partial<CreateProjectRequest>): Promise<Project> { return this.makeRequest<Project>(`/api/v1/projects/${projectId}`, { method: 'PUT', body: JSON.stringify(updates) }); } async deleteProject(projectId: string): Promise<void> { await this.makeRequest<void>(`/api/v1/projects/${projectId}`, { method: 'DELETE' }); } // ============================================================================ // API KEY MANAGEMENT // ============================================================================ async getApiKeys(projectId?: string): Promise<ApiKey[]> { const endpoint = projectId ? `/api/v1/projects/${projectId}/api-keys` : '/api/v1/auth/api-keys'; const response = await this.makeRequest<any>(endpoint); // Handle wrapped response format from /api/v1/auth/api-keys // which returns { success: true, data: [...] } if (response && typeof response === 'object' && 'data' in response && Array.isArray(response.data)) { return response.data; } // Handle direct array response from /api/v1/projects/:projectId/api-keys if (Array.isArray(response)) { return response; } // Fallback: return empty array if response format is unexpected return []; } async getApiKey(keyId: string): Promise<ApiKey> { return this.makeRequest<ApiKey>(`/api/v1/auth/api-keys/${keyId}`); } async createApiKey(request: CreateApiKeyRequest): Promise<ApiKey> { return this.makeRequest<ApiKey>('/api/v1/auth/api-keys', { method: 'POST', body: JSON.stringify(request) }); } async updateApiKey(keyId: string, updates: Partial<CreateApiKeyRequest>): Promise<ApiKey> { return this.makeRequest<ApiKey>(`/api/v1/auth/api-keys/${keyId}`, { method: 'PUT', body: JSON.stringify(updates) }); } async deleteApiKey(keyId: string): Promise<void> { await this.makeRequest<void>(`/api/v1/auth/api-keys/${keyId}`, { method: 'DELETE' }); } async rotateApiKey(keyId: string): Promise<ApiKey> { return this.makeRequest<ApiKey>(`/api/v1/auth/api-keys/${keyId}/rotate`, { method: 'POST' }); } // ============================================================================ // UTILITY METHODS // ============================================================================ async testConnection(): Promise<boolean> { try { await this.makeRequest<any>('/api/v1/health'); return true; } catch (error) { return false; } } async getUserInfo(): Promise<any> { return this.makeRequest<any>('/api/v1/auth/me'); } }