All files / src/global-config global-config.service.ts

66.66% Statements 24/36
20% Branches 1/5
77.77% Functions 7/9
64.7% Lines 22/34

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 92 93 94 95 96 97 98 99 100 101 102 103 104 10537x 37x 37x 37x 37x 37x                                                   37x 37x     37x 15x         15x       15x                                             3x 3x                         8x       3x 4x     3x 3x 3x       76x         5x      
import { Injectable } from '@nestjs/common';
import { existsSync } from 'fs';
import { readFile, writeFile } from 'fs/promises';
import { ensureDirSync } from 'fs-extra';
import { join } from 'path';
import { homedir } from 'os';
 
export interface GlobalConfig {
  executionStrategy?: string;
  websocketEnabled?: boolean;
  autoContextFetchEnabled?: boolean;
  autoSendToAIStudioEnabled?: boolean;
  manualLlmEnabled?: boolean;
  yoloModeEnabled?: boolean;
  yoloModeMessage?: string;
  streamingEnabled?: boolean;
  toolsEnabled?: boolean;
  syntaxValidationEnabled?: boolean;
  historyCompressionEnabled?: boolean;
  llmRetryEnabled?: boolean;
  llmRetryMaxAttempts?: number;
  contextTokenLimit?: number;
  followupTokenLimit?: number;
  openrouterApiKey?: string;
  zaiApiKey?: string;
  alibabaApiKey?: string;
  ollamaApiKey?: string;
  openaiApiKey?: string;
  openaiBaseUrl?: string;
}
 
const REPOBURG_DIR = join(homedir(), '.repoburg');
const CONFIG_FILE = join(REPOBURG_DIR, 'config.json');
 
@Injectable()
export class GlobalConfigService {
  private config: GlobalConfig = {};
  private initPromise: Promise<void>;
 
  constructor() {
    // Only run file operations if not in test environment
    Iif (process.env.NODE_ENV !== 'test') {
      ensureDirSync(REPOBURG_DIR);
      this.initPromise = this.loadConfig();
    } else {
      this.initPromise = Promise.resolve();
    }
  }
 
  async waitForInit(): Promise<void> {
    await this.initPromise;
  }
 
  private async loadConfig(): Promise<void> {
    try {
      if (existsSync(CONFIG_FILE)) {
        const content = await readFile(CONFIG_FILE, 'utf-8');
        this.config = JSON.parse(content);
      } else {
        await this.saveConfig();
      }
    } catch (error) {
      console.error('Failed to load global config:', error);
      this.config = {};
    }
  }
 
  private async saveConfig(): Promise<void> {
    try {
      Iif (process.env.NODE_ENV !== 'test') {
        await writeFile(
          CONFIG_FILE,
          JSON.stringify(this.config, null, 2),
          'utf-8',
        );
      }
    } catch (error) {
      console.error('Failed to save global config:', error);
    }
  }
 
  getConfig(): GlobalConfig {
    return { ...this.config };
  }
 
  async updateConfig(updates: Partial<GlobalConfig>): Promise<GlobalConfig> {
    const cleanUpdates = Object.fromEntries(
      Object.entries(updates).filter(([_, v]) => v !== undefined),
    );
 
    this.config = { ...this.config, ...cleanUpdates };
    await this.saveConfig();
    return this.getConfig();
  }
 
  get<K extends keyof GlobalConfig>(key: K): GlobalConfig[K] | undefined {
    return this.config[key];
  }
 
  // For testing: set config directly
  _setConfig(config: GlobalConfig): void {
    this.config = { ...config };
  }
}