import { existsSync, readFileSync } from 'fs'; import { join } from 'path'; export interface CliConfig { prekeyServer: string; storage: string; observerToken?: string; observerUrl?: string; address?: string; } const DEFAULT_STORAGE = 'sqlite:./.shade/client.db'; /** Read config from .shaderc.json in cwd, then env vars as fallback */ export function loadConfig(cwd: string = process.cwd()): CliConfig { const configPath = join(cwd, '.shaderc.json'); let fileConfig: Partial = {}; if (existsSync(configPath)) { try { fileConfig = JSON.parse(readFileSync(configPath, 'utf-8')); } catch (err) { throw new Error(`Failed to parse .shaderc.json: ${(err as Error).message}`); } } const prekeyServer = fileConfig.prekeyServer ?? process.env.SHADE_PREKEY_SERVER; if (!prekeyServer) { throw new Error( 'Missing prekeyServer. Set it in .shaderc.json or via SHADE_PREKEY_SERVER env var.', ); } return { prekeyServer, storage: fileConfig.storage ?? process.env.SHADE_DB_PATH ?? DEFAULT_STORAGE, observerToken: fileConfig.observerToken ?? process.env.SHADE_OBSERVER_TOKEN, observerUrl: fileConfig.observerUrl ?? process.env.SHADE_OBSERVER_URL, address: fileConfig.address ?? process.env.SHADE_ADDRESS, }; } /** Check config is loadable without throwing; for `shade doctor`. */ export function tryLoadConfig(cwd: string = process.cwd()): { ok: true; config: CliConfig } | { ok: false; error: string } { try { return { ok: true, config: loadConfig(cwd) }; } catch (err) { return { ok: false, error: (err as Error).message }; } }