refactor: Rework sandbox tool registration logic, simplify sandbox state directory paths, and update default container image and prefix.

This commit is contained in:
0x5487 2026-02-22 18:49:43 +08:00
parent 658cf6a1be
commit 549195406e
6 changed files with 43 additions and 31 deletions

View file

@ -59,30 +59,42 @@ func NewAgentInstance(
restrict := defaults.RestrictToWorkspace restrict := defaults.RestrictToWorkspace
sb := sandbox.NewFromConfigWithAgent(workspace, restrict, cfg, agentID) sb := sandbox.NewFromConfigWithAgent(workspace, restrict, cfg, agentID)
readSb := sb isSandboxOff := true
if !sandbox.IsToolSandboxEnabled(cfg, "read_file") { if cfg != nil {
readSb = nil mode := strings.ToLower(strings.TrimSpace(cfg.Agents.Defaults.Sandbox.Mode))
if mode == "all" || mode == "non-main" {
isSandboxOff = false
} }
writeSb := sb
if !sandbox.IsToolSandboxEnabled(cfg, "write_file") {
writeSb = nil
}
execSb := sb
if !sandbox.IsToolSandboxEnabled(cfg, "exec") {
execSb = nil
} }
roContainer := isContainerReadOnlySandbox(cfg) roContainer := isContainerReadOnlySandbox(cfg)
toolsRegistry := tools.NewToolRegistry() toolsRegistry := tools.NewToolRegistry()
toolsRegistry.Register(tools.NewReadFileToolWithSandbox(workspace, restrict, readSb))
if !roContainer { // Helper to check if tool is allowed (either sandbox is off or policy allows it)
toolsRegistry.Register(tools.NewWriteFileToolWithSandbox(workspace, restrict, writeSb)) isAllowed := func(toolName string) bool {
return isSandboxOff || sandbox.IsToolSandboxEnabled(cfg, toolName)
} }
if isAllowed("read_file") {
toolsRegistry.Register(tools.NewReadFileToolWithSandbox(workspace, restrict, sb))
}
if !roContainer && isAllowed("write_file") {
toolsRegistry.Register(tools.NewWriteFileToolWithSandbox(workspace, restrict, sb))
}
if isAllowed("list_dir") {
toolsRegistry.Register(tools.NewListDirTool(workspace, restrict)) toolsRegistry.Register(tools.NewListDirTool(workspace, restrict))
toolsRegistry.Register(tools.NewExecToolWithSandbox(workspace, restrict, cfg, execSb)) }
if isAllowed("exec") {
toolsRegistry.Register(tools.NewExecToolWithSandbox(workspace, restrict, cfg, sb))
}
if !roContainer { if !roContainer {
if isAllowed("edit_file") {
toolsRegistry.Register(tools.NewEditFileTool(workspace, restrict)) toolsRegistry.Register(tools.NewEditFileTool(workspace, restrict))
}
if isAllowed("append_file") {
toolsRegistry.Register(tools.NewAppendFileTool(workspace, restrict)) toolsRegistry.Register(tools.NewAppendFileTool(workspace, restrict))
} }
}
sessionsDir := filepath.Join(workspace, "sessions") sessionsDir := filepath.Join(workspace, "sessions")
sessionsManager := session.NewSessionManager(sessionsDir) sessionsManager := session.NewSessionManager(sessionsDir)

View file

@ -73,7 +73,7 @@ func NewContainerSandbox(cfg ContainerSandboxConfig) *ContainerSandbox {
cfg.Image = "openclaw-sandbox:bookworm-slim" cfg.Image = "openclaw-sandbox:bookworm-slim"
} }
if strings.TrimSpace(cfg.ContainerPrefix) == "" { if strings.TrimSpace(cfg.ContainerPrefix) == "" {
cfg.ContainerPrefix = "picoclaw-sandbox-" cfg.ContainerPrefix = "picoclaw-sbx-"
} }
if strings.TrimSpace(cfg.ContainerName) == "" { if strings.TrimSpace(cfg.ContainerName) == "" {
cfg.ContainerName = cfg.ContainerPrefix + "default" cfg.ContainerName = cfg.ContainerPrefix + "default"
@ -375,7 +375,7 @@ func (c *ContainerSandbox) registryPath() string {
} }
func (c *ContainerSandbox) sandboxStateDir() string { func (c *ContainerSandbox) sandboxStateDir() string {
return filepath.Join(resolvePicoClawHomeDir(), "state", "sandbox") return filepath.Join(resolvePicoClawHomeDir(), "sandbox")
} }
func resolvePicoClawHomeDir() string { func resolvePicoClawHomeDir() string {

View file

@ -270,7 +270,7 @@ func TestContainerSandbox_RegistryPath_UsesSandboxStateDir(t *testing.T) {
Workspace: "/tmp/ws", Workspace: "/tmp/ws",
WorkspaceRoot: "/tmp/sbx", WorkspaceRoot: "/tmp/sbx",
}) })
want := filepath.Join(home, ".picoclaw", "state", "sandbox", "containers.json") want := filepath.Join(home, ".picoclaw", "sandbox", "containers.json")
if got := sb.registryPath(); got != want { if got := sb.registryPath(); got != want {
t.Fatalf("registryPath = %q, want %q", got, want) t.Fatalf("registryPath = %q, want %q", got, want)
} }
@ -280,7 +280,7 @@ func TestContainerSandbox_RegistryPath_UsesPicoClawHomeOverride(t *testing.T) {
picoHome := t.TempDir() picoHome := t.TempDir()
t.Setenv("PICOCLAW_HOME", picoHome) t.Setenv("PICOCLAW_HOME", picoHome)
sb := NewContainerSandbox(ContainerSandboxConfig{}) sb := NewContainerSandbox(ContainerSandboxConfig{})
want := filepath.Join(picoHome, "state", "sandbox", "containers.json") want := filepath.Join(picoHome, "sandbox", "containers.json")
if got := sb.registryPath(); got != want { if got := sb.registryPath(); got != want {
t.Fatalf("registryPath = %q, want %q", got, want) t.Fatalf("registryPath = %q, want %q", got, want)
} }

View file

@ -269,7 +269,7 @@ func (m *scopedSandboxManager) pruneOnce(ctx context.Context) error {
return nil return nil
} }
regPath := filepath.Join(resolvePicoClawHomeDir(), "state", "sandbox", defaultSandboxRegistryFile) regPath := filepath.Join(resolvePicoClawHomeDir(), "sandbox", defaultSandboxRegistryFile)
registryMu.Lock() registryMu.Lock()
data, err := loadRegistry(regPath) data, err := loadRegistry(regPath)
registryMu.Unlock() registryMu.Unlock()

