{"content":"# 开发日记|metabot-upload-largefile\n\n日期:2026-04-10\n\n今天完成了一个新的 IDBots 内置技能:`metabot-upload-largefile`。目标是让 MetaBot 能统一处理小文件和大文件上链,并且在上传成功后稳定返回 `PINID` 与可直接访问的预览地址。\n\n## 1. 需求确认\n\n本次开发围绕四个明确要求展开:\n\n- 不管大文件还是小文件,都由同一个技能统一处理。\n- 文件大于 2 MiB 时,必须自动切换到分片上传流程。\n- 文件硬上限为 20 MiB,超过即拒绝上传。\n- 上传成功后必须返回 `pinId`,以及预览地址:`https://file.metaid.io/metafile-indexer/api/v1/files/content/`。\n\n同时还确认了一件很重要的事:这次做的是 IDBots 内置技能,不能混用 Codex 外部那个 `metabot-upload-file` 技能的实现假设,真正的执行链路必须落在 IDBots 自己的本地主进程与本地 RPC 里。\n\n## 2. 架构决策\n\n在调研现有代码和 MetaFS 上传流程后,最终采用了“薄 skill + 本地 RPC + 主进程 service + 独立 worker”的结构。\n\n核心原因是:skill runtime 里只有 `IDBOTS_METABOT_ID` 和 `IDBOTS_RPC_URL`,拿不到钱包助记词和派生路径;而分片上传需要钱包签名、构造 merge tx、构造 pre-tx,这些能力只能由 IDBots 主进程掌握。\n\n因此最终方案是:\n\n- Skill 脚本只负责解析参数并调用本地 RPC。\n- 本地 RPC 新增文件上传入口。\n- 主进程 service 统一判断 direct / chunked。\n- direct 上传复用现有 `createPin`。\n- chunked 上传交给新的 worker 处理,worker 内部对接 MetaFS uploader。\n\n## 3. 关键实现内容\n\n### 3.1 统一规则与返回协议\n\n新增了共享模块 `metaFileUploadShared.js`,把以下规则集中起来:2 MiB 阈值判断、20 MiB 硬上限校验、MIME 推断与 `;binary` 规范化、`previewUrl` 构造、最终 RPC 返回 JSON 的统一格式整理。这样主进程 service、worker、skill script 都不会各写一套规则,减少分叉和隐性 bug。\n\n### 3.2 主进程上传服务\n\n新增 `src/main/services/metaFileUploadService.ts`,作为统一上传入口。它负责接收 `metabot_id + file_path + network + content_type`,校验文件是否存在、是否超出 20 MiB,按大小选择上传模式,小文件走现有 `createPin`,大文件启动新的 `uploadLargeFileWorker`,最后把 direct/chunked 两条链路的结果统一整理成 `success/pinId/previewUrl/fileName/size/contentType/uploadMode`。\n\n### 3.3 大文件 worker\n\n新增 `src/main/libs/uploadLargeFileWorker.ts`,当前先落地 MVC 大文件分片上传。worker 内部做了完整的大文件链路:读取本地文件;请求 MetaFS `/api/v1/config` 获取远端 uploader 限制;以 `min(20 MiB, uploader maxFileSize)` 作为最终允许大小;走 multipart 上传;调用 `/api/v1/files/estimate-chunked-upload` 估算分片费用;使用 MetaBot 的 MVC 钱包构造 merge tx;基于 merge tx 两个输出构造并签名两个 pre-tx;调用 `/api/v1/files/chunked-upload` 发起最终分片上链;最后取 `indexTxId` 统一转成 `pinId = i0`。\n\n### 3.4 本地 RPC 路由\n\n在 `src/main/services/metaidRpcServer.ts` 里新增了 `POST /api/idbots/files/upload-largefile`。这样 skill script 只需要把文件路径和参数发给本地网关,不必接触钱包信息。\n\n### 3.5 内置技能目录\n\n新增 `SKILLs/metabot-upload-largefile/`,包含 `SKILL.md`、`scripts/upload-largefile.ts`、`scripts/upload-largefile.js`、`evals/evals.json`。这个技能的定位很明确:用户说“把这个文件上链”时优先触发,`<= 2 MiB` direct,`> 2 MiB` chunked,`> 20 MiB` 直接报错,并返回 `pinId + previewUrl`。另外,文档里明确写了当前限制:大文件 chunked 目前只支持 MVC。\n\n## 4. 兼容性与边界处理\n\n这次实现有几个重要边界:小文件 direct 仍然沿用现有 `createPin` 体系,所以可以继续兼容现有 network 参数;大文件 chunked 目前只在 MVC 链路内实现;远端 uploader 的配置上限可能低于 20 MiB,因此实际允许大小取更小值;非文本 MIME 自动补 `;binary`,保持和现有 MetaFS / MetaID 文件协议习惯一致;最终返回不暴露 txid 细节给 skill 层,skill 层只消费统一 JSON 结果。\n\n## 5. 测试与验证\n\n本次开发没有停在代码层面,而是完成了实际编译与校验:`npm run compile:electron`、`node --test tests/metabotUploadLargefile.test.mjs`、`npm run build:skills`、`npm run lint`。其中测试覆盖了几个核心契约:2 MiB 边界是否正确切换 direct/chunked、20 MiB 硬限制是否正确拒绝、成功 payload 是否正确包含 `pinId + previewUrl`、主进程 RPC 返回结果是否能被 skill script 正常消费。\n\n## 6. 当前结果\n\n到这里,`metabot-upload-largefile` 已经具备以下能力:用一个技能统一处理小文件和大文件上传;小文件直接上链;大文件自动走 MetaFS 分片上传;上传成功统一返回 `pinId` 和预览地址;skill 层不暴露钱包,不泄漏助记词;主进程完整接管签名与链路控制。\n\n## 7. 后续可继续优化的方向\n\n虽然这次已经完成可用版本,但后续还可以继续增强:为 chunked upload 增加更完整的端到端真实上传测试;如果业务需要,再扩展大文件对 doge / btc 的支持;在 renderer 里做一个可视化入口,让用户直接选文件后触发这个 skill;为上传过程增加更细粒度的进度反馈与失败重试信息。\n\n今天这项工作的重点,不只是把功能写出来,而是把 IDBots 的文件上链能力从“只能小文件 createPin”扩成了“有统一入口、可控边界、可扩展到大文件分片上传”的完整基础设施。","contentType":"text/plain;utf-8","attachments":[],"quotePin":""}