@yandy0725/mediagen-cli 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +271 -2
- package/README.zh-CN.md +245 -0
- package/dist/cli.js +3306 -0
- package/package.json +62 -4
- package/skills/mediagen/SKILL.md +88 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Michael Ding
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,272 @@
|
|
|
1
|
-
#
|
|
1
|
+
# mediagen
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
English | [简体中文](README.zh-CN.md)
|
|
4
|
+
|
|
5
|
+
Generate and edit images through Alibaba Bailian (Qwen-Image) and Volcengine Ark
|
|
6
|
+
(**doubao / seedream**), and generate video through Ark (**doubao / seedance**) or
|
|
7
|
+
MiniMax (**H3**). Save results to disk, print their paths. Explicit flags, no
|
|
8
|
+
interactive prompts, `--json` for programs.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install -g @yandy0725/mediagen-cli
|
|
12
|
+
|
|
13
|
+
# Any command creates a commented config.toml on first use, and every run
|
|
14
|
+
# validates it before the command runs; config show itself only reads it.
|
|
15
|
+
mediagen config show
|
|
16
|
+
|
|
17
|
+
# Add an entry: one account (origin + key); the protocol goes on its task table
|
|
18
|
+
mediagen config add my-proxy --api-type openai \
|
|
19
|
+
--base-url https://abc123.cn-beijing.maas.aliyuncs.com \
|
|
20
|
+
--model qwen-image-3.0 --api-key-env DASHSCOPE_API_KEY
|
|
21
|
+
# add --task video for a video table; another model is one more entry in models
|
|
22
|
+
|
|
23
|
+
mediagen login --provider my-proxy --api-key sk-...
|
|
24
|
+
|
|
25
|
+
mediagen image generate "a red fox in fresh snow, soft morning light" --provider my-proxy --out-dir ./out
|
|
26
|
+
# /abs/path/out/my-proxy-qwen-image-3-0-20261025-020839-0.png
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Node >= 18, two runtime dependencies, a DashScope and/or Ark API key.
|
|
30
|
+
|
|
31
|
+
## Commands
|
|
32
|
+
|
|
33
|
+
| command | what it does |
|
|
34
|
+
|---|---|
|
|
35
|
+
| `mediagen image generate <prompt>` | text to image; add `--image` to edit |
|
|
36
|
+
| `mediagen image edit <prompt>` | the same, with `--image` required |
|
|
37
|
+
| `mediagen video generate <prompt>` | text to video, or from one first frame; creates a paid task |
|
|
38
|
+
| `mediagen video status <task-id>` | look at one task once, and save its video if it finished |
|
|
39
|
+
| `mediagen login` / `logout` / `auth status` | store, remove, inspect API keys |
|
|
40
|
+
| `mediagen config show` / `config path` | resolved config, and where it lives |
|
|
41
|
+
| `mediagen config add <name>` | create a provider in config.toml; everything else is a manual edit |
|
|
42
|
+
|
|
43
|
+
`mediagen image generate --help` and `mediagen video generate --help` are the
|
|
44
|
+
reference: every flag, the model table, the size reference, examples. Everything below
|
|
45
|
+
is the short version.
|
|
46
|
+
|
|
47
|
+
## Models and flags
|
|
48
|
+
|
|
49
|
+
| model | provider | pick it when |
|
|
50
|
+
|---|---|---|
|
|
51
|
+
| `qwen-image-3.0` | `--provider aliyun` | text rendering (CJK + Latin), editing; needs your workspace host (see below) |
|
|
52
|
+
| `doubao-seedream-5-0-flash-260915` | `--provider volcengine` | speed: drafts, iteration |
|
|
53
|
+
| `doubao-seedream-5-0-pro` | `--provider volcengine --model doubao-seedream-5-0-pro` | highest quality |
|
|
54
|
+
|
|
55
|
+
| video model | provider | pick it when |
|
|
56
|
+
|---|---|---|
|
|
57
|
+
| `doubao-seedance-1-0-pro-250528` | `--provider volcengine` | text to video, or animate one first frame |
|
|
58
|
+
| `MiniMax-H3` | `--provider minimax` | the same, up to 2K, from MiniMax |
|
|
59
|
+
|
|
60
|
+
`aliyun`, `volcengine` and `minimax` are the provider names the first run writes into
|
|
61
|
+
`config.toml`; rename them freely. A model is usable only if it is listed in
|
|
62
|
+
`models = [...]` under that provider's table for the task (`image` or `video`), and a
|
|
63
|
+
second model of the same vendor is one more entry there — never another provider. Ids are dated snapshots, so
|
|
64
|
+
edit `models` (and `default`) in `config.toml` as they roll forward.
|
|
65
|
+
|
|
66
|
+
### Protocols
|
|
67
|
+
|
|
68
|
+
Each provider's task table declares its own `api_type`, so one account can speak one
|
|
69
|
+
protocol for images and another for video:
|
|
70
|
+
|
|
71
|
+
| `api_type` | wire format | pick it when |
|
|
72
|
+
|---|---|---|
|
|
73
|
+
| `openai` | the OpenAI Images shape: flat `{model, prompt, size, n}`, `data[].url` back | Bailian's `compatible-mode`, most third-party proxies |
|
|
74
|
+
| `dashscope` | Alibaba's native shape: `input.messages` + `parameters`, `output.choices[].message.content[].image` back | Bailian's native endpoint, or a gateway that exposes it (see below) |
|
|
75
|
+
| `ark` | Volcengine's own API: OpenAI-shaped for images, with its own parameters and per-item errors | Volcengine Ark, for images and video |
|
|
76
|
+
| `minimax` | MiniMax's video API: `content[]` items typed `text`/`image_url`, result nested under `task` | MiniMax H3, video only |
|
|
77
|
+
|
|
78
|
+
`dashscope` serves images only; it has no video task. `video generate` speaks `ark`
|
|
79
|
+
or `minimax`, and each declares its own resolution tiers (`ark`: 480p/720p/1080p/4k,
|
|
80
|
+
`minimax`: 480p/768p/2k) — a tier the selected dialect does not have fails locally,
|
|
81
|
+
while which tier a *model* supports is the vendor's call, checked when the task is
|
|
82
|
+
created and before anything is billed.
|
|
83
|
+
|
|
84
|
+
`qwen` still works as a deprecated alias of `openai`. A flag the selected protocol
|
|
85
|
+
does not accept fails before any paid request is sent (`--output-format` is ark-only,
|
|
86
|
+
`--prompt-extend-mode` is openai-only; on video, `--seed`/`--generate-audio`/
|
|
87
|
+
`--output-format`/`--watermark` are ark-only). `--size` takes a tier (`2K`), pixels
|
|
88
|
+
(`2048x2048`), or `auto` (openai only); `dashscope` takes pixels only, because it
|
|
89
|
+
sends `宽*高` and has no tier or `auto` concept. `--n` takes 1-6 — on ark each image
|
|
90
|
+
is a separate paid request.
|
|
91
|
+
|
|
92
|
+
## Output
|
|
93
|
+
|
|
94
|
+
Files are written to `--out-dir` (else `output_dir`, else the current directory) as
|
|
95
|
+
`<provider>-<model>-<yyyyMMdd-HHmmss>-<index>.<ext>`, never overwriting. Human mode
|
|
96
|
+
prints one absolute path per line on stdout; `--json` prints one object:
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
{"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"}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`partial: true` with exit `0` means some images were saved and some failed: those
|
|
103
|
+
files are already paid for, so do not re-run the request. Exit codes: `0` success
|
|
104
|
+
(including partial), `1` API/config/runtime failure, `2` usage error — always
|
|
105
|
+
raised before a request is sent, so it costs nothing. Paid requests are never
|
|
106
|
+
retried.
|
|
107
|
+
|
|
108
|
+
A video run reports the task it created, because creating the task is what is billed:
|
|
109
|
+
|
|
110
|
+
```json
|
|
111
|
+
{"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}}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
There is no `partial` on video: one request makes one clip. **`ok` means the command
|
|
115
|
+
succeeded, not that a file exists** — `--no-wait`, and a `video status` that finds the
|
|
116
|
+
task still `pending` or `running`, are `{"ok":true,...,"files":[]}` with exit `0`.
|
|
117
|
+
Read `status` and `files`: an `ok: false` there would invite you to submit a task that
|
|
118
|
+
was already paid for. Note that `--no-wait` reports `status: "pending"` without having
|
|
119
|
+
asked — treat it as "unknown" until a `video status` call answers. When the poll budget runs out the error carries `task_id` and
|
|
120
|
+
code `TaskNotFinished`, and `video status <id>` resumes it without paying again.
|
|
121
|
+
|
|
122
|
+
## Configuration
|
|
123
|
+
|
|
124
|
+
`config.toml` — the file you edit — lives in the first of
|
|
125
|
+
`$MEDIAGEN_CLI_CONFIG_DIR`, `$XDG_CONFIG_HOME/mediagen-cli`, `~/.config/mediagen-cli`.
|
|
126
|
+
|
|
127
|
+
`auth.json` (mode `600`) is **state, not config**, and lives apart from it: the first
|
|
128
|
+
of `$MEDIAGEN_CLI_STATE_DIR`, `$XDG_STATE_HOME/mediagen-cli`,
|
|
129
|
+
`~/.local/state/mediagen-cli`. `~/.config` is the directory people sync, share and
|
|
130
|
+
commit; a key store there is one `.gitignore` mistake away from a leaked key. The
|
|
131
|
+
state directory is machine-local by definition, which is what a device-bound
|
|
132
|
+
credential is.
|
|
133
|
+
|
|
134
|
+
```toml
|
|
135
|
+
default_provider = "volcengine"
|
|
136
|
+
# output_dir = "./media-output"
|
|
137
|
+
|
|
138
|
+
[providers.volcengine] # the account: host + key
|
|
139
|
+
base_url = "https://ark.cn-beijing.volces.com"
|
|
140
|
+
api_key_env = "ARK_API_KEY"
|
|
141
|
+
|
|
142
|
+
[providers.volcengine.image] # what that account can do, one table per task
|
|
143
|
+
api_type = "ark" # the protocol is declared here, not on the account
|
|
144
|
+
default = "doubao-seedream-5-0-flash-260915"
|
|
145
|
+
models = ["doubao-seedream-5-0-flash-260915", "doubao-seedream-5-0-pro"]
|
|
146
|
+
|
|
147
|
+
[providers.volcengine.video]
|
|
148
|
+
api_type = "ark"
|
|
149
|
+
default = "doubao-seedance-1-0-pro"
|
|
150
|
+
models = ["doubao-seedance-1-0-pro"]
|
|
151
|
+
|
|
152
|
+
[providers.aliyun]
|
|
153
|
+
base_url = "https://dashscope.aliyuncs.com"
|
|
154
|
+
api_key_env = "DASHSCOPE_API_KEY"
|
|
155
|
+
|
|
156
|
+
[providers.aliyun.image]
|
|
157
|
+
api_type = "openai"
|
|
158
|
+
default = "qwen-image-3.0"
|
|
159
|
+
models = ["qwen-image-3.0"]
|
|
160
|
+
|
|
161
|
+
[providers.minimax] # a video-only account
|
|
162
|
+
base_url = "https://api.minimax.io"
|
|
163
|
+
api_key_env = "MINIMAX_API_KEY"
|
|
164
|
+
|
|
165
|
+
[providers.minimax.video]
|
|
166
|
+
api_type = "minimax"
|
|
167
|
+
default = "MiniMax-H3"
|
|
168
|
+
models = ["MiniMax-H3", "MiniMax-H3-Max"]
|
|
169
|
+
|
|
170
|
+
[providers.my-gateway] # a gateway that serves the native protocol
|
|
171
|
+
base_url = "https://gateway.example.com"
|
|
172
|
+
api_key_env = "MY_GATEWAY_KEY"
|
|
173
|
+
|
|
174
|
+
[providers.my-gateway.image]
|
|
175
|
+
api_type = "dashscope"
|
|
176
|
+
path = "/v1/images/generations" # the gateway's own path, not Bailian's
|
|
177
|
+
default = "qwen-image-2.0-pro"
|
|
178
|
+
models = ["qwen-image-2.0-pro"]
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
A provider is **one account**: a host (`base_url`) and an optional `api_key_env`.
|
|
182
|
+
Each task it serves hangs off it by name (`[providers.<name>.image]`,
|
|
183
|
+
`[providers.<name>.video]`) and declares its own `api_type`, the model ids you allow
|
|
184
|
+
there plus the `default` used when `--model` is absent. `timeout_seconds` is optional
|
|
185
|
+
per task (300 for image, 1800 for video). Two models of the same vendor are two
|
|
186
|
+
entries in one `models` list, not two providers; two protocols for one account are two
|
|
187
|
+
`api_type` lines, not two providers. `api_type` may also sit on the account table as
|
|
188
|
+
the default for every task under it.
|
|
189
|
+
|
|
190
|
+
**`base_url` is the origin** — an optional gateway prefix, never a version segment.
|
|
191
|
+
The version segment belongs to the endpoint, which the dialect owns per task
|
|
192
|
+
(`/api/v3/images/generations`, `/compatible-mode/v1/images/generations`,
|
|
193
|
+
`/api/v1/services/aigc/multimodal-generation/generation`), so switching
|
|
194
|
+
task changes only the path. A `base_url` that still ends with `/api/v3`, `/api/v1` or
|
|
195
|
+
`/compatible-mode/v1` on a vendor host is refused before any request, with the origin
|
|
196
|
+
to paste in. The file is the only source of truth: nothing is built in, and the first
|
|
197
|
+
run just writes the above into `config.toml` for you to edit. Both seeded entries run as
|
|
198
|
+
written: `volcengine` on Ark and `aliyun` on Bailian's native protocol, which is served on
|
|
199
|
+
the public host. Move `aliyun` to your workspace host
|
|
200
|
+
(`https://{workspace-id}.cn-beijing.maas.aliyuncs.com`) when you switch it to the
|
|
201
|
+
OpenAI-compatible protocol — that endpoint is not served publicly.
|
|
202
|
+
|
|
203
|
+
**A host whose API does not live at the vendor's own path** — a gateway, or a plan
|
|
204
|
+
that serves `/api/plan/v3` instead of `/api/v3` — declares that prefix on the task
|
|
205
|
+
with `path`, and the adapter appends nothing:
|
|
206
|
+
|
|
207
|
+
```toml
|
|
208
|
+
[providers.volcengine]
|
|
209
|
+
base_url = "https://ark.cn-beijing.volces.com/api/plan/v3"
|
|
210
|
+
|
|
211
|
+
[providers.volcengine.image]
|
|
212
|
+
path = "/images/generations" # request goes to .../api/plan/v3/images/generations
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Without `path`, a sub-path under a vendor's own domain is refused before any request
|
|
216
|
+
(it would be doubled), with the origin or the `path` value to use. `path` is only for
|
|
217
|
+
hosts that do not mirror the vendor's layout; the adapter's own path stays the default.
|
|
218
|
+
|
|
219
|
+
Video is asynchronous: `video generate` creates a task (that is what gets billed),
|
|
220
|
+
writes its id to stderr, then polls until the clip exists — two to five minutes is
|
|
221
|
+
normal, and the poll budget is the task's `timeout_seconds` (default 1800). The clip
|
|
222
|
+
URL is good for 24 hours, so `video status <id>` can fetch it later; `--no-wait` hands
|
|
223
|
+
the id back immediately, which is what an agent wants when its own timeout is shorter
|
|
224
|
+
than the vendor's render. One clip per request: `--n` belongs to images.
|
|
225
|
+
|
|
226
|
+
A key belongs to a provider entry: `--api-key`, else that entry's `api_key_env`,
|
|
227
|
+
else `auth.json`. There is no dialect-level default key variable, so two entries of
|
|
228
|
+
the same dialect can use different keys. Keys are only ever printed masked.
|
|
229
|
+
`base_url` lives in the file (with a one-off `--base-url` override on
|
|
230
|
+
`image generate`), and it is validated before any request: Bailian's
|
|
231
|
+
OpenAI-compatible endpoint (`compatible-mode`) is served only on a workspace-scoped
|
|
232
|
+
host, and the seed therefore ships the native protocol, whose endpoint is served
|
|
233
|
+
publicly. A `base_url` that still contains `<...>` is rejected before spending anything. `mediagen config show` prints each provider's
|
|
234
|
+
tasks, their models, and the endpoint each one would actually hit. Set
|
|
235
|
+
`NODE_USE_ENV_PROXY=1` on Node >= 24 to honour `HTTP_PROXY`. Timeouts: per request
|
|
236
|
+
`timeout_seconds` for the task (default 300 s, video 1800 s), 120 s per image
|
|
237
|
+
download and 600 s per video download. For video that budget is the *whole* poll
|
|
238
|
+
window, not one request: each create or poll gets 60 s, so a hung request cannot eat
|
|
239
|
+
the budget, and the download has its own 600 s and is not counted against it.
|
|
240
|
+
|
|
241
|
+
Working from an agent? `skills/mediagen/SKILL.md` describes what this CLI does, which tool
|
|
242
|
+
to prefer when the project already generates media another way, and the rules that keep a
|
|
243
|
+
paid request from being wasted.
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Development
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
npm install
|
|
251
|
+
npm run build # tsup -> dist/cli.js
|
|
252
|
+
npm test # vitest, fully offline: fetch is stubbed everywhere
|
|
253
|
+
npm run dev -- image generate --help
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
`src/cli.ts` wires commander and maps errors to exit codes; `src/generate.ts` runs
|
|
257
|
+
the pipeline (validate → resolve → call → download → report); `src/api/{qwen,ark,dashscope}.ts`
|
|
258
|
+
are the dialect adapters behind the `DialectAdapter` seam in `src/types.ts`, with
|
|
259
|
+
`config.ts`, `auth.ts`, `http.ts`, `download.ts` and `output.ts` around them.
|
|
260
|
+
|
|
261
|
+
Worth preserving: every check runs before the first `fetch`; paid requests are
|
|
262
|
+
never retried; stdout carries the result only; no output path ever holds an
|
|
263
|
+
unmasked key. A provider speaking an existing dialect needs no code — only a
|
|
264
|
+
`config.toml` table. A new dialect means an adapter in `src/api/`, a row in
|
|
265
|
+
`DIALECTS` (`src/api/index.ts`) plus its `path` field — the endpoint file name the
|
|
266
|
+
dialect appends to `base_url`.
|
|
267
|
+
|
|
268
|
+
Release: `npm pack --dry-run` to inspect the tarball (`dist/` and `skills/` are
|
|
269
|
+
published), then `npm version patch && npm publish`. Design docs live in
|
|
270
|
+
`docs/superpowers/specs/`.
|
|
271
|
+
|
|
272
|
+
MIT
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# mediagen
|
|
2
|
+
|
|
3
|
+
[English](README.md) | 简体中文
|
|
4
|
+
|
|
5
|
+
通过阿里云百炼(**qwen**)和火山方舟(**doubao / seedream**)生成与编辑图片,
|
|
6
|
+
通过火山方舟(**doubao / seedance**)或 MiniMax(**H3**)生成视频。结果写到磁盘、
|
|
7
|
+
打印文件路径。参数全部显式、没有交互式提问、`--json` 供程序解析。
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g @yandy0725/mediagen-cli
|
|
11
|
+
|
|
12
|
+
# 任何命令首次运行都会写出一份带注释的 config.toml;之后每次运行都会先校验它,
|
|
13
|
+
# config show 本身只读。直接编辑这个文件即可
|
|
14
|
+
mediagen config show
|
|
15
|
+
|
|
16
|
+
# 新增一条入口:一个账号(协议 + origin + key)带一张 image 任务表
|
|
17
|
+
mediagen config add my-proxy --api-type openai \
|
|
18
|
+
--base-url https://abc123.cn-beijing.maas.aliyuncs.com \
|
|
19
|
+
--model qwen-image-3.0 --api-key-env DASHSCOPE_API_KEY
|
|
20
|
+
# 视频表加 --task video;再加一个模型只需往 models 里多写一项
|
|
21
|
+
|
|
22
|
+
mediagen login --provider my-proxy --api-key sk-...
|
|
23
|
+
|
|
24
|
+
mediagen image generate "a red fox in fresh snow, soft morning light" --provider my-proxy --out-dir ./out
|
|
25
|
+
# /abs/path/out/my-proxy-qwen-image-3-0-20261025-020839-0.png
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Node >= 18,两个运行时依赖,需要 DashScope 和/或方舟的 API key。
|
|
29
|
+
|
|
30
|
+
## 命令
|
|
31
|
+
|
|
32
|
+
| 命令 | 作用 |
|
|
33
|
+
|---|---|
|
|
34
|
+
| `mediagen image generate <prompt>` | 文生图;加 `--image` 即变为编辑 |
|
|
35
|
+
| `mediagen image edit <prompt>` | 同上,但 `--image` 必填 |
|
|
36
|
+
| `mediagen video generate <prompt>` | 文生视频,或用一张首帧;建任务即计费 |
|
|
37
|
+
| `mediagen video status <task-id>` | 查一次任务,成功则把视频取下来 |
|
|
38
|
+
| `mediagen login` / `logout` / `auth status` | 保存、删除、查看 API key |
|
|
39
|
+
| `mediagen config show` / `config path` | 解析后的配置,以及它存在哪里 |
|
|
40
|
+
| `mediagen config add <name>` | 在 config.toml 里新建一条 provider(其余修改手写文件) |
|
|
41
|
+
|
|
42
|
+
`mediagen image generate --help` 与 `mediagen video generate --help` 才是参考手册:
|
|
43
|
+
全部参数、模型表、尺寸表、示例。下面只是简化版。
|
|
44
|
+
|
|
45
|
+
## 模型与参数
|
|
46
|
+
|
|
47
|
+
| 模型 | provider | 什么时候选它 |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| `qwen-image-3.0` | `--provider aliyun` | 文字渲染(中日韩 + 拉丁)、图像编辑 |
|
|
50
|
+
| `doubao-seedream-5-0-flash-260915` | `--provider volcengine` | 速度:草稿、迭代 |
|
|
51
|
+
| `doubao-seedream-5-0-pro` | `--provider volcengine --model doubao-seedream-5-0-pro` | 最高质量 |
|
|
52
|
+
|
|
53
|
+
| 视频模型 | provider | 什么时候选它 |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| `doubao-seedance-1-0-pro-250528` | `--provider volcengine` | 文生视频,或让一张首帧动起来 |
|
|
56
|
+
| `MiniMax-H3` | `--provider minimax` | 同样的事,最高 2K,来自 MiniMax |
|
|
57
|
+
|
|
58
|
+
`aliyun` / `volcengine` / `minimax` 是首次运行写进 `config.toml` 的三个 provider 名,可以随意改。
|
|
59
|
+
一个模型只有被列在该 provider **对应任务**(`image` 或 `video`)表的 `models = [...]` 里才可用;同一家厂商的
|
|
60
|
+
第二个模型是往那张表里加一行 —— 不需要新增 provider。模型 id 是带日期的快照,随版本
|
|
61
|
+
滚动时改 `models`(以及 `default`)即可。
|
|
62
|
+
|
|
63
|
+
### 协议
|
|
64
|
+
|
|
65
|
+
每个任务的表自己声明 `api_type`,所以同一个账号可以让图片走一种协议、视频走另一种:
|
|
66
|
+
|
|
67
|
+
| `api_type` | 线上形态 | 什么时候选它 |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| `openai` | OpenAI Images 形态:扁平的 `{model, prompt, size, n}`,返回 `data[].url` | 百炼的 `compatible-mode`、多数第三方代理 |
|
|
70
|
+
| `dashscope` | 阿里原生态:`input.messages` + `parameters`,返回 `output.choices[].message.content[].image` | 百炼原生端点,或暴露该协议的网关(见下) |
|
|
71
|
+
| `ark` | 火山自有 API:图片形态接近 OpenAI,但参数与逐条错误语义自成一套 | 火山 Ark,图片与视频 |
|
|
72
|
+
| `minimax` | MiniMax 的视频 API:`content[]` 元素按 `text`/`image_url` 分类型,结果嵌在 `task` 里 | MiniMax H3,仅视频 |
|
|
73
|
+
|
|
74
|
+
`dashscope` 只做图片,没有视频任务。`video generate` 讲 `ark` 或 `minimax`,两者各自
|
|
75
|
+
声明分辨率档位(`ark`:480p/720p/1080p/4k;`minimax`:480p/768p/2k)——所选方言没有的档位
|
|
76
|
+
在本地就失败,而**某个模型**支持哪一档由厂商在建任务时校验,此时还没计费。
|
|
77
|
+
|
|
78
|
+
`qwen` 仍可用,是 `openai` 的弃用别名。所选协议不接受的参数会在发出任何付费请求之前失败
|
|
79
|
+
(`--output-format` 仅 ark,`--prompt-extend-mode` 仅 openai;视频上
|
|
80
|
+
`--seed`/`--generate-audio`/`--output-format`/`--watermark` 仅 ark)。`--size` 接受档位(`2K`)、
|
|
81
|
+
像素(`2048x2048`)或 `auto`(仅 openai);`dashscope` 只收像素,因为它发送 `宽*高`,
|
|
82
|
+
没有档位与 `auto` 的概念。`--n` 接受 1-6 —— 在 ark 上每张图都是一个独立的付费请求。
|
|
83
|
+
|
|
84
|
+
## 输出
|
|
85
|
+
|
|
86
|
+
文件写到 `--out-dir`(否则 `output_dir`,否则当前目录),文件名
|
|
87
|
+
`<provider>-<model>-<yyyyMMdd-HHmmss>-<index>.<ext>`,永不覆盖。人类可读模式在
|
|
88
|
+
stdout 上每行打印一个绝对路径;`--json` 打印一个对象:
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{"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"}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`partial: true` 且退出码 `0` 表示部分图片成功、部分失败:磁盘上那些已经付过费了,
|
|
95
|
+
不要整条请求重跑。退出码:`0` 成功(含部分成功),`1` API / 配置 / 运行时失败,
|
|
96
|
+
`2` 用法错误 —— 总是在发请求之前抛出,所以不花钱。付费请求不会自动重试。
|
|
97
|
+
|
|
98
|
+
视频返回的是它创建的那个任务,因为**建任务那一刻就已经计费**:
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{"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}}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
视频没有 `partial`:一次请求一个片子。**`ok` 表示命令本身成功,不表示有文件** ——
|
|
105
|
+
`--no-wait`、以及 `video status` 查到 `pending`/`running`,都是 `{"ok":true,...,"files":[]}`
|
|
106
|
+
且退出码 `0`。要看 `status` 与 `files`:写成 `ok: false` 会让调用方去重跑一个已经付过费的
|
|
107
|
+
任务。轮询预算耗尽时错误里带 `task_id` 与 `TaskNotFinished`,用 `video status <id>`
|
|
108
|
+
续查即可,不会再付一次。
|
|
109
|
+
|
|
110
|
+
## 配置
|
|
111
|
+
|
|
112
|
+
`config.toml` —— 你手动编辑的那个文件 —— 位于
|
|
113
|
+
`$MEDIAGEN_CLI_CONFIG_DIR`、`$XDG_CONFIG_HOME/mediagen-cli`、
|
|
114
|
+
`~/.config/mediagen-cli` 三者中第一个存在的。
|
|
115
|
+
|
|
116
|
+
`auth.json`(权限 `600`)属于**状态而非配置**,单独存放:取
|
|
117
|
+
`$MEDIAGEN_CLI_STATE_DIR`、`$XDG_STATE_HOME/mediagen-cli`、
|
|
118
|
+
`~/.local/state/mediagen-cli` 三者中第一个。`~/.config` 是人们会同步、分享、提交到
|
|
119
|
+
仓库的目录,把密钥放在那里,一次 `.gitignore` 疏忽就等于泄露。state 目录按定义是
|
|
120
|
+
「本机状态、不该被同步」,正合设备绑定的凭证。
|
|
121
|
+
|
|
122
|
+
```toml
|
|
123
|
+
default_provider = "volcengine"
|
|
124
|
+
# output_dir = "./media-output"
|
|
125
|
+
|
|
126
|
+
[providers.volcengine] # 账号:主机 + key
|
|
127
|
+
base_url = "https://ark.cn-beijing.volces.com"
|
|
128
|
+
api_key_env = "ARK_API_KEY"
|
|
129
|
+
|
|
130
|
+
[providers.volcengine.image] # 能力:一个任务一张表
|
|
131
|
+
api_type = "ark" # 协议声明在任务上,不在账号上
|
|
132
|
+
default = "doubao-seedream-5-0-flash-260915"
|
|
133
|
+
models = ["doubao-seedream-5-0-flash-260915", "doubao-seedream-5-0-pro"]
|
|
134
|
+
|
|
135
|
+
[providers.volcengine.video]
|
|
136
|
+
api_type = "ark"
|
|
137
|
+
default = "doubao-seedance-1-0-pro"
|
|
138
|
+
models = ["doubao-seedance-1-0-pro"]
|
|
139
|
+
|
|
140
|
+
[providers.aliyun]
|
|
141
|
+
base_url = "https://dashscope.aliyuncs.com"
|
|
142
|
+
api_key_env = "DASHSCOPE_API_KEY"
|
|
143
|
+
|
|
144
|
+
[providers.aliyun.image]
|
|
145
|
+
api_type = "openai"
|
|
146
|
+
default = "qwen-image-3.0"
|
|
147
|
+
models = ["qwen-image-3.0"]
|
|
148
|
+
|
|
149
|
+
[providers.minimax] # 只做视频的账号
|
|
150
|
+
base_url = "https://api.minimax.io"
|
|
151
|
+
api_key_env = "MINIMAX_API_KEY"
|
|
152
|
+
|
|
153
|
+
[providers.minimax.video]
|
|
154
|
+
api_type = "minimax"
|
|
155
|
+
default = "MiniMax-H3"
|
|
156
|
+
models = ["MiniMax-H3", "MiniMax-H3-Max"]
|
|
157
|
+
|
|
158
|
+
[providers.my-gateway] # 提供原生协议的网关
|
|
159
|
+
base_url = "https://gateway.example.com"
|
|
160
|
+
api_key_env = "MY_GATEWAY_KEY"
|
|
161
|
+
|
|
162
|
+
[providers.my-gateway.image]
|
|
163
|
+
api_type = "dashscope"
|
|
164
|
+
path = "/v1/images/generations" # 网关自己的路径,不是百炼的
|
|
165
|
+
default = "qwen-image-2.0-pro"
|
|
166
|
+
models = ["qwen-image-2.0-pro"]
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
一个 provider = **一个账号**:一台主机(`base_url`)+ 可选的 `api_key_env`。它能提供的
|
|
170
|
+
每个任务按名字挂在下面(`[providers.<name>.image]`、`[providers.<name>.video]`),
|
|
171
|
+
各自声明 `api_type`、允许使用的模型 id,以及 `--model` 缺省时用的 `default`。
|
|
172
|
+
`timeout_seconds` 按任务可选(图片 300、视频 1800)。同一家厂商的两个模型是同一个
|
|
173
|
+
`models` 列表里的两项,不是两个 provider;同一个账号的两种协议是两行 `api_type`,
|
|
174
|
+
也不是两个 provider。`api_type` 也可以写在账号表上,作为其下所有任务的默认值。
|
|
175
|
+
|
|
176
|
+
**`base_url` 就是 origin** —— 可以带网关前缀,但不带版本段。版本段属于端点,由方言按任务
|
|
177
|
+
自己拼接(`/api/v3/images/generations`、`/compatible-mode/v1/images/generations`、
|
|
178
|
+
`/api/v1/services/aigc/multimodal-generation/generation`),
|
|
179
|
+
所以换任务只换 path。若在厂商自有域名上仍以 `/api/v3`、`/api/v1` 或
|
|
180
|
+
`/compatible-mode/v1` 结尾,会在发请求前被拒绝,并给出应填的 origin。
|
|
181
|
+
文件是唯一真相:内置没有任何 provider,首次运行只是把上面这份内容写进 `config.toml`
|
|
182
|
+
供你修改。种子三条都能直接跑:`volcengine` 走 Ark,`aliyun` 走百炼原生协议(公网域名即可),`minimax` 走 MiniMax 的视频 API。
|
|
183
|
+
若要把 `aliyun` 换成 OpenAI 兼容协议,需改用业务空间专属域名
|
|
184
|
+
`https://{workspace-id}.cn-beijing.maas.aliyuncs.com` —— 那个端点不对外公开提供。
|
|
185
|
+
|
|
186
|
+
**如果某个主机的 API 不在厂商自己的路径下** —— 例如网关,或某个把 `/api/v3` 换成
|
|
187
|
+
`/api/plan/v3` 的套餐 —— 在任务表上用 `path` 声明这段前缀,适配器就不再追加:
|
|
188
|
+
|
|
189
|
+
```toml
|
|
190
|
+
[providers.volcengine]
|
|
191
|
+
base_url = "https://ark.cn-beijing.volces.com/api/plan/v3"
|
|
192
|
+
|
|
193
|
+
[providers.volcengine.image]
|
|
194
|
+
path = "/images/generations" # 请求打到 .../api/plan/v3/images/generations
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
不写 `path` 时,厂商自有域名下的子路径会在发请求前被拒绝(否则会被拼重复),并给出
|
|
198
|
+
可用的 origin 或 `path` 值。`path` 只用于「不镜像厂商路径」的主机;平时适配器自带的
|
|
199
|
+
路径仍是默认值。
|
|
200
|
+
|
|
201
|
+
视频是异步的:`video generate` 先建任务(**计费就发生在这一刻**),把 task id 写进 stderr,
|
|
202
|
+
然后轮询到成片出现 —— 两三分钟是常态,轮询预算取该任务的 `timeout_seconds`(默认 1800)。
|
|
203
|
+
成片 URL 24 小时内有效,所以 `video status <id>` 可以稍后再取;`--no-wait` 立刻把 id 交回来,
|
|
204
|
+
这正是「自己的超时比厂商渲染更短」的 agent 需要的。一次请求一个片子:`--n` 属于图片。
|
|
205
|
+
|
|
206
|
+
key 跟着 provider 实例走,依次来自 `--api-key`、该实例声明的 `api_key_env`、
|
|
207
|
+
`auth.json`。没有任何方言级的默认 key 变量,所以同一方言的两条入口可以各用各的 key。
|
|
208
|
+
key 永远只以打码形式输出。`base_url` 写在文件里(`image generate` 有一次性的 `--base-url` 覆盖),并且会在发请求前校验:百炼的
|
|
209
|
+
OpenAI 兼容端点(`compatible-mode`)只在业务空间专属域名上提供,所以种子保留一个会明确失败
|
|
210
|
+
的占位符;而 `dashscope` 协议所用的百炼原生端点,公共域名同样可用。`mediagen config show`
|
|
211
|
+
会打印每个 provider 的任务、模型,以及各自实际会请求的 endpoint。Node >= 24 上设
|
|
212
|
+
`NODE_USE_ENV_PROXY=1` 可走 `HTTP_PROXY`。超时:每次请求取该任务的 `timeout_seconds`
|
|
213
|
+
(默认图片 300 秒、视频 1800 秒),每次图片下载 120 秒、视频下载 600 秒。对视频来说,
|
|
214
|
+
`timeout_seconds` 是**整个轮询窗口**,不是单次请求:建任务与每次轮询各自 60 秒,这样一个挂住
|
|
215
|
+
的请求吃不掉预算;下载另有 600 秒,不计入预算。
|
|
216
|
+
|
|
217
|
+
在 Agent 里用?`skills/mediagen/SKILL.md` 说明这个 CLI 能做什么、项目已有别的生成工具时该用哪个,
|
|
218
|
+
以及哪些规则能避免白花钱。
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## 开发
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
npm install
|
|
226
|
+
npm run build # tsup -> dist/cli.js
|
|
227
|
+
npm test # vitest,完全离线:所有地方的 fetch 都被打桩
|
|
228
|
+
npm run dev -- image generate --help
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
`src/cli.ts` 负责 commander 接线和错误到退出码的映射;`src/generate.ts` 是流水线
|
|
232
|
+
(校验 → 解析 → 调用 → 下载 → 汇报);`src/api/{qwen,ark,dashscope}.ts` 是
|
|
233
|
+
`src/types.ts` 中 `DialectAdapter` 接缝背后的方言适配器,周围是 `config.ts`、
|
|
234
|
+
`auth.ts`、`http.ts`、`download.ts`、`output.ts`。
|
|
235
|
+
|
|
236
|
+
值得守住的东西:所有检查都在第一次 `fetch` 之前完成;付费请求从不重试;stdout 只
|
|
237
|
+
放结果;任何输出路径都不含未打码的 key。说现有方言的新 provider 不需要写代码 ——
|
|
238
|
+
一张 `config.toml` 表就够。新方言则是 `src/api/` 下的适配器、`DIALECTS`
|
|
239
|
+
(`src/api/index.ts`)里的一行,以及适配器上的 `path` 字段 —— 方言拼在 `base_url`
|
|
240
|
+
后面的那个端点文件名。
|
|
241
|
+
|
|
242
|
+
发布:`npm pack --dry-run` 检查 tarball(发布 `dist/` 和 `skills/`),然后
|
|
243
|
+
`npm version patch && npm publish`。设计文档在 `docs/superpowers/specs/`。
|
|
244
|
+
|
|
245
|
+
MIT
|