package sandbox import ( "os" "path/filepath" "strconv" "time" ) // Config holds sandbox configuration type Config struct { Image string `json:"image,omitempty"` // Docker image, default: yao/sandbox-claude:latest WorkspaceRoot string `json:"workspace_root,omitempty"` // Host workspace root directory IPCDir string `json:"ipc_dir,omitempty"` // IPC socket directory MaxContainers int `json:"max_containers,omitempty"` // Maximum concurrent containers IdleTimeout time.Duration `json:"idle_timeout,omitempty"` // Idle timeout before stopping container MaxMemory string `json:"max_memory,omitempty"` // Memory limit, e.g., "2g" MaxCPU float64 `json:"max_cpu,omitempty"` // CPU limit, e.g., 1.0 // Container internal paths ContainerWorkDir string `json:"container_workdir,omitempty"` // Container working directory, default: /workspace ContainerIPCSocket string `json:"container_ipc_socket,omitempty"` // Container IPC socket path, default: /run/yao.sock ContainerUser string `json:"container_user,omitempty"` // Container user, default: "" (use image default). Set to "0" for root. // VNC port mapping (for Docker Desktop on macOS/Windows where container IPs are not directly accessible) VNCPortMapping bool `json:"vnc_port_mapping,omitempty"` // Enable VNC port mapping to host, default: false } // DefaultConfig returns a Config with default values func DefaultConfig() *Config { return &Config{ Image: "yaoapp/sandbox-claude:latest", MaxContainers: 100, IdleTimeout: 30 * time.Minute, MaxMemory: "2g", MaxCPU: 1.0, ContainerWorkDir: "/workspace", ContainerIPCSocket: "/run/yao.sock", } } // Init initializes the config with defaults based on environment variables and data root func (c *Config) Init(dataRoot string) { // Image if env := os.Getenv("YAO_SANDBOX_IMAGE"); env != "" { c.Image = env } else if c.Image == "" { c.Image = "yaoapp/sandbox-claude:latest" } // Workspace root if env := os.Getenv("YAO_SANDBOX_WORKSPACE"); env != "" { c.WorkspaceRoot = env } else if c.WorkspaceRoot == "" { c.WorkspaceRoot = filepath.Join(dataRoot, "sandbox", "workspace") } // IPC directory if env := os.Getenv("YAO_SANDBOX_IPC"); env != "" { c.IPCDir = env } else if c.IPCDir == "" { c.IPCDir = filepath.Join(dataRoot, "sandbox", "ipc") } // Max containers - set default first if zero, then try env override if c.MaxContainers == 0 { c.MaxContainers = 100 } if env := os.Getenv("YAO_SANDBOX_MAX"); env != "" { if v, err := strconv.Atoi(env); err == nil && v > 0 { c.MaxContainers = v } // Invalid env value: keep existing/default value } // Idle timeout - set default first if zero, then try env override if c.IdleTimeout == 0 { c.IdleTimeout = 30 * time.Minute } if env := os.Getenv("YAO_SANDBOX_IDLE_TIMEOUT"); env != "" { if v, err := time.ParseDuration(env); err == nil && v > 0 { c.IdleTimeout = v } // Invalid env value: keep existing/default value } // Max memory if env := os.Getenv("YAO_SANDBOX_MEMORY"); env != "" { c.MaxMemory = env } else if c.MaxMemory == "" { c.MaxMemory = "2g" } // Max CPU - set default first if zero, then try env override if c.MaxCPU == 0 { c.MaxCPU = 1.0 } if env := os.Getenv("YAO_SANDBOX_CPU"); env != "" { if v, err := strconv.ParseFloat(env, 64); err == nil && v > 0 { c.MaxCPU = v } // Invalid env value: keep existing/default value } // Container internal paths if env := os.Getenv("YAO_SANDBOX_CONTAINER_WORKDIR"); env != "" { c.ContainerWorkDir = env } else if c.ContainerWorkDir == "" { c.ContainerWorkDir = "/workspace" } if env := os.Getenv("YAO_SANDBOX_CONTAINER_IPC"); env != "" { c.ContainerIPCSocket = env } else if c.ContainerIPCSocket == "" { c.ContainerIPCSocket = "/run/yao.sock" } // Container user (for CI environments with UID mismatch) if env := os.Getenv("YAO_SANDBOX_CONTAINER_USER"); env != "" { c.ContainerUser = env } // VNC port mapping (for Docker Desktop on macOS/Windows) if env := os.Getenv("YAO_SANDBOX_VNC_PORT_MAPPING"); env != "" { c.VNCPortMapping = env == "true" || env == "1" || env == "yes" } }