All files / core config.ts

92.3% Statements 36/39
66.66% Branches 10/15
100% Functions 4/4
92.3% Lines 36/39

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91      2x 2x 2x 2x                               2x 2x   2x     6x             2x 10x 10x 6x 4x           4x     4x     4x     4x   6x       2x 8x 8x 1x     8x 8x 8x       8x 8x 8x 7x           2x 4x 4x 1x   3x 3x 3x    
// Copyright 2026 ForgeKit Contributors
// SPDX-License-Identifier: Apache-2.0
// https://github.com/SubhanshuMG/ForgeKit
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
import * as crypto from 'crypto';
 
export interface ForgeKitConfig {
  telemetry: boolean;
  userId: string;
  firstRun: boolean;
  ai?: {
    provider: 'openai' | 'anthropic';
    model?: string;
  };
  plugins?: string[];
  envSync?: {
    defaultEnvironment?: string;
  };
}
 
const CONFIG_DIR = path.join(os.homedir(), '.forgekit');
const CONFIG_PATH = path.join(CONFIG_DIR, 'config.json');
 
export { CONFIG_DIR };
 
function defaultConfig(): ForgeKitConfig {
  return {
    telemetry: false,
    userId: '',
    firstRun: true,
  };
}
 
export function loadConfig(): ForgeKitConfig {
  try {
    const raw = fs.readFileSync(CONFIG_PATH, 'utf-8');
    const parsed = JSON.parse(raw);
    const config: ForgeKitConfig = {
      telemetry: typeof parsed.telemetry === 'boolean' ? parsed.telemetry : false,
      userId: typeof parsed.userId === 'string' ? parsed.userId : '',
      firstRun: typeof parsed.firstRun === 'boolean' ? parsed.firstRun : true,
    };
    // Preserve optional fields if present
    Iif (parsed.ai && typeof parsed.ai === 'object') {
      config.ai = parsed.ai;
    }
    Iif (Array.isArray(parsed.plugins)) {
      config.plugins = parsed.plugins;
    }
    Iif (parsed.envSync && typeof parsed.envSync === 'object') {
      config.envSync = parsed.envSync;
    }
    return config;
  } catch {
    return defaultConfig();
  }
}
 
export function saveConfig(config: ForgeKitConfig): void {
  try {
    if (!fs.existsSync(CONFIG_DIR)) {
      fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
    }
    // Merge with existing config to preserve unknown fields
    let existing: Record<string, unknown> = {};
    try {
      existing = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8'));
    } catch {
      // No existing config
    }
    const merged = { ...existing, ...config };
    const tmpPath = CONFIG_PATH + '.tmp';
    fs.writeFileSync(tmpPath, JSON.stringify(merged, null, 2), { encoding: 'utf-8', mode: 0o600 });
    fs.renameSync(tmpPath, CONFIG_PATH);
  } catch {
    // Never throw from config writes
  }
}
 
export function getUserId(): string {
  const config = loadConfig();
  if (config.userId) {
    return config.userId;
  }
  const userId = crypto.randomUUID();
  saveConfig({ ...config, userId });
  return userId;
}