View file

@ -90,7 +90,7 @@ func TestScopedSandboxManager_PruneLoopLifecycle(t *testing.T) {
func TestScopedSandboxManager_PruneOnceLoadRegistryError(t *testing.T) { func TestScopedSandboxManager_PruneOnceLoadRegistryError(t *testing.T) {
home := t.TempDir() home := t.TempDir()
t.Setenv("HOME", home) t.Setenv("HOME", home)
stateDir := filepath.Join(home, ".picoclaw", "state", "sandbox") stateDir := filepath.Join(home, ".picoclaw", "sandbox")
if err := os.MkdirAll(stateDir, 0o755); err != nil { if err := os.MkdirAll(stateDir, 0o755); err != nil {
t.Fatalf("mkdir state dir: %v", err) t.Fatalf("mkdir state dir: %v", err)
} }

View file

@ -23,8 +23,8 @@ func DefaultConfig() *Config {
WorkspaceAccess: "none", WorkspaceAccess: "none",
WorkspaceRoot: "~/.picoclaw/sandboxes", WorkspaceRoot: "~/.picoclaw/sandboxes",
Docker: AgentSandboxDockerConfig{ Docker: AgentSandboxDockerConfig{
Image: "debian:bookworm-slim", Image: "openclaw-sandbox:bookworm-slim",
ContainerPrefix: "picoclaw-sandbox-", ContainerPrefix: "picoclaw-sbx-",
Workdir: "/workspace", Workdir: "/workspace",
ReadOnlyRoot: true, ReadOnlyRoot: true,
Tmpfs: []string{"/tmp", "/var/tmp", "/run"}, Tmpfs: []string{"/tmp", "/var/tmp", "/run"},