'use node'; import { getAuthUserId } from '@convex-dev/auth/server'; import { ConvexError, v } from 'convex/values'; import type { Id } from './_generated/dataModel'; import type { ActionCtx } from './_generated/server'; import { internal } from './_generated/api'; import { action } from './_generated/server'; import { encryptSecret } from './secretCrypto'; const reasoningEffort = v.union( v.literal('none'), v.literal('minimal'), v.literal('low'), v.literal('medium'), v.literal('high'), v.literal('xhigh'), ); const getRequiredUserId = async (ctx: ActionCtx): Promise> => { const userId = await getAuthUserId(ctx); if (!userId) throw new ConvexError('Not authenticated.'); return userId; }; const previewKey = (apiKey: string) => { const trimmed = apiKey.trim(); if (trimmed.length <= 10) return 'configured'; return `${trimmed.slice(0, 7)}...${trimmed.slice(-4)}`; }; export const saveOpenAiSettings = action({ args: { apiKey: v.string(), model: v.string(), reasoningEffort, }, handler: async (ctx, args): Promise<{ success: true }> => { const userId = await getRequiredUserId(ctx); const apiKey = args.apiKey.trim(); if (!apiKey) throw new ConvexError('OpenAI API key is required.'); await ctx.runMutation(internal.aiSettings.upsertEncryptedInternal, { userId, encryptedApiKey: encryptSecret(apiKey), apiKeyPreview: previewKey(apiKey), model: args.model.trim() || 'gpt-5.5', reasoningEffort: args.reasoningEffort, }); return { success: true }; }, });