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
  }
}
相关推荐
遇见~未来6 小时前
第四篇_强化视觉_字体_动画_变换
前端·css3
He少年6 小时前
【Cursor 工程rules实际感悟】
人工智能·ai编程
开开心心_Every6 小时前
图片转PDF合并工具,支持扫描仪输入
运维·前端·人工智能·随机森林·edge·pdf·逻辑回归
盼小辉丶6 小时前
TextIn xParse Skill上架ClawHub,补齐Agent“读文档”短板
文档解析·openclaw·xparse-parser
Mr -老鬼6 小时前
EasyClick 安卓CLI全栈专家能力手册
android·自动化·ai编程·easyclick·易点云测
旺财矿工6 小时前
高效搭建:OpenClaw 2.6.6 Windows 11 一键安装教程
人工智能·自动化·ai自动化·openclaw·小龙虾
spencer_tseng6 小时前
openclaw_2026.04.09_2
ai·openclaw
垦利不6 小时前
TS基础篇
开发语言·前端·typescript