132 lines
5.2 KiB
Python
132 lines
5.2 KiB
Python
"""Agent configuration via environment variables."""
|
|
|
|
import socket
|
|
from functools import lru_cache
|
|
from typing import Optional
|
|
|
|
from pydantic import Field
|
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
|
|
from app import __version__
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
"""Agent settings loaded from environment variables.
|
|
|
|
All settings are frozen after initialization to prevent runtime mutation.
|
|
"""
|
|
|
|
model_config = SettingsConfigDict(
|
|
env_file=".env",
|
|
env_file_encoding="utf-8",
|
|
frozen=True, # Prevent runtime mutation
|
|
)
|
|
|
|
# Agent identity
|
|
agent_version: str = Field(default=__version__, description="Agent version for API headers")
|
|
hostname: str = Field(default_factory=socket.gethostname, description="Agent hostname")
|
|
agent_id: Optional[str] = Field(default=None, description="Assigned by orchestrator after registration")
|
|
|
|
# New secure registration (recommended)
|
|
registration_token: Optional[str] = Field(
|
|
default=None,
|
|
description="Registration token from orchestrator. Required for first-time registration."
|
|
)
|
|
|
|
# Agent credentials (set after registration, persisted to disk)
|
|
agent_secret: Optional[str] = Field(
|
|
default=None,
|
|
description="Agent secret for authentication. Set after registration."
|
|
)
|
|
|
|
# Tenant assignment (derived from registration token, or can be set directly for legacy)
|
|
tenant_id: Optional[str] = Field(
|
|
default=None,
|
|
description="Tenant UUID this agent belongs to. Set after registration."
|
|
)
|
|
|
|
# Orchestrator connection
|
|
# Default URL is for Docker-based dev where orchestrator runs on the host.
|
|
# When running directly on a Linux tenant server, set ORCHESTRATOR_URL to
|
|
# the orchestrator's public URL (e.g., "https://orchestrator.letsbe.io").
|
|
orchestrator_url: str = Field(
|
|
default="http://host.docker.internal:8000",
|
|
description="Orchestrator API base URL"
|
|
)
|
|
|
|
# Legacy auth (deprecated - use registration_token + agent_secret instead)
|
|
agent_token: Optional[str] = Field(
|
|
default=None,
|
|
description="[DEPRECATED] Legacy authentication token. Use agent_secret instead."
|
|
)
|
|
|
|
# Timing intervals (seconds)
|
|
heartbeat_interval: int = Field(default=30, ge=5, le=300, description="Heartbeat interval")
|
|
poll_interval: int = Field(default=5, ge=1, le=60, description="Task polling interval")
|
|
|
|
# Logging
|
|
log_level: str = Field(default="INFO", description="Log level (DEBUG, INFO, WARNING, ERROR)")
|
|
log_json: bool = Field(default=True, description="Output logs as JSON")
|
|
|
|
# Resilience
|
|
max_concurrent_tasks: int = Field(default=3, ge=1, le=10, description="Max concurrent task executions")
|
|
backoff_base: float = Field(default=1.0, ge=0.1, le=10.0, description="Base backoff time in seconds")
|
|
backoff_max: float = Field(default=60.0, ge=10.0, le=300.0, description="Max backoff time in seconds")
|
|
circuit_breaker_threshold: int = Field(default=5, ge=1, le=20, description="Consecutive failures to trip breaker")
|
|
circuit_breaker_cooldown: int = Field(default=300, ge=30, le=900, description="Cooldown period in seconds")
|
|
|
|
# Security - File operations
|
|
allowed_file_root: str = Field(default="/opt/letsbe", description="Root directory for file operations")
|
|
allowed_env_root: str = Field(default="/opt/letsbe/env", description="Root directory for ENV file operations")
|
|
max_file_size: int = Field(default=10 * 1024 * 1024, description="Max file size in bytes (default 10MB)")
|
|
|
|
# Security - Shell operations
|
|
shell_timeout: int = Field(default=60, ge=5, le=600, description="Default shell command timeout")
|
|
|
|
# Security - Docker operations
|
|
allowed_compose_paths: list[str] = Field(
|
|
default=["/opt/letsbe", "/home/letsbe"],
|
|
description="Allowed directories for compose files"
|
|
)
|
|
allowed_stacks_root: str = Field(
|
|
default="/opt/letsbe/stacks",
|
|
description="Root directory for Docker stack operations"
|
|
)
|
|
|
|
# Local persistence
|
|
pending_results_path: str = Field(
|
|
default="~/.letsbe-agent/pending_results.json",
|
|
description="Path for buffering unsent task results"
|
|
)
|
|
credentials_path: str = Field(
|
|
default="~/.letsbe-agent/credentials.json",
|
|
description="Path for persisting agent credentials after registration"
|
|
)
|
|
|
|
# Playwright browser automation
|
|
playwright_artifacts_dir: str = Field(
|
|
default="/opt/letsbe/playwright-artifacts",
|
|
description="Directory for screenshots, traces, and other browser artifacts"
|
|
)
|
|
playwright_default_timeout_ms: int = Field(
|
|
default=60000, ge=5000, le=300000,
|
|
description="Default timeout for Playwright actions in milliseconds"
|
|
)
|
|
playwright_navigation_timeout_ms: int = Field(
|
|
default=120000, ge=10000, le=300000,
|
|
description="Timeout for page navigation in milliseconds"
|
|
)
|
|
mcp_service_url: Optional[str] = Field(
|
|
default=None,
|
|
description="URL for Playwright MCP sidecar service (for exploratory mode)"
|
|
)
|
|
|
|
|
|
@lru_cache
|
|
def get_settings() -> Settings:
|
|
"""Get cached settings instance.
|
|
|
|
Settings are loaded once and cached for the lifetime of the process.
|
|
"""
|
|
return Settings()
|