管线 · 流引擎 · 交换所
数据库迁移、自定义多 Agent 管线、Flow(流引擎)运维、Broker(交换所)完整手册。 来源
OPERATION.md§39–§42、src/bridge/routes/pipelines.py、flows.py、flow_templates.py、broker.py、broker_workshop.py、broker_app.py、src/flow/、src/broker/。
目录
- 数据库迁移与升级注意
- 自定义多 Agent 管线 CustomPipeline
- 流引擎 Flow Engine
- Flow Template CRUD 与编辑器
- Flow Instance 运维(REST API)
- 断点续跑(Resume)
- 交换所 Broker 概述
- Broker manifest.json 格式
- Broker 包结构(.broker.zip)
- Broker 签名与验证
- 交换所工坊 BrokerWorkshop(安装/管理/使用)
- Broker App 运行时(iframe + SDK)
- Broker 事件系统与 Trigger
- Broker Plugin 沙箱架构
- Marketplace 发布与安装
- CLI 命令速查
1. 数据库迁移与升级注意
Familiars 使用 SQLite(默认 workspace/familiars.db,可用 FAMILIARS_DB_PATH 覆盖)。
迁移机制:启动时自动执行 src/storage/migrations/ 下的顺序迁移脚本。迁移器支持 *.sql 与 *.py 两种载体。
当前迁移覆盖:v1 ~ v19:
| 版本 | 载体 | 内容 |
|---|---|---|
| v1-v15 | .sql | 核心表:agents, dialogues, nodes, memories, todos, schedules, cron_jobs, etc. |
| v16 | .py | v16__schedule_dedup_and_unique.py — 日程去重 + 唯一约束 |
| v17 | .sql | 补充索引 |
| v18 | .sql | broker_clusters + broker_rooms + broker_threads + broker_messages — Broker 核心表 |
| v19 | .sql | broker_instances + broker_workshop_rooms + broker_triggers + broker_reputation — 交换所工坊表 |
| v20+ | .sql/.py | flow_templates, flow_instances, flow_mutations, flow_traces — Flow 引擎表 |
升级注意事项:
- 首次启动新版本:迁移在 bridge 启动时自动跑;日志中出现
migration failed→ 退出并检查workspace/.bridge.log - WAL 文件:
familiars.db-wal+familiars.db-shm是 WAL 模式伴随文件,不能单独删除;备份时三文件一起复制 - 备份:整个
workspace/目录可迁移。跨机器搬移时SECRET.json需重新绑定(src/base/crypto.py用机器盐值 + Fernet,换机器无法解密) - 回滚:不提供自动降级;需回到旧版本请从备份恢复整份
workspace/
2. 自定义多 Agent 管线 CustomPipeline
入口:Manager 技能工坊(§38)→ 选择 custom_pipeline
2.1 5 步向导
| 步骤 | 名称 | 说明 |
|---|---|---|
| 1 | 草稿(draft) | 填管线目标 + 分工思路,可保存草稿 |
| 2 | 补齐(complete) | 让 manager 用更强模型补齐空处,已填字段完全保留 |
| 3 | 预览(preview) | 查看 pipeline 配置 + 子 agent 身份文档 + contract.md |
| 4 | 提交(commit) | 新建驱动 agent(AgentRail 出现)+ 挂载所有子 agent |
| 5 | 打包/分享 | 导出 .pipeline.zip(整套管线 + 技能配方) |
2.2 与普通 agent 的区别
- 普通 agent:你说话它做事
- 管线 agent:你说话 → 它拆给子 agent 做 → 统一回你
2.3 后端 API
POST /api/pipelines — 创建管线
GET /api/pipelines — 列出所有管线
GET /api/pipelines/<id> — 管线详情
DELETE /api/pipelines/<id> — 删除管线
POST /api/pipelines/<id>/export — 导出 .pipeline.zip
POST /api/pipelines/import — 导入 .pipeline.zip3. 流引擎 Flow Engine
核心文件:src/flow/engine.py
3.1 架构
FlowEngine 采用 owner thread + per-role worker thread 模型:
- Owner thread(调用者线程):唯一写入者,接收 worker 消息,折叠进
FlowState,应用 mutations - Worker thread(每个 role 一个):执行节点上的 WorkUnit,通过消息队列向 owner 汇报
三个不变量:
- 只有 owner 写入:worker 发消息,owner 折叠进状态。唯一例外是 role status(
Scheduler在自己锁下写,因为agent_state谓词必须立即观察) - 图是冻结的,不是撕裂的:
rewrite.apply_spec拷贝 → 验证 → owner 交换引用。worker 持有一张完整、自洽的图,可能过期但从不半重写 - 过时被捕获,不是被防止:worker 在每个节点前比较 generation,图移动时自动重定位
3.2 Mutation 类型
| kind | 说明 | 是否影响图 |
|---|---|---|
add_unit | 添加工作单元 | ✅ 重写图 |
remove_unit | 移除工作单元 | ✅ 重写图 |
rewire | 改变连接 | ✅ 重写图 |
replace_workflow | 替换整个 workflow | ✅ 重写图 |
expand_repeat | 展开 repeat 节点 | ✅ 重写图 |
set_config | 修改配置 | ✅ 重写图 |
set_field | 设置运行状态字段 | ❌ 不影响图 |
3.3 状态机
pending → running → done
→ failed| 状态 | 含义 |
|---|---|
pending | 实例已创建,engine 尚未开始 |
running | engine 正在执行 |
done | 正常完成 |
failed | 异常终止(crash / 超时 / 不可恢复错误) |
3.4 数据库表
| 表 | 说明 |
|---|---|
flow_templates | 模板列表(id, version, body_json) |
flow_instances | 实例(instance_id, template_id, status, pinned_template_json, base_graph_hash) |
flow_mutations | 变更日志(seq, kind, spec_json, result_graph_hash) |
flow_traces | 事件 trace(JSONL 文件,append-only) |
4. Flow Template CRUD 与编辑器
后端:src/bridge/routes/flow_templates.py
前端:FlowTemplateList.vue + FlowTemplateEditor.vue(在 CreativeWorkshop 内)
4.1 API 端点
| 端点 | 说明 |
|---|---|
GET /api/flow-templates | 列表(摘要行,不含大 body) |
POST /api/flow-templates | 创建新模板 |
GET /api/flow-templates/<id> | 获取完整 body(含 template.json + layout.json) |
PUT /api/flow-templates/<id> | 保存(validate + persist to disk + SQLite) |
DELETE /api/flow-templates/<id> | 删除(有实例时拒绝) |
POST /api/flow-templates/<id>/validate | 干跑验证(不持久化) |
POST /api/flow-templates/<id>/duplicate | 克隆为新 id |
GET /api/flow-templates/<id>/export | 下载 .flow.json |
POST /api/flow-templates/import | 上传 .flow.json |
4.2 Bundle 协议(V2 track layout, schema_version=3)
| 端点 | 说明 |
|---|---|
POST /api/flow-templates/preview_bundle | 预检 .flow.zip bundle(dry-run) |
POST /api/flow-templates/import_bundle | 导入 .flow.zip 或文件夹路径 |
GET /api/flow-templates/<id>/export_bundle | 下载 .flow.zip |
Bundle 结构:
my_flow.flow.zip
├── manifest.json 元数据(id, name, version, schema_version)
├── template.json V1 pool 层(节点 + 连接)
├── layout.json V2 track/module/variable 层
├── contract.md 协作契约(可选)
├── README.md 说明(可选)
├── schemas/ 数据 schema(可选)
├── docs/ 文档(可选)
│ ├── DESIGN.md 设计文档
│ └── DEVELOPMENT.md 开发文档(→ 全局 FLOW_DEVELOPMENT.md)
└── renderers/ 自定义字段渲染器(可选)
└── *.vue Vue SFC,编译为 ESM JS模板存储位置:
- 磁盘:
workspace/manager/flows/<template_id>/template.json(source of truth,git-trackable) - SQLite:
flow_templates表(快速 listing + instance launch)
4.3 内置模板(Built-in)
- 位置:
src/flow/_builtin/<id>/ install_built_ins()在 bridge 启动时同步read_only: true→ UI 分 系统 / 我的 两个列表- 系统模板隐藏删除按钮,但允许 复制 到"我的"再编辑
4.4 Per-flow Docs
每个 flow 有 docs/ 目录:
| 端点 | 说明 |
|---|---|
GET /api/flow-templates/<id>/docs | 列出所有文档 |
GET /api/flow-templates/<id>/docs/<name> | 读取文档内容 |
PUT /api/flow-templates/<id>/docs/<name> | 写入文档(≤1MB) |
DESIGN.md:flow 的设计文档(不存在时返回骨架模板)DEVELOPMENT:始终解析到全局FLOW_DEVELOPMENT.md(is_global: true)
4.5 自定义字段渲染器
renderers/*.vue 是 Vue SFC:
src/flow/render/compiler预编译为 ESM JS- Chat UI 在 input/output field 引用渲染器名称时动态挂载
- 用于自定义字段类型的可视化编辑(如 markdown 编辑器、图片选择器等)
5. Flow Instance 运维(REST API)
后端:src/bridge/routes/flows.py
5.1 API 端点
| 端点 | 说明 |
|---|---|
GET /api/flows | 列表(摘要行,不含 pinned_template_json) |
GET /api/flows/<instance_id> | 详情 + 最近 N 条 trace(默认 20) |
GET /api/flows/<instance_id>/trace | 完整 trace JSON |
POST /api/flows/<instance_id>/cancel | 取消(幂等) |
5.2 列表查询参数
| 参数 | 类型 | 说明 |
|---|---|---|
status | string | 过滤:pending / running / done / failed |
template_id | string | 按模板筛选 |
limit | int | 返回数量上限(1-500,默认 100) |
5.3 CLI 命令
python main.py flows list # 列出所有 flow 实例
python main.py flows list --status running # 只看运行中的
python main.py flows list --template-id content_studio # 按模板筛选
python main.py flows get flow_xxxxx # 详情 + 最近 20 条 trace
python main.py flows get flow_xxxxx --trace-tail 50 # 看最近 50 条 trace
python main.py flows trace flow_xxxxx # 导出完整 trace JSON
python main.py flows trace flow_xxxxx --tail 100 # 只看最后 100 条事件
python main.py flows cancel flow_xxxxx # 取消5.4 取消机制
POST /api/flows/<id>/cancel→FlowRepo.request_cancel(instance_id)→ 设置cancel_requested标志- Engine 在当前步骤完成后优雅停止
- 幂等:对已结束实例再次 cancel 返回
ok: false,不报错
6. 断点续跑(Resume)
核心文件:src/flow/resume.py
6.1 机制
当 bridge 崩溃导致 flow 状态停留在 running 时,可用 rehydrate() 从数据库重建 engine 状态:
- 读取
pinned_template_json重建基础图 - 按
seq顺序重放flow_mutations表中的变更记录 - 每步验证
result_graph_hash与apply_and_validate产出一致 - 任何 hash 不匹配 →
FlowResumeError(拒绝续跑)
6.2 关键约束
- Replay = 实时应用:使用同一个
apply_and_validate函数,不可能出现两套实现分歧 set_field重放:写入FlowState(不影响图 hash),按 seq 顺序重放所有 set_field 变更- 新鲜实例:无 mutation 的实例返回基于模板基础图的 engine(no-op resume)
- 模板被修改:hash 不匹配 → 拒绝 resume → 报错
6.3 数据流
bridge crash → flow status='running' (stale)
→ 用户重启 bridge
→ chain 检测 stale running instance
→ rehydrate(instance_id)
→ repo.get_instance → pinned_template_json
→ apply_and_validate × N mutations
→ verify hash at each step
→ return engine at post-replay state
→ engine.run() continues from where it left off7. 交换所 Broker 概述
是什么:Broker 是 Familiars 的集群互联层。
集群(cluster)= 一个用户 + 该用户名下的全部 agent。它是 Familiars 最小的信任单元:集群内部所有 agent 共享一份记忆与一套权限,对外作为单一签名身份出现(cluster_id = dev_<device_fingerprint>)。
7.1 四子系统
| 子系统 | 本质工作 | 当前状态 |
|---|---|---|
| A. 集群内编排 | 一个集群内部多 agent 的结构化协同:rooms / threads / deals / reputation | ✅ 已落地(v18 broker + v19 workshop) |
| B. 应用容器 | 第三方代码(plugins / UI / triggers / recipes)作为可插拔能力挂进任意集群 | ✅ 已落地(AST 白名单 + 子进程沙箱) |
| C. 分发与信任 | 作者签名 → 静态站 → 用户安装;Ed25519 + sha256 + 作者公钥交叉核对 | ✅ 已落地(Layer 1 MVP) |
| D. 跨集群联邦 | 不同用户的集群之间传消息、可信路由 | 🔶 钩子已留,中继与协议未实现 |
7.2 核心数据模型
| 概念 | 含义 | 实现位置 |
|---|---|---|
cluster_id | dev_<device_fingerprint> | src/broker/identity.py |
| ClusterCard | 集群对外可见卡片 | src/broker/card.py |
| ConsentScope | 集群授予 broker 的能力边界 | src/broker/scopes.py |
| DomainStore / Ledger | 每 broker 的领域数据 + 事件账本 | src/broker/store.py, ledger.py |
| MatchEngine | 撮合引擎(rooms/deals) | src/broker/matching.py |
| DealProtocol | 状态机:PROPOSED → NEGOTIATING → SETTLED / ABORTED | src/broker/protocol.py |
7.3 后端 API(src/bridge/routes/broker.py)
| 端点 | 说明 |
|---|---|
GET /api/broker/brokers | 列出所有已注册 broker |
POST /api/broker/brokers/<id>/register | 注册集群 |
POST /api/broker/brokers/<id>/deregister | 注销集群 |
8. Broker manifest.json 格式
必需字段:
| 字段 | 类型 | 说明 |
|---|---|---|
broker_id | string | 全局唯一 ID(也是 zip 名前缀) |
name | string | 显示名称 |
version | string | 推荐 semver |
schema_version | int | 当前 1 |
推荐字段:
| 字段 | 类型 | 说明 |
|---|---|---|
description | string | 一句话描述 |
domain | string | 领域分类 |
author_pubkey | string | 作者 Ed25519 公钥(base64) |
ui.entry | string | iframe UI 入口 HTML(默认 index.html) |
ui.title | string | 窗口标题 |
ui.width / ui.height | int | 窗口尺寸(px) |
ui.single_instance | bool | 是否单实例 |
capabilities_required | string[] | 向集群声明的本地 API:guild / ledger / recipe_call / scheduler |
role_defs | object | role → capability 集合映射 |
reputation_dimensions | object[] | 信誉维度定义 |
permissions | object | 沙箱权限:extra_modules / rss_mb / cpu_budget_s / rate_limit / timeout_s |
permissions.extra_modules | string[] | 申请额外可 import 的模块 |
9. Broker 包结构(.broker.zip)
my_broker.broker.zip
├── manifest.json 必需,描述 broker 的一切
├── SIGNATURE 必需(marketplace 强制),格式:
│ <pub_key_b64>\n<sig_b64>\n
├── ui/ 可选,iframe UI 目录
│ └── index.html manifest.ui.entry 指向的入口
├── plugins/ 可选,*.py 通过 AST 白名单 + 子进程沙箱加载
├── triggers.json 可选,事件 → plugin/recipe 分发表
├── recipes/ 可选,recipe 归档
├── cover.png 可选,桌面图标
└── README.md 可选打包器(broker pack)拒绝:backslash 路径、.. 转义、绝对路径、奇怪字符文件名。
10. Broker 签名与验证
10.1 签名域
签名 = Ed25519(sha256(manifest.json))
修改 manifest 后必须 re-sign。其他文件可改而不失效签名(这是当前签名域的简化)。
10.2 CLI 命令
python main.py broker keygen --name <author_name>
→ 生成 Ed25519 密钥对 → ~/.familiars/authors/<name>.key (base64 seed)
→ ~/.familiars/authors/<name>.pub (base64 pubkey)
python main.py broker pack <broker_dir>
→ 打包为 <broker_id>.broker.zip(含 manifest.json + ui/ + plugins/ 等)
python main.py broker sign <broker_zip> --author <name>
→ 计算 sha256(manifest.json) → Ed25519 签名 → 写入 SIGNATURE 文件
python main.py broker verify <broker_zip>
→ 读取 SIGNATURE → 提取 pub_key → 验证 sha256(manifest.json) 签名
→ 交叉核对 manifest.author_pubkey == pub_key
python main.py broker install <broker_zip>
→ 解压到 workspace/manager/brokers/<broker_id>/
→ 注册到 broker_instances 表10.3 安装时验证
install_from_url(url) →
下载 .broker.zip →
验证 sha256 + Ed25519 签名 →
验证 author_pubkey 匹配 →
解压 → 注册11. 交换所工坊 BrokerWorkshop(安装/管理/使用)
组件:BrokerWorkshopPane.vue + 子组件
入口:Desktop → 应用商场 图标(apps app → AppsApp.vue)
11.1 后端 API(src/bridge/routes/broker_workshop.py)
Instances(实例管理):
| 端点 | 说明 |
|---|---|
GET /api/broker_workshop/instances | 列出已安装 broker |
POST /api/broker_workshop/instances/import | 从上传的 .broker.zip 安装 |
POST /api/broker_workshop/instances/install_from_url | 从 URL 下载并安装 |
GET /api/broker_workshop/instances/<broker_id> | 单个 broker + manifest |
DELETE /api/broker_workshop/instances/<broker_id> | 卸载 |
GET /api/broker_workshop/instances/<broker_id>/export | 下载 .broker.zip |
POST /api/broker_workshop/instances/<broker_id>/status | pause / resume |
Rooms(房间):
| 端点 | 说明 |
|---|---|
GET /api/broker_workshop/instances/<id>/rooms | 列出房间 |
POST /api/broker_workshop/instances/<id>/rooms | 创建房间 |
DELETE /api/broker_workshop/instances/<id>/rooms/<room_id> | 归档房间 |
Threads(话题):
| 端点 | 说明 |
|---|---|
GET /api/broker_workshop/instances/<id>/rooms/<room_id>/threads | 列出话题 |
POST /api/broker_workshop/instances/<id>/rooms/<room_id>/threads | 创建话题 |
DELETE /api/broker_workshop/instances/<id>/threads/<thread_id> | 归档话题 |
Members(成员):
| 端点 | 说明 |
|---|---|
GET /api/broker_workshop/instances/<id>/rooms/<room_id>/members | 列出成员 |
POST /api/broker_workshop/instances/<id>/rooms/<room_id>/members | 添加成员 |
DELETE /api/broker_workshop/instances/<id>/rooms/<room_id>/members/<cluster_id> | 踢出成员 |
Reputation(信誉):
| 端点 | 说明 |
|---|---|
GET /api/broker_workshop/instances/<id>/reputation/<cluster_id> | 查信誉分 |
POST /api/broker_workshop/instances/<id>/reputation/rate | 评分 |
GET /api/broker_workshop/instances/<id>/reputation/<cluster_id>/events | 评分事件 |
Triggers(触发器):
| 端点 | 说明 |
|---|---|
GET /api/broker_workshop/instances/<id>/triggers | 列出触发器 |
POST /api/broker_workshop/instances/<id>/triggers | 创建触发器 |
DELETE /api/broker_workshop/instances/<id>/triggers/<trigger_id> | 删除触发器 |
11.2 来源与只读
来源(installed_from) | 行为 |
|---|---|
store | 🔒 市场只读黑盒:不可删除/替换/覆盖包级文件。运行时数据(rooms/threads/messages/triggers/reputation)正常开放 |
built_in | 本地开发,可打开目录二次开发 |
user_created | 本地开发,可编辑 |
imported_zip | 本地导入,可编辑 |
11.3 界面操作
- 已安装面板(
InstalledBrokersPanel.vue):三档 "已安装 / 已暂停 / 未启用" - 新建(
BrokerCreateModal.vue):填名字/图标/简介 + 开放给哪些 agent - 安装外部包:拖入/选择
.broker.zip→ 显示声明权限 → 确认安装 - 市场浏览(
MarketplaceModal.vue):从FAMILIARS_MARKETPLACE_URL获取 index.json → 浏览/安装 - 导出/分享:已装 broker → 导出 .broker.zip
- 进入使用:双击桌面图标 →
BrokerAppWindow.vue(iframe 加载) - 卸载/暂停:右键图标 → 暂停(保留数据)或卸载(连数据删)
12. Broker App 运行时(iframe + SDK)
后端:src/bridge/routes/broker_app.py
前端:BrokerAppWindow.vue
12.1 架构
每个已安装 broker 的 manifest.ui 配置使其成为沙箱化 HTML 应用:
Desktop shell
└─ BrokerAppWindow.vue
└─ <iframe src="/api/broker_app/<broker_id>/ui/<entry>?token=...">
└─ broker 的 index.html
└─ <script src="../sdk.js"> → window.familiars RPC facade12.2 SDK RPC 方法
| 方法 | 说明 |
|---|---|
me() | { cluster_id } |
manifest() | BrokerManifest |
recipe.list() | { recipes: [{ id, title }] } |
rooms.list() | { rooms } |
rooms.threads(room_id) | { threads } |
rooms.members(room_id) | { members } |
reputation.get(cluster_id) | { scores: [...] } |
storage.get/set/list | per-broker KV 存储 |
ui.close/resize/setTitle | 窗口控制 |
12.3 安全
X-Bridge-Token通过?token=query param 传入 iframe- Content-Security-Policy 限制可访问 origin
- 路径遍历防护:regex + resolved-parent 双重检查
- iframe 的 broker_id 由父窗口注入,不信任 iframe 自报
13. Broker 事件系统与 Trigger
13.1 事件目录
Runtime 主动 emit 下列事件,路由到 broker_triggers 表匹配的 handler:
| Event | Payload keys |
|---|---|
app.installed | broker_id, installed_from |
app.started / paused / resumed / uninstalled | broker_id |
message.posted | broker_id, room_id, thread_id?, cluster_id, kind, message_id |
approval.created / voted / decided / withdrawn | approval_id, ... |
deal.transition | deal_id, from_state, to_state, actor, evidence |
member.join / leave / kick | room_id, cluster_id, ... |
reputation.threshold | subject, dimension, from, to, delta |
cron.tick | cron_expr, ts |
custom.<name> | plugin-defined |
13.2 Trigger 配置
{
"event": "deal.transition",
"match": {"to_state": "SETTLED"},
"action_kind": "recipe",
"action_ref": "my_recipe",
"enabled": true
}match字段过滤 payload:{"to_state": "SETTLED"}只匹配对应字段相等的事件action_kind:recipe/plugin_fnaction_ref:recipe id 或 plugin 函数名
13.3 事件分发
event_bus.py 收到事件 → 查 broker_triggers 表 → 匹配 match → 分发到 action:
recipe→ 调用对应 recipeplugin_fn→ 优先走沙箱子进程 RPC,失败 fallback 到进程内调用
14. Broker Plugin 沙箱架构
文件:src/broker/workshop/sandbox/ast_guard.py + 双轨运行时
14.1 两道关卡
- AST 白名单校验(安装时):拒绝禁止的 import、属性链、
exec/eval/open等 - 运行时沙箱(启动时):子进程隔离 + 资源控制
14.2 模块权限
默认允许: json / math / re / datetime / dataclasses / typing / statistics / collections / collections.abc / enum / functools / itertools / decimal / __future__ / broker_sdk
可申请加入(通过 manifest.permissions.extra_modules): os / subprocess / requests / aiohttp / httpx / sqlite3 / pathlib / tempfile / hashlib / hmac / base64 / binascii / uuid / secrets / shutil / glob / fnmatch / textwrap / io / copy / pprint / urllib / xml / csv / string / bisect / heapq / operator / random / fractions / warnings / contextlib / abc / types / inspect / argparse
永久禁止(即使申请也不放行): ctypes / pickle / importlib / code / codeop / pdb / trace / tracemalloc / sys / signal / multiprocessing / threading / asyncio / socket / select / asyncore / asynchat / ssl / mmap / platform / __import__ / eval / exec / compile / open / globals / locals / vars / getattr / setattr / delattr / breakpoint / input / exit / quit
14.3 双轨运行
| 路径 | 说明 |
|---|---|
| Legacy 进程内 | bundle.install() → exec 到 sys.modules["broker_plugins.<safe_id>.<name>"] |
| 沙箱子进程 | bundle.install_sandboxed() → 独立 worker 子进程 → JSON-RPC(stdin/stdout) |
事件分发优先走沙箱子进程,失败时 fallback 到进程内。
14.4 资源控制
| 资源 | 默认 | 可调(manifest.permissions) |
|---|---|---|
| RSS 内存 | 512MB | rss_mb |
| CPU 预算 | 30s 窗口 | cpu_budget_s |
| 调用频率 | 60 次/分钟 | rate_limit |
| 单次超时 | 30s | timeout_s |
14.5 能力审批门
当 manifest.permissions 声明了非空能力(net / fs_read / fs_write / extra_modules):
- 安装时要求用户显式审批能力 hash
- 审批通过才启动 worker
- 未审批 →
CapabilityNotApproved - 能力漂移(manifest 升级后 hash 变化)→
CapabilityDrifted
15. Marketplace 发布与安装
15.1 服务端布局
<marketplace_host>/
├── index.json 索引文件(列出所有可用 broker)
├── packages/
│ ├── ventaris.broker.zip
│ ├── example_broker.broker.zip
│ └── ...15.2 index.json 格式
{
"version": 1,
"packages": [
{
"broker_id": "ventaris",
"name": "Ventaris",
"version": "1.0.0",
"description": "公会·任务·勋章 协作平台",
"author_pubkey": "base64...",
"download_url": "https://marketplace.example.com/packages/ventaris.broker.zip",
"cover_url": "https://marketplace.example.com/packages/ventaris_cover.png"
}
]
}15.3 发布流程
# 1. 打包
python main.py broker pack workspace/manager/brokers/my_broker
# 2. 签名
python main.py broker sign my_broker.broker.zip --author my_name
# 3. 上传到静态站
# (手动上传到 marketplace host 的 packages/ 目录,更新 index.json)15.4 安装流程
MarketplaceModal → GET <FAMILIARS_MARKETPLACE_URL>/index.json → 浏览列表
→ 用户选择 → POST /api/broker_workshop/instances/install_from_url
→ 下载 .broker.zip(带 browser UA 绕过 Cloudflare WAF)
→ 验证 sha256 + Ed25519 签名
→ 验证 author_pubkey 匹配
→ 解压到 workspace/manager/brokers/<broker_id>/
→ 注册到 broker_instances 表
→ 桌面 shell 出现新图标15.5 环境变量
| 变量 | 说明 |
|---|---|
FAMILIARS_MARKETPLACE_URL | marketplace 静态站 URL |
16. CLI 命令速查
Broker CLI
python main.py broker keygen --name <author> # 生成密钥对
python main.py broker pack <dir> # 打包 .broker.zip
python main.py broker sign <zip> --author <name> # 签名
python main.py broker verify <zip> # 验证签名
python main.py broker install <zip> # 本地安装
python main.py broker keys-list # 列出已生成密钥
python main.py broker marketplace-index # 查看 marketplace 索引Flow CLI
python main.py flows list # 列出 flow 实例
python main.py flows list --status running # 过滤运行中
python main.py flows get <instance_id> # 详情 + trace tail
python main.py flows get <instance_id> --trace-tail 50
python main.py flows trace <instance_id> # 完整 trace
python main.py flows trace <instance_id> --tail 100
python main.py flows cancel <instance_id> # 取消