OpenClaw工具拆解之sandboxed_write+sandboxed_edit

一、sandboxed_write 工具

1.1 工具概述

功能 :沙盒中写入文件
核心特性

  • 通过 bridge 写入隔离文件系统
  • 自动创建父目录
  • 支持沙盒根目录限制
  • 参数标准化包装

1.2 创建函数

位置:第 115006 行

javascript 复制代码
function createSandboxedWriteTool(params) {
    return wrapToolParamNormalization(createWriteTool(params.root, { 
        operations: createSandboxWriteOperations(params) 
    }), CLAUDE_PARAM_GROUPS.write);
}

1.3 沙盒写入操作

位置:第 115068 行

javascript 复制代码
function createSandboxWriteOperations(params) {
    return {
        // 1. 创建目录
        mkdir: async (dir) => {
            await params.bridge.mkdirp({
                filePath: dir,
                cwd: params.root
            });
        },
        
        // 2. 写入文件
        writeFile: async (absolutePath, content) => {
            await params.bridge.writeFile({
                filePath: absolutePath,
                cwd: params.root,
                data: content
            });
        }
    };
}

1.4 包装链

复制代码
createWriteTool (外部库)
    ↓ (使用沙盒操作)
wrapToolParamNormalization (参数标准化)
    ↓ (CLAUDE_PARAM_GROUPS.write)
最终工具

1.5 参数标准化

javascript 复制代码
const CLAUDE_PARAM_GROUPS = {
    write: [
        {
            keys: ["path", "file_path", "filePath", "file"],
            label: "path alias"
        },
        {
            keys: ["content"],
            label: "content"
        }
    ]
};

const CLAUDE_PARAM_ALIASES = [
    { original: "path", alias: "file_path" },
    { original: "path", alias: "filePath" },
    { original: "path", alias: "file" }
];

1.6 执行流程图

复制代码
sandboxed_write 工具调用
    ↓
1. 参数标准化
   ├─ path/file_path/filePath/file → path
   └─ content → content
    ↓
2. 验证必填参数
    ↓
3. 沙盒桥接写入
    ├─ bridge.mkdirp(创建父目录)
    └─ bridge.writeFile(写入文件)
    ↓
4. 返回结果

1.7 返回结果格式

成功

json 复制代码
{
  "content": [{
    "type": "text",
    "text": "Successfully wrote 123 bytes to sandbox:src/main.js"
  }],
  "details": {
    "path": "src/main.js",
    "bytesWritten": 123,
    "sandbox": true
  }
}

二、sandboxed_edit 工具

2.1 工具概述

功能 :沙盒中编辑文件
核心特性

  • 通过 bridge 访问隔离文件系统
  • 支持编辑失败恢复
  • 支持参数别名
  • 精确文本替换

2.2 创建函数

位置:第 115009 行

javascript 复制代码
function createSandboxedEditTool(params) {
    return wrapToolParamNormalization(
        wrapEditToolWithRecovery(createEditTool(params.root, { 
            operations: createSandboxEditOperations(params) 
        }), {
            root: params.root,
            readFile: async (absolutePath) => (await params.bridge.readFile({
                filePath: absolutePath,
                cwd: params.root
            })).toString("utf8")
        }), 
        CLAUDE_PARAM_GROUPS.edit
    );
}

2.3 沙盒编辑操作

位置:第 115077 行

javascript 复制代码
function createSandboxEditOperations(params) {
    return {
        // 1. 读取文件
        readFile: (absolutePath) => params.bridge.readFile({
            filePath: absolutePath,
            cwd: params.root
        }),
        
        // 2. 写入文件
        writeFile: (absolutePath, content) => params.bridge.writeFile({
            filePath: absolutePath,
            cwd: params.root,
            data: content
        }),
        
        // 3. 访问检查
        access: async (absolutePath) => {
            if (!await params.bridge.stat({
                filePath: absolutePath,
                cwd: params.root
            })) {
                throw createFsAccessError("ENOENT", absolutePath);
            }
        }
    };
}

2.4 编辑恢复包装

位置:第 114462 行

javascript 复制代码
function wrapEditToolWithRecovery(base, options) {
    return {
        ...base,
        execute: async (toolCallId, params, signal, onUpdate) => {
            // 1. 读取参数
            const { pathParam, oldText, newText } = readEditToolParams(params);
            
            // 2. 解析绝对路径
            const absolutePath = typeof pathParam === "string" ? 
                resolveEditPath(options.root, pathParam) : void 0;
            
            // 3. 读取原始内容
            let originalContent;
            if (absolutePath && newText !== void 0) {
                try {
                    originalContent = await options.readFile(absolutePath);
                } catch {}
            }
            
            // 4. 执行编辑
            try {
                return await base.execute(toolCallId, params, signal, onUpdate);
            } catch (err) {
                // 5. 编辑失败处理
                if (!absolutePath) throw err;
                
                let currentContent;
                try {
                    currentContent = await options.readFile(absolutePath);
                } catch {}
                
                // 6. 判断编辑是否已应用
                if (typeof currentContent === "string" && newText !== void 0) {
                    if (didEditLikelyApply({
                        originalContent,
                        currentContent,
                        oldText,
                        newText
                    })) {
                        return buildEditSuccessResult(pathParam ?? absolutePath);
                    }
                }
                
                // 7. 添加不匹配提示
                if (typeof currentContent === "string" && 
                    err instanceof Error && 
                    shouldAddMismatchHint(err)) {
                    throw appendMismatchHint(err, currentContent);
                }
                
                throw err;
            }
        }
    };
}

