OpenClaw File Transfer 插件深度指南:节点文件读取、目录归档传输与授权策略迁移
【免费下载链接】openclawThe AI that really does things. Any OS. Any Platform. The lobster way. 🦞项目地址: https://gitcode.com/GitHub_Trending/cl/openclaw
File Transfer 是 OpenClaw 内置的文件传输插件(包名@openclaw/file-transfer),它通过专用 node 命令让 Agent 在已配对节点上读取文件、列出目录、打包拉取整个目录树并写入文件,从而绕过 bash stdout 截断限制。本文以 File Transfer 插件参考文档 为主体,结合插件源码与 CLI 参考,完整讲解四个工具的参数、默认值、授权策略模型、目录归档行为,以及升级后的权限迁移命令,帮助你完成插件的安装、配置、审计与安全加固。
插件概览:四个工具、一条 CLI、一种策略
从插件清单 openclaw.plugin.json 可以看到插件的核心契约:
| 维度 | 内容 |
|---|---|
| 插件 ID | file-transfer |
| 包名 | @openclaw/file-transfer |
| 安装方式 | OpenClaw 内置(enabledByDefault: true,onStartup激活) |
| CLI 命令 | openclaw file-transfer(含子命令approvals migrate) |
| 契约类型 | tools,暴露file_fetch、dir_list、dir_fetch、file_write四个工具 |
| 分类 | documents-files |
这四个工具彼此独立可选:允许其中一个并不会让其他工具自动可用,node 命令与路径策略仍然分别生效(详见 节点文件传输)。
插件入口 extensions/file-transfer/index.ts 在register()中完成四件事:
- 注册 CLI 命令组
file-transfer; - 注册 node invoke 策略(
registerNodeInvokePolicy),这是所有授权检查的入口; - 注册四个 Agent 工具,全部通过
createLazyTool懒加载,首次调用才真正import对应实现,降低启动开销; - 注册四个节点宿主命令
file.fetch、dir.list、dir.fetch、file.write,均标记cap: "file"、dangerous: true,走node.invoke通道执行。
核心设计理念是:文件字节通过 base64 编码后经node.invoke传输,从而绕过 bash stdout 对二进制输出的截断,单个往返可承载最大 16 MB 的二进制数据(见 descriptors.ts 中FILE_FETCH_HARD_MAX_BYTES = 16 * 1024 * 1024)。
默认拒绝:路径授权策略模型
插件的默认行为是DENY(默认拒绝)。操作者必须在配置文件(~/.openclaw/openclaw.json)的plugins.entries.file-transfer.config.nodes下显式添加策略块,否则所有文件操作在到达节点前就会被拒绝(见 policy.ts 顶部注释)。
完整配置模板如下:
{ "plugins": { "entries": { "file-transfer": { "config": { "nodes": { "<nodeId-or-displayName>": { "ask": "off", "allowReadPaths": ["~/Screenshots/**", "/tmp/**"], "allowWritePaths": ["~/Downloads/**"], "denyPaths": ["**/.ssh/**", "**/.aws/**"], "maxBytes": 16777216, "followSymlinks": false }, "*": { "ask": "on-miss" } } } } } } }节点级配置项
| 配置项 | 类型 | 默认值 | 含义 |
|---|---|---|---|
ask | "off" \| "on-miss" \| "always" | "off" | 询问模式(见下文) |
allowReadPaths | string[] | 无 | 允许读取的路径 glob 列表 |
allowWritePaths | string[] | 无 | 允许写入的路径 glob 列表 |
denyPaths | string[] | 无 | 硬拒绝的路径 glob,永远优先 |
maxBytes | number | 无(取工具默认值) | 单次传输的字节上限 |
followSymlinks | boolean | false | 是否允许跟随符号链接 |
ask 三种模式
off:静默模式(当前默认)——匹配即允许,不匹配即拒绝;on-miss:匹配则静默允许;未匹配时弹出提示征求操作者批准;always:每次调用都提示操作者(denyPaths仍然硬拒绝)。
策略匹配时支持~展开(~/Downloads/**会展开为当前用户主目录),并且节点选择支持按nodeId、displayName或通配符"*"匹配(见 policy.ts 的resolveNodePolicy)。
评估顺序(源码级)
policy.ts 中evaluateFilePolicyInternal的执行顺序,是理解授权行为的关键:
- 原始路径含
..段 → 直接拒绝:检查的是未归一化的原始字符串,防止/allowed/../etc/passwd这类字面穿越序列先匹配到/allowed/**导致字节在 realpath 事后检查前就跨过节点边界; - 没有任何
nodes配置 →NO_POLICY(拒绝且不可询问,因为操作者根本没启用); - 存在旧版正向权限且
policyVersion不是 2 →POLICY_MIGRATION_REQUIRED(拒绝,提示运行迁移命令); denyPaths命中 →POLICY_DENIED(硬拒绝,不可询问);ask: always→ 每次都询问;allowReadPaths/allowWritePaths命中 → 静默允许(matched-allow);literalGrants(精确授权记录,见下文)命中 → 允许(matched-literal),并携带期望的 canonical path 供节点侧校验;ask: on-miss未命中 → 可询问的拒绝;- 其余情况 → 硬拒绝。
精确授权(literalGrants)与符号链接防护
除操作者手写的 glob 外,策略还保存一种精确授权记录(literalGrants):每次操作者批准一次调用后,系统会记录完整的四元组——稳定的节点 ID、命令、请求路径、以及节点权威的 canonical 路径(policy.ts 的persistLiteralGrant)。这些字符串是节点侧的不透明路径,Gateway 不会对其做归一化或喂给 glob 匹配器。
followSymlinks(默认false)提供了符号链接防护:节点侧 handler 会在任何 I/O之前对请求路径(新文件写入则对其父目录)执行 realpath,若与请求路径不一致则返回SYMLINK_REDIRECT拒绝。这能阻止用户可控目录中的符号链接(如~/Downloads/evil → /etc)把看似允许的路径重定向到被禁止的 canonical 位置。在 macOS 上/var → /private/var会误伤/var/folders路径时,可显式设为true恢复"跟随 + 事后校验"的宽松行为。
四个工具详解
所有工具的参数 schema 定义在 descriptors.ts,以下参数均来自该文件。node参数接受已配对节点的 ID、显示名或 IP(由nodes status展示),不接受local、host、gateway、auto等关键字——本地工作区文件请使用本地 file/exec 工具。
file_fetch:拉取单个文件
从配对节点按绝对路径读取文件,全部字节存入 Gateway 的 file-transfer 媒体库,返回localPath与mediaId。支持图片以 image content block 返回,小文本文件(≤8 KB)以内联方式返回。典型用途:截图、照片、收据、日志、源代码。
| 参数 | 说明 |
|---|---|
node | 已配对节点 ID / 显示名 / IP |
path | 节点上的绝对路径,服务端做 canonicalize |
maxBytes | 最大读取字节数,默认 8 MB,硬上限 16 MB(单次往返) |
gatewayUrl/gatewayToken | 可选,指定 Gateway 连接 |
timeoutMs | 可选超时 |
读取到的mediaId可复用作file_write的sourceMediaId做二进制拷贝(要求节点具备写入能力)。
dir_list:目录列表
从配对节点获取目录列表(非本地工作区)。文本输出限制为 8192 UTF-8 字节,展示完整文件名、isDir标记与(可表示时的)文件大小;完整元数据保留在结构化 details 中。适合先发现远端路径,再决定读取哪些文件。
| 参数 | 说明 |
|---|---|
node | 已配对节点 |
path | 节点上的目录绝对路径 |
pageToken | 分页令牌;来自上一次dir_list调用的文本nextPageToken,配合相同的 node 和 path 使用 |
maxEntries | 每页最大条目数,默认 200,硬上限 5000 |
分页有讲究:文本分页令牌(text 的nextPageToken)与结构化令牌可能不同,传入文本的nextPageToken会从最后一个已展示条目之后继续;若第一个条目就无法表示,工具会明确报告"分页无法推进"。
dir_fetch:整目录归档拉取
将配对节点的**整个目录树(含 dotfiles 与隐藏目录)**打包为 gzip tar 归档后在 Gateway 解包。文本输出限制 8192 字节,展示rootDir、总fileCount及一段完整的relPath+ size 记录前缀;完整清单与附件元数据保留在结构化 details 中。没有分页机制,超过 16 MB(压缩后)的树会被拒绝。
| 参数 | 说明 |
|---|---|
node | 已配对节点 |
path | 节点上的目录绝对路径 |
maxBytes | 最大 gzip tar 字节数,默认 8 MB,硬上限 16 MB |
gatewayUrl/gatewayToken/timeoutMs | 可选 |
file_write:写入文件
向配对节点按绝对路径写入文件字节。采用原子写入(临时文件 + rename),默认拒绝覆盖(overwrite: true才替换),默认拒绝通过符号链接目标写入(除非策略显式允许跟随符号链接)。
| 参数 | 说明 |
|---|---|
node | 已配对节点 |
path | 节点上的写入绝对路径 |
contentBase64 | 内联字节(base64),解码后最大 16 MB |
sourceMediaId | 复用之前file_fetch保存在媒体库中的mediaId(不是本地路径,也不是其他媒体库的 ID),用于二进制拷贝 |
mimeType | 内容类型提示,不校验 |
overwrite | 是否允许覆盖已有文件,默认false |
createParents | 是否创建缺失的父目录(等价mkdir -p),默认false |
目录归档传输的边界行为
节点文件传输 对dir_fetch的归档安全做了详细说明,要点如下:
- 逐后代策略检查:file-transfer 策略会检查源树的每一个后代,包括隐式目录(归档未包含目录头时也会检查父路径);任一后代被拒绝则整个传输被拒绝,而不是过滤掉该项。5000 后代上限包含这些隐式目录,共享父路径只计一次;
- 同一解析器:归档成员身份由与解包相同的受限解析器与策略规划器校验,不使用人可读的
tar列表——这样 Unicode 与换行文件名能保留精确拼写,生产方附加的 AppleDouble 文件会被检查而非隐藏; - 严格校验仍然生效:canonical 源路径/设备/inode 绑定、字节数与 SHA-256 校验、链接/穿越/碰撞检查、解包限制全部保留;畸形归档头与目标平台不允许的文件名仍会拒绝,文件名不会被截断或"修复"以让归档通过;
- 输出落盘:每次成功的文件读取都会把字节存入 Gateway 的 file-transfer 媒体库并同时返回
localPath和mediaId(包括内联文本与图片);已读取文件会保留清洗过的文件名主干,媒体类型决定扩展名(如train.py被识别为纯文本会变成train.txt),并附加唯一后缀以区分重复读取。
CLI:openclaw file-transfer approvals migrate权限迁移
升级 OpenClaw 后,旧版正向文件传输权限(allowReadPaths/allowWritePaths中的条目)会保持不活跃,直到你逐一审查。拒绝规则、大小限制与符号链接设置在整个过程中继续生效(不会因迁移而暂时放开)。
命令形式与选项
openclaw file-transfer approvals migrate openclaw file-transfer approvals migrate --dry-run openclaw file-transfer approvals migrate --json| 选项 | 效果 |
|---|---|
--dry-run | 走完所有提示并打印计划,不写任何配置 |
--json | 以 JSON 输出未审查的权限后直接退出,不提示、不写配置 |
两个选项默认均为关闭(cli.ts)。
运行位置约束
迁移命令必须在 Gateway 主机的交互式终端中运行,因为它直接更新该主机的 file-transfer 策略(cli.ts):
- 当
gateway.mode为remote时拒绝运行; - 当 OpenClaw 配置无效时拒绝运行,需先修复配置再重试。
交互式审查流程
命令逐条列出旧权限,每项显示为<node selector> · read|write · <path>,需要为每条选择一种结局:
| 选项 | 行为 |
|---|---|
| Require exact reapproval | 移除这条含歧义的权限;下次使用时弹窗询问一次,并记录精确的节点、命令、请求路径与 canonical 目标 |
| Keep as an intentional wildcard | 保留该条目为操作者手写的 glob |
| Remove this permission | 直接删除这条正向权限 |
全部选择后,命令打印包含各结局数量的计划,以及降级提示(旧版 OpenClaw 无法读取迁移后的格式),在一次确认后才写入配置。写入成功后报告相邻配置文件备份(.bak)的路径,若无法校验备份也会明确说明(cli.ts)。
脚本化与非交互使用
--json不修改任何东西,适合放入健康检查或升级脚本:
| 场景 | 输出 | 退出码 |
|---|---|---|
| 没有需要审查的权限 | {"status":"ok","changed":false,"message":"No legacy permissions need review."} | 0 |
| 仍有权限待审查 | {"status":"needs-input","changed":false,"items":[...],"command":"openclaw file-transfer approvals migrate"} | 2 |
不带--json时,命令先检查是否有工作可做:非交互 shell 仅在仍有权限待审查时报错,并提示到终端重跑,而不是替每条权限猜测结局;非交互且无待审查项时打印同样的 no-work 消息并以 0 退出,因此重复的升级脚本可以保持安静(docs/cli/file-transfer.md)。
迁移结果与降级
迁移成功后写入新格式:policyVersion升为 2,选 "exact" 的条目进入pendingReapprovals(下一次使用时询问并记录完整四元组),选 "keep-glob" 的条目保留在allowReadPaths/allowWritePaths中(approvals-migration.ts)。
需要降级时:先还原迁移后报告的.bak文件,再启动旧版 OpenClaw——旧版无法读取迁移后的格式,还原备份同时也会恢复旧版权限语义。
底层实现原理速览
- base64 单往返传输:二进制字节 base64 编码后经
node.invoke传输,绕过 bash stdout 截断;8 MB 默认 / 16 MB 硬上限(单次往返)定义于 descriptors.ts; - 默认拒绝:无策略配置时每个调用都被拒绝,策略块必须写在
plugins.entries.file-transfer.config.nodes下(policy.ts); - 字面
..穿越拦截:匹配任何 allow/deny glob 之前先拒绝原始字符串中含..段的路径,Windows 反斜杠同样处理(policy.ts); - canonical 路径绑定:精确授权只在节点侧 canonical 路径校验通过后才持久化,符号链接默认在 I/O 前被 realpath 检查拦截(policy.ts);
- 懒加载工具:四个工具首次调用才加载实现,控制启动成本(index.ts)。
相关文档
- File Transfer 插件参考:本文主体来源;
- CLI 参考:openclaw file-transfer:完整 flag 面与退出码;
- 节点文件传输:目录归档边界、终端文件上传与媒体库细节。
【免费下载链接】openclawThe AI that really does things. Any OS. Any Platform. The lobster way. 🦞项目地址: https://gitcode.com/GitHub_Trending/cl/openclaw
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考