Refly CLI 文件命令完全解析:refly file 的 list、get、download、upload 实操与实现原理
2026/9/16 13:05:22 网站建设 项目流程

Refly CLI 文件命令完全解析:refly file 的 list、get、download、upload 实操与实现原理

【免费下载链接】reflyThe first open-source agent skills builder. Define skills by vibe workflow, run on Claude Code, Cursor, Codex & more. Build Clawdbot 🦞· APIs for Lovable · Bots for Slack & Lark/Feishu · Skills are infrastructure, not prompts.项目地址: https://gitcode.com/GitHub_Trending/re/refly

在 Refly 开源项目中,@refly/cli是让 Agent(如 Claude Code、Cursor)通过命令行编排后端工作流的入口,而refly file命令组正是 Agent 获取和回传工作流产出物(图片、文档、报表等)的关键通道。本文以 File Reference 文档 为骨架,结合 file 命令组源码 与后端 Drive CLI 控制器,逐一拆解refly file list / get / download / upload四个子命令的完整参数、底层实现链路、鉴权与错误处理机制,读完即可在自己的脚本或 Skill 中安全地拉取和上传 Refly Drive 文件。

1. 文件命令组的定位:Agent 与 Drive 之间的桥梁

refly file是 File Reference 参考文档 所描述的命令集合,在 CLI 中注册为一个独立的命令组。从 入口定义 可以看到,它聚合了四个子命令:

export const fileCommand = new Command('file') .description('Manage files and documents') .addCommand(fileListCommand) .addCommand(fileGetCommand) .addCommand(fileDownloadCommand) .addCommand(fileUploadCommand);

这四个子命令分别对应:

  • refly file list—— 分页列出 Drive 中的文件,支持按画布/执行结果过滤;
  • refly file get—— 查询单个文件的元数据,可选返回文件内容;
  • refly file download—— 将文件下载为本地文件(二进制流);
  • refly file upload—— 将本地文件或目录批量上传到指定画布(canvas)。

Refly CLI 遵循 JSON-First 设计:所有命令输出统一的结构化 JSON,Agent 只需信任okpayloaderrorhint字段即可完成自动化决策。这一约定见 SKILL.md 基础规则,也是 File Reference 中"Trust CLI JSON"理念的基础。

File Reference 文档还给出了文件命令的使用语境:文件 ID 通常来自action results(见 Node Reference)或workflow outputs(见 Workflow Reference),推荐用--result-id--canvas-id把文件列表收敛到某次具体的运行上下文中。

2.refly file list:分页列出文件并支持上下文档位过滤

File Reference 中给出的用法:

# List files refly file list [options] --page <n> # Page number (default: 1) --page-size <n> # Files per page (default: 20) --canvas-id <id> # Filter by canvas ID --result-id <id> # Filter by action result ID --include-content # Include file content in response

对照 list 命令源码,参数定义与默认值完全一致:

export const fileListCommand = new Command('list') .description('List files') .option('--page <n>', 'Page number (default: 1)', '1') .option('--page-size <n>', 'Number of files per page (default: 20)', '20') .option('--canvas-id <id>', 'Filter by canvas ID') .option('--result-id <id>', 'Filter by action result ID') .option('--include-content', 'Include file content in response')

实现上有两个值得注意的细节:

  1. 查询参数拼装pagepageSize必发,canvasIdresultIdincludeContent(值为true)按需追加,最终请求/v1/cli/drive/files?${params}(list.ts 第 35-49 行)。
  2. 后端默认值兜底:服务端在 DriveCliController.listFiles 中对查询参数做了parseInt并回退默认值(page回退 1、pageSize回退 20),includeContent仅当字面量'true'时生效。这意味着即使 CLI 侧传参异常,后端也能保证分页语义稳定。

响应结构由 CLI 侧的ListFilesResponse类型声明(list.ts 第 19-24 行):

