/** * ───────────────────────────────────────────── * SPB Git — Personal Git Platform * ───────────────────────────────────────────── * Author : Simon-Pierre Boucher * Contact : contact@spboucher.ai * File : cli/lib/config.mjs * Purpose : ~/.spbgit/config.json loading, saving, validation * License : MIT © Simon-Pierre Boucher * ───────────────────────────────────────────── */ import { readFileSync, writeFileSync, mkdirSync, chmodSync, existsSync } from 'node:fs'; import { homedir } from 'node:os'; import { join } from 'node:path'; export const CONFIG_DIR = join(homedir(), '.spbgit'); export const CONFIG_PATH = join(CONFIG_DIR, 'config.json'); const DEFAULTS = { server: 'https://git.spboucher.ai', token: '', workspace: join(homedir(), 'code'), author: { name: 'Simon-Pierre Boucher', email: 'contact@spboucher.ai' }, }; /** Expand a leading `~` to the home directory. */ export function expandHome(path) { if (!path) return path; return path.startsWith('~') ? join(homedir(), path.slice(1)) : path; } /** * @returns {object|null} parsed config or null when not initialized */ export function loadCliConfig() { if (!existsSync(CONFIG_PATH)) return null; try { const parsed = JSON.parse(readFileSync(CONFIG_PATH, 'utf8')); return { ...DEFAULTS, ...parsed, workspace: expandHome(parsed.workspace ?? DEFAULTS.workspace) }; } catch { return null; } } /** * Load config or exit(1) with a helpful message. * @returns {object} */ export function requireCliConfig() { const config = loadCliConfig(); if (!config || !config.server) { console.error('spbgit is not configured. Run: spbgit init'); process.exit(1); } return config; } /** * Persist config with 600 permissions (contains the PAT). * @param {object} config */ export function saveCliConfig(config) { mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 }); writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + '\n', { mode: 0o600 }); chmodSync(CONFIG_PATH, 0o600); }