@mrgoonie/multix 1.0.6 → 1.0.8
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/CHANGELOG.md +14 -0
- package/README.md +2 -2
- package/dist/cli.js +46 -21
- package/package.json +1 -2
- package/skill/SKILL.md +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.0.8](https://github.com/mrgoonie/multix-cli/compare/v1.0.7...v1.0.8) (2026-10-11)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
* **mcp:** tighten Worker duration/resolution defaults and Cloudflare video errors ([#120](https://github.com/mrgoonie/multix-cli/issues/120)) ([e082feb](https://github.com/mrgoonie/multix-cli/commit/e082feb3c1b3d0b88c96aa0a837b903759d6db00))
|
|
14
|
+
|
|
15
|
+
## [1.0.7](https://github.com/mrgoonie/multix-cli/compare/v1.0.6...v1.0.7) (2026-10-11)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
* **core:** let a later .env file fill a blank value ([#129](https://github.com/mrgoonie/multix-cli/issues/129)) ([bdfb673](https://github.com/mrgoonie/multix-cli/commit/bdfb6739770f0e7bb9d2623606cdb32d454779d4))
|
|
21
|
+
|
|
8
22
|
## [1.0.6](https://github.com/mrgoonie/multix-cli/compare/v1.0.5...v1.0.6) (2026-10-11)
|
|
9
23
|
|
|
10
24
|
|
package/README.md
CHANGED
|
@@ -115,7 +115,7 @@ Set at least one provider key. Add to `.env` in your project root or `~/.multix/
|
|
|
115
115
|
|
|
116
116
|
Set extra keys as `GEMINI_API_KEY_2`, `GEMINI_API_KEY_3`, … (any numeric suffix; numbered keys alone are enough). `multix` tries `GEMINI_API_KEY` first, then the numbered keys in ascending order, skipping duplicates. When a key returns HTTP 429 (or 400/403 with `RESOURCE_EXHAUSTED`), it cools down for the server's `Retry-After`/`retryDelay` (default 60s) and the same request is retried with the next ready key; other errors are not rotated. A Files API upload is repeated on the new key if the following generate call is rate-limited. Veo rotates on submit only, so a rate limit while polling or downloading never starts a second billed job. `doc convert` stops its own retries on a rate limit. Cooldowns last for the current process only, and logs name the env var, never the key. `multix check` lists the keys in rotation and pings each one. Quotas are per Google Cloud project: keys from the same project share one quota, so rotation only helps across projects.
|
|
117
117
|
|
|
118
|
-
Priority: `process.env` > `cwd/.env` > `~/.multix/.env`. Set `MULTIX_LEGACY_CLAUDE_ENV=1` (shell or `~/.multix/.env`) to also read legacy AgentKit files (`.claude/skills/{ai-multimodal,ak-ai-multimodal}/.env`, `.claude/skills/.env`, `.claude/.env`, project first, then home) with the lowest priority (credential names only, never endpoint/proxy settings); `multix check -v` lists the env files that were loaded.
|
|
118
|
+
Priority: `process.env` > `cwd/.env` > `~/.multix/.env`. A blank or whitespace-only value in a `.env` file does not count as set, so a project `.env` copied from `.env.example` does not hide `~/.multix/.env`; a shell variable wins even when blank. Set `MULTIX_LEGACY_CLAUDE_ENV=1` (shell or `~/.multix/.env`) to also read legacy AgentKit files (`.claude/skills/{ai-multimodal,ak-ai-multimodal}/.env`, `.claude/skills/.env`, `.claude/.env`, project first, then home) with the lowest priority (credential names only, never endpoint/proxy settings); `multix check -v` lists the env files that were loaded.
|
|
119
119
|
|
|
120
120
|
A project `.env` may only set variables `multix` reads: provider keys, the provider settings in `.env.example` (`*_BASE_URL`, model overrides, Cloudflare IDs), the documented `MULTIX_*` settings (`MULTIX_OUTPUT_DIR`, so a project can choose where outputs go, `MULTIX_IMAGE_FORMAT`, `MULTIX_MAX_DOWNLOAD_MB`, `MULTIX_UPLOAD_MIN_KBPS`) and `NO_COLOR`. The opt-ins `MULTIX_ALLOW_PROJECT_BASE_URL` and `MULTIX_LEGACY_CLAUDE_ENV` are refused there; set them in the shell or `~/.multix/.env`. Other names, such as `NODE_OPTIONS`, `npm_config_registry`, `LD_PRELOAD` or `HTTPS_PROXY`, are not loaded, so they never reach `multix` or the tools it starts (`npm` in `multix update`, ffmpeg, ImageMagick, cwebp); one warning on stderr lists the ignored names, never their values. The shell and `~/.multix/.env` are not filtered.
|
|
121
121
|
|
|
@@ -697,7 +697,7 @@ CLI.
|
|
|
697
697
|
`MULTIX_MCP_ALLOWED_MODELS`, `MULTIX_MCP_MAX_OUTPUTS`,
|
|
698
698
|
`MULTIX_MCP_MAX_VIDEO_SECONDS` (also sent as `min(5, cap)` when a caller
|
|
699
699
|
omits `duration`), `MULTIX_MCP_ALLOWED_RESOLUTIONS` (default
|
|
700
|
-
`480p,720p,1080p`), `MULTIX_MCP_ALLOWED_ORIGINS` (browser origins, with
|
|
700
|
+
`480p,720p,768p,1080p`), `MULTIX_MCP_ALLOWED_ORIGINS` (exact browser origins, with
|
|
701
701
|
CORS), and the approximate per-IP rate limiter. JSON-RPC batches are
|
|
702
702
|
rejected.
|
|
703
703
|
|
package/dist/cli.js
CHANGED
|
@@ -259,8 +259,9 @@ function buildModelInput(spec, requests, base = {}) {
|
|
|
259
259
|
}
|
|
260
260
|
const field = pickField(spec, req);
|
|
261
261
|
if (!field) {
|
|
262
|
+
const hint = /^(--|\[)/.test(req.flag) ? ` Use \`multix atlas run ${spec.model} --input '{...}'\` for model-specific fields.` : "";
|
|
262
263
|
throw new ValidationError(
|
|
263
|
-
`Model ${spec.model} does not accept ${req.flag}. Its inputs: ${listInputs(spec)}
|
|
264
|
+
`Model ${spec.model} does not accept ${req.flag}. Its inputs: ${listInputs(spec)}.${hint}`
|
|
264
265
|
);
|
|
265
266
|
}
|
|
266
267
|
const prop = spec.properties[field];
|
|
@@ -402,6 +403,12 @@ function parseFile(file) {
|
|
|
402
403
|
return void 0;
|
|
403
404
|
}
|
|
404
405
|
}
|
|
406
|
+
function fileMaySet(name, value) {
|
|
407
|
+
const current = process.env[name];
|
|
408
|
+
if (current === void 0) return true;
|
|
409
|
+
if (current.trim() !== "" || value.trim() === "") return false;
|
|
410
|
+
return fileOrigins.get(envNameKey(name))?.value === current;
|
|
411
|
+
}
|
|
405
412
|
function loadFile(file, source, accept = () => true, onRefused) {
|
|
406
413
|
const parsed = parseFile(file);
|
|
407
414
|
if (!parsed) return void 0;
|
|
@@ -410,7 +417,7 @@ function loadFile(file, source, accept = () => true, onRefused) {
|
|
|
410
417
|
onRefused?.(name);
|
|
411
418
|
continue;
|
|
412
419
|
}
|
|
413
|
-
if (
|
|
420
|
+
if (!fileMaySet(name, value)) continue;
|
|
414
421
|
process.env[name] = value;
|
|
415
422
|
fileOrigins.set(envNameKey(name), { source, file, value });
|
|
416
423
|
}
|
|
@@ -495,10 +502,8 @@ function inspectTrustedSetting(name, credentials, harmless) {
|
|
|
495
502
|
if (origin?.source !== "project") {
|
|
496
503
|
return value ? { status: "used", value, source: origin?.source ?? "shell" } : { status: "unset" };
|
|
497
504
|
}
|
|
505
|
+
if (value === void 0) return { status: "unset" };
|
|
498
506
|
const userValue = userFileValues[envNameKey(name)]?.trim() || void 0;
|
|
499
|
-
if (value === void 0) {
|
|
500
|
-
return userValue ? { status: "fallback", value: userValue, file: origin.file, reason: "it is empty" } : { status: "unset" };
|
|
501
|
-
}
|
|
502
507
|
const project = (basis) => ({
|
|
503
508
|
status: "used",
|
|
504
509
|
value,
|
|
@@ -518,10 +523,9 @@ function applyTrustedSetting(name, state) {
|
|
|
518
523
|
if (state.status === "unset") return void 0;
|
|
519
524
|
if (state.status === "used") return state.value;
|
|
520
525
|
if (state.status === "refused") throw new ConfigError(trustedSettingRefusal(name, state));
|
|
521
|
-
const hint = state.reason === "it is empty" ? "" : ` ${OPT_IN_HINT}`;
|
|
522
526
|
warnOnce(
|
|
523
527
|
`ignored:${name}`,
|
|
524
|
-
`Ignoring ${name} from project env file ${state.file} (${state.reason}); using the value from ~/.multix/.env
|
|
528
|
+
`Ignoring ${name} from project env file ${state.file} (${state.reason}); using the value from ~/.multix/.env. ${OPT_IN_HINT}`
|
|
525
529
|
);
|
|
526
530
|
return state.value;
|
|
527
531
|
}
|
|
@@ -3849,7 +3853,7 @@ var CLOUDFLARE_VIDEO_CREDENTIALS = [
|
|
|
3849
3853
|
function getCloudflareModel(kind, requested) {
|
|
3850
3854
|
const expected = kind === "image" ? CLOUDFLARE_IMAGE_MODEL : CLOUDFLARE_TTS_MODEL;
|
|
3851
3855
|
const overrideName = kind === "image" ? "CLOUDFLARE_AI_IMAGE_MODEL" : "CLOUDFLARE_AI_TTS_MODEL";
|
|
3852
|
-
const model = requested ??
|
|
3856
|
+
const model = requested ?? envDefault(overrideName, expected);
|
|
3853
3857
|
if (model !== expected) {
|
|
3854
3858
|
throw new ValidationError(`Unsupported Cloudflare ${kind} model: ${model}`);
|
|
3855
3859
|
}
|
|
@@ -3949,12 +3953,31 @@ function videoHeaders(config, opts) {
|
|
|
3949
3953
|
...opts.wait ? { Prefer: "wait" } : {}
|
|
3950
3954
|
};
|
|
3951
3955
|
}
|
|
3952
|
-
function
|
|
3953
|
-
const
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3956
|
+
function isAbort(cause) {
|
|
3957
|
+
const name = cause?.name;
|
|
3958
|
+
return name === "TimeoutError" || name === "AbortError";
|
|
3959
|
+
}
|
|
3960
|
+
function cloudflareFetchFailure(cause, url, timeoutMs) {
|
|
3961
|
+
const origin = new URL(url).origin;
|
|
3962
|
+
if (cause instanceof HttpError) {
|
|
3963
|
+
return new HttpError(cause.status, cause.snippet, origin, cause.retryAfterMs);
|
|
3964
|
+
}
|
|
3965
|
+
if (cause instanceof MultixError) return cause;
|
|
3966
|
+
if (isAbort(cause)) {
|
|
3967
|
+
return new HttpError(0, `request timed out after ${timeoutMs / 1e3}s`, origin, void 0, {
|
|
3968
|
+
cause
|
|
3969
|
+
});
|
|
3970
|
+
}
|
|
3971
|
+
return toFetchError(cause, origin, { provider: "Cloudflare" });
|
|
3972
|
+
}
|
|
3973
|
+
async function send(opts, url, init) {
|
|
3974
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
3975
|
+
const withTimeout = { ...init, signal: AbortSignal.timeout(timeoutMs) };
|
|
3976
|
+
try {
|
|
3977
|
+
return await fetchSameOrigin(url, withTimeout, opts.fetch);
|
|
3978
|
+
} catch (cause) {
|
|
3979
|
+
throw cloudflareFetchFailure(cause, url, timeoutMs);
|
|
3980
|
+
}
|
|
3958
3981
|
}
|
|
3959
3982
|
function parseCloudflareVideoPrediction(value) {
|
|
3960
3983
|
if (!value || typeof value !== "object") {
|
|
@@ -10806,7 +10829,7 @@ function registerOpenAIGenerateCommand(parent) {
|
|
|
10806
10829
|
parent.command("generate").description("Generate images with OpenAI Image API or experimental Codex driver").requiredOption("--prompt <text>", "Text prompt").option("--driver <driver>", "api|codex|auto", "auto").option(
|
|
10807
10830
|
"-m, --model <id>",
|
|
10808
10831
|
"Image model",
|
|
10809
|
-
|
|
10832
|
+
envDefault("OPENAI_IMAGE_MODEL", DEFAULT_OPENAI_IMAGE_MODEL)
|
|
10810
10833
|
).option("--size <size>", "1024x1024|1024x1536|1536x1024|auto", "1024x1024").option("--quality <q>", "low|medium|high", "medium").option(
|
|
10811
10834
|
"--format <fmt>",
|
|
10812
10835
|
"Provider output format png|jpeg|webp (explicit value keeps it; default png then WebP)"
|
|
@@ -10852,7 +10875,7 @@ function registerOpenAIImageToImageCommand(parent) {
|
|
|
10852
10875
|
).option("--driver <driver>", "api|codex|auto", "auto").option(
|
|
10853
10876
|
"-m, --model <id>",
|
|
10854
10877
|
"Image model",
|
|
10855
|
-
|
|
10878
|
+
envDefault("OPENAI_IMAGE_MODEL", DEFAULT_OPENAI_IMAGE_MODEL)
|
|
10856
10879
|
).option("--size <size>", "1024x1024|1024x1536|1536x1024|auto", "1024x1024").option("--quality <q>", "low|medium|high", "medium").option(
|
|
10857
10880
|
"--format <fmt>",
|
|
10858
10881
|
"Provider output format png|jpeg|webp (explicit value keeps it; default png then WebP)"
|
|
@@ -11129,7 +11152,7 @@ function registerOpenRouterGenerateCommand(parent) {
|
|
|
11129
11152
|
).requiredOption("--prompt <text>", "Image generation prompt").option("--model <id>", "OpenRouter model id (e.g. google/gemini-3.1-flash-image-preview)").option("--aspect-ratio <ratio>", "Aspect ratio (e.g. 1:1, 16:9)", "1:1").option("--image-size <size>", "Image size hint (e.g. 1K, 2K)").option("--num-images <n>", "Number of images to generate", "1").option("--output <path>", "Copy first image to this path").addOption(imageFormatOption()).addOption(noWebpOption()).option("-v, --verbose", "Verbose logging").action(
|
|
11130
11153
|
async (opts) => {
|
|
11131
11154
|
const logger = createLogger({ verbose: opts.verbose ?? false });
|
|
11132
|
-
const model = opts.model ??
|
|
11155
|
+
const model = opts.model ?? envDefault("OPENROUTER_IMAGE_MODEL", DEFAULT_OPENROUTER_MODEL);
|
|
11133
11156
|
const numImages = Math.max(1, Number.parseInt(opts.numImages, 10) || 1);
|
|
11134
11157
|
logger.debug(`Generating ${numImages} image(s) via OpenRouter: ${model}`);
|
|
11135
11158
|
const result = await generateOpenRouterImage({
|
|
@@ -11178,7 +11201,7 @@ function registerOpenRouterImageToImageCommand(parent) {
|
|
|
11178
11201
|
throw new ValidationError("At least one --ref is required for image-to-image.");
|
|
11179
11202
|
}
|
|
11180
11203
|
const logger = createLogger({ verbose: opts.verbose ?? false });
|
|
11181
|
-
const model = opts.model ??
|
|
11204
|
+
const model = opts.model ?? envDefault("OPENROUTER_IMAGE_MODEL", DEFAULT_MODEL);
|
|
11182
11205
|
const strength = parseStrength(opts.strength);
|
|
11183
11206
|
const fallbackModels = (resolveKey("OPENROUTER_FALLBACK_MODELS") ?? "").split(",").map((m) => m.trim()).filter(Boolean);
|
|
11184
11207
|
const resolvedRefs = [];
|
|
@@ -11394,7 +11417,7 @@ function registerOpenRouterImageToVideoCommand(parent) {
|
|
|
11394
11417
|
).requiredOption("--prompt <text>", "Motion/scene prompt").requiredOption("--image-url <url>", "First-frame image URL (https)").option("--last-frame-url <url>", "Optional last-frame image URL").option("--model <id>", "OpenRouter video model id (e.g. google/veo-3.1)").option("--resolution <r>", "Resolution hint (e.g. 720p, 1080p)").option("--aspect-ratio <r>", "Aspect ratio hint (e.g. 16:9, 9:16)").option("--duration <n>", "Duration seconds").option("--seed <n>", "Fixed seed").option("--wait", "Poll until status is 'completed' or 'failed'").option("--wait-timeout <ms>", "Poll timeout in ms (with --wait)", "600000").option("--download", "Download the generated video on success (implies --wait)").option("--output <path>", "Copy downloaded video to this path").option("--no-thumb", "Skip downloading the thumbnail if the API returns one").option("-v, --verbose", "Verbose logging").action(
|
|
11395
11418
|
async (opts) => {
|
|
11396
11419
|
const logger = createLogger({ verbose: opts.verbose ?? false });
|
|
11397
|
-
const model = opts.model ??
|
|
11420
|
+
const model = opts.model ?? envDefault("OPENROUTER_VIDEO_MODEL", "google/veo-3.1");
|
|
11398
11421
|
const frame_images = [
|
|
11399
11422
|
{
|
|
11400
11423
|
type: "image_url",
|
|
@@ -12631,8 +12654,10 @@ function settingLine(label, state, commands = "commands that use it") {
|
|
|
12631
12654
|
if (state.status === "used") {
|
|
12632
12655
|
return { level: "info", message: `${label} in effect (from ${usedFrom(state)})` };
|
|
12633
12656
|
}
|
|
12634
|
-
|
|
12635
|
-
|
|
12657
|
+
return {
|
|
12658
|
+
level: "warn",
|
|
12659
|
+
message: `${label} ignored: set in project .env while ${state.reason}; using ~/.multix/.env`
|
|
12660
|
+
};
|
|
12636
12661
|
}
|
|
12637
12662
|
var sameState = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
12638
12663
|
function routingSettingReport() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrgoonie/multix",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "AI multimodal CLI — OpenAI, Gemini, MiniMax, OpenRouter, Leonardo, BytePlus, ElevenLabs, fal.ai, Atlas Cloud, Replicate, HeyGen, ffmpeg, ImageMagick, doc-to-md",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -81,7 +81,6 @@
|
|
|
81
81
|
"commander": "^12.1.0",
|
|
82
82
|
"dotenv": "^16.4.5",
|
|
83
83
|
"execa": "^9.4.0",
|
|
84
|
-
"undici": "^7.2.0",
|
|
85
84
|
"zod": "^3.23.8"
|
|
86
85
|
},
|
|
87
86
|
"devDependencies": {
|
package/skill/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: multix
|
|
3
3
|
description: AI multimodal CLI — generate and edit images (OpenAI, Codex driver, Gemini Nano Banana, Imagen, MiniMax, OpenRouter, Leonardo, BytePlus Seedream, Cloudflare Workers AI FLUX.1 Schnell, fal.ai FLUX, Replicate FLUX), generate video (Gemini Veo, MiniMax Hailuo, OpenRouter via Veo/Seedance/Kling, Leonardo via Motion/Veo/Kling/Hailuo/Seedance/LTX, BytePlus Seedance, Cloudflare AI Gateway via Replicate, fal.ai queue models, Replicate direct API models incl. P-Video, HeyGen Video Agent and scripted avatar videos), video translation and lipsync (HeyGen), TTS/STT/music (OpenAI, Gemini Flash TTS incl. Gemini 3.8 with delivery-style control, MiniMax, Cloudflare Workers AI MeloTTS, ElevenLabs), 3D models (BytePlus Hyper3D/Hitem3d), analyze/transcribe media, convert documents to Markdown, optimize media via ffmpeg/ImageMagick, and self-update the CLI. Use whenever the user wants to create, edit, or transform images/video/audio/3D/documents from a CLI, mentions any of: OpenAI, Codex image driver, image-to-image, i2i, image edit, watercolor/cyberpunk style transfer, reference image, OpenRouter, Nano Banana, Flux, Seedream, Veo, Hailuo, Seedance, Kling, Cloudflare Workers AI, AI Gateway, fal.ai, Atlas Cloud, Replicate, replicate.com models, Replicate predictions, HeyGen, avatar video, talking head, Video Agent, video translation, dubbing, lipsync, video generation, text-to-video, image-to-video, i2v, ElevenLabs voice cloning, updating/upgrading multix, or asks for batch media optimization.
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.8 # x-release-please-version
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# multix — AI Multimodal CLI
|
|
@@ -54,7 +54,7 @@ CLOUDFLARE_API_TOKEN # Cloudflare Workers AI / AI Gateway token
|
|
|
54
54
|
# Video additionally requires CLOUDFLARE_AI_GATEWAY_ID and REPLICATE_API_TOKEN.
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
Priority: `process.env` > `cwd/.env` > `~/.multix/.env`. Set `MULTIX_LEGACY_CLAUDE_ENV=1` to also read legacy AgentKit `.claude` env files (credential names only, lowest priority); `multix check -v` lists loaded env files. A project `.env` may only set names multix reads (provider keys and settings from `.env.example`, the documented `MULTIX_*` settings such as `MULTIX_OUTPUT_DIR`, `NO_COLOR`; never `MULTIX_ALLOW_PROJECT_BASE_URL` or `MULTIX_LEGACY_CLAUDE_ENV`); anything else there (`NODE_OPTIONS`, `npm_config_*`, `LD_PRELOAD`, proxy/TLS variables) is not loaded, with one stderr warning naming the ignored variables. Put such variables in the shell.
|
|
57
|
+
Priority: `process.env` > `cwd/.env` > `~/.multix/.env`; a blank value in a `.env` file does not count as set, so a later file can fill it (a shell value wins even when blank). Set `MULTIX_LEGACY_CLAUDE_ENV=1` to also read legacy AgentKit `.claude` env files (credential names only, lowest priority); `multix check -v` lists loaded env files. A project `.env` may only set names multix reads (provider keys and settings from `.env.example`, the documented `MULTIX_*` settings such as `MULTIX_OUTPUT_DIR`, `NO_COLOR`; never `MULTIX_ALLOW_PROJECT_BASE_URL` or `MULTIX_LEGACY_CLAUDE_ENV`); anything else there (`NODE_OPTIONS`, `npm_config_*`, `LD_PRELOAD`, proxy/TLS variables) is not loaded, with one stderr warning naming the ignored variables. Put such variables in the shell.
|
|
58
58
|
|
|
59
59
|
## Gemini text-output tasks
|
|
60
60
|
|
|
@@ -479,7 +479,8 @@ local files), supports only Replicate, Atlas Cloud, HeyGen, and Cloudflare
|
|
|
479
479
|
video, and accepts only public `https://` image URLs. Check `limits` from
|
|
480
480
|
`multix_list_providers` before a video call: `duration` above
|
|
481
481
|
`max_video_seconds` or a `resolution` outside `allowed_resolutions` is
|
|
482
|
-
rejected, and an omitted `duration` becomes `default_video_seconds
|
|
482
|
+
rejected, and an omitted `duration` becomes `default_video_seconds` (on Atlas
|
|
483
|
+
Cloud, the model's own default when it is within `max_video_seconds`).
|
|
483
484
|
|
|
484
485
|
## Security & scope
|
|
485
486
|
|