diff --git a/sandbox/config.go b/sandbox/config.go index 4b544a67..b73b5542 100644 --- a/sandbox/config.go +++ b/sandbox/config.go @@ -16,16 +16,22 @@ type Config struct { 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: /tmp/yao.sock } // 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, + Image: "yaoapp/sandbox-claude:latest", + MaxContainers: 100, + IdleTimeout: 30 * time.Minute, + MaxMemory: "2g", + MaxCPU: 1.0, + ContainerWorkDir: "/workspace", + ContainerIPCSocket: "/tmp/yao.sock", } } @@ -91,4 +97,17 @@ func (c *Config) Init(dataRoot string) { } // 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 = "/tmp/yao.sock" + } } diff --git a/sandbox/manager.go b/sandbox/manager.go index a6bda668..37238c2c 100644 --- a/sandbox/manager.go +++ b/sandbox/manager.go @@ -48,6 +48,14 @@ func NewManager(config *Config) (*Manager, error) { config = DefaultConfig() } + // Apply defaults for missing container paths + if config.ContainerWorkDir == "" { + config.ContainerWorkDir = "/workspace" + } + if config.ContainerIPCSocket == "" { + config.ContainerIPCSocket = "/tmp/yao.sock" + } + // Initialize Docker client cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) if err != nil { @@ -153,18 +161,23 @@ func (m *Manager) createContainer(ctx context.Context, userID, chatID string) (* containerConfig := &container.Config{ Image: m.config.Image, Cmd: []string{"sleep", "infinity"}, - WorkingDir: "/workspace", + WorkingDir: m.config.ContainerWorkDir, Env: []string{ - "YAO_IPC_SOCKET=/tmp/yao.sock", + "YAO_IPC_SOCKET=" + m.config.ContainerIPCSocket, }, } - // Host configuration + // Host configuration - only mount IPC socket if it exists + binds := []string{ + workspaceHost + ":" + m.config.ContainerWorkDir, + } + // Only mount IPC socket if the file exists (it's created by IPC manager) + if _, err := os.Stat(ipcSocketHost); err == nil { + binds = append(binds, ipcSocketHost+":"+m.config.ContainerIPCSocket) + } + hostConfig := &container.HostConfig{ - Binds: []string{ - workspaceHost + ":/workspace", - ipcSocketHost + ":/tmp/yao.sock", - }, + Binds: binds, Resources: container.Resources{ Memory: parseMemory(m.config.MaxMemory), NanoCPUs: int64(m.config.MaxCPU * 1e9), @@ -342,7 +355,7 @@ func (m *Manager) Exec(ctx context.Context, name string, cmd []string, opts *Exe // Default options if opts.WorkDir == "" { - opts.WorkDir = "/workspace" + opts.WorkDir = m.config.ContainerWorkDir } // Create exec instance @@ -544,9 +557,22 @@ func (m *Manager) WriteFile(ctx context.Context, name, path string, content []by // Ensure parent directory exists dir := filepath.Dir(path) if dir != "/" && dir != "." { - if _, err := m.Exec(ctx, name, []string{"mkdir", "-p", dir}, nil); err != nil { + result, err := m.Exec(ctx, name, []string{"mkdir", "-p", dir}, nil) + if err != nil { return fmt.Errorf("failed to create parent directory: %w", err) } + if result.ExitCode != 0 { + return fmt.Errorf("mkdir failed with exit code %d: %s", result.ExitCode, result.Stdout) + } + + // Verify directory was created + verifyResult, err := m.Exec(ctx, name, []string{"test", "-d", dir}, nil) + if err != nil { + return fmt.Errorf("failed to verify directory: %w", err) + } + if verifyResult.ExitCode != 0 { + return fmt.Errorf("directory %s was not created", dir) + } } // Create a tar archive with the file