deployment-guide.md 7.1 KB

交付物上报技能 — 运维部署指南

本文档写给运维001,确保新初始化的数字生命容器能正确上报交付物。 读完这篇,你就知道每个新容器启动后该检查什么、配在哪里。


一、容器初始化检查清单

□ 1. 确定 AGENT_ID

每台容器需要一个语义唯一名,格式:{主人名}-{角色}

容器 AGENT_ID 示例
刘雨飏 主容器 agent-node
刘雨飏 雨飏001 容器 agent-node-tuye
芯葶 容器 agent-node-xinting

存放位置(优先级从高到低):

位置 命令/文件 说明
环境变量 export FMODE_AGENT_ID=agent-node 最优先,重构建容不失
环境变量 export AGENT_ID=agent-node 兼容旧脚本
配置文件 ~/.fmode/config.jsonagentId 持久化,被 env 覆盖
项目配置 <cwd>/.fmode/config.jsonagentId 项目级覆盖

推荐做法echo 'export FMODE_AGENT_ID=agent-node' >> ~/.bashrc

⚠️ 不要依赖 hostname。Docker 容器重启后 hostname 变随机 hash(如 6ec71e98949e),云函数会判 404 agent not found

□ 2. 确保 FMODE_SESSION_TOKEN 可被读取

SessionToken 是用户登录后生成的凭据,控制 ACL(访问权限)。

存放位置

位置 文件 说明
环境变量 FMODE_SESSION_TOKEN 最优
用户配置 ~/.fmode/config/user.jsonsessionToken FmodeAgent / Hermes Agent、Fmode Studio 自动维护
旧配置 ~/.fmode/config.jsonsessionToken 兼容旧版本

推荐做法:登录一次 FMODE Studio 后,token 自动写入 user.json。 如 env 缺失,技能会从配置文件兜底读取。

□ 3. 验证

# 查看当前容器身份
skill-deliverable check

# 成功输出示例:
# {
#   "agentId": "agent-node",
#   "sessionToken": "r:852951...",
#   "status": "ready",
#   "hostname": "6ec71e98949e"   // 仅显示,不用于上报
# }

二、FmodeStudio 容器生命周期集成

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

三、容灾:云函数 404/403 怎么办

「404 agent not found」

→ FmodeAgent 表里没有这个 agentId 的记录。

  • 新容器首次上报时会自动注册(云函数 v2 特性),等待第一次 report() 即可
  • 若等不及,用 master key 手动在 Parse FmodeAgent 表创建同名记录

「403 only owner or superadmin can report」

→ session 用户不是这个 agent 的 owner。

  • 确认该容器的报告人的 session 和该 agent 是否是同一个人
  • 云函数 v2 已移除 superadmin 检查,只校验 owner(用户=他自己容器的 owner)
  • 若 agent 无 owner(旧数据),首次成功上报会自动绑定

四、常见问题

Q: 容器重建后上报失败?

A: 重建后 AGENT_ID 丢失 → 重设 env FMODE_AGENT_ID。skill-deliverable 已做 hostname 兜底但语义名丢失时 hostname 会变导致 404。必须持久化 AGENT_ID

Q: 一台机器跑了多个容器怎么办?

A: 每个容器设不同的 AGENT_ID,各自上报各自的。FmodeAgent 表以 agentId 区分。

Q: 能不能手动上报测试?

A: 可以。skill-deliverable report --title "测试" --project test。失败看 /tmp/agent-deliverable-report.log


版本记录

版本 日期 变更
0.0.1 2026-09-23 初版:分离 fmode-hub 独立成技能 + 云函数 v2 改良

五、运维001 实测补充(2026-09-23)

5.1 云函数代码必须是「平台形态」,不是 Parse.Cloud.define

本平台 /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)。

5.2 云函数不需要「发布」,改 Function 记录即生效

executor 每次调用都从库里查 Function 记录(无内存缓存),所以:

  • 更新 = 更新 Function.code(+ version / updatedAt
  • 不需要部署源码、不需要重启服务

Parse REST 对 Function 类有 CLP 限制(masterKey 也被拒,code 119), 运维侧用 nova 库直改 "Function" 表那一行即可。

5.3 字段类型: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 比较要取 .idtypeof v === 'string' ? v : v.id)。

5.4 关键 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。 批量开通的容器尤其要逐个核。

5.5 技能只读这几个位置(fmode-identity.json 不在其中!)

lib/index.mjs 的解析链:

  • agentId:env.FMODE_AGENT_IDenv.AGENT_ID~/.fmode/config.json#agentId./.fmode/config.json#agentId → hostname(仅兜底,会 404)
  • sessionToken: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 兜底)。

5.6 CLI 的 Node 版本坑

bin/skill-deliverable.mjs 原稿在 ES 模块里用了 require('os'), Node ≥ 20 会直接抛 ERR_AMBIGUOUS_MODULE_SYNTAX(top-level await + require 混用)。 已改为 import { hostname } from 'node:os'