import { getPreferenceValues, showToast, Toast } from "@raycast/api"; export interface Project { id: string; name: string; containerTag: string; description?: string; } export interface Memory { id: string; content: string; title?: string; url?: string; containerTag?: string; createdAt: string; } export interface SearchResult { documentId: string; chunks: unknown[]; title?: string; metadata: Record; score?: number; createdAt: string; updatedAt: string; type: string; } export interface AddMemoryRequest { content: string; containerTags?: string[]; title?: string; url?: string; metadata?: Record; } export interface SearchRequest { q: string; containerTags?: string[]; limit?: number; } export interface SearchResponse { results: SearchResult[]; timing: number; total: number; } const API_BASE_URL = "https://api.supermemory.ai"; class SupermemoryAPIError extends Error { constructor( message: string, public status?: number, ) { super(message); this.name = "SupermemoryAPIError"; } } class AuthenticationError extends Error { constructor(message: string) { super(message); this.name = "AuthenticationError"; } } async function getApiKey(): Promise { try { const preferences = getPreferenceValues<{ apiKey: string }>(); const apiKey = preferences.apiKey?.trim(); if (!apiKey) { throw new AuthenticationError( "API key is required. Please add your Supermemory API key in preferences.", ); } return apiKey; } catch { throw new AuthenticationError("Failed to get API key from preferences."); } } async function makeAuthenticatedRequest( endpoint: string, options: RequestInit = {}, ): Promise { const apiKey = await getApiKey(); const url = `${API_BASE_URL}${endpoint}`; try { const response = await fetch(url, { ...options, headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json", ...options.headers, }, }); if (!response.ok) { if (response.status === 401) { throw new AuthenticationError( "Invalid API key. Please check your API key in preferences. Get a new one from https://supermemory.link/raycast", ); } let errorMessage = `API request failed: ${response.statusText}`; try { const errorBody = (await response.json()) as { message?: string }; if (errorBody.message) { errorMessage = errorBody.message; } } catch { // Ignore JSON parsing errors, use default message } throw new SupermemoryAPIError(errorMessage, response.status); } if (!response.headers.get("content-type")?.includes("application/json")) { throw new SupermemoryAPIError("Invalid response format from API"); } const data = (await response.json()) as T; return data; } catch (err) { if ( err instanceof AuthenticationError || err instanceof SupermemoryAPIError ) { throw err; } // Handle network errors or other fetch errors throw new SupermemoryAPIError( `Network error: ${err instanceof Error ? err.message : "Unknown error"}`, ); } } export async function fetchProjects(): Promise { try { const response = await makeAuthenticatedRequest<{ projects: Project[] }>( "/v3/projects", ); return response.projects || []; } catch (error) { await showToast({ style: Toast.Style.Failure, title: "Failed to fetch projects", message: error instanceof Error ? error.message : "Unknown error occurred", }); throw error; } } export async function addMemory(request: AddMemoryRequest): Promise { try { const response = await makeAuthenticatedRequest("/v3/documents", { method: "POST", body: JSON.stringify(request), }); await showToast({ style: Toast.Style.Success, title: "Memory Added", message: "Successfully added memory to Supermemory", }); return response; } catch (error) { await showToast({ style: Toast.Style.Failure, title: "Failed to add memory", message: error instanceof Error ? error.message : "Unknown error occurred", }); throw error; } } export async function searchMemories( request: SearchRequest, ): Promise { try { const response = await makeAuthenticatedRequest( "/v3/search", { method: "POST", body: JSON.stringify(request), }, ); return response.results || []; } catch (error) { await showToast({ style: Toast.Style.Failure, title: "Failed to search memories", message: error instanceof Error ? error.message : "Unknown error occurred", }); throw error; } } // Helper function to check if API key is configured and valid export async function checkApiConnection(): Promise { try { await fetchProjects(); return true; } catch (error) { if (error instanceof AuthenticationError) { await showToast({ style: Toast.Style.Failure, title: "API Key Required", message: "Please configure your Supermemory API key in preferences. Get it from https://supermemory.link/raycast", }); } return false; } }