Enhance sandbox environment setup for VNC and Python compatibility
- Ensure the existence of $HOME/.Xauthority for PyAutoGUI/Xlib compatibility. - Adjust PYTHONPATH to maintain access to user-installed Python packages. - Explicitly set XAUTHORITY to the correct path for X11 authentication. - Pass secrets as environment variables for Claude CLI to ensure proper access. - Refactor VNC image support check to utilize a centralized keyword list for better maintainability.
This commit is contained in:
parent
95cdf7961c
commit
ce4864cfde
3 changed files with 45 additions and 7 deletions
|
|
@ -121,6 +121,10 @@ func BuildCommandWithContinuation(messages []agentContext.Message, opts *Options
|
|||
// System prompt may contain quotes, newlines, special characters that break shell quoting
|
||||
var bashCmd strings.Builder
|
||||
|
||||
// Ensure $HOME/.Xauthority exists for PyAutoGUI/Xlib (HOME=/workspace).
|
||||
// Xvfb runs without auth, but Xlib requires the file to exist.
|
||||
bashCmd.WriteString("touch /home/sandbox/.Xauthority 2>/dev/null; touch \"$HOME/.Xauthority\" 2>/dev/null\n")
|
||||
|
||||
// If we have a system prompt (first request only), write it to a temp file via heredoc first
|
||||
// then use --append-system-prompt-file
|
||||
if systemPrompt != "" {
|
||||
|
|
@ -306,10 +310,30 @@ func buildEnvironment(opts *Options, systemPrompt string) map[string]string {
|
|||
// Session data is stored in $HOME/.claude/ (i.e., /workspace/.claude/)
|
||||
env["HOME"] = "/workspace"
|
||||
|
||||
// Fix Python user-site-packages: changing HOME from /home/sandbox to /workspace
|
||||
// breaks Python's ability to find packages installed via pip --user (e.g., playwright,
|
||||
// pyautogui, playwright-stealth) which live in /home/sandbox/.local/lib/pythonX.Y/site-packages/
|
||||
env["PYTHONPATH"] = "/home/sandbox/.local/lib/python3.12/site-packages"
|
||||
|
||||
// Fix X11 auth: PyAutoGUI/Xlib looks for $HOME/.Xauthority, but HOME=/workspace
|
||||
// so it fails to find /home/sandbox/.Xauthority created during image build.
|
||||
// Explicitly set XAUTHORITY to the correct path.
|
||||
env["XAUTHORITY"] = "/home/sandbox/.Xauthority"
|
||||
|
||||
// claude-proxy runs on localhost:3456, Claude CLI connects to it
|
||||
env["ANTHROPIC_BASE_URL"] = "http://127.0.0.1:3456"
|
||||
env["ANTHROPIC_API_KEY"] = "dummy" // Proxy doesn't verify this
|
||||
|
||||
// Pass secrets as environment variables for Claude CLI to use
|
||||
// These are configured in package.yao sandbox.secrets (e.g., LLM_API_KEY, GITHUB_TOKEN)
|
||||
// start-claude-proxy also exports them for the proxy process, but Claude CLI
|
||||
// is launched via a separate docker exec, so it needs them passed explicitly here.
|
||||
if len(opts.Secrets) > 0 {
|
||||
for k, v := range opts.Secrets {
|
||||
env[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Note: System prompt and max_turns are passed via CLI flags in BuildCommand
|
||||
// CLAUDE_SYSTEM_PROMPT environment variable is NOT supported by Claude CLI
|
||||
// --append-system-prompt or --system-prompt flags must be used instead
|
||||
|
|
|
|||
|
|
@ -1103,14 +1103,13 @@ func (e *Executor) GetVNCUrl() string {
|
|||
return ""
|
||||
}
|
||||
|
||||
// Check if the image supports VNC (playwright or desktop variants)
|
||||
imageName := e.opts.Image
|
||||
if imageName == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
// VNC is only available for playwright and desktop images
|
||||
if !strings.Contains(imageName, "playwright") && !strings.Contains(imageName, "desktop") {
|
||||
// Check if the image supports VNC using the shared keyword list in sandbox package
|
||||
if !infraSandbox.IsVNCImage(imageName) {
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,10 @@ import (
|
|||
"github.com/yaoapp/yao/sandbox/ipc"
|
||||
)
|
||||
|
||||
// vncImageKeywords lists image name keywords that indicate VNC support.
|
||||
// Add new keywords here when adding new VNC-capable sandbox images.
|
||||
var vncImageKeywords = []string{"playwright", "desktop", "chrome"}
|
||||
|
||||
// execReadCloser wraps a Reader with a Closer
|
||||
type execReadCloser struct {
|
||||
*bufio.Reader
|
||||
|
|
@ -324,9 +328,15 @@ func (m *Manager) createContainer(ctx context.Context, opts CreateOptions) (*Con
|
|||
CapDrop: []string{"ALL"},
|
||||
}
|
||||
|
||||
// Chrome/browser images need SYS_ADMIN for namespace-based process isolation.
|
||||
// Without it, Chrome renderer/GPU processes crash with error code 5.
|
||||
if IsVNCImage(image) {
|
||||
hostConfig.CapAdd = []string{"SYS_ADMIN"}
|
||||
}
|
||||
|
||||
// VNC port mapping for Docker Desktop (macOS/Windows)
|
||||
// Only enable for VNC-capable images (playwright/desktop) when config is enabled
|
||||
if m.config.VNCPortMapping && isVNCImage(image) {
|
||||
if m.config.VNCPortMapping && IsVNCImage(image) {
|
||||
// Expose VNC ports in container config
|
||||
containerConfig.ExposedPorts = nat.PortSet{
|
||||
"6080/tcp": struct{}{}, // noVNC websockify
|
||||
|
|
@ -952,9 +962,14 @@ func (m *Manager) fixIPCSocketPermissions(ctx context.Context, containerID strin
|
|||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
|
||||
// isVNCImage checks if the image is VNC-capable (playwright or desktop variants)
|
||||
func isVNCImage(imageName string) bool {
|
||||
return strings.Contains(imageName, "playwright") || strings.Contains(imageName, "desktop")
|
||||
// IsVNCImage checks if the image is VNC-capable based on vncImageKeywords.
|
||||
func IsVNCImage(imageName string) bool {
|
||||
for _, kw := range vncImageKeywords {
|
||||
if strings.Contains(imageName, kw) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// findAvailablePort finds an available port on the host
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue