import { db, mothershipInboxWebhook, workspace } from '@sim/db' import { createLogger } from '@sim/logger' import { generateId } from '@sim/utils/id' import { eq } from 'drizzle-orm' import { getBaseUrl } from '@/lib/core/utils/urls' import * as agentmail from '@/lib/mothership/inbox/agentmail-client' import type { InboxConfig } from '@/lib/mothership/inbox/types' const logger = createLogger('InboxLifecycle') /** * Enable inbox for a workspace: * 1. Create AgentMail inbox (with optional custom username) * 2. Create AgentMail webhook scoped to this inbox * 3. Store inbox details + webhook secret in DB * 4. Update workspace.inboxEnabled = true */ export async function enableInbox( workspaceId: string, opts?: { username?: string } ): Promise { const inbox = await agentmail.createInbox({ username: opts?.username, displayName: 'Sim', }) logger.info('AgentMail createInbox response', { inbox: JSON.stringify(inbox) }) if (!inbox?.inbox_id) { throw new Error('AgentMail createInbox response missing inbox_id') } let webhook: Awaited> | null = null try { webhook = await agentmail.createWebhook({ url: `${getBaseUrl()}/api/webhooks/agentmail`, eventTypes: ['message.received'], inboxIds: [inbox.inbox_id], }) await db.insert(mothershipInboxWebhook).values({ id: generateId(), workspaceId, webhookId: webhook.webhook_id, secret: webhook.secret, }) await db .update(workspace) .set({ inboxEnabled: true, inboxAddress: inbox.inbox_id, inboxProviderId: inbox.inbox_id, updatedAt: new Date(), }) .where(eq(workspace.id, workspaceId)) logger.info('Inbox enabled', { workspaceId, address: inbox.inbox_id }) return { enabled: true, address: inbox.inbox_id, providerId: inbox.inbox_id, } } catch (error) { try { if (webhook) await agentmail.deleteWebhook(webhook.webhook_id) await agentmail.deleteInbox(inbox.inbox_id) await db .delete(mothershipInboxWebhook) .where(eq(mothershipInboxWebhook.workspaceId, workspaceId)) } catch (rollbackError) { logger.error('Failed to rollback AgentMail resources', { rollbackError }) } throw error } } /** * Disable inbox: * 1. Delete AgentMail webhook * 2. Delete AgentMail inbox * 3. Clear workspace inbox columns * 4. Delete mothershipInboxWebhook row */ export async function disableInbox(workspaceId: string): Promise { const [[ws], [webhookRow]] = await Promise.all([ db .select({ inboxProviderId: workspace.inboxProviderId }) .from(workspace) .where(eq(workspace.id, workspaceId)) .limit(1), db .select({ webhookId: mothershipInboxWebhook.webhookId }) .from(mothershipInboxWebhook) .where(eq(mothershipInboxWebhook.workspaceId, workspaceId)) .limit(1), ]) const deletePromises: Promise[] = [] if (webhookRow) { deletePromises.push( agentmail.deleteWebhook(webhookRow.webhookId).catch((error) => { logger.warn('Failed to delete AgentMail webhook', { error }) }) ) } if (ws?.inboxProviderId) { deletePromises.push( agentmail.deleteInbox(ws.inboxProviderId).catch((error) => { logger.warn('Failed to delete AgentMail inbox', { error }) }) ) } await Promise.all(deletePromises) await Promise.all([ db.delete(mothershipInboxWebhook).where(eq(mothershipInboxWebhook.workspaceId, workspaceId)), db .update(workspace) .set({ inboxEnabled: false, inboxAddress: null, inboxProviderId: null, updatedAt: new Date(), }) .where(eq(workspace.id, workspaceId)), ]) logger.info('Inbox disabled', { workspaceId }) } /** * Update inbox address (regenerate): * 1. Disable old inbox * 2. Enable new inbox with new username */ export async function updateInboxAddress( workspaceId: string, newUsername: string ): Promise { await disableInbox(workspaceId) return enableInbox(workspaceId, { username: newUsername }) }