Skip to main content
Glama
config.ts6.01 kB
import * as fs from "node:fs"; import * as path from "node:path"; import * as yaml from "js-yaml"; import { logger } from "./logger.js"; import type { LogLevel, ModelConfig, ServerConfig } from "./types.js"; type RawConfig = Record<string, unknown>; /** * Resolve environment variable references in config values * Supports syntax like "${ENV_VAR_NAME}" */ function resolveEnvVars(value: unknown): unknown { if (typeof value === "string") { return value.replace(/\$\{([^}]+)\}/g, (_match, varName: string) => { const envValue = process.env[varName]; if (!envValue) { throw new Error(`Environment variable not found: ${varName}`); } return envValue; }); } if (typeof value === "object" && value !== null) { if (Array.isArray(value)) { return value.map((v) => resolveEnvVars(v)); } return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, resolveEnvVars(v)])); } return value; } /** * Find config file location */ function getConfigPath(): string | null { // Check if custom path is provided const customPath = process.env.POLYBRAIN_CONFIG_PATH; if (customPath) { return customPath; } // Check project-local config const localPath = path.join(process.cwd(), ".polybrain.yaml"); if (fs.existsSync(localPath)) { return localPath; } // Check user home config const homeDir = process.env.HOME || process.env.USERPROFILE || ""; const homePath = path.join(homeDir, ".polybrain.yaml"); if (fs.existsSync(homePath)) { return homePath; } return null; } /** * Load config from YAML file */ function loadConfigFile(): RawConfig | null { const configPath = getConfigPath(); if (!configPath) { return null; } try { const content = fs.readFileSync(configPath, "utf-8"); const config = yaml.load(content) as RawConfig; logger.debug("Loaded config from file", { configPath }); return config; } catch (error) { logger.error( "Failed to load config file", error instanceof Error ? error : new Error(String(error)) ); throw error; } } /** * Create a simple single-model config from environment variables */ function createSimpleConfig(): ServerConfig { const baseUrl = process.env.POLYBRAIN_BASE_URL; const apiKey = process.env.POLYBRAIN_API_KEY; const modelName = process.env.POLYBRAIN_MODEL_NAME; if (!baseUrl || !apiKey || !modelName) { throw new Error( "When using simple environment variable config, POLYBRAIN_BASE_URL, POLYBRAIN_API_KEY, and POLYBRAIN_MODEL_NAME must all be set" ); } const httpPort = process.env.POLYBRAIN_HTTP_PORT ? Number(process.env.POLYBRAIN_HTTP_PORT) : 32701; const truncateLimit = process.env.POLYBRAIN_TRUNCATE_LIMIT ? Number(process.env.POLYBRAIN_TRUNCATE_LIMIT) : 500; const logLevel = (process.env.POLYBRAIN_LOG_LEVEL || "info") as LogLevel; logger.debug("Using simple environment variable config", { modelName }); return { models: [ { id: modelName, // Use modelName as ID in simple mode modelName, baseUrl, apiKey, }, ], httpPort, truncateLimit, logLevel, }; } /** * Parse and validate config from YAML file */ function parseYamlConfig(rawConfig: RawConfig): ServerConfig { const models = rawConfig.models as unknown[]; if (!Array.isArray(models) || models.length === 0) { throw new Error("Config must have at least one model in the 'models' array"); } const parsedModels: ModelConfig[] = models.map((model) => { if (typeof model !== "object" || model === null) { throw new Error("Each model must be an object"); } const m = model as Record<string, unknown>; const id = m.id as string | undefined; const modelName = m.modelName as string | undefined; const baseUrl = m.baseUrl as string | undefined; const apiKey = m.apiKey as string | undefined; const provider = m.provider as string | undefined; if (!id) throw new Error("Model must have 'id' field"); if (!modelName) throw new Error("Model must have 'modelName' field for API calls"); if (!baseUrl) throw new Error("Model must have 'baseUrl' field"); if (!apiKey) throw new Error("Model must have 'apiKey' field"); // Validate provider if provided if (provider && !["openai", "openrouter"].includes(provider)) { throw new Error(`Invalid provider '${provider}'. Must be 'openai' or 'openrouter'`); } return { id, modelName, baseUrl, apiKey, provider: provider as "openai" | "openrouter" | undefined, }; }); const httpPort = rawConfig.httpPort ? Number(rawConfig.httpPort) : 32701; const truncateLimit = rawConfig.truncateLimit ? Number(rawConfig.truncateLimit) : 500; const logLevel = (rawConfig.logLevel || "info") as LogLevel; return { models: parsedModels, httpPort, truncateLimit, logLevel, }; } /** * Load configuration with precedence: * 1. Simple env vars (POLYBRAIN_BASE_URL, POLYBRAIN_API_KEY, POLYBRAIN_MODEL_NAME) * 2. YAML config file * 3. Error if neither available */ export function loadConfig(): ServerConfig { // Check if simple env var config is complete const hasSimpleEnvConfig = process.env.POLYBRAIN_BASE_URL && process.env.POLYBRAIN_API_KEY && process.env.POLYBRAIN_MODEL_NAME; if (hasSimpleEnvConfig) { logger.info("Using simple environment variable configuration"); return createSimpleConfig(); } // Try to load YAML config const rawConfig = loadConfigFile(); if (rawConfig) { logger.info("Using YAML configuration file"); // Resolve env var references in config const resolvedConfig = resolveEnvVars(rawConfig) as RawConfig; return parseYamlConfig(resolvedConfig); } // No configuration found throw new Error( "No configuration found. Set POLYBRAIN_BASE_URL, POLYBRAIN_API_KEY, " + "and POLYBRAIN_MODEL_NAME environment variables, or create ~/.polybrain.yaml" ); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/danielwpz/polybrain-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server