OpenClaw工具拆解之sandboxed_write+sandboxed_edit
一、sandboxed_write 工具
1.1 工具概述
功能:沙盒中写入文件
核心特性:
-
• 通过 bridge 写入隔离文件系统 -
• 自动创建父目录 -
• 支持沙盒根目录限制 -
• 参数标准化包装
1.2 创建函数
位置:第 115006 行
functioncreateSandboxedWriteTool(params) {
returnwrapToolParamNormalization(createWriteTool(params.root, {
operations: createSandboxWriteOperations(params)
}), CLAUDE_PARAM_GROUPS.write);
}
1.3 沙盒写入操作
位置:第 115068 行
functioncreateSandboxWriteOperations(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 参数标准化
constCLAUDE_PARAM_GROUPS = {
write: [
{
keys: ["path", "file_path", "filePath", "file"],
label: "path alias"
},
{
keys: ["content"],
label: "content"
}
]
};
constCLAUDE_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 返回结果格式
成功:
{
"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 行
functioncreateSandboxedEditTool(params) {
returnwrapToolParamNormalization(
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 行
functioncreateSandboxEditOperations(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
})) {
throwcreateFsAccessError("ENOENT", absolutePath);
}
}
};
}
2.4 编辑恢复包装
位置:第 114462 行
functionwrapEditToolWithRecovery(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) : void0;
// 3. 读取原始内容
let originalContent;
if (absolutePath && newText !== void0) {
try {
originalContent = await options.readFile(absolutePath);
} catch {}
}
// 4. 执行编辑
try {
returnawait 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 !== void0) {
if (didEditLikelyApply({
originalContent,
currentContent,
oldText,
newText
})) {
returnbuildEditSuccessResult(pathParam ?? absolutePath);
}
}
// 7. 添加不匹配提示
if (typeof currentContent === "string" &&
err instanceofError &&
shouldAddMismatchHint(err)) {
throwappendMismatchHint(err, currentContent);
}
throw err;
}
}
};
}
2.5 参数标准化
constCLAUDE_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
}
]
};
constCLAUDE_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 功能定位
|
|
|
|
| 用途 |
|
|
| 写操作 |
|
|
| 恢复机制 |
|
|
3.2 参数支持
|
|
|
|
| 参数别名 |
|
|
| 必填参数 |
|
|
| 可选参数 |
|
|
3.3 安全限制
|
|
|
|
| 沙盒根目录 |
|
|
| 桥接访问 |
|
|
| 路径边界 |
|
|
四、沙盒工具系列总结
4.1 三个沙盒工具
|
|
|
|
| sandboxed_read |
|
|
| sandboxed_write |
|
|
| sandboxed_edit |
|
|
4.2 共同特点
-
• 通过 bridge访问隔离文件系统 -
• 不能直接访问 host 文件系统 -
• 用于容器/Docker/沙盒环境 -
• 支持 modelContextWindowTokens限制 -
• 支持图片 sanitization
4.3 使用场景
沙盒模式启用时:
├─ 读取文件 → sandboxed_read
├─ 写入文件 → sandboxed_write
└─ 编辑文件 → sandboxed_edit
非沙盒模式时:
├─ 读取文件 → read
├─ 写入文件 → write
└─ 编辑文件 → edit
五、使用示例
5.1 sandboxed_write 工具调用
用户:在沙盒中创建文件
大模型返回:
{
"tool_call":{
"name":"sandboxed_write",
"arguments":{
"path":"src/main.js",
"content":"console.log(\"Hello\");"
}
}
}
执行结果:
{
"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 工具调用
用户:修改沙盒中的文件
大模型返回:
{
"tool_call":{
"name":"sandboxed_edit",
"arguments":{
"path":"src/main.js",
"oldText":"console.log(\"Hello\");",
"newText":"console.log(\"Hello, World!\");"
}
}
}
执行结果:
{
"content":[{
"type":"text",
"text":"Successfully edited sandbox:src/main.js"
}],
"details":{
"path":"src/main.js",
"sandbox":true
}
}
夜雨聆风