focalapi-cli 0.5.1 → 0.6.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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.0 - 2026-08-26
4
+
5
+ - Retired `gen omni-video` with a local redirect to `gen video -m gemini-omni-flash-preview`: the gateway rejects the Interactions endpoint for this model in every shape (video matrix evidence 2026-08-26). All four capabilities (t2v/first-frame/reference/edit) ride the video facade.
6
+ - Added task-parameter flags to `gen video`, mapped to the task DTO's top-level fields with local enum validation: `--omni-reference-task-type` (seedance 2.5 edit/extend semantics were unreachable — auto-inference conflicts with the default duration), `--voice`/`--avatar` (HeyGen, enums now published by the platform), `--operation` (Bria), `--enhance-model` (HitPaw), and `--upscaler-model` (Topaz).
7
+ - Added local constraint tables for the August video wave (wan2.7 ×4, pixverse-v5, kling-v3-omni, luma-ray-3-2, MiniMax-H3, HeyGen ×2, happyhorse ×4, topaz/hitpaw video, beeble, bria, veo 3.1 ×3): duration tiers and resolutions are validated locally, and source-length families (heygen, topaz/hitpaw video, beeble, bria, happyhorse video-edit) reject `--seconds` up front instead of round-tripping a guaranteed 400.
8
+
3
9
  ## 0.5.1 - 2026-08-26
4
10
 
5
11
  - Added `@file` support to `--style-references` and `--moodboards`: the JSON array can now be read from a file instead of inline argv. Windows `cmd` mangles embedded double quotes in JSON arguments (matrix-run evidence 2026-08-25), and the file source sidesteps shell quoting entirely.
package/dist/cli.js CHANGED
@@ -176,7 +176,7 @@ function displayWidth(s) {
176
176
  }
177
177
 
178
178
  // src/lib/version.ts
179
- var VERSION = true ? "0.5.1" : "0.0.0-dev";
179
+ var VERSION = true ? "0.6.0" : "0.0.0-dev";
180
180
 
181
181
  // src/commands/auth.ts
182
182
  import { createInterface } from "readline/promises";
@@ -1321,6 +1321,42 @@ var VIDEO_CONSTRAINTS = {
1321
1321
  maxReferenceImages: 2,
1322
1322
  supportsGenerateAudio: true
1323
1323
  },
