import { createClient } from '@/lib/supabase/client'; import { backendApi, supabaseClient } from './api-client'; import { handleApiSuccess } from './error-handler'; import { Project, Thread, Message, AgentRun, InitiateAgentResponse, HealthCheckResponse, FileInfo, CreateCheckoutSessionRequest, CreateCheckoutSessionResponse, CreatePortalSessionRequest, SubscriptionStatus, AvailableModelsResponse, BillingStatusResponse, BillingError, UsageLogsResponse } from './api'; export * from './api'; export const projectsApi = { async getAll(): Promise { const result = await supabaseClient.execute( async () => { const supabase = createClient(); const { data: userData, error: userError } = await supabase.auth.getUser(); if (userError) { return { data: null, error: userError }; } if (!userData.user) { return { data: [], error: null }; } const { data, error } = await supabase .from('projects') .select('*') .eq('account_id', userData.user.id); if (error) { if (error.code === '42501' && error.message.includes('has_role_on_account')) { return { data: [], error: null }; } return { data: null, error }; } const mappedProjects: Project[] = (data || []).map((project) => ({ id: project.project_id, name: project.name || '', description: project.description || '', account_id: project.account_id, created_at: project.created_at, updated_at: project.updated_at, sandbox: project.sandbox || { id: '', pass: '', vnc_preview: '', sandbox_url: '', }, })); return { data: mappedProjects, error: null }; }, { operation: 'load projects', resource: 'projects' } ); return result.data || []; }, async getById(projectId: string): Promise { const result = await supabaseClient.execute( async () => { const supabase = createClient(); const { data, error } = await supabase .from('projects') .select('*') .eq('project_id', projectId) .single(); if (error) { if (error.code === 'PGRST116') { return { data: null, error: new Error(`Project not found: ${projectId}`) }; } return { data: null, error }; } // Ensure sandbox is active if it exists if (data.sandbox?.id) { backendApi.post(`/project/${projectId}/sandbox/ensure-active`, undefined, { showErrors: false, errorContext: { silent: true } }); } const mappedProject: Project = { id: data.project_id, name: data.name || '', description: data.description || '', account_id: data.account_id, is_public: data.is_public || false, created_at: data.created_at, sandbox: data.sandbox || { id: '', pass: '', vnc_preview: '', sandbox_url: '', }, }; return { data: mappedProject, error: null }; }, { operation: 'load project', resource: `project ${projectId}` } ); return result.data || null; }, async create(projectData: { name: string; description: string }, accountId?: string): Promise { const result = await supabaseClient.execute( async () => { const supabase = createClient(); if (!accountId) { const { data: userData, error: userError } = await supabase.auth.getUser(); if (userError) return { data: null, error: userError }; if (!userData.user) return { data: null, error: new Error('You must be logged in to create a project') }; accountId = userData.user.id; } const { data, error } = await supabase .from('projects') .insert({ name: projectData.name, description: projectData.description || null, account_id: accountId, }) .select() .single(); if (error) return { data: null, error }; const project: Project = { id: data.project_id, name: data.name, description: data.description || '', account_id: data.account_id, created_at: data.created_at, sandbox: { id: '', pass: '', vnc_preview: '' }, }; return { data: project, error: null }; }, { operation: 'create project', resource: 'project' } ); return result.data || null; }, async update(projectId: string, data: Partial): Promise { if (!projectId || projectId === '') { throw new Error('Cannot update project: Invalid project ID'); } const result = await supabaseClient.execute( async () => { const supabase = createClient(); const { data: updatedData, error } = await supabase .from('projects') .update(data) .eq('project_id', projectId) .select() .single(); if (error) return { data: null, error }; if (!updatedData) return { data: null, error: new Error('No data returned from update') }; // Dispatch custom event for project updates if (typeof window !== 'undefined') { window.dispatchEvent( new CustomEvent('project-updated', { detail: { projectId, updatedData: { id: updatedData.project_id, name: updatedData.name, description: updatedData.description, }, }, }), ); } const project: Project = { id: updatedData.project_id, name: updatedData.name, description: updatedData.description || '', account_id: updatedData.account_id, created_at: updatedData.created_at, sandbox: updatedData.sandbox || { id: '', pass: '', vnc_preview: '', sandbox_url: '', }, }; return { data: project, error: null }; }, { operation: 'update project', resource: `project ${projectId}` } ); return result.data || null; }, async delete(projectId: string): Promise { const result = await supabaseClient.execute( async () => { const supabase = createClient(); const { error } = await supabase .from('projects') .delete() .eq('project_id', projectId); return { data: !error, error }; }, { operation: 'delete project', resource: `project ${projectId}` } ); return result.success; }, }; export const threadsApi = { async getAll(projectId?: string): Promise { const result = await supabaseClient.execute( async () => { const supabase = createClient(); const { data: userData, error: userError } = await supabase.auth.getUser(); if (userError) return { data: null, error: userError }; if (!userData.user) return { data: [], error: null }; let query = supabase.from('threads').select('*').eq('account_id', userData.user.id); if (projectId) { query = query.eq('project_id', projectId); } const { data, error } = await query; if (error) return { data: null, error }; const mappedThreads: Thread[] = (data || []).map((thread) => ({ thread_id: thread.thread_id, account_id: thread.account_id, project_id: thread.project_id, created_at: thread.created_at, updated_at: thread.updated_at, metadata: thread.metadata, })); return { data: mappedThreads, error: null }; }, { operation: 'load threads', resource: projectId ? `threads for project ${projectId}` : 'threads' } ); return result.data || []; }, async getById(threadId: string): Promise { const result = await supabaseClient.execute( async () => { const supabase = createClient(); const { data, error } = await supabase .from('threads') .select('*') .eq('thread_id', threadId) .single(); return { data, error }; }, { operation: 'load thread', resource: `thread ${threadId}` } ); return result.data || null; }, async create(projectId: string): Promise { const result = await supabaseClient.execute( async () => { const supabase = createClient(); const { data: { user } } = await supabase.auth.getUser(); if (!user) { return { data: null, error: new Error('You must be logged in to create a thread') }; } const { data, error } = await supabase .from('threads') .insert({ project_id: projectId, account_id: user.id, }) .select() .single(); return { data, error }; }, { operation: 'create thread', resource: 'thread' } ); return result.data || null; }, }; export const agentApi = { async start( threadId: string, options?: { model_name?: string; enable_thinking?: boolean; reasoning_effort?: string; stream?: boolean; } ): Promise<{ agent_run_id: string } | null> { const result = await backendApi.post( `/thread/${threadId}/agent/start`, options, { errorContext: { operation: 'start agent', resource: 'AI assistant' }, timeout: 60000, } ); return result.data || null; }, async stop(agentRunId: string): Promise { const result = await backendApi.post( `/agent/${agentRunId}/stop`, undefined, { errorContext: { operation: 'stop agent', resource: 'AI assistant' }, } ); if (result.success) { handleApiSuccess('AI assistant stopped'); } return result.success; }, async getStatus(agentRunId: string): Promise { const result = await backendApi.get( `/agent/${agentRunId}/status`, { errorContext: { operation: 'get agent status', resource: 'AI assistant status' }, showErrors: false, } ); return result.data || null; }, async getRuns(threadId: string): Promise { const result = await backendApi.get( `/thread/${threadId}/agent/runs`, { errorContext: { operation: 'load agent runs', resource: 'conversation history' }, } ); return result.data || []; }, }; export const billingApi = { async getSubscription(): Promise { const result = await backendApi.get( '/billing/subscription', { errorContext: { operation: 'load subscription', resource: 'billing information' }, } ); return result.data || null; }, async checkStatus(): Promise { const result = await backendApi.get( '/billing/status', { errorContext: { operation: 'check billing status', resource: 'account status' }, } ); return result.data || null; }, async createCheckoutSession(request: CreateCheckoutSessionRequest): Promise { const result = await backendApi.post( '/billing/create-checkout-session', request, { errorContext: { operation: 'create checkout session', resource: 'billing' }, } ); return result.data || null; }, async createPortalSession(request: CreatePortalSessionRequest): Promise<{ url: string } | null> { const result = await backendApi.post( '/billing/create-portal-session', request, { errorContext: { operation: 'create portal session', resource: 'billing portal' }, } ); return result.data || null; }, async getAvailableModels(): Promise { const result = await backendApi.get( '/billing/available-models', { errorContext: { operation: 'load available models', resource: 'AI models' }, } ); return result.data || null; }, async getUsageLogs(page: number = 0, itemsPerPage: number = 1000): Promise { const result = await backendApi.get( `/billing/usage-logs?page=${page}&items_per_page=${itemsPerPage}`, { errorContext: { operation: 'load usage logs', resource: 'usage history' }, } ); return result.data || null; }, }; export const healthApi = { async check(): Promise { const result = await backendApi.get( '/health', { errorContext: { operation: 'check system health', resource: 'system status' }, timeout: 10000, } ); return result.data || null; }, };