omp packages

@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.

GitHub Actions Published 7 hours ago MIT ~577 downloads/mo

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.

Did it work for you?

Result

One report per person. A new report replaces your old one.

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