interface ListFilesResponse { files: FileInfo[]; // fileId / name / type / size? / createdAt / updatedAt total: number; page: number; pageSize: number; }

成功时 CLI 输出ok('file.list', ...)并原样透出total/page/pageSize/files字段;失败时走统一的错误通道(见第 7 节)。

实践提示:当一次工作流运行产生多个文件时,先用refly file list --canvas-id <c-xxx>拿到该画布下所有fileId,再结合--result-id <ar-xxx>精确到某一次 action 结果,比盲目翻页效率高得多。

3.refly file get:查询文件详情,控制是否返回内容

File Reference 中的定义:

# Get file details refly file get <fileId> [options] --no-content # Exclude file content

get 命令源码 中有一个容易踩坑的 Commander 语义:--no-content反向开关,默认行为是包含内容:

.option('--no-content', 'Exclude file content from response') .action(async (fileId, options) => { const includeContent = options.content !== false; const result = await apiRequest<FileInfo>( `/v1/cli/drive/files/${fileId}?includeContent=${includeContent}`, );

也就是说:

  • refly file get <fileId>→ 请求带includeContent=true,返回含content字段的完整信息;
  • refly file get <fileId> --no-content→ 请求带includeContent=false,响应中content被省略。

这与后端 DriveCliController.getFile 的默认值一致——服务端同样以includeContent查询参数是否为'true'决定返回内容。对于只需要元数据(名称、类型、大小、时间戳)的场景,加上--no-content可以显著减小 JSON 体积,在 Agent 多轮调用时节省 token。

get返回的FileInfo类型(get.ts 第 10-18 行)在 list 的基础上多了可选的content?: string字段。

4.refly file download:流式下载与默认文件名的来源

File Reference 给出的用法:

# Download file refly file download <fileId> [options] -o, --output <path> # Output path (defaults to original filename)

"defaults to original filename" 这句承诺背后有一条完整的实现链路,值得展开:

  1. 后端以附件流返回:DriveCliController.downloadFile 调用driveService.getDriveFileStream后,设置了四个关键响应头再发送二进制数据:
res.setHeader('Content-Type', contentType || 'application/octet-stream'); res.setHeader('Content-Disposition', `attachment; filename="${encodeURIComponent(filename)}"`); res.setHeader('Content-Length', data.length.toString()); res.setHeader('Last-Modified', lastModified.toUTCString());
  1. CLI 侧用流式请求解析:apiRequestStream 与普通apiRequest共用同一套 OAuth / API Key 鉴权逻辑,但默认超时放宽到5 分钟(普通 JSON 请求为 30 秒),并会从Content-Disposition头中解析原始文件名。它同时兼容filename="name.ext"与 RFC 5987 的filename*=UTF-8''name.ext两种写法,并对解析结果做decodeURIComponent
const match = contentDisposition.match(/filename\*?=(?:UTF-8'')?["']?([^"';\n]+)["']?/i); if (match) { filename = decodeURIComponent(match[1]); }
  1. 落地写入:download 命令 按优先级确定落盘路径:-o/--output指定路径 > 响应头中的原始文件名 >fileId本身,最终通过path.resolve转为绝对路径并用fs.writeFileSync写入,成功后输出ok('file.download', { fileId, path, filename, contentType, size })

这条链路意味着:不指定-o时,中文文件名也能被正确还原(后端encodeURIComponent+ 前端decodeURIComponent的对称处理);而在脚本中批量下载时,用-o显式指定路径可以完全绕过响应头依赖,行为更确定。

SKILL.md 中的 "Pattern A: File Generation Skills" 就示范了典型用法——工作流跑完后遍历工具调用产出的文件列表,逐个执行refly file download "$FILE_ID" -o "$HOME/Desktop/${FILE_NAME}"并打开,这正是图片/视频/音频生成类技能的标准收尾动作。

5.refly file upload:预签名三步上传流程与目录过滤

File Reference 中的定义:

# Upload file(s) refly file upload <path> [options] --canvas-id <id> # Canvas ID (required) --filter <ext> # Filter by extensions (e.g., pdf,docx,png)

<path>可以是单个文件也可以是目录,--canvas-id为必填项。其实现分为"本地文件解析"与"上传协议"两层。

5.1 本地文件解析:过滤、排序与数量上限

upload 命令源码 中定义了MAX_FILES = 10,并通过 resolveFilesToUpload 处理两种输入:

  • 单文件:若给了--filter,则检查扩展名(不含点、转小写)是否在白名单中,不匹配直接返回空列表,最终以NOT_FOUND错误退出并提示 "No files matching filter";
  • 目录:读取目录下第一层的文件(不递归子目录),先剔除非文件条目,再按--filter过滤,然后按文件大小升序排序(小文件优先上传,快速出结果),最后截取前 10 个。

5.2 上传协议:presign → PUT → confirm

apiUploadDriveFile 注释明确写道 "3-step process: presign -> PUT to OSS -> confirm",对应后端两个端点(DriveCliController):

步骤请求说明
1. presignPOST /v1/cli/drive/file/upload/presign提交canvasId / filename / size / contentType,返回uploadIdpresignedUrlexpiresIn
2. PUT 存储PUT <presignedUrl>文件字节直传对象存储,带Content-TypeContent-Length,超时 5 分钟,网络错误自动重试 1 次(uploadToPresignedUrl)
3. confirmPOST /v1/cli/drive/file/upload/confirm提交uploadId,返回最终DriveFileUploadResultfileId / name / type / size / storageKey / url?

MIME 类型由mime包按扩展名推断,未知类型回退application/octet-stream(getMimeType)。

5.3 顺序上传、进度展示与部分失败语义

  • 多文件顺序串行上传,pretty 输出模式下逐阶段刷新进度:Getting upload URL...Uploading <name> (<size>)...Confirming upload...(upload.ts 第 68-89 行);
  • 单个文件失败不会中断整批:错误被收集进errors数组,批次结束后若results.length === 0则以INTERNAL_ERROR退出并附每个文件的错误明细,否则输出Uploaded X of Y file(s)的部分成功摘要(upload.ts 第 121-148 行)。

因此在脚本中判断上传结果时,应检查 JSON 中payload.uploadedpayload.failed两个计数,而不是只看进程退出码。

6. 鉴权机制:OAuth 与 API Key 双通道

所有 file 子命令的 API 调用都经由 apiRequest / apiRequestStream 发起,两者复用同一套鉴权逻辑,由getAuthMethod()决定走哪条通道:

  • API Key 模式:请求头携带X-API-Key: <apiKey>
  • OAuth 模式(默认):请求头携带Authorization: Bearer <accessToken>;若本地 token 已过期,会先调用POST /v1/auth/cli/oauth/refresh用 refresh token 换新 token 并持久化(refreshAccessToken),刷新失败则抛出Session expired, please login again

后端侧对应地,整个v1/cli/drive路由都挂载了JwtAuthGuard(DriveCliController 类定义),并从中提取登录用户作为数据隔离依据——文件列表、详情、下载、上传均只对当前用户自己的 Drive 数据可见。

使用前提:先完成npm install -g @refly/clirefly login(见 CLI README),可用refly status验证连接与认证状态。

7. 统一输出契约与错误处理:让 Agent 可机读

File Reference 面向的读者不仅是人,更是执行 SKILL.md 规则的 Agent,因此输出契约值得单独说明。所有 file 子命令成功时调用ok(type, payload)、失败时调用fail(code, message, ...),这两者定义在 output.ts:

  • 成功格式{ ok: true, type: 'file.list' | 'file.get' | 'file.download' | 'file.upload', version: '1.0', payload: {...} },退出码 0;
  • 错误格式{ ok: false, type: 'error', error: { code, message, details?, hint?, suggestedFix?, recoverable? } },其中recoverable标记该错误是否可通过调整参数重试同一命令(如INVALID_INPUTTIMEOUTRATE_LIMIT属可恢复,见 isRecoverableError)。

错误码到退出码的映射(getExitCode):

错误类别退出码
认证类(AUTH_*2
参数校验(VALIDATION_*/INVALID_INPUT3
网络 / 超时(NETWORK_*/TIMEOUT4
未找到(*_NOT_FOUND/NOT_FOUND5
其他1

服务端 HTTP 状态还会被 mapAPIError 细化映射:401/403 →AuthError,404 →NOT_FOUND(hint: "Check the resource ID"),409 →CONFLICT,422 →INVALID_INPUT,5xx →API_ERROR。这对脚本化很有价值:refly file get拿错 ID 会得到退出码 5,与"网络不通"(退出码 4)在自动化分支中可以明确区分。

8. 与工作流的协作:fileId 从哪里来

回到 File Reference 的 Interaction 一节,完整的取文件闭环是:

  1. 运行技能/工作流refly skill run --id <installationId> --input '<json>'返回RUN_IDwe-xxx前缀);
  2. 等待完成refly workflow status <runId> --watch
  3. 拿到文件列表refly workflow toolcalls <runId> --files --latest直接返回最近一次工具调用的files数组(含fileIddf-xxx前缀);
  4. 下载refly file download <fileId> -o <path>
  5. 回传输入:反向流程则用refly file upload <path> --canvas-id <c-xxx>把本地材料挂到画布,供后续工作流节点引用。

各类 ID 的前缀约定在 SKILL.md 的 ID Types 表 中有明确登记:df-xxx专用于file downloadwe-xxx用于 workflow 命令,c-xxx仅出现在浏览器 URL 中——这也是 File Reference 强调"不要伪造 ID"(No fabricated IDs)的原因,所有 ID 都应从 CLI JSON 输出中提取。

9. 速查小结

命令关键参数底层端点适用场景
refly file list--page--page-size--canvas-id--result-id--include-contentGET /v1/cli/drive/files盘点画布/某次运行产出的文件
refly file get <fileId>--no-content(默认含内容)GET /v1/cli/drive/files/:fileId读取文本类文件内容或仅取元数据
refly file download <fileId>-o, --output(默认原始文件名)GET /v1/cli/drive/files/:fileId/download把二进制产物落到本地
refly file upload <path>--canvas-id(必填)、--filterPOST .../file/upload/presignPUTPOST .../file/upload/confirm上传单文件或目录(≤10 个,小文件优先)

以上全部内容均可在当前仓库中追溯验证:命令参考见 file.md,命令实现见 packages/cli/src/commands/file/,客户端传输逻辑见 packages/cli/src/api/client.ts,服务端路由见 apps/api/src/modules/drive/drive-cli.controller.ts。掌握了这四个命令及其 JSON 契约,就能在任意 Agent 环境中稳定地完成 Refly 工作流"产出文件 → 拉取到本地 → 输入文件 → 回传到画布"的双向文件交换。

【免费下载链接】reflyThe first open-source agent skills builder. Define skills by vibe workflow, run on Claude Code, Cursor, Codex & more. Build Clawdbot 🦞· APIs for Lovable · Bots for Slack & Lark/Feishu · Skills are infrastructure, not prompts.项目地址: https://gitcode.com/GitHub_Trending/re/refly

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询