Overview
The config module provides environment-based configuration loading with comprehensive validation and sensible defaults. All orchestrator settings are centralized here.
Location: packages/orchestrator/src/config.ts
Main Functions
loadConfig()
Loads and validates configuration from environment variables.
function loadConfig(): OrchestratorConfig
Returns: Complete OrchestratorConfig object
Throws: Error if required environment variables are missing or invalid
getConfig()
Returns cached config, loading it if not already loaded.
function getConfig(): OrchestratorConfig
Returns: Cached OrchestratorConfig
clearConfigCache()
Clears the configuration cache.
function clearConfigCache(): void
Useful for testing or dynamic reconfiguration.
Configuration Types
OrchestratorConfig
Main configuration interface extending HarnessConfig from @longshot/core.
interface OrchestratorConfig extends HarnessConfig {
targetRepoPath: string // Local path to target repository
pythonPath: string // Path to Python interpreter
healthCheckInterval: number // Monitor tick interval (seconds)
readinessTimeoutMs: number // LLM readiness probe timeout (ms)
finalization: FinalizationConfig
}
FinalizationConfig
interface FinalizationConfig {
maxAttempts: number // Max finalization sweep attempts
enabled: boolean // Whether to run finalization phase
sweepTimeoutMs: number // Max time to wait for fix tasks
}
LLMConfig
Extracted from HarnessConfig:
type LLMConfig = OrchestratorConfig['llm']
interface LLMConfig {
endpoints: LLMEndpoint[]
model: string
maxTokens: number
temperature: number
timeoutMs?: number
}
Environment Variables
Required
Array of LLM endpoint configurations. Format:[
{
"name": "primary",
"endpoint": "http://localhost:8000",
"apiKey": "optional-key",
"weight": 70
}
]
Fallback if LLM_ENDPOINTS not provided. Single endpoint URL.
Target repository URL (e.g., https://github.com/user/repo.git)
Optional - LLM
API key for single-endpoint mode (used with LLM_BASE_URL)
Maximum tokens per completion
Sampling temperature (0.0 - 2.0)
Request timeout in milliseconds (2 minutes)
Startup readiness probe timeout (0 = skip probe)
Optional - Git
Prefix for worker branches
MERGE_STRATEGY
'fast-forward' | 'rebase' | 'merge-commit'
default:"rebase"
Primary merge strategy
Optional - Orchestrator
Maximum concurrent workers (min: 1)
Worker timeout in seconds (30 minutes)
TARGET_REPO_PATH
string
default:"./target-repo"
Local path to target repository
Path to Python interpreter
Monitor tick interval in seconds
Optional - Sandbox
Sandbox container image tag
Memory per sandbox in MB (8 GB)
Idle timeout in seconds (5 minutes)
Optional - Finalization
Whether to run finalization phase (set to “false” to disable)
FINALIZATION_MAX_ATTEMPTS
Maximum finalization sweep attempts (min: 1)
FINALIZATION_SWEEP_TIMEOUT_MS
Max time to wait for fix tasks in milliseconds (2 minutes)
Optional - Logging
LOG_LEVEL
'debug' | 'info' | 'warn' | 'error'
default:"info"
Logging verbosity
Validation & Defaults
Endpoint Parsing
function parseEndpoints(): LLMEndpoint[]
Priority:
LLM_ENDPOINTS (JSON array)
LLM_BASE_URL (single endpoint, backwards compatible)
Validation:
- At least one endpoint required
- Each endpoint must have
name, endpoint, and numeric weight
- URLs normalized: trailing slashes removed,
/v1 suffix stripped
Example:
export LLM_ENDPOINTS='[
{"name":"primary","endpoint":"http://localhost:8000/v1/","weight":70},
{"name":"backup","endpoint":"http://backup:8000","weight":30}
]'
Normalized to:
[
{ name: 'primary', endpoint: 'http://localhost:8000', weight: 70 },
{ name: 'backup', endpoint: 'http://backup:8000', weight: 30 }
]
Number Parsing
function parseNumberWithDefault(
raw: string | undefined,
fallback: number,
envName: string
): number
Parses and validates numeric environment variables:
- Returns fallback if undefined or invalid
- Logs warning on invalid values
- Never throws
Clamping
function clampMin(value: number, min: number, envName: string): number
Ensures value is at least min:
const maxWorkers = clampMin(
parseNumberWithDefault(process.env.MAX_WORKERS, 50, 'MAX_WORKERS'),
1,
'MAX_WORKERS'
)
// MAX_WORKERS=0 → clamped to 1 (with warning)
Positive Enforcement
function ensurePositiveOrDefault(
value: number,
fallback: number,
envName: string
): number
Returns fallback if value <= 0.
Usage Example
Environment File (.env)
# Required
GIT_REPO_URL=https://github.com/user/my-project.git
LLM_ENDPOINTS='[{"name":"local","endpoint":"http://localhost:8000","weight":100}]'
# Optional overrides
MAX_WORKERS=20
WORKER_TIMEOUT=3600
LLM_MODEL=gpt-4
LLM_MAX_TOKENS=32768
MERGE_STRATEGY=rebase
FINALIZATION_MAX_ATTEMPTS=5
TypeScript Usage
import { loadConfig, getConfig } from '@longshot/orchestrator'
// Load on startup
const config = loadConfig()
console.log(`Max workers: ${config.maxWorkers}`)
console.log(`LLM model: ${config.llm.model}`)
console.log(`Endpoints: ${config.llm.endpoints.map(e => e.name).join(', ')}`)
console.log(`Merge strategy: ${config.mergeStrategy}`)
console.log(`Finalization enabled: ${config.finalization.enabled}`)
// Later, get cached config
const cached = getConfig() // Same instance
// Testing: clear cache between tests
import { clearConfigCache } from '@longshot/orchestrator'
afterEach(() => {
clearConfigCache()
})
Merge Strategy Validation
const ALLOWED_MERGE_STRATEGIES = ['fast-forward', 'rebase', 'merge-commit'] as const
type MergeStrategy = typeof ALLOWED_MERGE_STRATEGIES[number]
const mergeStrategy = process.env.MERGE_STRATEGY || 'rebase'
if (!ALLOWED_MERGE_STRATEGIES.includes(mergeStrategy)) {
throw new Error(`Invalid MERGE_STRATEGY: ${mergeStrategy}`)
}
Error Handling
Missing Required Vars:
if (!gitRepoUrl) {
throw new Error('Missing required env: GIT_REPO_URL')
}
Invalid JSON:
try {
parsedRaw = JSON.parse(endpointsJson)
} catch (error) {
throw new Error(`Invalid JSON in LLM_ENDPOINTS: ${error.message}`)
}
Invalid Structure:
if (!isEndpointEnvConfig(ep)) {
throw new Error('Each LLM_ENDPOINTS item must contain name, endpoint, and numeric weight')
}
Best Practices
Use Environment Files:
# Load .env automatically in main.ts
import { config as loadDotenv } from 'dotenv'
loadDotenv({ path: resolve(process.cwd(), '.env') })
Validate Early:
// Load config at application startup to fail fast
const config = loadConfig()
logger.info('Config loaded successfully', { maxWorkers: config.maxWorkers })
Use Typed Access:
// TypeScript provides autocomplete and type checking
const timeout: number = config.workerTimeout
const strategy: MergeStrategy = config.mergeStrategy
Clear Cache in Tests:
import { clearConfigCache } from '@longshot/orchestrator'
afterEach(() => {
clearConfigCache() // Prevent test pollution
})