--- name: fmode-storage description: "把二进制大文件(图片/音频/视频/HTML 报告)上传到对象存储并拿到公开分享链接,本地零长期占用。**默认通道免配置密钥**:只要有登录 sessionToken,平台云函数就签发上传地址(对象键 user/<你的id>/...,公网 https://s3.fmode.cn/)。适用:(1) 报告/课件发布即分享 (2) 图片/音视频素材托管 (3) 批量上传+公开读 (4) 需要公开 URL 供转发或嵌入。" description_en: "Upload binary files (images/audio/video/HTML reports) to object storage and get public share URLs, keeping local disk clean. The default channel needs no credentials: with a login sessionToken the platform cloud function issues an upload URL (object key user//..., public https://s3.fmode.cn/). Use for publishing reports/courseware, hosting media assets, batch upload with public-read, or any scenario needing public URLs." schema_version: 0.2.0 level: system category: publishing keywords: - storage - obs - s3 - upload - cdn - share-link version: 1.1.0 author: Yuyang001 (FmodeAgent) license: MPL-2.0 copyright: "Copyright (c) 2026 未来飞马 Fmode" tags: [未来飞马, 智能体技能, 超级技能, 服务级, 数据管理, FmodeAgent, Hermes Agent, FmodeCode, Claude Code, storage, obs, s3, cdn, upload] --- # Fmode Storage — 对象存储与公开分享 ## Overview 把本地二进制文件交给对象存储,生成公开 URL 供分享、转发或嵌入。 设计原则:**本地零长期占用**;**公开即所得**;**凭据零入库**——密钥永不落盘、永不进日志、永不进对话;**诚实失败**——通道全失败时打印初始化向导并 `exit 2`,绝不伪装成功。 ## 首选用法(免配置,0 步) **不需要申请 AK/SK、不需要 init**。只要容器里有登录态 `sessionToken`(数字生命容器默认就有),直接: ```bash node /scripts/uploader.mjs put ./report.html --key report/202609/report.html # → { "ok": true, "key": "user/<你的id>/report/202609/report.html", # "url": "https://s3.fmode.cn/user/<你的id>/report/202609/report.html", # "via": "level0:cloudfunc(AlP56LCKFm)" } ``` 自检(验证这条通道是否可用): ```bash node /scripts/uploader.mjs test # → { "ok": true, "via": "level0:cloudfunc(...)", "message": "云函数免密钥通道自检成功(本机无需任何 AK/SK)" } ``` > **`--key` 给相对路径即可**,脚本/云函数会强制挂到 `user/<你的id>/` 前缀下——写不进别人的前缀。 > 省略 `--key` 则自动生成 `user/<你的id>/report//-<文件名>`。 > 上传即 `public-read`,**不需要**再调 `setacl`。 ## 凭据链(5 级,命中即用) ``` ┌─ 第0级【首选·免密钥】sessionToken → 云函数 fmodeagent-upload-url 签发预签名 PUT │ → AK/SK 由云函数保管,容器只拿 5 分钟有效的上传地址。无需任何本地配置。 │ 第1级 环境变量 OBS_AK/OBS_SK(/OBS_ENDPOINT/OBS_BUCKET) │ 第2级 obsutil config(OBSUTIL_CONFIG 或 ~/.obsutilconfig) │ 第3级 sessionToken + storageProjectId → POST /api/apig/deploy/huaweicloud │ (项目级 STS,作用域 obs://nova-cloud/dev//,非个人空间) │ 第4级 项目级 ./.fmode/config.json(obsBucket/obsEndpoint/cdnDomain) │ 全失败 → 打印初始化向导并 exit 2 └──────────────────────────────────────────────────────────────────────────┘ ``` **为什么第 0 级是首选**:第 1/2 级要每台容器各自配 OBS 密钥、第 3 级要 `storageProjectId` 且作用域是项目目录、第 4 级只有 bucket 名没有凭据——都无法做到"开箱即用"。第 0 级只依赖登录态,是唯一零配置通道。 第 1–4 级作为**兜底**保留(例如需要写 `obs://www-fmode.cn/` 之类平台桶,而不是个人空间)。 ## 何时用 - 生成了 HTML 报告/课件要"发给别人看" → `put` 后把 URL 发出 - 图片/音频/视频素材需要稳定外链 → `put` - 本地磁盘被大文件占满 → `put` 完成后可删本地原件 ## 命令 ```bash node uploader.mjs test # 自检(优先走第0级) node uploader.mjs put --key <相对key> # 上传并返回公开 URL node uploader.mjs put # 省略 --key:自动命名 node uploader.mjs put --key k --ns report node uploader.mjs setacl --key # 第0级下上传即 public-read,无需调用 node uploader.mjs config # 诊断(输出绝不含密钥本体) node uploader.mjs init --ak .. --sk .. # 仅第1/2级兜底场景需要 ``` ## 需要完整 SDK 时 ```js import { putViaCloudFunction } from './scripts/uploader.mjs'; const r = await putViaCloudFunction('/path/report.html', 'report/202609/report.html', 'report'); // r = { ok, key, url, bytes, via } ``` ## 详细文档 见仓库根 `README.md`(多工具安装:FmodeCode / Claude Code / Codex / Gemini CLI / WorkBuddy / FmodeAgent / Hermes Agent + 完整 changelog)。 ## 铁律 - ⛔ 绝不把 AK/SK、sessionToken、STS 或任何密钥写进仓库、日志、对话 - ⛔ 绝不尝试从 git 仓库/代码/文档/聊天记录中寻找他人或公司凭证 - ✅ 对象只能写自己 `user/<自己的id>/` 前缀(云函数在服务端强制)