本文档写给运维001,确保新初始化的数字生命容器能正确上报交付物。 读完这篇,你就知道每个新容器启动后该检查什么、配在哪里。
每台容器需要一个语义唯一名,格式:{主人名}-{角色}
| 容器 | AGENT_ID 示例 |
|---|---|
| 刘雨飏 主容器 | agent-node |
| 刘雨飏 雨飏001 容器 | agent-node-tuye |
| 芯葶 容器 | agent-node-xinting |
存放位置(优先级从高到低):
| 位置 | 命令/文件 | 说明 |
|---|---|---|
| 环境变量 | export FMODE_AGENT_ID=agent-node |
最优先,重构建容不失 |
| 环境变量 | export AGENT_ID=agent-node |
兼容旧脚本 |
| 配置文件 | ~/.fmode/config.json → agentId |
持久化,被 env 覆盖 |
| 项目配置 | <cwd>/.fmode/config.json → agentId |
项目级覆盖 |
推荐做法:echo 'export FMODE_AGENT_ID=agent-node' >> ~/.bashrc
⚠️ 不要依赖 hostname。Docker 容器重启后 hostname 变随机 hash(如
6ec71e98949e),云函数会判404 agent not found。
SessionToken 是用户登录后生成的凭据,控制 ACL(访问权限)。
存放位置:
| 位置 | 文件 | 说明 |
|---|---|---|
| 环境变量 | FMODE_SESSION_TOKEN |
最优 |
| 用户配置 | ~/.fmode/config/user.json → sessionToken |
FmodeAgent / Hermes Agent、Fmode Studio 自动维护 |
| 旧配置 | ~/.fmode/config.json → sessionToken |
兼容旧版本 |
推荐做法:登录一次 FMODE Studio 后,token 自动写入 user.json。 如 env 缺失,技能会从配置文件兜底读取。
# 查看当前容器身份
skill-deliverable check
# 成功输出示例:
# {
# "agentId": "agent-node",
# "sessionToken": "r:852951...",
# "status": "ready",
# "hostname": "6ec71e98949e" // 仅显示,不用于上报
# }
在 fmode-studio 的 Docker 部署脚本中,新增:
# 设置 AGENT_ID
export FMODE_AGENT_ID="agent-$(hostname -s)" # 或用固定语义名
# 确认 sessionToken 有来源
if [ -z "$FMODE_SESSION_TOKEN" ] && [ -f "$HOME/.fmode/config/user.json" ]; then
export FMODE_SESSION_TOKEN=$(python3 -c "import json;print(json.load(open('$HOME/.fmode/config/user.json'))['sessionToken'])")
fi
# 运行检查
npx --yes skill-deliverable@latest check
→ FmodeAgent 表里没有这个 agentId 的记录。
FmodeAgent 表创建同名记录→ session 用户不是这个 agent 的 owner。
A: 重建后 AGENT_ID 丢失 → 重设 env FMODE_AGENT_ID。skill-deliverable 已做 hostname 兜底但语义名丢失时 hostname 会变导致 404。必须持久化 AGENT_ID。
A: 每个容器设不同的 AGENT_ID,各自上报各自的。FmodeAgent 表以 agentId 区分。
A: 可以。skill-deliverable report --title "测试" --project test。失败看 /tmp/agent-deliverable-report.log。
| 版本 | 日期 | 变更 |
|---|---|---|
| 0.0.1 | 2026-09-23 | 初版:分离 fmode-hub 独立成技能 + 云函数 v2 改良 |
本平台 /api/functions 的契约是:代码里定义 function handler(request, response),
可用的全局变量是 Parse / Psql / request / response / params / user;
鉴权信息挂在 request.user / request.company / request.account。
仓库里的 cloud/deliverable-report-v2.js 用的是 Parse.Cloud.define('deliverable-report', ...),
直接写进 Function 记录会跑不起来。平台形态的正确版本见
cloud/deliverable-report-v2.platform.js(已按此上线到 lfYlgU7SkK,version 0.0.2)。
Function 记录即生效executor 每次调用都从库里查 Function 记录(无内存缓存),所以:
Function.code(+ version / updatedAt)Parse REST 对 Function 类有 CLP 限制(masterKey 也被拒,code 119),
运维侧用 nova 库直改 "Function" 表那一行即可。
user / company 是 Parse 指针,不是字符串AgentDeliverable.user / .company 的 schema 是 Pointer,写字符串会报
schema mismatch for AgentDeliverable.user; expected Pointer<_User> but got String。
要传 Parse.User 对象(ACL 里才用 user.id 字符串)。FmodeAgent.user 同理,owner 比较要取 .id(typeof v === 'string' ? v : v.id)。容器能上报的三个前提,缺一个就 403/500:
| 前提 | 检查方法 |
|---|---|
FMODE_AGENT_ID 有值且与 FmodeAgent.agentId 一致 |
见 5.5 |
容器的 sessionToken 属于该 agent 的 owner |
用 token 查 _Session."user",必须等于 FmodeAgent."user" |
FmodeAgent 记录存在(或允许自注册) |
SELECT * FROM "FmodeAgent" WHERE "agentId"='...' |
实测踩到的坑:某容器 fmode-identity.json 里的 session_token 是别人(平台主账号)的,
且容器 env 里 FMODE_SESSION_TOKEN 是空字符串 —— 于是 owner 校验必然 403。
批量开通的容器尤其要逐个核。
fmode-identity.json 不在其中!)lib/index.mjs 的解析链:
env.FMODE_AGENT_ID → env.AGENT_ID → ~/.fmode/config.json#agentId → ./.fmode/config.json#agentId → hostname(仅兜底,会 404)env.FMODE_SESSION_TOKEN → ~/.fmode/config/user.json#sessionToken → ~/.fmode/config.json#sessionToken → ./.fmode/config.json#sessionToken注意:~ 取的是 $HOME。这些容器里 hermes 用户的 HOME 是 /opt/data(持久卷),
root 的 HOME 是 /root(容器层,重建即丢)。所以持久化的正确位置是
/opt/data/.fmode/config.json(同时给 /root/.fmode/config.json 兜底)。
bin/skill-deliverable.mjs 原稿在 ES 模块里用了 require('os'),
Node ≥ 20 会直接抛 ERR_AMBIGUOUS_MODULE_SYNTAX(top-level await + require 混用)。
已改为 import { hostname } from 'node:os'。