2.5 参数标准化

javascript 复制代码
const CLAUDE_PARAM_GROUPS = {
    edit: [
        {
            keys: ["path", "file_path", "filePath", "file"],
            label: "path alias"
        },
        {
            keys: ["oldText", "old_string", "old_text", "oldString"],
            label: "oldText alias"
        },
        {
            keys: ["newText", "new_string", "new_text", "newString"],
            label: "newText alias",
            allowEmpty: true
        }
    ]
};

const CLAUDE_PARAM_ALIASES = [
    // path 别名
    { original: "path", alias: "file_path" },
    { original: "path", alias: "filePath" },
    { original: "path", alias: "file" },
    
    // oldText 别名
    { original: "oldText", alias: "old_string" },
    { original: "oldText", alias: "old_text" },
    { original: "oldText", alias: "oldString" },
    
    // newText 别名
    { original: "newText", alias: "new_string" },
    { original: "newText", alias: "new_text" },
    { original: "newText", alias: "newString" }
];

2.6 执行流程图

复制代码
sandboxed_edit 工具调用
    ↓
1. 参数标准化
   ├─ path 别名处理
   ├─ oldText 别名处理
   └─ newText 别名处理
    ↓
2. 验证必填参数
    ↓
3. 读取原始内容
    ↓
4. 执行编辑(精确替换)
    ↓
5. 编辑失败处理
   ├─ 读取当前内容
   ├─ 判断是否部分成功
   └─ 添加不匹配提示
    ↓
6. 返回结果

三、关键机制对比

3.1 功能定位

特性 sandboxed_write sandboxed_edit
用途 创建/覆盖文件 精确修改文件
写操作 覆盖写入 精确替换
恢复机制 不支持 支持

3.2 参数支持

特性 sandboxed_write sandboxed_edit
参数别名 4 个(path) 9 个(path/oldText/newText)
必填参数 path, content path, oldText
可选参数 newText(可为空)

3.3 安全限制

限制类型 sandboxed_write sandboxed_edit
沙盒根目录 root 限制 root 限制
桥接访问 必需 必需
路径边界 bridge 路径 bridge 路径

四、沙盒工具系列总结

4.1 三个沙盒工具

工具 功能 桥接操作
sandboxed_read 读取文件 bridge.readFile, bridge.stat, detectMime
sandboxed_write 写入文件 bridge.mkdirp, bridge.writeFile
sandboxed_edit 编辑文件 bridge.readFile, bridge.writeFile, bridge.stat

4.2 共同特点

  • 通过 bridge 访问隔离文件系统
  • 不能直接访问 host 文件系统
  • 用于容器/Docker/沙盒环境
  • 支持 modelContextWindowTokens 限制
  • 支持图片 sanitization

4.3 使用场景

复制代码
沙盒模式启用时:
├─ 读取文件 → sandboxed_read
├─ 写入文件 → sandboxed_write
└─ 编辑文件 → sandboxed_edit

非沙盒模式时:
├─ 读取文件 → read
├─ 写入文件 → write
└─ 编辑文件 → edit

五、使用示例

5.1 sandboxed_write 工具调用

用户在沙盒中创建文件

大模型返回

json 复制代码
{
  "tool_call": {
    "name": "sandboxed_write",
    "arguments": { 
      "path": "src/main.js",
      "content": "console.log(\"Hello\");"
    }
  }
}

执行结果

json 复制代码
{
  "content": [{
    "type": "text",
    "text": "Successfully wrote 27 bytes to sandbox:src/main.js"
  }],
  "details": {
    "path": "src/main.js",
    "bytesWritten": 27,
    "sandbox": true
  }
}

5.2 sandboxed_edit 工具调用

用户修改沙盒中的文件

大模型返回

json 复制代码
{
  "tool_call": {
    "name": "sandboxed_edit",
    "arguments": { 
      "path": "src/main.js",
      "oldText": "console.log(\"Hello\");",
      "newText": "console.log(\"Hello, World!\");"
    }
  }
}

执行结果

json 复制代码
{
  "content": [{
    "type": "text",
    "text": "Successfully edited sandbox:src/main.js"
  }],
  "details": {
    "path": "src/main.js",
    "sandbox": true
  }
}
相关推荐
孟健19 小时前
光会写提示词,用不好 AI Agent
ai编程
Elastic 中国社区官方博客20 小时前
快 12 倍的 Elasticsearch 向量索引:使用 GPU 和 CPU 分层部署 NVIDIA cuVS
大数据·人工智能·elasticsearch·搜索引擎·ai·全文检索·nvidia
环信20 小时前
2026年开发者选择即时通讯厂商应注意的几点
前端
绝知此事20 小时前
2026 AI 技术生态全景指南:从 LLM 到 Agent,从 MCP 到 A2A
人工智能·ai·ai编程
卷帘依旧20 小时前
Generator 全面解析 + async/await 深度对比
前端·javascript
Karl_wei20 小时前
AI Harness 简易版建设
openai·agent·ai编程
yqcoder20 小时前
数据劫持的双雄:深入解析 Object.defineProperty 与 Proxy
开发语言·前端·javascript
lichenyang45320 小时前
鸿蒙聊天 Demo 练习 03:接入 Next.js 后端接口,实现真机前后端联调
前端
Canicer20 小时前
【国内安装 Claude Code CLI版本纯净完整指南】
ai·agent·claude code
小三金21 小时前
EXPO+RN echarts图表库,以及如何使用
前端·javascript·react.js