@yandy0725/mediagen-cli0.3.1
Non-interactive image generation and editing, plus short video generation, for agents: qwen-image, doubao/seedream, doubao/seedance, MiniMax H3, files on disk, JSON output.
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
mediagen
English | 简体中文
通过阿里云百炼(qwen)和火山方舟(doubao / seedream)生成与编辑图片,
通过火山方舟(doubao / seedance)或 MiniMax(H3)生成视频。结果写到磁盘、
打印文件路径。参数全部显式、没有交互式提问、--json 供程序解析。
npm install -g @yandy0725/mediagen-cli
# 任何命令首次运行都会写出一份带注释的 config.toml;之后每次运行都会先校验它,
# config show 本身只读。直接编辑这个文件即可
mediagen config show
# 新增一条入口:一个账号(协议 + origin + key)带一张 image 任务表
mediagen config add my-proxy --api-type openai \
--base-url https://abc123.cn-beijing.maas.aliyuncs.com \
--model qwen-image-3.0 --api-key-env DASHSCOPE_API_KEY
# 视频表加 --task video;再加一个模型只需往 models 里多写一项
mediagen login --provider my-proxy --api-key sk-...
mediagen image generate "a red fox in fresh snow, soft morning light" --provider my-proxy --out-dir ./out
# /abs/path/out/my-proxy-qwen-image-3-0-20261025-020839-0.png
Node >= 18,两个运行时依赖,需要 DashScope 和/或方舟的 API key。
命令
| 命令 | 作用 |
|---|---|
mediagen image generate <prompt> |
文生图;加 --image 即变为编辑 |
mediagen image edit <prompt> |
同上,但 --image 必填 |
mediagen video generate <prompt> |
文生视频,或用一张首帧;建任务即计费 |
mediagen video status <task-id> |
查一次任务,成功则把视频取下来 |
mediagen login / logout / auth status |
保存、删除、查看 API key |
mediagen config show / config path |
解析后的配置,以及它存在哪里 |
mediagen config add <name> |
在 config.toml 里新建一条 provider(其余修改手写文件) |
mediagen skill install --dir <path> |
把自带的 agent 技能装到 <path>/mediagen/;重跑即覆盖 |
mediagen image generate --help 与 mediagen video generate --help 才是参考手册:
全部参数、模型表、尺寸表、示例。下面只是简化版。
模型与参数
| 模型 | provider | 什么时候选它 |
|---|---|---|
qwen-image-3.0 |
--provider aliyun |
文字渲染(中日韩 + 拉丁)、图像编辑 |
doubao-seedream-5-0-flash-260915 |
--provider volcengine |
速度:草稿、迭代 |
doubao-seedream-5-0-pro |
--provider volcengine --model doubao-seedream-5-0-pro |
最高质量 |
| 视频模型 | provider | 什么时候选它 |
|---|---|---|
doubao-seedance-1-0-pro-250528 |
--provider volcengine |
文生视频,或让一张首帧动起来 |
MiniMax-H3 |
--provider minimax |
同样的事,最高 2K,来自 MiniMax |
aliyun / volcengine / minimax 是首次运行写进 config.toml 的三个 provider 名,可以随意改。
一个模型只有被列在该 provider 对应任务(image 或 video)表的 models = [...] 里才可用;同一家厂商的
第二个模型是往那张表里加一行 —— 不需要新增 provider。模型 id 是带日期的快照,随版本
滚动时改 models(以及 default)即可。
协议
每个任务的表自己声明 api_type,所以同一个账号可以让图片走一种协议、视频走另一种:
api_type |
线上形态 | 什么时候选它 |
|---|---|---|
openai |
OpenAI Images 形态:扁平的 {model, prompt, size, n},返回 data[].url |
百炼的 compatible-mode、多数第三方代理 |
dashscope |
阿里原生态:input.messages + parameters,返回 output.choices[].message.content[].image |
百炼原生端点,或暴露该协议的网关(见下) |
ark |
火山自有 API:图片形态接近 OpenAI,但参数与逐条错误语义自成一套 | 火山 Ark,图片与视频 |
minimax |
MiniMax 的视频 API:content[] 元素按 text/image_url 分类型,结果嵌在 task 里 |
MiniMax H3,仅视频 |
dashscope 只做图片,没有视频任务。video generate 讲 ark 或 minimax,两者各自
声明分辨率档位(ark:480p/720p/1080p/4k;minimax:480p/768p/2k)——所选方言没有的档位
在本地就失败,而某个模型支持哪一档由厂商在建任务时校验,此时还没计费。
qwen 仍可用,是 openai 的弃用别名。所选协议不接受的参数会在发出任何付费请求之前失败
(--output-format 仅 ark,--prompt-extend-mode 仅 openai;视频上
--seed/--generate-audio/--output-format/--watermark 仅 ark)。--size 接受档位(2K)、
像素(2048x2048)或 auto(仅 openai);dashscope 只收像素,因为它发送 宽*高,
没有档位与 auto 的概念。--n 接受 1-6 —— 在 ark 上每张图都是一个独立的付费请求。
输出
文件写到 --out-dir(否则 output_dir,否则当前目录),文件名
<provider>-<model>-<yyyyMMdd-HHmmss>-<index>.<ext>,永不覆盖。人类可读模式在
stdout 上每行打印一个绝对路径;--json 打印一个对象:
{"ok":true,"partial":false,"files":[{"path":"/tmp/out/aliyun-qwen-image-3-0-20261025-020839-0.png","index":0,"url":"https://dashscope-result.oss-cn-beijing.aliyuncs.com/1.png"}],"errors":[],"usage":{"image_count":1},"request_id":"a1b2c3d4"}
partial: true 且退出码 0 表示部分图片成功、部分失败:磁盘上那些已经付过费了,
不要整条请求重跑。退出码:0 成功(含部分成功),1 API / 配置 / 运行时失败,
2 用法错误 —— 总是在发请求之前抛出,所以不花钱。付费请求不会自动重试。
视频返回的是它创建的那个任务,因为建任务那一刻就已经计费:
{"ok":true,"task_id":"cgt-20261028-abcd","status":"succeeded","files":[{"path":"/tmp/out/volcengine-doubao-seedance-1-0-pro-250528-20261028-101500-0.mp4","index":0,"url":"https://...","duration":5,"resolution":"720p"}],"errors":[],"usage":{"completion_tokens":108900}}
视频没有 partial:一次请求一个片子。ok 表示命令本身成功,不表示有文件 ——
--no-wait、以及 video status 查到 pending/running,都是 {"ok":true,...,"files":[]}
且退出码 0。要看 status 与 files:写成 ok: false 会让调用方去重跑一个已经付过费的
任务。轮询预算耗尽时错误里带 task_id 与 TaskNotFinished,用 video status <id>
续查即可,不会再付一次。
配置
config.toml —— 你手动编辑的那个文件 —— 位于
$MEDIAGEN_CLI_CONFIG_DIR、$XDG_CONFIG_HOME/mediagen、
~/.config/mediagen 三者中第一个存在的。
auth.json(权限 600)属于状态而非配置,单独存放:取
$MEDIAGEN_CLI_STATE_DIR、$XDG_STATE_HOME/mediagen、
~/.local/state/mediagen 三者中第一个。~/.config 是人们会同步、分享、提交到
仓库的目录,把密钥放在那里,一次 .gitignore 疏忽就等于泄露。state 目录按定义是
「本机状态、不该被同步」,正合设备绑定的凭证。
从 0.1.1 及更早版本升级:两个目录都去掉了 -cli 后缀,程序不会替你迁移。挪文件,别挪目录
—— 新版本第一次运行就已经建好了新目录,mv 目录 目录 只会把你的配置埋进去:
mkdir -p ~/.config/mediagen ~/.local/state/mediagen
mv ~/.config/mediagen-cli/config.toml ~/.config/mediagen/config.toml
mv ~/.local/state/mediagen-cli/auth.json ~/.local/state/mediagen/auth.json
(设置了 XDG_CONFIG_HOME 或 XDG_STATE_HOME 的,路径相应改为 <该目录>/mediagen-cli →
<该目录>/mediagen。用了 MEDIAGEN_CLI_CONFIG_DIR 或 MEDIAGEN_CLI_STATE_DIR 的,变量名
没变,不需要动。mediagen-cli 路径不存在的那一行跳过就好 —— 大多数用户本来就没有
auth.json。)
default_provider = "volcengine"
# output_dir = "./media-output"
[providers.volcengine] # 账号:主机 + key
base_url = "https://ark.cn-beijing.volces.com"
api_key_env = "ARK_API_KEY"
[providers.volcengine.image] # 能力:一个任务一张表
api_type = "ark" # 协议声明在任务上,不在账号上
default = "doubao-seedream-5-0-flash-260915"
models = ["doubao-seedream-5-0-flash-260915", "doubao-seedream-5-0-pro"]
[providers.volcengine.video]
api_type = "ark"
default = "doubao-seedance-1-0-pro"
models = ["doubao-seedance-1-0-pro"]
[providers.aliyun]
base_url = "https://dashscope.aliyuncs.com"
api_key_env = "DASHSCOPE_API_KEY"
[providers.aliyun.image]
api_type = "openai"
default = "qwen-image-3.0"
models = ["qwen-image-3.0"]
[providers.minimax] # 只做视频的账号
base_url = "https://api.minimax.io"
api_key_env = "MINIMAX_API_KEY"
[providers.minimax.video]
api_type = "minimax"
default = "MiniMax-H3"
models = ["MiniMax-H3", "MiniMax-H3-Max"]
[providers.my-gateway] # 提供原生协议的网关
base_url = "https://gateway.example.com"
api_key_env = "MY_GATEWAY_KEY"
[providers.my-gateway.image]
api_type = "dashscope"
path = "/v1/images/generations" # 网关自己的路径,不是百炼的
default = "qwen-image-2.0-pro"
models = ["qwen-image-2.0-pro"]
一个 provider = 一个账号:一台主机(base_url)+ 可选的 api_key_env。它能提供的
每个任务按名字挂在下面([providers.<name>.image]、[providers.<name>.video]),
各自声明 api_type、允许使用的模型 id,以及 --model 缺省时用的 default。
timeout_seconds 按任务可选(图片 300、视频 1800)。同一家厂商的两个模型是同一个
models 列表里的两项,不是两个 provider;同一个账号的两种协议是两行 api_type,
也不是两个 provider。api_type 也可以写在账号表上,作为其下所有任务的默认值。
base_url 就是 origin —— 可以带网关前缀,但不带版本段。版本段属于端点,由方言按任务
自己拼接(/api/v3/images/generations、/compatible-mode/v1/images/generations、
/api/v1/services/aigc/multimodal-generation/generation),
所以换任务只换 path。若在厂商自有域名上仍以 /api/v3、/api/v1 或
/compatible-mode/v1 结尾,会在发请求前被拒绝,并给出应填的 origin。
文件是唯一真相:内置没有任何 provider,首次运行只是把上面这份内容写进 config.toml
供你修改。种子三条都能直接跑:volcengine 走 Ark,aliyun 走百炼原生协议(公网域名即可),minimax 走 MiniMax 的视频 API。
若要把 aliyun 换成 OpenAI 兼容协议,需改用业务空间专属域名
https://{workspace-id}.cn-beijing.maas.aliyuncs.com —— 那个端点不对外公开提供。
如果某个主机的 API 不在厂商自己的路径下 —— 例如网关,或某个把 /api/v3 换成
/api/plan/v3 的套餐 —— 在任务表上用 path 声明这段前缀,适配器就不再追加:
[providers.volcengine]
base_url = "https://ark.cn-beijing.volces.com/api/plan/v3"
[providers.volcengine.image]
path = "/images/generations" # 请求打到 .../api/plan/v3/images/generations
不写 path 时,厂商自有域名下的子路径会在发请求前被拒绝(否则会被拼重复),并给出
可用的 origin 或 path 值。path 只用于「不镜像厂商路径」的主机;平时适配器自带的
路径仍是默认值。
视频是异步的:video generate 先建任务(计费就发生在这一刻),把 task id 写进 stderr,
然后轮询到成片出现 —— 两三分钟是常态,轮询预算取该任务的 timeout_seconds(默认 1800)。
成片 URL 24 小时内有效,所以 video status <id> 可以稍后再取;--no-wait 立刻把 id 交回来,
这正是「自己的超时比厂商渲染更短」的 agent 需要的。一次请求一个片子:--n 属于图片。
key 跟着 provider 实例走,依次来自 --api-key、该实例声明的 api_key_env、
auth.json。没有任何方言级的默认 key 变量,所以同一方言的两条入口可以各用各的 key。
key 永远只以打码形式输出。base_url 写在文件里(image generate 有一次性的 --base-url 覆盖),并且会在发请求前校验:百炼的
OpenAI 兼容端点(compatible-mode)只在业务空间专属域名上提供,所以种子保留一个会明确失败
的占位符;而 dashscope 协议所用的百炼原生端点,公共域名同样可用。mediagen config show
会打印每个 provider 的任务、模型,以及各自实际会请求的 endpoint。Node >= 24 上设
NODE_USE_ENV_PROXY=1 可走 HTTP_PROXY。超时:每次请求取该任务的 timeout_seconds
(默认图片 300 秒、视频 1800 秒),每次图片下载 120 秒、视频下载 600 秒。对视频来说,
timeout_seconds 是整个轮询窗口,不是单次请求:建任务与每次轮询各自 60 秒,这样一个挂住
的请求吃不掉预算;下载另有 600 秒,不计入预算。
在 Agent 里用?在 pi 里,把这份技能当成 pi 包安装:
pi install npm:@yandy0725/mediagen-cli
它只送 skills/mediagen/SKILL.md:pi 不会把 mediagen 命令放进 PATH,命令仍来自
npm install -g @yandy0725/mediagen-cli。其他 agent 用 mediagen skill install --dir <技能目录>
把同一个文件装到它已经在读的位置(例如 --dir ~/.claude/skills),升级后重跑一次即可覆盖。
两条路线只选一条:pi 按技能名加载,两份同名技能会冲突——复制出来的那份胜出,pi 只给一条
name "mediagen" collision 警告,于是旧副本会静默盖住升级后的包。这份技能说明这个 CLI
能做什么、项目已有别的生成工具时该用哪个,以及哪些规则能避免白花钱。
开发
npm install
npm run build # tsup -> dist/cli.js
npm test # vitest,完全离线:所有地方的 fetch 都被打桩
npm run dev -- image generate --help
测试统一放在 tests/:一个模块一个文件,从 ../src/ 引入,不再与源码同目录;
vitest.config.ts 只收集 tests/**/*.test.ts,tsc --noEmit 也一并检查它们。
src/cli.ts 负责 commander 接线和错误到退出码的映射;src/generate.ts 是流水线
(校验 → 解析 → 调用 → 下载 → 汇报);src/api/{qwen,ark,dashscope}.ts 是
src/types.ts 中 DialectAdapter 接缝背后的方言适配器,周围是 config.ts、
auth.ts、http.ts、download.ts、output.ts。
值得守住的东西:所有检查都在第一次 fetch 之前完成;付费请求从不重试;stdout 只
放结果;任何输出路径都不含未打码的 key。说现有方言的新 provider 不需要写代码 ——
一张 config.toml 表就够。新方言则是 src/api/ 下的适配器、DIALECTS
(src/api/index.ts)里的一行,以及适配器上的 path 字段 —— 方言拼在 base_url
后面的那个端点文件名。
发布:打一个 vX.Y.Z 的 GitHub Release,Actions 会自动发布 —— 见
docs/guides/release.md(发布的是 dist/ 和 skills/,CI 每次 push 都会跑
npm pack --dry-run)。设计文档在 docs/superpowers/specs/。
MIT