Add BMAD, Claude, Cursor, and OpenCode configuration directories along with AGENTS.md documentation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
168 lines
5.1 KiB
TypeScript
168 lines
5.1 KiB
TypeScript
import { readFile } from "fs/promises"
|
|
import { resolve } from "path"
|
|
|
|
/**
|
|
* Configuration for environment variable loading
|
|
*/
|
|
export interface EnvLoaderConfig {
|
|
/** Custom paths to search for .env files (relative to current working directory) */
|
|
searchPaths?: string[]
|
|
/** Whether to log when environment variables are loaded */
|
|
verbose?: boolean
|
|
/** Whether to override existing environment variables */
|
|
override?: boolean
|
|
}
|
|
|
|
/**
|
|
* Default search paths for .env files
|
|
*/
|
|
const DEFAULT_ENV_PATHS = [
|
|
'./.env',
|
|
'../.env',
|
|
'../../.env',
|
|
'../plugin/.env',
|
|
'../../../.env'
|
|
]
|
|
|
|
/**
|
|
* Load environment variables from .env files
|
|
* Searches multiple common locations for .env files and loads them into process.env
|
|
*
|
|
* @param config Configuration options
|
|
* @returns Object containing loaded environment variables
|
|
*/
|
|
export async function loadEnvVariables(config: EnvLoaderConfig = {}): Promise<Record<string, string>> {
|
|
const {
|
|
searchPaths = DEFAULT_ENV_PATHS,
|
|
verbose = false,
|
|
override = false
|
|
} = config
|
|
|
|
const loadedVars: Record<string, string> = {}
|
|
|
|
for (const envPath of searchPaths) {
|
|
try {
|
|
const fullPath = resolve(envPath)
|
|
const content = await readFile(fullPath, 'utf8')
|
|
|
|
if (verbose) {
|
|
console.log(`Checking .env file: ${envPath}`)
|
|
}
|
|
|
|
// Parse .env file content
|
|
const lines = content.split('\n')
|
|
for (const line of lines) {
|
|
const trimmed = line.trim()
|
|
if (trimmed && !trimmed.startsWith('#') && trimmed.includes('=')) {
|
|
const [key, ...valueParts] = trimmed.split('=')
|
|
const value = valueParts.join('=').trim()
|
|
|
|
// Remove quotes if present
|
|
const cleanValue = value.replace(/^["']|["']$/g, '')
|
|
|
|
if (key && cleanValue && (override || !process.env[key])) {
|
|
process.env[key] = cleanValue
|
|
loadedVars[key] = cleanValue
|
|
|
|
if (verbose) {
|
|
console.log(`Loaded ${key} from ${envPath}`)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} catch (error) {
|
|
// File doesn't exist or can't be read, continue to next
|
|
if (verbose) {
|
|
console.log(`Could not read ${envPath}: ${error.message}`)
|
|
}
|
|
}
|
|
}
|
|
|
|
return loadedVars
|
|
}
|
|
|
|
/**
|
|
* Get a specific environment variable with automatic .env file loading
|
|
*
|
|
* @param varName Name of the environment variable
|
|
* @param config Configuration options
|
|
* @returns The environment variable value or null if not found
|
|
*/
|
|
export async function getEnvVariable(varName: string, config: EnvLoaderConfig = {}): Promise<string | null> {
|
|
// First check if it's already in the environment
|
|
let value = process.env[varName]
|
|
|
|
if (!value) {
|
|
// Try to load from .env files
|
|
const loadedVars = await loadEnvVariables(config)
|
|
value = loadedVars[varName] || process.env[varName]
|
|
}
|
|
|
|
return value || null
|
|
}
|
|
|
|
/**
|
|
* Get a required environment variable with automatic .env file loading
|
|
* Throws an error if the variable is not found
|
|
*
|
|
* @param varName Name of the environment variable
|
|
* @param config Configuration options
|
|
* @returns The environment variable value
|
|
* @throws Error if the variable is not found
|
|
*/
|
|
export async function getRequiredEnvVariable(varName: string, config: EnvLoaderConfig = {}): Promise<string> {
|
|
const value = await getEnvVariable(varName, config)
|
|
|
|
if (!value) {
|
|
const searchPaths = config.searchPaths || DEFAULT_ENV_PATHS
|
|
throw new Error(`${varName} not found. Please set it in your environment or .env file.
|
|
|
|
To fix this:
|
|
1. Add to .env file: ${varName}=your_value_here
|
|
2. Or export it: export ${varName}=your_value_here
|
|
|
|
Current working directory: ${process.cwd()}
|
|
Searched paths: ${searchPaths.join(', ')}
|
|
Environment variables available: ${Object.keys(process.env).filter(k => k.includes(varName.split('_')[0])).join(', ') || 'none matching'}`)
|
|
}
|
|
|
|
return value
|
|
}
|
|
|
|
/**
|
|
* Load multiple required environment variables at once
|
|
*
|
|
* @param varNames Array of environment variable names
|
|
* @param config Configuration options
|
|
* @returns Object with variable names as keys and values as values
|
|
* @throws Error if any variable is not found
|
|
*/
|
|
export async function getRequiredEnvVariables(varNames: string[], config: EnvLoaderConfig = {}): Promise<Record<string, string>> {
|
|
const result: Record<string, string> = {}
|
|
|
|
// Load all .env files first
|
|
await loadEnvVariables(config)
|
|
|
|
// Check each required variable
|
|
for (const varName of varNames) {
|
|
const value = process.env[varName]
|
|
if (!value) {
|
|
throw new Error(`Required environment variable ${varName} not found. Please set it in your environment or .env file.`)
|
|
}
|
|
result[varName] = value
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
/**
|
|
* Utility function specifically for API keys
|
|
*
|
|
* @param apiKeyName Name of the API key environment variable
|
|
* @param config Configuration options
|
|
* @returns The API key value
|
|
* @throws Error if the API key is not found
|
|
*/
|
|
export async function getApiKey(apiKeyName: string, config: EnvLoaderConfig = {}): Promise<string> {
|
|
return getRequiredEnvVariable(apiKeyName, config)
|
|
} |