Schema 驱动的 fal.ai CLI。加新 model = 加一份 TypeScript schema 声明,零改 engine。
npm install
npm run build
export FAL_KEY=your_fal_key
node dist/fal-cli.js listnpm link 可把 fal-cli 暴露到 PATH(指向 dist/fal-cli.js)。
fal-cli list # 列出所有 model alias
fal-cli describe <alias> # 查看该 alias 的完整 schema(字段/类型/枚举)
fal-cli describe <alias> --json # 机读 JSON,方便 agent 消费
fal-cli run <alias> --help # 查看该 model 的 CLI flag
fal-cli run gpt-image-2 -p "a cozy cabin at dawn"
fal-cli run gpt-image-2-edit -p "neon tokyo" -i ./photo.png
fal-cli run nano-banana-2 -p "..." --aspect 16:9 --resolution 2K
fal-cli fetch gpt-image-2 <request_id> # 按 request_id 补捞历史结果通用运行时 flag:
-o, --output-dir <dir>下载目录,默认./fal-output--no-download不下载,仅打印 URL--json输出原始 JSON--logs打印队列进度日志
每次调用写入 ~/.fal-cli/history.jsonl(JSONL,一行一条):
{"ts":"...","status":"submitted","alias":"...","endpoint":"...","request_id":"...","input":{...}}
{"ts":"...","status":"ok","...":"...","urls":[...]}
{"ts":"...","status":"error","...":"...","error":"..."}jq 示例:
# 最近一次失败的 request_id
tac ~/.fal-cli/history.jsonl | jq -r 'select(.status=="error") | .request_id' | head -1
# 所有成功生成的 URL
jq -r 'select(.status=="ok") | .urls[]' ~/.fal-cli/history.jsonlbin/fal-cli.ts # 入口:list / run / fetch 分派
src/
types.ts # FieldSpec discriminated union / Schema / ModalityModule
schema/ # 每个 model 一份 schema 声明
_shared.ts # 跨 model 复用的字段
<provider>/<model>.ts # 按 provider 归类(bytedance/openai/google/seedvr 等)
index.ts # 注册表
engine/
build-command.ts # schema → commander Command
validate.ts # opts → fal input payload(类型 coerce + 范围校验)
run.ts # subscribe / queue.result / 结果派发
modality/
image.ts # 本地图上传 / 产物下载 / zod 解析 fal 返回
index.ts
client.ts # FAL_KEY → fal.config
history.ts # ~/.fal-cli/history.jsonl
设计原则:
- engine 是 schema 的唯一消费者。bin 不懂字段类型,不 import zod。
- FieldSpec 的
type是 discriminated union。engine 里的 switch 被assertNever兜底 —— 加一种 type 但忘改 engine 会在tsc失败。 - modality 承担「这个模态的数据长啥样」。zod 解析 fal 返回只出现在 modality 层。
-
在
src/schema/<provider>/新建<model>.ts(provider 目录不存在就新建):import type { Schema } from "../../types.js"; import { numImagesField, outputFormatField } from "../_shared.js"; const schema: Schema = { alias: "flux-pro", endpoint: "fal-ai/flux-pro", modality: "image", summary: "Black Forest Labs FLUX Pro", fields: [ { name: "prompt", cliKey: "prompt", type: "string", required: true, flag: "-p, --prompt <text>", desc: "描述", }, numImagesField, outputFormatField, // 新独有字段直接加在这里 ], }; export default schema;
-
在
src/schema/index.ts里 import 并加入ALL。 -
npm run build,fal-cli list能看到。
FieldSpec 支持的 type:string | enum | integer | boolean | size | image | image[]。如果新 model 需要别的 type(如 float、url、audio),在 src/types.ts 的 discriminated union 里加一个分支,tsc 会指出哪些文件漏处理。
src/types.ts把Modality = "image"改成"image" | "video",给ModalityModule看看是否还需要调整(比如extractArtifacts的返回类型是否要扩成Artifact)。src/modality/video.ts实现ModalityModule的三个函数。src/modality/index.ts注册到REGISTRY。- schema 里写
modality: "video"。
npm run typecheck # 只做类型检查
npm run build # 编译到 dist/
npm run start # 运行 dist/fal-cli.js仅读环境变量 FAL_KEY。未设置会直接报错并退出。