1324
+ "wan2.7-t2v": { resolutions: ["720p", "1080p"], minSeconds: 2, maxSeconds: 15 },
1325
+ "wan2.7-i2v": { resolutions: ["720p", "1080p"], minSeconds: 2, maxSeconds: 15 },
1326
+ "wan2.7-r2v": { resolutions: ["720p", "1080p"], minSeconds: 2, maxSeconds: 15 },
1327
+ "wan2.7-videoedit": { resolutions: ["720p", "1080p"], minSeconds: 2, maxSeconds: 10 },
1328
+ "pixverse-v5": { resolutions: ["360p", "540p", "720p", "1080p"], minSeconds: 5, maxSeconds: 8, allowedSeconds: [5, 8] },
1329
+ "kling-v3-omni": { resolutions: ["720p", "1080p", "4k"], minSeconds: 3, maxSeconds: 15 },
1330
+ "luma-ray-3-2": { resolutions: ["540p", "720p", "1080p"], minSeconds: 5, maxSeconds: 10, allowedSeconds: [5, 10] },
1331
+ "MiniMax-H3": { resolutions: ["768p", "2k"], minSeconds: 5, maxSeconds: 15 },
1332
+ "heygen-talking-photo": { resolutions: ["720p", "1080p"], noDuration: true },
1333
+ "heygen-avatar": { resolutions: ["720p", "1080p"], noDuration: true },
1334
+ "happyhorse-1.1-t2v": { resolutions: ["720p", "1080p"], minSeconds: 3, maxSeconds: 15 },
1335
+ "happyhorse-1.1-i2v": { resolutions: ["720p", "1080p"], minSeconds: 3, maxSeconds: 15 },
1336
+ "happyhorse-1.1-r2v": { resolutions: ["720p", "1080p"], minSeconds: 3, maxSeconds: 15 },
1337
+ "happyhorse-1.0-video-edit": { resolutions: ["720p", "1080p"], noDuration: true },
1338
+ "topaz-video-enhance": { resolutions: ["1080p", "2160p"], noDuration: true },
1339
+ "hitpaw-video-enhance": { resolutions: ["original", "720p", "1080p", "2k/qhd", "4k/uhd", "8k"], noDuration: true },
1340
+ "beeble-switchx-video": { resolutions: ["720p", "1080p"], noDuration: true },
1341
+ "bria-video-edit": { noDuration: true },
1342
+ "veo-3.1-generate-preview": {
1343
+ resolutions: ["720p", "1080p"],
1344
+ minSeconds: 4,
1345
+ maxSeconds: 8,
1346
+ allowedSeconds: [4, 6, 8]
1347
+ },
1348
+ "veo-3.1-fast-generate-preview": {
1349
+ resolutions: ["720p", "1080p"],
1350
+ minSeconds: 4,
1351
+ maxSeconds: 8,
1352
+ allowedSeconds: [4, 6, 8]
1353
+ },
1354
+ "veo-3.1-lite-generate-preview": {
1355
+ resolutions: ["720p", "1080p"],
1356
+ minSeconds: 4,
1357
+ maxSeconds: 8,
1358
+ allowedSeconds: [4, 6, 8]
1359
+ },
1324
1360
  "flux-3": {
1325
1361
  resolutions: ["hd", "fhd"],
1326
1362
  ratios: FLUX_VIDEO_RATIOS,
@@ -1523,6 +1559,9 @@ function validateGeminiImageGeneration(model, input) {
1523
1559
  function validateVideoGeneration(model, input) {
1524
1560
  const constraint = VIDEO_CONSTRAINTS[model.trim()];
1525
1561
  if (!constraint) return;
1562
+ if (constraint.noDuration && input.seconds !== void 0) {
1563
+ throw new ApiError("invalid_request", `${model} does not accept a duration; the output length follows the source media`);
1564
+ }
1526
1565
  if (constraint.maxPromptRunes !== void 0 && input.promptRunes !== void 0) {
1527
1566
  const referenceCount = input.imageCount ?? input.firstFrameCount ?? 0;
1528
1567
  if (input.promptRunes + 500 * referenceCount > constraint.maxPromptRunes) {
@@ -1547,13 +1586,13 @@ function validateVideoGeneration(model, input) {
1547
1586
  if (input.watermark !== void 0 && constraint.supportsWatermark === false) {
1548
1587
  throw new ApiError("invalid_request", `${model} does not support watermark`);
1549
1588
  }
1550
- if (input.seconds !== void 0 && (input.seconds < constraint.minSeconds || input.seconds > constraint.maxSeconds)) {
1589
+ if (input.seconds !== void 0 && constraint.minSeconds !== void 0 && constraint.maxSeconds !== void 0 && (input.seconds < constraint.minSeconds || input.seconds > constraint.maxSeconds)) {
1551
1590
  throw new ApiError("invalid_request", `${model} seconds must be ${constraint.minSeconds}-${constraint.maxSeconds} (received: ${input.seconds})`);
1552
1591
  }
1553
1592
  if (input.seconds !== void 0 && constraint.allowedSeconds && !constraint.allowedSeconds.includes(input.seconds)) {
1554
1593
  throw new ApiError("invalid_request", `${model} seconds must be one of ${constraint.allowedSeconds.join(", ")} (received: ${input.seconds})`);
1555
1594
  }
1556
- if (input.resolution && !constraint.resolutions.includes(input.resolution.toLowerCase())) {
1595
+ if (input.resolution && constraint.resolutions && !constraint.resolutions.includes(input.resolution.toLowerCase())) {
1557
1596
  throw new ApiError("invalid_request", `${model} resolution must be one of ${constraint.resolutions.join(", ")} (received: ${input.resolution})`);
1558
1597
  }
1559
1598
  if (input.resolution && (input.imageCount ?? 0) > 0 && constraint.referenceResolutions && !constraint.referenceResolutions.includes(input.resolution.toLowerCase())) {
@@ -2055,24 +2094,6 @@ function geminiImagePart(source) {
2055
2094
  }
2056
2095
  return { fileData: { fileUri: source } };
2057
2096
  }
2058
- function geminiOmniImageInput(source) {
2059
- const dataUri = /^data:([^;,]+);base64,([a-z0-9+/=\r\n]+)$/i.exec(source.trim());
2060
- if (!dataUri) {
2061
- throw new ApiError("invalid_request", "--image for Gemini Omni must be a base64 data URI");
2062
- }
2063
- const [, mimeType = "", data = ""] = dataUri;
2064
- return { type: "image", mime_type: mimeType, data: data.replace(/[\r\n]/g, "") };
2065
- }
2066
- function extractGeminiOmniVideo(response) {
2067
- for (const step of response.steps ?? []) {
2068
- for (const content of step.content ?? []) {
2069
- if (content.type === "video" && content.data) {
2070
- return { data: content.data, mimeType: content.mime_type };
2071
- }
2072
- }
2073
- }
2074
- return void 0;
2075
- }
2076
2097
  function extractGeminiImageItems(response) {
2077
2098
  return (response.candidates ?? []).flatMap(
2078
2099
  (candidate) => (candidate.content?.parts ?? []).flatMap(
@@ -2241,49 +2262,10 @@ function registerGen(program) {
2241
2262
  for (const file of files) info(`\u2713 ${file}`);
2242
2263
  }
2243
2264
  });
2244
- gen.command("omni-video").description("\u4F7F\u7528 Gemini Omni Flash \u539F\u751F Interactions API \u751F\u6210\u6216\u7F16\u8F91\u89C6\u9891").argument("<prompt...>", "\u63D0\u793A\u8BCD").option("--image <data-uri...>", "\u56FE\u751F\u89C6\u9891\u53C2\u8003\u56FE\uFF1B\u4EC5\u652F\u6301 base64 data URI\uFF0C\u53EF\u591A\u4E2A").option("--previous-interaction-id <id>", "\u4E0A\u4E00\u6B21\u4EA4\u4E92 ID\uFF0C\u7528\u4E8E\u8FDE\u7EED\u89C6\u9891\u7F16\u8F91").option("--aspect-ratio <ratio>", "\u753B\u9762\u6BD4\u4F8B\uFF1A16:9 \u6216 9:16").option("--task <task>", "\u89C6\u9891\u4EFB\u52A1\uFF1Atext_to_video\u3001image_to_video\u3001reference_to_video \u6216 edit").option("-o, --out <dir>", "\u8F93\u51FA\u76EE\u5F55", DEFAULT_OUT_DIR).action(async (promptParts, opts, cmd) => {
2245
- const g = cmd.optsWithGlobals();
2246
- const auth = resolveAuth(g);
2247
- if (opts.aspectRatio && !["16:9", "9:16"].includes(opts.aspectRatio)) {
2248
- throw new ApiError("invalid_request", "--aspect-ratio must be 16:9 or 9:16");
2249
- }
2250
- if (opts.task && !["text_to_video", "image_to_video", "reference_to_video", "edit"].includes(opts.task)) {
2251
- throw new ApiError("invalid_request", "--task must be text_to_video, image_to_video, reference_to_video, or edit");
2252
- }
2253
- const prompt = promptParts.join(" ");
2254
- const imageInputs = (opts.image ?? []).map(geminiOmniImageInput);
2255
- const input = imageInputs.length === 0 ? prompt : [...imageInputs, { type: "text", text: prompt }];
2256
- const res = await withProgress("\u6B63\u5728\u751F\u6210 Gemini Omni \u89C6\u9891", () => request({
2257
- baseUrl: auth.baseUrl,
2258
- path: "/v1beta/interactions",
2259
- apiKey: auth.apiKey,
2260
- body: {
2261
- model: "gemini-omni-flash-preview",
2262
- input,
2263
- ...opts.previousInteractionId ? { previous_interaction_id: opts.previousInteractionId } : {},
2264
- ...opts.aspectRatio ? { response_format: { type: "video", aspect_ratio: opts.aspectRatio } } : {},
2265
- ...opts.task ? { generation_config: { video_config: { task: opts.task } } } : {}
2266
- },
2267
- timeoutMs: 6e5
2268
- }));
2269
- const video = extractGeminiOmniVideo(res);
2270
- if (!video) {
2271
- throw new ApiError("bad_response", "Gemini Omni \u54CD\u5E94\u4E2D\u672A\u627E\u5230\u89C6\u9891\u6570\u636E", { body: res });
2272
- }
2273
- const dir = resolve2(opts.out);
2274
- await mkdir2(dir, { recursive: true });
2275
- const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
2276
- const extension = video.mimeType?.includes("webm") ? ".webm" : ".mp4";
2277
- const file = join3(dir, `gemini-omni-${timestamp}${extension}`);
2278
- await writeFile(file, Buffer.from(video.data, "base64"));
2279
- if (g.json) {
2280
- printJson({ interaction_id: res.id, file });
2281
- } else {
2282
- info(`\u2713 ${file}`);
2283
- if (res.id) info(`\u4EA4\u4E92 ID\uFF1A${res.id}`);
2284
- }
2265
+ gen.command("omni-video").description("\u5DF2\u4E0B\u7EBF\uFF1AGemini Omni Flash \u5168\u90E8\u80FD\u529B\u6539\u8D70 gen video \u95E8\u9762").allowUnknownOption().action(async () => {
2266
+ throw new ApiError("invalid_request", "gen omni-video \u5DF2\u4E0B\u7EBF\uFF1Agemini-omni-flash-preview \u7684\u6587\u751F\u89C6\u9891/\u9996\u5E27/\u53C2\u8003/\u7F16\u8F91\u80FD\u529B\u5168\u90E8\u6539\u8D70 `focalapi gen video -m gemini-omni-flash-preview`\uFF08\u652F\u6301 --first-frame/--image/--content\uFF09");
2285
2267
  });
2286
- gen.command("video").description("\u751F\u6210\u89C6\u9891\uFF08\u7701\u7565 --model \u65F6\u81EA\u52A8\u9009\u62E9\u5F53\u524D\u53EF\u7528\u9ED8\u8BA4\u6A21\u578B\uFF09").argument("<prompt...>", "\u63D0\u793A\u8BCD").option("-m, --model <model>", "\u89C6\u9891\u6A21\u578B ID\uFF1B\u7701\u7565\u65F6\u7531 focalapi \u81EA\u52A8\u9009\u62E9").option("--seconds <n>", "\u65F6\u957F\u79D2\u6570\uFF1B--duration \u4E3A\u540C\u4E49\u522B\u540D\u3002\u7CBE\u786E\u8303\u56F4\u8FD0\u884C focalapi models get <model> \u67E5\u770B", (v) => Number.parseInt(v, 10)).option("--duration <n>", "\u65F6\u957F\u79D2\u6570\uFF08--seconds \u7684\u522B\u540D\uFF0C\u4E0E API \u5951\u7EA6\u5B57\u6BB5\u540C\u540D\uFF09", (v) => Number.parseInt(v, 10)).option("--size <size>", "\u5206\u8FA8\u7387\uFF0C\u5982 1280x720").option("--resolution <resolution>", "\u539F\u751F\u8F93\u51FA\u5206\u8FA8\u7387\uFF0C\u5982 480p\u3001720p\u30011080p\u30014k").option("--ratio <ratio>", "\u539F\u751F\u5BBD\u9AD8\u6BD4\uFF0C\u5982 16:9\u30019:16\u3001adaptive").option("--aspect-ratio <ratio>", "\u6A21\u578B\u539F\u751F\u753B\u9762\u6BD4\u4F8B\uFF0C\u5982 16:9\u30019:16\u3001auto").option("--seed <n>", "\u6A21\u578B\u968F\u673A\u79CD\u5B50\uFF08\u975E\u8D1F\u6574\u6570\uFF09", (v) => Number.parseInt(v, 10)).option("--fps <n>", "\u8F93\u51FA\u5E27\u7387\uFF08\u4EC5\u652F\u6301\u8BE5\u53C2\u6570\u7684\u6A21\u578B\u751F\u6548\uFF09", (v) => Number.parseInt(v, 10)).option("--safety-tolerance <n>", "\u5B89\u5168\u5BB9\u5FCD\u5EA6\uFF08\u4EC5\u652F\u6301\u8BE5\u53C2\u6570\u7684\u6A21\u578B\u751F\u6548\uFF09", (v) => Number.parseInt(v, 10)).option("--image <url...>", "\u53C2\u8003\u56FE URL \u6216\u672C\u5730\u8DEF\u5F84\uFF08@C:/path/x.jpg\uFF09\uFF0CGrok \u89C6\u9891\u4E3A reference-to-video \u6A21\u5F0F").option("--first-frame <url>", "\u56FE\u751F\u89C6\u9891\u9996\u5E27\u56FE URL\uFF08image-to-video \u6A21\u5F0F\uFF1B\u4E0E --image \u4E92\u65A5\uFF09").option("--generate-audio <boolean>", "\u662F\u5426\u751F\u6210\u97F3\u9891\uFF08\u53EA\u63A5\u53D7 true \u6216 false\uFF09", (v) => parseBooleanOption(v, "generate-audio")).option("--watermark <boolean>", "\u662F\u5426\u6DFB\u52A0\u6C34\u5370\uFF08\u53EA\u63A5\u53D7 true \u6216 false\uFF09", (v) => parseBooleanOption(v, "watermark")).option("--service-tier <tier>", "\u670D\u52A1\u5C42\u7EA7\uFF08Seedance 2.0 \u9ED8\u8BA4 default\uFF09").option("--priority <n>", "\u4EFB\u52A1\u4F18\u5148\u7EA7\uFF08\u4EC5 Seedance 2.0 \u7CFB\u5217\uFF09", (v) => Number.parseInt(v, 10)).option("--callback-url <url>", "\u4EFB\u52A1\u5B8C\u6210\u56DE\u8C03 URL").option("--return-last-frame <boolean>", "\u662F\u5426\u8FD4\u56DE\u6700\u540E\u4E00\u5E27\uFF08\u53EA\u63A5\u53D7 true \u6216 false\uFF09", (v) => parseBooleanOption(v, "return-last-frame")).option("--execution-expires-after <seconds>", "\u4EFB\u52A1\u8FC7\u671F\u79D2\u6570\uFF083600\u2013259200\uFF09", (v) => Number.parseInt(v, 10)).option("--safety-identifier <identifier>", "Seedance \u5B89\u5168\u6807\u8BC6\u7B26\uFF081\u201364 \u4E2A\u53EF\u6253\u5370 ASCII \u5B57\u7B26\uFF09").option("--no-wait", "\u63D0\u4EA4\u540E\u7ACB\u5373\u8FD4\u56DE task_id\uFF0C\u4E0D\u7B49\u5F85\u5B8C\u6210").option("--idempotency-key <key>", "\u5E42\u7B49\u952E\uFF088\u2013128 \u4E2A\u53EF\u6253\u5370 ASCII \u5B57\u7B26\uFF09\u3002\u540C\u4E00 key \u7684\u91CD\u590D\u63D0\u4EA4\u62FF\u56DE\u539F\u4EFB\u52A1\u800C\u4E0D\u91CD\u590D\u8BA1\u8D39\uFF1B\u7701\u7565\u65F6\u81EA\u52A8\u751F\u6210\u3002\u91CD\u8BD5\u4E0D\u786E\u5B9A\u7684\u63D0\u4EA4\u65F6\u52A1\u5FC5\u590D\u7528\u539F key").option("--poll-interval <ms>", "\u8F6E\u8BE2\u95F4\u9694\u6BEB\u79D2", (v) => Number.parseInt(v, 10), 5e3).option("--timeout <minutes>", "\u6700\u957F\u7B49\u5F85\u5206\u949F", (v) => Number.parseInt(v, 10), 30).option("-o, --out <dir>", "\u8F93\u51FA\u76EE\u5F55", DEFAULT_OUT_DIR).option("--content <json>", "Ark content JSON \u6570\u7EC4\uFF1B\u652F\u6301\u5185\u8054 JSON\u3001@\u6587\u4EF6\u8DEF\u5F84\uFF08\u591A\u56FE base64 \u5FC5\u987B\u8D70\u6587\u4EF6\uFF09\u6216 - \uFF08stdin\uFF09\uFF1B\u8986\u76D6 prompt/image \u95E8\u9762\u5B57\u6BB5").action(
2268
+ gen.command("video").description("\u751F\u6210\u89C6\u9891\uFF08\u7701\u7565 --model \u65F6\u81EA\u52A8\u9009\u62E9\u5F53\u524D\u53EF\u7528\u9ED8\u8BA4\u6A21\u578B\uFF09").argument("<prompt...>", "\u63D0\u793A\u8BCD").option("-m, --model <model>", "\u89C6\u9891\u6A21\u578B ID\uFF1B\u7701\u7565\u65F6\u7531 focalapi \u81EA\u52A8\u9009\u62E9").option("--seconds <n>", "\u65F6\u957F\u79D2\u6570\uFF1B--duration \u4E3A\u540C\u4E49\u522B\u540D\u3002\u7CBE\u786E\u8303\u56F4\u8FD0\u884C focalapi models get <model> \u67E5\u770B", (v) => Number.parseInt(v, 10)).option("--duration <n>", "\u65F6\u957F\u79D2\u6570\uFF08--seconds \u7684\u522B\u540D\uFF0C\u4E0E API \u5951\u7EA6\u5B57\u6BB5\u540C\u540D\uFF09", (v) => Number.parseInt(v, 10)).option("--size <size>", "\u5206\u8FA8\u7387\uFF0C\u5982 1280x720").option("--resolution <resolution>", "\u539F\u751F\u8F93\u51FA\u5206\u8FA8\u7387\uFF0C\u5982 480p\u3001720p\u30011080p\u30014k").option("--ratio <ratio>", "\u539F\u751F\u5BBD\u9AD8\u6BD4\uFF0C\u5982 16:9\u30019:16\u3001adaptive").option("--aspect-ratio <ratio>", "\u6A21\u578B\u539F\u751F\u753B\u9762\u6BD4\u4F8B\uFF0C\u5982 16:9\u30019:16\u3001auto").option("--seed <n>", "\u6A21\u578B\u968F\u673A\u79CD\u5B50\uFF08\u975E\u8D1F\u6574\u6570\uFF09", (v) => Number.parseInt(v, 10)).option("--fps <n>", "\u8F93\u51FA\u5E27\u7387\uFF08\u4EC5\u652F\u6301\u8BE5\u53C2\u6570\u7684\u6A21\u578B\u751F\u6548\uFF09", (v) => Number.parseInt(v, 10)).option("--safety-tolerance <n>", "\u5B89\u5168\u5BB9\u5FCD\u5EA6\uFF08\u4EC5\u652F\u6301\u8BE5\u53C2\u6570\u7684\u6A21\u578B\u751F\u6548\uFF09", (v) => Number.parseInt(v, 10)).option("--image <url...>", "\u53C2\u8003\u56FE URL \u6216\u672C\u5730\u8DEF\u5F84\uFF08@C:/path/x.jpg\uFF09\uFF0CGrok \u89C6\u9891\u4E3A reference-to-video \u6A21\u5F0F").option("--first-frame <url>", "\u56FE\u751F\u89C6\u9891\u9996\u5E27\u56FE URL\uFF08image-to-video \u6A21\u5F0F\uFF1B\u4E0E --image \u4E92\u65A5\uFF09").option("--generate-audio <boolean>", "\u662F\u5426\u751F\u6210\u97F3\u9891\uFF08\u53EA\u63A5\u53D7 true \u6216 false\uFF09", (v) => parseBooleanOption(v, "generate-audio")).option("--watermark <boolean>", "\u662F\u5426\u6DFB\u52A0\u6C34\u5370\uFF08\u53EA\u63A5\u53D7 true \u6216 false\uFF09", (v) => parseBooleanOption(v, "watermark")).option("--service-tier <tier>", "\u670D\u52A1\u5C42\u7EA7\uFF08Seedance 2.0 \u9ED8\u8BA4 default\uFF09").option("--priority <n>", "\u4EFB\u52A1\u4F18\u5148\u7EA7\uFF08\u4EC5 Seedance 2.0 \u7CFB\u5217\uFF09", (v) => Number.parseInt(v, 10)).option("--callback-url <url>", "\u4EFB\u52A1\u5B8C\u6210\u56DE\u8C03 URL").option("--return-last-frame <boolean>", "\u662F\u5426\u8FD4\u56DE\u6700\u540E\u4E00\u5E27\uFF08\u53EA\u63A5\u53D7 true \u6216 false\uFF09", (v) => parseBooleanOption(v, "return-last-frame")).option("--execution-expires-after <seconds>", "\u4EFB\u52A1\u8FC7\u671F\u79D2\u6570\uFF083600\u2013259200\uFF09", (v) => Number.parseInt(v, 10)).option("--safety-identifier <identifier>", "Seedance \u5B89\u5168\u6807\u8BC6\u7B26\uFF081\u201364 \u4E2A\u53EF\u6253\u5370 ASCII \u5B57\u7B26\uFF09").option("--omni-reference-task-type <type>", "Seedance 2.5 \u591A\u6A21\u6001\u4EFB\u52A1\u7C7B\u578B\uFF1Aauto\u3001reference\u3001edit \u6216 extend\uFF08\u7F16\u8F91/\u5EF6\u957F\u53C2\u8003\u89C6\u9891\u65F6\u5FC5\u586B\uFF0Cauto \u4F1A\u4E0E\u9ED8\u8BA4\u65F6\u957F\u51B2\u7A81\uFF09").option("--voice <label>", "HeyGen \u8BED\u97F3\u6807\u7B7E\uFF08talking-photo \u5267\u672C\u5FC5\u586B\uFF1B\u5408\u6CD5\u503C\u89C1 models get heygen-talking-photo \u7684 voice \u679A\u4E3E\uFF09").option("--avatar <label>", "HeyGen Avatar \u5F62\u8C61\u6807\u7B7E\uFF08heygen-avatar \u5FC5\u586B\uFF1B\u5408\u6CD5\u503C\u89C1 models get heygen-avatar \u7684 avatar \u679A\u4E3E\uFF09").option("--operation <op>", "Bria \u89C6\u9891\u64CD\u4F5C\uFF1Aremove_background\u3001green_screen \u6216 replace_background").option("--enhance-model <id>", "HitPaw \u89C6\u9891\u589E\u5F3A\u6A21\u578B\uFF08\u5982 general_restore_2x\uFF1B\u5B8C\u6574\u679A\u4E3E\u89C1 models get hitpaw-video-enhance\uFF09").option("--upscaler-model <family>", "Topaz \u89C6\u9891\u653E\u5927\u6A21\u578B\uFF1Astarlight_fast\u3001starlight_creative\u3001starlight_precise_25 \u6216 astra_2").option("--no-wait", "\u63D0\u4EA4\u540E\u7ACB\u5373\u8FD4\u56DE task_id\uFF0C\u4E0D\u7B49\u5F85\u5B8C\u6210").option("--idempotency-key <key>", "\u5E42\u7B49\u952E\uFF088\u2013128 \u4E2A\u53EF\u6253\u5370 ASCII \u5B57\u7B26\uFF09\u3002\u540C\u4E00 key \u7684\u91CD\u590D\u63D0\u4EA4\u62FF\u56DE\u539F\u4EFB\u52A1\u800C\u4E0D\u91CD\u590D\u8BA1\u8D39\uFF1B\u7701\u7565\u65F6\u81EA\u52A8\u751F\u6210\u3002\u91CD\u8BD5\u4E0D\u786E\u5B9A\u7684\u63D0\u4EA4\u65F6\u52A1\u5FC5\u590D\u7528\u539F key").option("--poll-interval <ms>", "\u8F6E\u8BE2\u95F4\u9694\u6BEB\u79D2", (v) => Number.parseInt(v, 10), 5e3).option("--timeout <minutes>", "\u6700\u957F\u7B49\u5F85\u5206\u949F", (v) => Number.parseInt(v, 10), 30).option("-o, --out <dir>", "\u8F93\u51FA\u76EE\u5F55", DEFAULT_OUT_DIR).option("--content <json>", "Ark content JSON \u6570\u7EC4\uFF1B\u652F\u6301\u5185\u8054 JSON\u3001@\u6587\u4EF6\u8DEF\u5F84\uFF08\u591A\u56FE base64 \u5FC5\u987B\u8D70\u6587\u4EF6\uFF09\u6216 - \uFF08stdin\uFF09\uFF1B\u8986\u76D6 prompt/image \u95E8\u9762\u5B57\u6BB5").action(
2287
2269
  async (promptParts, opts, cmd) => {
2288
2270
  const g = cmd.optsWithGlobals();
2289
2271
  const auth = resolveAuth(g);
@@ -2318,6 +2300,28 @@ function registerGen(program) {
2318
2300
  if (opts.returnLastFrame !== void 0) metadata.return_last_frame = opts.returnLastFrame;
2319
2301
  if (opts.executionExpiresAfter !== void 0) metadata.execution_expires_after = opts.executionExpiresAfter;
2320
2302
  if (opts.safetyIdentifier) metadata.safety_identifier = opts.safetyIdentifier;
2303
+ if (opts.omniReferenceTaskType) {
2304
+ const taskType = opts.omniReferenceTaskType.toLowerCase();
2305
+ if (!["auto", "reference", "edit", "extend"].includes(taskType)) {
2306
+ throw new ApiError("invalid_request", "--omni-reference-task-type must be auto, reference, edit, or extend");
2307
+ }
2308
+ body.omni_reference_task_type = taskType;
2309
+ }
2310
+ if (opts.voice) body.voice = opts.voice;
2311
+ if (opts.avatar) body.avatar = opts.avatar;
2312
+ if (opts.operation) {
2313
+ if (!["remove_background", "green_screen", "replace_background"].includes(opts.operation)) {
2314
+ throw new ApiError("invalid_request", "--operation must be remove_background, green_screen, or replace_background");
2315
+ }
2316
+ body.operation = opts.operation;
2317
+ }
2318
+ if (opts.enhanceModel) body.enhance_model = opts.enhanceModel;
2319
+ if (opts.upscalerModel) {
2320
+ if (!["starlight_fast", "starlight_creative", "starlight_precise_25", "astra_2"].includes(opts.upscalerModel)) {
2321
+ throw new ApiError("invalid_request", "--upscaler-model must be starlight_fast, starlight_creative, starlight_precise_25, or astra_2");
2322
+ }
2323
+ body.upscaler_model = opts.upscalerModel;
2324
+ }
2321
2325
  if (opts.content) metadata.content = parseJsonArray(await readContentArgument(opts.content), "content");
2322
2326
  validateVideoGeneration(model, {
2323
2327
  promptRunes: [...promptParts.join(" ")].length,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "focalapi-cli",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "让任意 AI Agent 直接调用 focalapi 创作模型的命令行工具",
5
5
  "type": "module",
6
6
  "bin": {