environment.tsโข4.35 kB
/**
 * Environment configuration for HyperTool MCP
 * Supports production and test modes with configurable paths
 */
import { join } from "path";
import { homedir } from "os";
/**
 * Environment mode enumeration
 */
export enum EnvironmentMode {
  PRODUCTION = "production",
  TEST = "test",
}
/**
 * Environment configuration interface
 */
export interface EnvironmentConfig {
  mode: EnvironmentMode;
  configRoot: string;
  registryPath?: string;
  backupPath?: string;
  cachePath?: string;
}
/**
 * Environment configuration manager
 */
export class EnvironmentManager {
  private static instance: EnvironmentManager;
  private config: EnvironmentConfig;
  private constructor() {
    // Default to production mode
    this.config = this.createProductionConfig();
    // Load any environment overrides
    this.loadFromEnv();
  }
  /**
   * Get singleton instance
   */
  static getInstance(): EnvironmentManager {
    if (!EnvironmentManager.instance) {
      EnvironmentManager.instance = new EnvironmentManager();
    }
    return EnvironmentManager.instance;
  }
  /**
   * Create production configuration
   */
  private createProductionConfig(): EnvironmentConfig {
    const configRoot = join(homedir(), ".toolprint/hypertool-mcp");
    return {
      mode: EnvironmentMode.PRODUCTION,
      configRoot,
      registryPath: join(configRoot, "apps/registry.json"),
      backupPath: join(configRoot, "backups"),
      cachePath: join(configRoot, "cache"),
    };
  }
  /**
   * Create test configuration
   */
  private createTestConfig(baseDir: string): EnvironmentConfig {
    const configRoot = join(baseDir, ".toolprint/hypertool-mcp");
    return {
      mode: EnvironmentMode.TEST,
      configRoot,
      registryPath: join(configRoot, "apps/registry.json"),
      backupPath: join(configRoot, "backups"),
      cachePath: join(configRoot, "cache"),
    };
  }
  /**
   * Get current configuration
   */
  getConfig(): EnvironmentConfig {
    return { ...this.config };
  }
  /**
   * Set environment mode
   */
  setMode(mode: EnvironmentMode, testBaseDir?: string): void {
    if (mode === EnvironmentMode.TEST) {
      if (!testBaseDir) {
        throw new Error("Test base directory must be provided for test mode");
      }
      this.config = this.createTestConfig(testBaseDir);
    } else {
      this.config = this.createProductionConfig();
    }
  }
  /**
   * Override specific configuration values
   */
  override(overrides: Partial<EnvironmentConfig>): void {
    this.config = {
      ...this.config,
      ...overrides,
    };
  }
  /**
   * Check if running in test mode
   */
  isTestMode(): boolean {
    return this.config.mode === EnvironmentMode.TEST;
  }
  /**
   * Check if running in production mode
   */
  isProductionMode(): boolean {
    return this.config.mode === EnvironmentMode.PRODUCTION;
  }
  /**
   * Load configuration from environment variables
   */
  loadFromEnv(): void {
    const mode = process.env.HYPERTOOL_ENV as EnvironmentMode;
    const configRoot = process.env.HYPERTOOL_CONFIG_ROOT;
    if (mode === EnvironmentMode.TEST || mode === EnvironmentMode.PRODUCTION) {
      this.config.mode = mode;
    }
    if (configRoot) {
      this.config.configRoot = configRoot;
      // Update derived paths
      this.config.registryPath = join(configRoot, "apps/registry.json");
      this.config.backupPath = join(configRoot, "backups");
      this.config.cachePath = join(configRoot, "cache");
    }
  }
  /**
   * Reset to default configuration
   */
  reset(): void {
    this.config = this.createProductionConfig();
  }
}
/**
 * Convenience function to get environment config
 */
export function getEnvironmentConfig(): EnvironmentConfig {
  return EnvironmentManager.getInstance().getConfig();
}
/**
 * Convenience function to check if in test mode
 */
export function isTestMode(): boolean {
  return EnvironmentManager.getInstance().isTestMode();
}
/**
 * Load feature flags from config.json
 */
async function loadFeatureFlagsFromConfig(): Promise<
  Record<string, boolean> | undefined
> {
  try {
    // Dynamic import to avoid circular dependencies
    const { getFeatureFlags } = await import("./preferenceStore.js");
    return await getFeatureFlags();
  } catch (error) {
    // If we can't load from config file, return undefined
    return undefined;
  }
}