import fs from "fs/promises"; import path from "path"; import { AppSettings } from "@/lib/types"; import { DEFAULT_AUTH_PASSWORD_HASH, DEFAULT_AUTH_USERNAME, } from "@/lib/auth/password"; const DATA_DIR = path.join(process.cwd(), "data"); const SETTINGS_DIR = path.join(DATA_DIR, "settings"); const SETTINGS_FILE = path.join(SETTINGS_DIR, "settings.json"); async function ensureDir(dir: string) { await fs.mkdir(dir, { recursive: true }); } export const DEFAULT_SETTINGS: AppSettings = { chatModel: { provider: "openai", model: "gpt-4o", temperature: 0.7, maxTokens: 4096, }, utilityModel: { provider: "openai", model: "gpt-4o-mini", temperature: 0.3, maxTokens: 2048, }, embeddingsModel: { provider: "openai", model: "text-embedding-3-small", dimensions: 1536, }, codeExecution: { enabled: true, timeout: 600, maxOutputLength: 120000, }, memory: { enabled: true, similarityThreshold: 0.35, maxResults: 10, chunkSize: 400, }, search: { enabled: false, provider: "none", }, general: { darkMode: false, language: "en", }, auth: { enabled: true, username: DEFAULT_AUTH_USERNAME, passwordHash: DEFAULT_AUTH_PASSWORD_HASH, mustChangeCredentials: true, }, }; export async function getSettings(): Promise { await ensureDir(SETTINGS_DIR); try { const content = await fs.readFile(SETTINGS_FILE, "utf-8"); const saved = JSON.parse(content) as unknown; if (!saved || typeof saved !== "object" || Array.isArray(saved)) { return structuredClone(DEFAULT_SETTINGS); } // Deep merge with defaults to handle new nested fields. return deepMerge( structuredClone(DEFAULT_SETTINGS) as unknown as Record, saved as Record ) as unknown as AppSettings; } catch { return structuredClone(DEFAULT_SETTINGS); } } export async function saveSettings( settings: Partial ): Promise { await ensureDir(SETTINGS_DIR); const current = await getSettings(); const merged = deepMerge(current as unknown as Record, settings as unknown as Record) as unknown as AppSettings; await fs.writeFile(SETTINGS_FILE, JSON.stringify(merged, null, 2), "utf-8"); return merged; } function deepMerge(target: Record, source: Record): Record { const result = { ...target }; for (const key of Object.keys(source)) { if ( source[key] && typeof source[key] === "object" && !Array.isArray(source[key]) && target[key] && typeof target[key] === "object" && !Array.isArray(target[key]) ) { result[key] = deepMerge( target[key] as Record, source[key] as Record ); } else { result[key] = source[key]; } } return result; }