supermemory/apps/raycast-extension/src/api.ts
MaheshtheDev 163c9daca7 feat(raycast-extension): initial version of supermemory extension for raycast (#440)
feat(raycast-extension): initial version of supermemory extension for raycast

chore: update the metadata and ui for app to get api key

![supermemory-1.png](https://app.graphite.dev/user-attachments/assets/631a865e-8d7b-43df-8753-480f6b80a6d8.png)

![supermemory-2.png](https://app.graphite.dev/user-attachments/assets/956fff54-5447-4feb-a88b-8b465d4cda68.png)
2025-10-02 16:22:56 +00:00

227 lines
5.3 KiB
TypeScript

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<string, unknown>;
score?: number;
createdAt: string;
updatedAt: string;
type: string;
}
export interface AddMemoryRequest {
content: string;
containerTags?: string[];
title?: string;
url?: string;
metadata?: Record<string, unknown>;
}
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<string> {
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<T>(
endpoint: string,
options: RequestInit = {},
): Promise<T> {
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<Project[]> {
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<Memory> {
try {
const response = await makeAuthenticatedRequest<Memory>("/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<SearchResult[]> {
try {
const response = await makeAuthenticatedRequest<SearchResponse>(
"/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<boolean> {
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;
}
}