focalapi-cli 0.2.1 → 0.3.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 +35 -17
- package/LICENSE +202 -202
- package/README.md +144 -139
- package/dist/cli.js +401 -72
- package/package.json +57 -57
- package/scripts/postinstall.cjs +41 -41
- package/skills/focalapi/SKILL.md +48 -47
- package/skills/focalapi-auth/SKILL.md +28 -28
- package/skills/focalapi-chat/SKILL.md +29 -29
- package/skills/focalapi-gen/SKILL.md +10 -2
- package/skills/focalapi-models/SKILL.md +7 -1
- package/skills/focalapi-task/SKILL.md +29 -25
- package/skills/focalapi-usage/SKILL.md +27 -26
package/dist/cli.js
CHANGED
|
@@ -37,6 +37,20 @@ var ApiError = class extends Error {
|
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
39
|
};
|
|
40
|
+
var ERROR_HINTS = {
|
|
41
|
+
missing_api_key: "\u672A\u914D\u7F6E API Key\u3002\u8FD0\u884C focalapi auth login --key <sk-...>\uFF0C\u6216\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF FOCALAPI_API_KEY\u3002",
|
|
42
|
+
invalid_api_key: "API Key \u65E0\u6548\u3001\u5DF2\u8FC7\u671F\u6216\u88AB\u5220\u9664\u3002\u524D\u5F80 https://focalapi.com/console/token \u68C0\u67E5\u6216\u65B0\u5EFA Key\uFF0C\u7136\u540E\u91CD\u65B0 focalapi auth login\u3002",
|
|
43
|
+
insufficient_quota: "\u8D26\u6237\u989D\u5EA6\u4E0D\u8DB3\u3002\u524D\u5F80 https://focalapi.com/console/topup \u5145\u503C\uFF1B\u53EF\u8FD0\u884C focalapi usage \u67E5\u770B\u5F53\u524D\u989D\u5EA6\u3002",
|
|
44
|
+
rate_limited: "\u8BF7\u6C42\u89E6\u53D1\u9650\u6D41\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",
|
|
45
|
+
capacity_exhausted: "\u5E73\u53F0\u521B\u4F5C\u5BB9\u91CF\u5DF2\u6EE1\uFF08\u6392\u961F\u51C6\u5165\u8FBE\u5230\u4E0A\u9650\uFF0CHTTP 503\uFF09\u3002\u670D\u52A1\u7AEF\u5EFA\u8BAE\u7EA6 10 \u79D2\u540E\u91CD\u8BD5\uFF1A\u76F4\u63A5\u91CD\u8DD1\u540C\u4E00\u6761\u547D\u4EE4\u5373\u53EF\uFF0C\u65E0\u9700\u4FEE\u6539\u53C2\u6570\u6216\u66F4\u6362\u6A21\u578B\u3002",
|
|
46
|
+
model_not_found: "\u6A21\u578B\u4E0D\u5B58\u5728\u6216\u672A\u5BF9\u4F60\u7684 Key \u5F00\u653E\u3002\u8FD0\u884C focalapi models list \u67E5\u770B\u53EF\u7528\u6A21\u578B\u3002",
|
|
47
|
+
network_error: "\u65E0\u6CD5\u8FDE\u63A5 focalapi \u670D\u52A1\u3002\u68C0\u67E5\u7F51\u7EDC\u4EE3\u7406\u4E0E FOCALAPI_BASE_URL \u914D\u7F6E\uFF1B\u53EF\u8FD0\u884C focalapi doctor \u505A\u94FE\u8DEF\u8BCA\u65AD\u3002",
|
|
48
|
+
timeout: "\u8BF7\u6C42\u8D85\u65F6\u3002\u7A0D\u540E\u91CD\u8BD5\uFF0C\u6216\u8FD0\u884C focalapi doctor \u68C0\u67E5\u94FE\u8DEF\u8D28\u91CF\u3002",
|
|
49
|
+
server_error: "focalapi \u670D\u52A1\u7AEF\u9519\u8BEF\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002\u82E5\u6301\u7EED\u51FA\u73B0\uFF0C\u8BF7\u643A\u5E26\u9519\u8BEF\u4FE1\u606F\u53CD\u9988\u7ED9\u670D\u52A1\u65B9\u3002",
|
|
50
|
+
invalid_request: "\u8BF7\u6C42\u53C2\u6570\u6709\u8BEF\uFF0C\u8BF7\u68C0\u67E5\u547D\u4EE4\u53C2\u6570\u3002",
|
|
51
|
+
authentication_failed: "\u9274\u6743\u5931\u8D25\u3002\u5148\u8FD0\u884C focalapi auth status \u9A8C\u8BC1\u672C\u7AD9 Key\uFF1B\u82E5\u672C\u7AD9 Key \u6709\u6548\uFF0C\u5219\u8BF7\u5C06\u8BF7\u6C42 ID \u63D0\u4F9B\u7ED9\u670D\u52A1\u65B9\u6392\u67E5\u6E20\u9053\u6743\u9650\u3002",
|
|
52
|
+
upstream_auth_failed: "\u4E0A\u6E38\u6E20\u9053\u9274\u6743\u5931\u8D25\uFF0C\u5E76\u4E0D\u8868\u793A\u4F60\u7684 FocalAPI Key \u65E0\u6548\u3002\u5148\u8FD0\u884C focalapi auth status\uFF1B\u82E5\u901A\u8FC7\uFF0C\u8BF7\u5C06\u8BF7\u6C42 ID \u63D0\u4F9B\u7ED9\u670D\u52A1\u65B9\u6392\u67E5\u6E20\u9053\u914D\u7F6E\u3002"
|
|
53
|
+
};
|
|
40
54
|
function refineErrorCode(status, message, opts) {
|
|
41
55
|
const m = message.toLowerCase();
|
|
42
56
|
if (m.includes("quota") || m.includes("\u989D\u5EA6") || m.includes("insufficient")) {
|
|
@@ -104,8 +118,15 @@ function printJson(data) {
|
|
|
104
118
|
function info(message) {
|
|
105
119
|
process.stderr.write(message + "\n");
|
|
106
120
|
}
|
|
107
|
-
function printError(
|
|
108
|
-
if (
|
|
121
|
+
function printError(errInput, opts) {
|
|
122
|
+
if (errInput instanceof ApiError) {
|
|
123
|
+
const err = !errInput.hint && ERROR_HINTS[errInput.code] ? new ApiError(errInput.code, errInput.message, {
|
|
124
|
+
status: errInput.status,
|
|
125
|
+
hint: ERROR_HINTS[errInput.code],
|
|
126
|
+
body: errInput.body,
|
|
127
|
+
upstreamCode: errInput.upstreamCode,
|
|
128
|
+
requestId: errInput.requestId
|
|
129
|
+
}) : errInput;
|
|
109
130
|
if (opts?.json) {
|
|
110
131
|
printJson(err.toJSON());
|
|
111
132
|
} else {
|
|
@@ -127,7 +148,7 @@ function printError(err, opts) {
|
|
|
127
148
|
}
|
|
128
149
|
return;
|
|
129
150
|
}
|
|
130
|
-
const message =
|
|
151
|
+
const message = errInput instanceof Error ? errInput.message : String(errInput);
|
|
131
152
|
if (opts?.json) {
|
|
132
153
|
printJson({ error: { code: "internal_error", message } });
|
|
133
154
|
} else {
|
|
@@ -155,7 +176,7 @@ function displayWidth(s) {
|
|
|
155
176
|
}
|
|
156
177
|
|
|
157
178
|
// src/lib/version.ts
|
|
158
|
-
var VERSION = true ? "0.
|
|
179
|
+
var VERSION = true ? "0.3.0" : "0.0.0-dev";
|
|
159
180
|
|
|
160
181
|
// src/commands/auth.ts
|
|
161
182
|
import { createInterface } from "readline/promises";
|
|
@@ -319,7 +340,10 @@ async function rawRequest(opts) {
|
|
|
319
340
|
if (!res.ok) {
|
|
320
341
|
const text = await res.text().catch(() => "");
|
|
321
342
|
const { message, body, upstreamCode } = extractErrorMessage(text);
|
|
322
|
-
|
|
343
|
+
let code = refineErrorCode(res.status, message, { authFailureIsInvalidApiKey: opts.authFailureIsInvalidApiKey });
|
|
344
|
+
if (upstreamCode === "capacity_exhausted" || res.status === 503 && /capacity/i.test(message)) {
|
|
345
|
+
code = "capacity_exhausted";
|
|
346
|
+
}
|
|
323
347
|
const requestId = res.headers.get("x-request-id") ?? res.headers.get("request-id") ?? res.headers.get("x-requestid") ?? void 0;
|
|
324
348
|
throw new ApiError(code, message, { status: res.status, body, upstreamCode, requestId });
|
|
325
349
|
}
|
|
@@ -471,15 +495,21 @@ var RECOMMENDED_MODELS = {
|
|
|
471
495
|
"seedream-5-0-260128",
|
|
472
496
|
"gpt-image-2",
|
|
473
497
|
"gemini-3.1-flash-image",
|
|
474
|
-
"grok-imagine-image-
|
|
498
|
+
"grok-imagine-image-2.0",
|
|
499
|
+
"kling-v3",
|
|
500
|
+
"qwen-image-3.0-pro",
|
|
501
|
+
"krea-2-large",
|
|
475
502
|
"seedream-4-5-251128"
|
|
476
503
|
],
|
|
477
504
|
video: [
|
|
478
505
|
"dreamina-seedance-2-5-260628",
|
|
479
|
-
"
|
|
506
|
+
"kling-3.0",
|
|
507
|
+
"viduq3-pro",
|
|
508
|
+
"gemini-omni-flash-preview",
|
|
480
509
|
"grok-imagine-video-1.5",
|
|
481
|
-
"
|
|
482
|
-
"
|
|
510
|
+
"ltx-2-5-fast",
|
|
511
|
+
"flux-3",
|
|
512
|
+
"dreamina-seedance-2-0-260128"
|
|
483
513
|
]
|
|
484
514
|
};
|
|
485
515
|
function parseCreativeCapability(value) {
|
|
@@ -888,6 +918,7 @@ import { pipeline as pipeline2 } from "stream/promises";
|
|
|
888
918
|
import { Readable as Readable2 } from "stream";
|
|
889
919
|
|
|
890
920
|
// src/lib/model-capabilities.ts
|
|
921
|
+
var GEMINI_IMAGE_MAX_SEED = 9007199254740991;
|
|
891
922
|
var SEEDREAM_OUTPUT_FORMATS = ["png", "jpeg"];
|
|
892
923
|
var SEEDREAM_OPTIMIZE_PROMPT_MODES = ["auto", "enabled", "disabled"];
|
|
893
924
|
var GROK_IMAGE_ASPECT_RATIOS = [
|
|
@@ -906,6 +937,24 @@ var GROK_IMAGE_ASPECT_RATIOS = [
|
|
|
906
937
|
"1:3",
|
|
907
938
|
"3:1"
|
|
908
939
|
];
|
|
940
|
+
var GROK_IMAGE_20_ASPECT_RATIOS = [
|
|
941
|
+
"auto",
|
|
942
|
+
"1:1",
|
|
943
|
+
"2:3",
|
|
944
|
+
"3:2",
|
|
945
|
+
"3:4",
|
|
946
|
+
"4:3",
|
|
947
|
+
"9:16",
|
|
948
|
+
"16:9",
|
|
949
|
+
"9:19.5",
|
|
950
|
+
"19.5:9",
|
|
951
|
+
"9:20",
|
|
952
|
+
"20:9",
|
|
953
|
+
"1:2",
|
|
954
|
+
"2:1"
|
|
955
|
+
];
|
|
956
|
+
var KLING_IMAGE_ASPECT_RATIOS = ["16:9", "9:16", "1:1", "4:3", "3:4", "3:2", "2:3", "21:9"];
|
|
957
|
+
var KREA_IMAGE_ASPECT_RATIOS = ["1:1", "4:3", "3:2", "16:9", "2.35:1", "4:5", "2:3", "9:16"];
|
|
909
958
|
var IMAGE_CONSTRAINTS = {
|
|
910
959
|
"gpt-image-2": {
|
|
911
960
|
defaultSize: "1024x1024",
|
|
@@ -921,7 +970,7 @@ var IMAGE_CONSTRAINTS = {
|
|
|
921
970
|
backgrounds: ["auto", "opaque"]
|
|
922
971
|
},
|
|
923
972
|
"seedream-4-0-250828": {
|
|
924
|
-
defaultSize: "
|
|
973
|
+
defaultSize: "2k",
|
|
925
974
|
sizeTiers: ["1k", "2k", "4k"],
|
|
926
975
|
maxN: 10,
|
|
927
976
|
maxReferenceImages: 10,
|
|
@@ -968,103 +1017,243 @@ var IMAGE_CONSTRAINTS = {
|
|
|
968
1017
|
optimizePromptModes: SEEDREAM_OPTIMIZE_PROMPT_MODES
|
|
969
1018
|
},
|
|
970
1019
|
"grok-imagine-image-quality": {
|
|
971
|
-
defaultSize: "1024x1024",
|
|
972
1020
|
maxN: 10,
|
|
973
1021
|
maxReferenceImages: 3,
|
|
974
1022
|
aspectRatios: GROK_IMAGE_ASPECT_RATIOS,
|
|
975
1023
|
resolutions: ["1k", "2k"],
|
|
976
|
-
supportsSeed: true
|
|
1024
|
+
supportsSeed: true,
|
|
1025
|
+
maxSeed: 2147483647
|
|
977
1026
|
},
|
|
978
1027
|
"grok-imagine-image": {
|
|
979
|
-
defaultSize: "1024x1024",
|
|
980
1028
|
maxN: 10,
|
|
981
1029
|
maxReferenceImages: 3,
|
|
982
1030
|
aspectRatios: GROK_IMAGE_ASPECT_RATIOS,
|
|
983
1031
|
resolutions: ["1k", "2k"],
|
|
984
|
-
supportsSeed: true
|
|
1032
|
+
supportsSeed: true,
|
|
1033
|
+
maxSeed: 2147483647
|
|
1034
|
+
},
|
|
1035
|
+
"grok-imagine-image-2.0": {
|
|
1036
|
+
maxN: 10,
|
|
1037
|
+
maxReferenceImages: 3,
|
|
1038
|
+
aspectRatios: GROK_IMAGE_20_ASPECT_RATIOS,
|
|
1039
|
+
resolutions: ["1k", "2k"],
|
|
1040
|
+
qualities: ["low", "medium"],
|
|
1041
|
+
supportsSeed: true,
|
|
1042
|
+
maxSeed: 2147483647
|
|
1043
|
+
},
|
|
1044
|
+
"kling-v3": {
|
|
1045
|
+
maxN: 9,
|
|
1046
|
+
maxReferenceImages: 1,
|
|
1047
|
+
aspectRatios: KLING_IMAGE_ASPECT_RATIOS,
|
|
1048
|
+
supportsNegativePrompt: true
|
|
1049
|
+
},
|
|
1050
|
+
"krea-2-medium": {
|
|
1051
|
+
maxN: 1,
|
|
1052
|
+
maxReferenceImages: 0,
|
|
1053
|
+
aspectRatios: KREA_IMAGE_ASPECT_RATIOS,
|
|
1054
|
+
resolutions: ["1k"],
|
|
1055
|
+
supportsSeed: true,
|
|
1056
|
+
maxSeed: 2147483647,
|
|
1057
|
+
creativityModes: ["raw", "low", "medium", "high"],
|
|
1058
|
+
maxStyleReferences: 10,
|
|
1059
|
+
maxMoodboards: 1
|
|
1060
|
+
},
|
|
1061
|
+
"krea-2-medium-turbo": {
|
|
1062
|
+
maxN: 1,
|
|
1063
|
+
maxReferenceImages: 0,
|
|
1064
|
+
aspectRatios: KREA_IMAGE_ASPECT_RATIOS,
|
|
1065
|
+
resolutions: ["1k"],
|
|
1066
|
+
supportsSeed: true,
|
|
1067
|
+
maxSeed: 2147483647,
|
|
1068
|
+
creativityModes: ["raw", "low", "medium", "high"],
|
|
1069
|
+
maxStyleReferences: 10,
|
|
1070
|
+
maxMoodboards: 1
|
|
1071
|
+
},
|
|
1072
|
+
"krea-2-large": {
|
|
1073
|
+
maxN: 1,
|
|
1074
|
+
maxReferenceImages: 0,
|
|
1075
|
+
aspectRatios: KREA_IMAGE_ASPECT_RATIOS,
|
|
1076
|
+
resolutions: ["1k"],
|
|
1077
|
+
supportsSeed: true,
|
|
1078
|
+
maxSeed: 2147483647,
|
|
1079
|
+
creativityModes: ["raw", "low", "medium", "high"],
|
|
1080
|
+
maxStyleReferences: 10,
|
|
1081
|
+
maxMoodboards: 1
|
|
1082
|
+
},
|
|
1083
|
+
"qwen-image-3.0": {
|
|
1084
|
+
defaultSize: "1024x1024",
|
|
1085
|
+
maxN: 6,
|
|
1086
|
+
maxReferenceImages: 3,
|
|
1087
|
+
minMegapixels: 0.262144,
|
|
1088
|
+
maxMegapixels: 4.194304,
|
|
1089
|
+
maxAspectRatio: 8,
|
|
1090
|
+
supportsSeed: true,
|
|
1091
|
+
maxSeed: 2147483647,
|
|
1092
|
+
supportsNegativePrompt: true,
|
|
1093
|
+
supportsPromptExtend: true,
|
|
1094
|
+
supportsWatermark: true
|
|
1095
|
+
},
|
|
1096
|
+
"qwen-image-3.0-pro": {
|
|
1097
|
+
defaultSize: "1024x1024",
|
|
1098
|
+
maxN: 6,
|
|
1099
|
+
maxReferenceImages: 3,
|
|
1100
|
+
minMegapixels: 0.262144,
|
|
1101
|
+
maxMegapixels: 4.194304,
|
|
1102
|
+
maxAspectRatio: 8,
|
|
1103
|
+
supportsSeed: true,
|
|
1104
|
+
maxSeed: 2147483647,
|
|
1105
|
+
supportsNegativePrompt: true,
|
|
1106
|
+
supportsPromptExtend: true,
|
|
1107
|
+
supportsWatermark: true
|
|
985
1108
|
}
|
|
986
1109
|
};
|
|
987
1110
|
var SEEDANCE_RATIOS = ["adaptive", "16:9", "4:3", "1:1", "3:4", "9:16", "21:9"];
|
|
988
1111
|
var GROK_VIDEO_ASPECT_RATIOS = ["auto", "16:9", "4:3", "3:2", "1:1", "2:3", "3:4", "9:16"];
|
|
1112
|
+
var KLING_VIDEO_ASPECT_RATIOS = ["16:9", "9:16", "1:1"];
|
|
1113
|
+
var VIDU_VIDEO_ASPECT_RATIOS = ["16:9", "9:16", "3:4", "4:3", "1:1"];
|
|
1114
|
+
var FLUX_VIDEO_RATIOS = ["auto", "21:9", "2:1", "16:9", "4:3", "1:1", "3:4", "9:16"];
|
|
989
1115
|
var VIDEO_CONSTRAINTS = {
|
|
990
|
-
"veo-3.1-generate-preview": {
|
|
991
|
-
resolutions: ["720p", "1080p", "4k"],
|
|
992
|
-
ratios: ["16:9", "9:16"],
|
|
993
|
-
minSeconds: 4,
|
|
994
|
-
maxSeconds: 8,
|
|
995
|
-
allowedSeconds: [4, 6, 8],
|
|
996
|
-
requiredSecondsByResolution: { "1080p": 8, "4k": 8 },
|
|
997
|
-
supportsSeed: true
|
|
998
|
-
},
|
|
999
|
-
"veo-3.1-fast-generate-preview": {
|
|
1000
|
-
resolutions: ["720p", "1080p", "4k"],
|
|
1001
|
-
ratios: ["16:9", "9:16"],
|
|
1002
|
-
minSeconds: 4,
|
|
1003
|
-
maxSeconds: 8,
|
|
1004
|
-
allowedSeconds: [4, 6, 8],
|
|
1005
|
-
requiredSecondsByResolution: { "1080p": 8, "4k": 8 },
|
|
1006
|
-
supportsSeed: true
|
|
1007
|
-
},
|
|
1008
|
-
"veo-3.1-lite-generate-preview": {
|
|
1009
|
-
resolutions: ["720p", "1080p"],
|
|
1010
|
-
ratios: ["16:9", "9:16"],
|
|
1011
|
-
minSeconds: 4,
|
|
1012
|
-
maxSeconds: 8,
|
|
1013
|
-
allowedSeconds: [4, 6, 8],
|
|
1014
|
-
requiredSecondsByResolution: { "1080p": 8 },
|
|
1015
|
-
supportsSeed: true
|
|
1016
|
-
},
|
|
1017
1116
|
"dreamina-seedance-2-0-260128": {
|
|
1018
1117
|
resolutions: ["480p", "720p", "1080p", "4k"],
|
|
1019
1118
|
ratios: SEEDANCE_RATIOS,
|
|
1020
1119
|
minSeconds: 4,
|
|
1021
1120
|
maxSeconds: 15,
|
|
1022
|
-
|
|
1121
|
+
maxReferenceImages: 9
|
|
1023
1122
|
},
|
|
1024
1123
|
"dreamina-seedance-2-0-fast-260128": {
|
|
1025
1124
|
resolutions: ["480p", "720p"],
|
|
1026
1125
|
ratios: SEEDANCE_RATIOS,
|
|
1027
1126
|
minSeconds: 4,
|
|
1028
1127
|
maxSeconds: 15,
|
|
1029
|
-
|
|
1128
|
+
maxReferenceImages: 9
|
|
1030
1129
|
},
|
|
1031
|
-
"
|
|
1130
|
+
"seed-2-0-mini-260428": {
|
|
1032
1131
|
resolutions: ["480p", "720p"],
|
|
1033
1132
|
ratios: SEEDANCE_RATIOS,
|
|
1034
1133
|
minSeconds: 4,
|
|
1035
1134
|
maxSeconds: 15,
|
|
1036
|
-
|
|
1135
|
+
maxReferenceImages: 9
|
|
1037
1136
|
},
|
|
1038
1137
|
"dreamina-seedance-2-5-260628": {
|
|
1039
|
-
resolutions: ["480p", "720p"],
|
|
1138
|
+
resolutions: ["480p", "720p", "1080p"],
|
|
1040
1139
|
ratios: SEEDANCE_RATIOS,
|
|
1041
1140
|
minSeconds: 4,
|
|
1042
|
-
maxSeconds: 30
|
|
1141
|
+
maxSeconds: 30,
|
|
1142
|
+
maxReferenceImages: 30
|
|
1143
|
+
},
|
|
1144
|
+
"gemini-omni-flash-preview": {
|
|
1145
|
+
resolutions: [],
|
|
1146
|
+
ratios: ["16:9", "9:16"],
|
|
1147
|
+
minSeconds: 3,
|
|
1148
|
+
maxSeconds: 10,
|
|
1149
|
+
maxReferenceImages: 14,
|
|
1150
|
+
supportsGenerateAudio: false
|
|
1043
1151
|
},
|
|
1044
1152
|
"grok-imagine-video": {
|
|
1045
|
-
resolutions: ["480p", "720p"
|
|
1153
|
+
resolutions: ["480p", "720p"],
|
|
1046
1154
|
aspectRatios: GROK_VIDEO_ASPECT_RATIOS,
|
|
1047
1155
|
minSeconds: 1,
|
|
1048
1156
|
maxSeconds: 15,
|
|
1049
|
-
|
|
1157
|
+
disallowReferences: true,
|
|
1158
|
+
maxFirstFrameImages: 1,
|
|
1159
|
+
supportsGenerateAudio: false,
|
|
1160
|
+
supportsWatermark: false
|
|
1050
1161
|
},
|
|
1051
1162
|
"grok-imagine-video-1.5": {
|
|
1052
1163
|
resolutions: ["480p", "720p", "1080p"],
|
|
1053
1164
|
aspectRatios: GROK_VIDEO_ASPECT_RATIOS,
|
|
1054
1165
|
minSeconds: 1,
|
|
1055
1166
|
maxSeconds: 15,
|
|
1056
|
-
|
|
1167
|
+
maxReferenceImages: 7,
|
|
1168
|
+
referenceResolutions: ["480p", "720p"],
|
|
1169
|
+
maxFirstFrameImages: 1,
|
|
1170
|
+
supportsGenerateAudio: false,
|
|
1171
|
+
supportsWatermark: false
|
|
1172
|
+
},
|
|
1173
|
+
"kling-3.0": {
|
|
1174
|
+
resolutions: ["720p", "1080p", "4k"],
|
|
1175
|
+
aspectRatios: KLING_VIDEO_ASPECT_RATIOS,
|
|
1176
|
+
minSeconds: 3,
|
|
1177
|
+
maxSeconds: 15,
|
|
1178
|
+
maxReferenceImages: 2,
|
|
1179
|
+
supportsGenerateAudio: true
|
|
1180
|
+
},
|
|
1181
|
+
"viduq3-pro": {
|
|
1182
|
+
resolutions: ["720p", "1080p"],
|
|
1183
|
+
aspectRatios: VIDU_VIDEO_ASPECT_RATIOS,
|
|
1184
|
+
minSeconds: 1,
|
|
1185
|
+
maxSeconds: 16,
|
|
1186
|
+
supportsSeed: true,
|
|
1187
|
+
maxSeed: 2147483647,
|
|
1188
|
+
maxReferenceImages: 2
|
|
1189
|
+
},
|
|
1190
|
+
"viduq3-turbo": {
|
|
1191
|
+
resolutions: ["720p", "1080p"],
|
|
1192
|
+
aspectRatios: VIDU_VIDEO_ASPECT_RATIOS,
|
|
1193
|
+
minSeconds: 1,
|
|
1194
|
+
maxSeconds: 16,
|
|
1195
|
+
supportsSeed: true,
|
|
1196
|
+
maxSeed: 2147483647,
|
|
1197
|
+
maxReferenceImages: 2
|
|
1198
|
+
},
|
|
1199
|
+
"ltx-2-5-fast": {
|
|
1200
|
+
resolutions: ["1280x720", "720x1280", "1920x1080", "1080x1920", "2560x1440", "1440x2560", "3840x2160", "2160x3840"],
|
|
1201
|
+
minSeconds: 6,
|
|
1202
|
+
maxSeconds: 20,
|
|
1203
|
+
allowedSeconds: [6, 8, 10, 12, 14, 16, 18, 20],
|
|
1204
|
+
allowedFps: [24, 25, 48, 50],
|
|
1205
|
+
maxReferenceImages: 2,
|
|
1206
|
+
supportsGenerateAudio: true
|
|
1207
|
+
},
|
|
1208
|
+
"ltx-2-5-pro": {
|
|
1209
|
+
resolutions: ["1280x720", "720x1280", "1920x1080", "1080x1920"],
|
|
1210
|
+
minSeconds: 6,
|
|
1211
|
+
maxSeconds: 10,
|
|
1212
|
+
allowedSeconds: [6, 8, 10],
|
|
1213
|
+
allowedFps: [24, 25, 50],
|
|
1214
|
+
maxReferenceImages: 2,
|
|
1215
|
+
supportsGenerateAudio: true
|
|
1216
|
+
},
|
|
1217
|
+
"flux-3": {
|
|
1218
|
+
resolutions: ["hd", "fhd"],
|
|
1219
|
+
ratios: FLUX_VIDEO_RATIOS,
|
|
1220
|
+
minSeconds: 5,
|
|
1221
|
+
maxSeconds: 20,
|
|
1222
|
+
safetyTolerance: { minimum: 0, maximum: 4 },
|
|
1223
|
+
safetyToleranceMaxWithImages: 2,
|
|
1224
|
+
maxReferenceImages: 10
|
|
1057
1225
|
}
|
|
1058
1226
|
};
|
|
1059
1227
|
var COMMON_GEMINI_RATIOS = ["auto", "1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"];
|
|
1228
|
+
var EXTENDED_GEMINI_RATIOS = [...COMMON_GEMINI_RATIOS, "1:4", "4:1", "1:8", "8:1"];
|
|
1060
1229
|
var GEMINI_IMAGE_CONSTRAINTS = {
|
|
1061
|
-
"gemini-2.5-flash-image": {
|
|
1062
|
-
|
|
1063
|
-
|
|
1230
|
+
"gemini-2.5-flash-image": {
|
|
1231
|
+
aspectRatios: COMMON_GEMINI_RATIOS,
|
|
1232
|
+
supportsSampling: false,
|
|
1233
|
+
maxReferenceImages: 1,
|
|
1234
|
+
referenceImagesInlineDataOnly: true,
|
|
1235
|
+
maxSeed: GEMINI_IMAGE_MAX_SEED
|
|
1236
|
+
},
|
|
1237
|
+
"gemini-3-pro-image": {
|
|
1238
|
+
aspectRatios: COMMON_GEMINI_RATIOS,
|
|
1239
|
+
imageSizes: ["1K", "2K", "4K"],
|
|
1240
|
+
supportsSampling: false,
|
|
1241
|
+
maxReferenceImages: 14,
|
|
1242
|
+
maxSeed: GEMINI_IMAGE_MAX_SEED
|
|
1243
|
+
},
|
|
1244
|
+
"gemini-3.1-flash-image": {
|
|
1245
|
+
aspectRatios: EXTENDED_GEMINI_RATIOS,
|
|
1246
|
+
imageSizes: ["1K", "2K", "4K"],
|
|
1247
|
+
supportsSampling: true,
|
|
1248
|
+
maxReferenceImages: 14,
|
|
1249
|
+
maxSeed: GEMINI_IMAGE_MAX_SEED
|
|
1250
|
+
},
|
|
1064
1251
|
"gemini-3.1-flash-lite-image": {
|
|
1065
|
-
aspectRatios:
|
|
1252
|
+
aspectRatios: EXTENDED_GEMINI_RATIOS,
|
|
1066
1253
|
imageSizes: ["1K"],
|
|
1067
|
-
supportsSampling: true
|
|
1254
|
+
supportsSampling: true,
|
|
1255
|
+
maxReferenceImages: 14,
|
|
1256
|
+
maxSeed: GEMINI_IMAGE_MAX_SEED
|
|
1068
1257
|
}
|
|
1069
1258
|
};
|
|
1070
1259
|
function parseSize(size, model) {
|
|
@@ -1113,17 +1302,40 @@ function validateImageGeneration(model, input) {
|
|
|
1113
1302
|
validateOptionalChoice(model, "resolution", input.resolution, constraint.resolutions);
|
|
1114
1303
|
validateOptionalChoice(model, "output_format", input.outputFormat, constraint.outputFormats);
|
|
1115
1304
|
validateOptionalChoice(model, "optimize_prompt", input.optimizePrompt, constraint.optimizePromptModes);
|
|
1305
|
+
validateOptionalChoice(model, "creativity", input.creativity, constraint.creativityModes);
|
|
1306
|
+
if (input.negativePrompt !== void 0 && !constraint.supportsNegativePrompt) {
|
|
1307
|
+
throw new ApiError("invalid_request", `${model} does not support negative_prompt`);
|
|
1308
|
+
}
|
|
1309
|
+
if (input.negativePrompt !== void 0 && model === "kling-v3" && (input.imageCount ?? 0) > 0) {
|
|
1310
|
+
throw new ApiError("invalid_request", "kling-v3 negative_prompt cannot be combined with an image input");
|
|
1311
|
+
}
|
|
1312
|
+
if (input.promptExtend !== void 0 && !constraint.supportsPromptExtend) {
|
|
1313
|
+
throw new ApiError("invalid_request", `${model} does not support prompt_extend`);
|
|
1314
|
+
}
|
|
1315
|
+
if (input.styleReferenceCount !== void 0 && (constraint.maxStyleReferences === void 0 || input.styleReferenceCount > constraint.maxStyleReferences)) {
|
|
1316
|
+
throw new ApiError("invalid_request", `${model} supports at most ${constraint.maxStyleReferences ?? 0} image style references`);
|
|
1317
|
+
}
|
|
1318
|
+
if (input.moodboardCount !== void 0 && (constraint.maxMoodboards === void 0 || input.moodboardCount > constraint.maxMoodboards)) {
|
|
1319
|
+
throw new ApiError("invalid_request", `${model} supports at most ${constraint.maxMoodboards ?? 0} moodboards`);
|
|
1320
|
+
}
|
|
1116
1321
|
if (input.seed !== void 0) {
|
|
1117
1322
|
if (!constraint.supportsSeed) {
|
|
1118
1323
|
throw new ApiError("invalid_request", `${model} does not support seed`);
|
|
1119
1324
|
}
|
|
1120
|
-
if (!Number.isInteger(input.seed) || input.seed < 0) {
|
|
1121
|
-
|
|
1325
|
+
if (!Number.isInteger(input.seed) || input.seed < 0 || constraint.maxSeed !== void 0 && input.seed > constraint.maxSeed) {
|
|
1326
|
+
const maximum = constraint.maxSeed === void 0 ? "" : ` no greater than ${constraint.maxSeed}`;
|
|
1327
|
+
throw new ApiError("invalid_request", `seed must be a non-negative integer${maximum}`);
|
|
1122
1328
|
}
|
|
1123
1329
|
}
|
|
1124
1330
|
if (input.watermark !== void 0 && !constraint.supportsWatermark) {
|
|
1125
1331
|
throw new ApiError("invalid_request", `${model} does not support watermark`);
|
|
1126
1332
|
}
|
|
1333
|
+
if (!constraint.defaultSize) {
|
|
1334
|
+
if (input.size) {
|
|
1335
|
+
throw new ApiError("invalid_request", `${model} does not support size; use its aspect_ratio or resolution parameter`);
|
|
1336
|
+
}
|
|
1337
|
+
return;
|
|
1338
|
+
}
|
|
1127
1339
|
const suppliedSize = input.size ?? constraint.defaultSize;
|
|
1128
1340
|
if (constraint.sizeTiers?.includes(suppliedSize.toLowerCase())) return;
|
|
1129
1341
|
if (constraint.sizeTiers && !/^\d+x\d+$/i.test(suppliedSize)) {
|
|
@@ -1156,11 +1368,18 @@ function validateGeminiImageGeneration(model, input) {
|
|
|
1156
1368
|
const supported = constraint.imageSizes?.join(", ") ?? "none";
|
|
1157
1369
|
throw new ApiError("invalid_request", `${model} imageSize must be one of ${supported} (received: ${input.imageSize})`);
|
|
1158
1370
|
}
|
|
1159
|
-
if (input.seed !== void 0 && (!Number.isInteger(input.seed) || input.seed < 0)) {
|
|
1160
|
-
|
|
1371
|
+
if (input.seed !== void 0 && (!Number.isInteger(input.seed) || input.seed < 0 || constraint.maxSeed !== void 0 && input.seed > constraint.maxSeed)) {
|
|
1372
|
+
const maximum = constraint.maxSeed === void 0 ? "" : ` no greater than ${constraint.maxSeed}`;
|
|
1373
|
+
throw new ApiError("invalid_request", `seed must be a non-negative integer${maximum}`);
|
|
1374
|
+
}
|
|
1375
|
+
if (input.referenceImageCount !== void 0 && constraint.maxReferenceImages !== void 0 && input.referenceImageCount > constraint.maxReferenceImages) {
|
|
1376
|
+
throw new ApiError("invalid_request", `${model} supports at most ${constraint.maxReferenceImages} reference image${constraint.maxReferenceImages === 1 ? "" : "s"}`);
|
|
1377
|
+
}
|
|
1378
|
+
if (constraint.referenceImagesInlineDataOnly && input.nonDataUriReferenceCount) {
|
|
1379
|
+
throw new ApiError("invalid_request", `${model} reference images must be base64 data URIs (inlineData); fileUri inputs are rejected by this model`);
|
|
1161
1380
|
}
|
|
1162
1381
|
if (!constraint.supportsSampling && (input.thinkingLevel || input.temperature !== void 0 || input.topP !== void 0)) {
|
|
1163
|
-
throw new ApiError("invalid_request", `${model} supports thinkingLevel, temperature, and topP only on gemini-3.1-flash-lite-image`);
|
|
1382
|
+
throw new ApiError("invalid_request", `${model} supports thinkingLevel, temperature, and topP only on gemini-3.1-flash-image and gemini-3.1-flash-lite-image`);
|
|
1164
1383
|
}
|
|
1165
1384
|
if (input.thinkingLevel && !["MINIMAL", "HIGH"].includes(input.thinkingLevel.toUpperCase())) {
|
|
1166
1385
|
throw new ApiError("invalid_request", "thinkingLevel must be MINIMAL or HIGH");
|
|
@@ -1175,6 +1394,24 @@ function validateGeminiImageGeneration(model, input) {
|
|
|
1175
1394
|
function validateVideoGeneration(model, input) {
|
|
1176
1395
|
const constraint = VIDEO_CONSTRAINTS[model.trim()];
|
|
1177
1396
|
if (!constraint) return;
|
|
1397
|
+
if ((input.imageCount ?? 0) > 0 && (input.firstFrameCount ?? 0) > 0) {
|
|
1398
|
+
throw new ApiError("invalid_request", `${model}: --image and --first-frame are mutually exclusive (reference-to-video vs image-to-video)`);
|
|
1399
|
+
}
|
|
1400
|
+
if (constraint.disallowReferences && (input.imageCount ?? 0) > 0) {
|
|
1401
|
+
throw new ApiError("invalid_request", `${model} supports image-to-video only (single --first-frame image); reference images require grok-imagine-video-1.5`);
|
|
1402
|
+
}
|
|
1403
|
+
if (input.imageCount !== void 0 && constraint.maxReferenceImages !== void 0 && input.imageCount > constraint.maxReferenceImages) {
|
|
1404
|
+
throw new ApiError("invalid_request", `${model} supports at most ${constraint.maxReferenceImages} reference image${constraint.maxReferenceImages === 1 ? "" : "s"}`);
|
|
1405
|
+
}
|
|
1406
|
+
if (input.firstFrameCount !== void 0 && constraint.maxFirstFrameImages !== void 0 && input.firstFrameCount > constraint.maxFirstFrameImages) {
|
|
1407
|
+
throw new ApiError("invalid_request", `${model} image-to-video supports exactly ${constraint.maxFirstFrameImages} starting image${constraint.maxFirstFrameImages === 1 ? "" : "s"} (--first-frame)`);
|
|
1408
|
+
}
|
|
1409
|
+
if (input.generateAudio !== void 0 && constraint.supportsGenerateAudio === false) {
|
|
1410
|
+
throw new ApiError("invalid_request", `${model} does not support generate_audio`);
|
|
1411
|
+
}
|
|
1412
|
+
if (input.watermark !== void 0 && constraint.supportsWatermark === false) {
|
|
1413
|
+
throw new ApiError("invalid_request", `${model} does not support watermark`);
|
|
1414
|
+
}
|
|
1178
1415
|
if (input.seconds !== void 0 && (input.seconds < constraint.minSeconds || input.seconds > constraint.maxSeconds)) {
|
|
1179
1416
|
throw new ApiError("invalid_request", `${model} seconds must be ${constraint.minSeconds}-${constraint.maxSeconds} (received: ${input.seconds})`);
|
|
1180
1417
|
}
|
|
@@ -1184,6 +1421,9 @@ function validateVideoGeneration(model, input) {
|
|
|
1184
1421
|
if (input.resolution && !constraint.resolutions.includes(input.resolution.toLowerCase())) {
|
|
1185
1422
|
throw new ApiError("invalid_request", `${model} resolution must be one of ${constraint.resolutions.join(", ")} (received: ${input.resolution})`);
|
|
1186
1423
|
}
|
|
1424
|
+
if (input.resolution && (input.imageCount ?? 0) > 0 && constraint.referenceResolutions && !constraint.referenceResolutions.includes(input.resolution.toLowerCase())) {
|
|
1425
|
+
throw new ApiError("invalid_request", `${model} reference-to-video mode supports only ${constraint.referenceResolutions.join(" and ")} (received: ${input.resolution}); ${input.resolution} requires text-to-video or a single --first-frame image`);
|
|
1426
|
+
}
|
|
1187
1427
|
if (input.seconds !== void 0 && input.resolution) {
|
|
1188
1428
|
const required = constraint.requiredSecondsByResolution?.[input.resolution.toLowerCase()];
|
|
1189
1429
|
if (required !== void 0 && input.seconds !== required) {
|
|
@@ -1210,8 +1450,19 @@ function validateVideoGeneration(model, input) {
|
|
|
1210
1450
|
if (!constraint.supportsSeed) {
|
|
1211
1451
|
throw new ApiError("invalid_request", `${model} does not support seed`);
|
|
1212
1452
|
}
|
|
1213
|
-
if (!Number.isInteger(input.seed) || input.seed < 0) {
|
|
1214
|
-
|
|
1453
|
+
if (!Number.isInteger(input.seed) || input.seed < 0 || constraint.maxSeed !== void 0 && input.seed > constraint.maxSeed) {
|
|
1454
|
+
const maximum = constraint.maxSeed === void 0 ? "" : ` no greater than ${constraint.maxSeed}`;
|
|
1455
|
+
throw new ApiError("invalid_request", `seed must be a non-negative integer${maximum}`);
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
if (input.fps !== void 0 && (!constraint.allowedFps || !constraint.allowedFps.includes(input.fps))) {
|
|
1459
|
+
throw new ApiError("invalid_request", `${model} fps must be one of ${constraint.allowedFps?.join(", ") ?? "none"} (received: ${input.fps})`);
|
|
1460
|
+
}
|
|
1461
|
+
if (input.safetyTolerance !== void 0 && constraint.safetyTolerance) {
|
|
1462
|
+
const ceiling = (input.imageCount ?? 0) > 0 && constraint.safetyToleranceMaxWithImages !== void 0 ? Math.min(constraint.safetyTolerance.maximum, constraint.safetyToleranceMaxWithImages) : constraint.safetyTolerance.maximum;
|
|
1463
|
+
if (!Number.isInteger(input.safetyTolerance) || input.safetyTolerance < constraint.safetyTolerance.minimum || input.safetyTolerance > ceiling) {
|
|
1464
|
+
const withImagesNote = constraint.safetyToleranceMaxWithImages !== void 0 && ceiling !== constraint.safetyTolerance.maximum ? ` while images are attached (text-only allows up to ${constraint.safetyTolerance.maximum})` : "";
|
|
1465
|
+
throw new ApiError("invalid_request", `${model} safety_tolerance must be ${constraint.safetyTolerance.minimum}-${ceiling}${withImagesNote} (received: ${input.safetyTolerance})`);
|
|
1215
1466
|
}
|
|
1216
1467
|
}
|
|
1217
1468
|
if (input.priority !== void 0 && !constraint.supportsPriority) {
|
|
@@ -1238,15 +1489,17 @@ import { join as join2, resolve } from "path";
|
|
|
1238
1489
|
import { pipeline } from "stream/promises";
|
|
1239
1490
|
import { Readable } from "stream";
|
|
1240
1491
|
var SUCCESS_STATES = /* @__PURE__ */ new Set(["success", "succeeded", "completed", "done", "finish", "finished"]);
|
|
1241
|
-
var FAILED_STATES = /* @__PURE__ */ new Set(["failed", "failure", "error", "
|
|
1492
|
+
var FAILED_STATES = /* @__PURE__ */ new Set(["failed", "failure", "error", "expired"]);
|
|
1493
|
+
var CANCELLED_STATES = /* @__PURE__ */ new Set(["cancelled", "canceled"]);
|
|
1242
1494
|
var RUNNING_STATES = /* @__PURE__ */ new Set(["running", "processing", "in_progress", "generating"]);
|
|
1243
1495
|
var PENDING_STATES = /* @__PURE__ */ new Set(["pending", "queued", "submitted", "waiting", "not_start"]);
|
|
1244
1496
|
function normalizeTaskStatus(raw) {
|
|
1245
1497
|
const s = String(raw ?? "").toLowerCase();
|
|
1246
1498
|
if (SUCCESS_STATES.has(s)) return "success";
|
|
1499
|
+
if (CANCELLED_STATES.has(s)) return "cancelled";
|
|
1247
1500
|
if (FAILED_STATES.has(s)) return "failed";
|
|
1248
1501
|
if (RUNNING_STATES.has(s)) return "running";
|
|
1249
|
-
if (PENDING_STATES.has(s)) return "pending";
|
|
1502
|
+
if (PENDING_STATES.has(s) || s.startsWith("queued_")) return "pending";
|
|
1250
1503
|
return "unknown";
|
|
1251
1504
|
}
|
|
1252
1505
|
function extractTaskId(body) {
|
|
@@ -1273,6 +1526,39 @@ function extractProgress(body) {
|
|
|
1273
1526
|
}
|
|
1274
1527
|
return void 0;
|
|
1275
1528
|
}
|
|
1529
|
+
async function cancelTask(baseUrl, apiKey, taskId) {
|
|
1530
|
+
try {
|
|
1531
|
+
await request({
|
|
1532
|
+
baseUrl,
|
|
1533
|
+
path: `/v1/video/generations/${encodeURIComponent(taskId)}`,
|
|
1534
|
+
method: "DELETE",
|
|
1535
|
+
apiKey
|
|
1536
|
+
});
|
|
1537
|
+
} catch (err) {
|
|
1538
|
+
if (err instanceof ApiError) {
|
|
1539
|
+
const code = err.upstreamCode;
|
|
1540
|
+
if (code === "task_already_running") {
|
|
1541
|
+
throw new ApiError("task_already_running", `\u4EFB\u52A1 ${taskId} \u5DF2\u5F00\u59CB\u8FD0\u884C\uFF0C\u65E0\u6CD5\u53D6\u6D88`, {
|
|
1542
|
+
status: err.status,
|
|
1543
|
+
hint: "\u8FD0\u884C\u4E2D\u7684\u4EFB\u52A1\u4E0D\u53EF\u53D6\u6D88\uFF1B\u7528 focalapi task status " + taskId + " \u8DDF\u8E2A\u5230\u5B8C\u6210\u540E\u4E0B\u8F7D\u4EA7\u7269\u3002"
|
|
1544
|
+
});
|
|
1545
|
+
}
|
|
1546
|
+
if (code === "task_already_finished") {
|
|
1547
|
+
throw new ApiError("task_already_finished", `\u4EFB\u52A1 ${taskId} \u5DF2\u7ED3\u675F\uFF0C\u65E0\u9700\u53D6\u6D88`, {
|
|
1548
|
+
status: err.status,
|
|
1549
|
+
hint: "\u8FD0\u884C focalapi task status " + taskId + " \u67E5\u770B\u7ED3\u679C\uFF1B\u6210\u529F\u540E\u53EF\u4E0B\u8F7D\u4EA7\u7269\u3002"
|
|
1550
|
+
});
|
|
1551
|
+
}
|
|
1552
|
+
if (code === "task_cancel_incomplete") {
|
|
1553
|
+
throw new ApiError("task_cancel_incomplete", `\u4EFB\u52A1 ${taskId} \u5DF2\u53D6\u6D88\u4F46\u6E05\u7406\u672A\u5B8C\u6210`, {
|
|
1554
|
+
status: err.status,
|
|
1555
|
+
hint: "\u8BF7\u91CD\u8BD5\u540C\u4E00\u6761 cancel \u547D\u4EE4\u5B8C\u6210\u6E05\u7406\u3002"
|
|
1556
|
+
});
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
throw err;
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1276
1562
|
async function fetchTask(baseUrl, apiKey, taskId) {
|
|
1277
1563
|
let raw;
|
|
1278
1564
|
try {
|
|
@@ -1308,10 +1594,17 @@ async function pollTask(baseUrl, apiKey, taskId, opts) {
|
|
|
1308
1594
|
last = await fetchTask(baseUrl, apiKey, taskId);
|
|
1309
1595
|
opts?.onUpdate?.(last);
|
|
1310
1596
|
if (last.status === "success") return last;
|
|
1597
|
+
if (last.status === "cancelled") {
|
|
1598
|
+
throw new ApiError("task_cancelled", `\u4EFB\u52A1 ${taskId} \u5DF2\u53D6\u6D88\uFF08\u4E0A\u6E38\u72B6\u6001\uFF1A${last.rawStatus || "cancelled"}\uFF09`, {
|
|
1599
|
+
body: last.raw,
|
|
1600
|
+
hint: "\u4EFB\u52A1\u5DF2\u505C\u6B62\u4E14\u4E0D\u518D\u4EA7\u751F\u8D39\u7528\uFF1B\u5982\u9700\u91CD\u65B0\u751F\u6210\u8BF7\u63D0\u4EA4\u65B0\u4EFB\u52A1\u3002"
|
|
1601
|
+
});
|
|
1602
|
+
}
|
|
1311
1603
|
if (last.status === "failed") {
|
|
1312
|
-
|
|
1604
|
+
const expired = last.rawStatus?.toLowerCase() === "expired";
|
|
1605
|
+
throw new ApiError("task_failed", expired ? `\u4EFB\u52A1 ${taskId} \u8D85\u8FC7\u6267\u884C\u671F\u9650\uFF08expired\uFF09\uFF0C\u8D39\u7528\u5DF2\u81EA\u52A8\u9000\u8FD8` : `\u4EFB\u52A1 ${taskId} \u5931\u8D25\uFF08\u4E0A\u6E38\u72B6\u6001\uFF1A${last.rawStatus || "unknown"}\uFF09`, {
|
|
1313
1606
|
body: last.raw,
|
|
1314
|
-
hint: "\u8FD0\u884C focalapi task status " + taskId + " --json \u67E5\u770B\u4E0A\u6E38\u8FD4\u56DE\u8BE6\u60C5\uFF1B\u82E5\u662F\u63D0\u793A\u8BCD\u6216\u53C2\u6570\u95EE\u9898\u8BF7\u8C03\u6574\u540E\u91CD\u8BD5\u3002"
|
|
1607
|
+
hint: expired ? "\u63D0\u4EA4\u72B6\u6001\u672A\u77E5\u4E14\u8D85\u8FC7 10 \u5206\u949F\u5BF9\u8D26\u671F\u9650\u7684\u4EFB\u52A1\u4F1A\u4EE5 expired \u7EC8\u6B62\u5E76\u9000\u6B3E\u3002\u53EF\u67E5\u770B focalapi task status " + taskId + " --json \u540E\u91CD\u65B0\u63D0\u4EA4\u3002" : "\u8FD0\u884C focalapi task status " + taskId + " --json \u67E5\u770B\u4E0A\u6E38\u8FD4\u56DE\u8BE6\u60C5\uFF1B\u82E5\u662F\u63D0\u793A\u8BCD\u6216\u53C2\u6570\u95EE\u9898\u8BF7\u8C03\u6574\u540E\u91CD\u8BD5\u3002"
|
|
1315
1608
|
});
|
|
1316
1609
|
}
|
|
1317
1610
|
if (Date.now() > deadline) {
|
|
@@ -1470,12 +1763,14 @@ function extractGeminiImageItems(response) {
|
|
|
1470
1763
|
}
|
|
1471
1764
|
function registerGen(program) {
|
|
1472
1765
|
const gen = program.command("gen").description("\u56FE\u50CF / \u89C6\u9891\u751F\u6210");
|
|
1473
|
-
gen.command("image").description("\u751F\u6210\u56FE\u50CF\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>", "\u56FE\u50CF\u6A21\u578B ID\uFF1B\u7701\u7565\u65F6\u7531 focalapi \u81EA\u52A8\u9009\u62E9").option("--size <size>", "\u5C3A\u5BF8\uFF0C\u5982 1024x1024").option("--aspect-ratio <ratio>", "\u539F\u751F\u753B\u9762\u6BD4\u4F8B\
|
|
1766
|
+
gen.command("image").description("\u751F\u6210\u56FE\u50CF\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>", "\u56FE\u50CF\u6A21\u578B ID\uFF1B\u7701\u7565\u65F6\u7531 focalapi \u81EA\u52A8\u9009\u62E9").option("--size <size>", "\u5C3A\u5BF8\uFF0C\u5982 1024x1024").option("--aspect-ratio <ratio>", "\u6A21\u578B\u539F\u751F\u753B\u9762\u6BD4\u4F8B\uFF0C\u5982 16:9").option("--resolution <resolution>", "\u6A21\u578B\u539F\u751F\u8F93\u51FA\u6863\u4F4D\uFF0C\u5982 1k\u30012k").option("--seed <n>", "\u6A21\u578B\u968F\u673A\u79CD\u5B50\uFF08\u975E\u8D1F\u6574\u6570\uFF09", (v) => Number.parseInt(v, 10)).option("--quality <quality>", "\u56FE\u50CF\u8D28\u91CF\u6863\u4F4D\uFF08\u4EC5\u652F\u6301\u8BE5\u53C2\u6570\u7684\u6A21\u578B\u751F\u6548\uFF09").option("--background <background>", "\u80CC\u666F\u6A21\u5F0F\uFF08\u4EC5 gpt-image-2 \u652F\u6301 auto/opaque\uFF09").option("--negative-prompt <text>", "\u8D1F\u9762\u63D0\u793A\u8BCD\uFF08\u4EC5\u652F\u6301\u8BE5\u53C2\u6570\u7684\u6A21\u578B\u751F\u6548\uFF09").option("--creativity <level>", "\u63D0\u793A\u8BCD\u6269\u5C55\u5F3A\u5EA6\uFF0C\u5982 raw\u3001low\u3001medium\u3001high").option("--prompt-extend <boolean>", "\u662F\u5426\u6269\u5C55\u63D0\u793A\u8BCD\uFF08\u53EA\u63A5\u53D7 true \u6216 false\uFF09", (v) => parseBooleanOption(v, "prompt-extend")).option("--style-references <json>", "Krea image_style_references JSON \u6570\u7EC4").option("--moodboards <json>", "Krea moodboards JSON \u6570\u7EC4").option("--watermark <boolean>", "\u662F\u5426\u6DFB\u52A0\u6C34\u5370\uFF08\u4EC5\u652F\u6301\u8BE5\u53C2\u6570\u7684\u6A21\u578B\u751F\u6548\uFF09", (v) => parseBooleanOption(v, "watermark")).option("--output-format <format>", "\u8F93\u51FA\u683C\u5F0F\uFF08\u4EC5 Seedream\uFF1Apng \u6216 jpeg\uFF09").option("--optimize-prompt <mode>", "\u63D0\u793A\u8BCD\u4F18\u5316\uFF08\u4EC5 Seedream\uFF1Aauto\u3001enabled \u6216 disabled\uFF09").option("--image <url...>", "\u53C2\u8003\u56FE\u6216\u7F16\u8F91\u56FE URL\uFF0C\u53EF\u591A\u4E2A").option("--mask <url>", "\u7F16\u8F91 mask URL\uFF08gpt-image-2 \u9700\u8981\u5355\u5F20\u53C2\u8003\u56FE\uFF09").option("--response-format <format>", "\u56FE\u50CF\u54CD\u5E94\u683C\u5F0F\uFF1Aurl \u6216 b64_json").option("--n <count>", "\u5F20\u6570\uFF081\u2013128\uFF09", (v) => Number.parseInt(v, 10), 1).option("--no-wait", "\u63D0\u4EA4\u540E\u7ACB\u5373\u8FD4\u56DE task_id\uFF0C\u4E0D\u7B49\u5F85\u56FE\u50CF\u751F\u6210\u5B8C\u6210").option("-o, --out <dir>", "\u8F93\u51FA\u76EE\u5F55", DEFAULT_OUT_DIR).action(async (promptParts, opts, cmd) => {
|
|
1474
1767
|
const g = cmd.optsWithGlobals();
|
|
1475
1768
|
const auth = resolveAuth(g);
|
|
1476
1769
|
const model = opts.model ?? (await resolveCreativeModel(auth, "image")).model.id;
|
|
1477
1770
|
if (!opts.model && !g.json) info(`\u5DF2\u81EA\u52A8\u9009\u62E9\u56FE\u50CF\u6A21\u578B\uFF1A${model}`);
|
|
1478
1771
|
const n = clampInt(opts.n, 1, MAX_IMAGE_N, "n");
|
|
1772
|
+
const styleReferences = opts.styleReferences ? parseJsonArray(opts.styleReferences, "style-references") : void 0;
|
|
1773
|
+
const moodboards = opts.moodboards ? parseJsonArray(opts.moodboards, "moodboards") : void 0;
|
|
1479
1774
|
validateImageGeneration(model, {
|
|
1480
1775
|
n,
|
|
1481
1776
|
size: opts.size,
|
|
@@ -1487,6 +1782,11 @@ function registerGen(program) {
|
|
|
1487
1782
|
watermark: opts.watermark,
|
|
1488
1783
|
outputFormat: opts.outputFormat,
|
|
1489
1784
|
optimizePrompt: opts.optimizePrompt,
|
|
1785
|
+
negativePrompt: opts.negativePrompt,
|
|
1786
|
+
creativity: opts.creativity,
|
|
1787
|
+
promptExtend: opts.promptExtend,
|
|
1788
|
+
styleReferenceCount: styleReferences?.length,
|
|
1789
|
+
moodboardCount: moodboards?.length,
|
|
1490
1790
|
responseFormat: opts.responseFormat,
|
|
1491
1791
|
imageCount: opts.image?.length,
|
|
1492
1792
|
hasMask: Boolean(opts.mask)
|
|
@@ -1501,6 +1801,11 @@ function registerGen(program) {
|
|
|
1501
1801
|
if (opts.seed !== void 0) body.seed = opts.seed;
|
|
1502
1802
|
if (opts.quality) body.quality = opts.quality;
|
|
1503
1803
|
if (opts.background) body.background = opts.background;
|
|
1804
|
+
if (opts.negativePrompt) body.negative_prompt = opts.negativePrompt;
|
|
1805
|
+
if (opts.creativity) body.creativity = opts.creativity.toLowerCase();
|
|
1806
|
+
if (opts.promptExtend !== void 0) body.prompt_extend = opts.promptExtend;
|
|
1807
|
+
if (styleReferences) body.image_style_references = styleReferences;
|
|
1808
|
+
if (moodboards) body.moodboards = moodboards;
|
|
1504
1809
|
if (opts.watermark !== void 0) body.watermark = opts.watermark;
|
|
1505
1810
|
if (opts.outputFormat) body.output_format = opts.outputFormat.toLowerCase();
|
|
1506
1811
|
if (opts.optimizePrompt) body.optimize_prompt_options = { thinking: opts.optimizePrompt.toLowerCase() };
|
|
@@ -1554,7 +1859,9 @@ function registerGen(program) {
|
|
|
1554
1859
|
seed: opts.seed,
|
|
1555
1860
|
thinkingLevel: opts.thinkingLevel,
|
|
1556
1861
|
temperature: opts.temperature,
|
|
1557
|
-
topP: opts.topP
|
|
1862
|
+
topP: opts.topP,
|
|
1863
|
+
referenceImageCount: opts.image?.length,
|
|
1864
|
+
nonDataUriReferenceCount: opts.image?.filter((source) => !source.trim().startsWith("data:")).length
|
|
1558
1865
|
});
|
|
1559
1866
|
const suppliedConfig = parseGenerationConfig(opts.config);
|
|
1560
1867
|
const suppliedResponseFormat = suppliedConfig.responseFormat;
|
|
@@ -1644,7 +1951,7 @@ function registerGen(program) {
|
|
|
1644
1951
|
if (res.id) info(`\u4EA4\u4E92 ID\uFF1A${res.id}`);
|
|
1645
1952
|
}
|
|
1646
1953
|
});
|
|
1647
|
-
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\u7CBE\u786E\u8303\u56F4\u8FD0\u884C focalapi models get <model> \u67E5\u770B", (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>", "
|
|
1954
|
+
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\u7CBE\u786E\u8303\u56F4\u8FD0\u884C focalapi models get <model> \u67E5\u770B", (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\uFF08Grok \u89C6\u9891\u4E3A reference-to-video \u6A21\u5F0F\uFF0C\u53EF\u591A\u4E2A\uFF09").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("--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-compatible content JSON array; overrides prompt/image facade fields").action(
|
|
1648
1955
|
async (promptParts, opts, cmd) => {
|
|
1649
1956
|
const g = cmd.optsWithGlobals();
|
|
1650
1957
|
const auth = resolveAuth(g);
|
|
@@ -1658,10 +1965,13 @@ function registerGen(program) {
|
|
|
1658
1965
|
}
|
|
1659
1966
|
if (opts.size) body.size = opts.size;
|
|
1660
1967
|
if (opts.image) body.images = opts.image;
|
|
1968
|
+
if (opts.firstFrame) body.image = opts.firstFrame;
|
|
1661
1969
|
if (opts.resolution) metadata.resolution = opts.resolution.toLowerCase();
|
|
1662
1970
|
if (opts.ratio) metadata.ratio = opts.ratio;
|
|
1663
1971
|
if (opts.aspectRatio) metadata.ratio = opts.aspectRatio;
|
|
1664
1972
|
if (opts.seed !== void 0) metadata.seed = opts.seed;
|
|
1973
|
+
if (opts.fps !== void 0) metadata.fps = opts.fps;
|
|
1974
|
+
if (opts.safetyTolerance !== void 0) metadata.safety_tolerance = opts.safetyTolerance;
|
|
1665
1975
|
if (opts.generateAudio !== void 0) metadata.generate_audio = opts.generateAudio;
|
|
1666
1976
|
if (opts.watermark !== void 0) metadata.watermark = opts.watermark;
|
|
1667
1977
|
if (opts.serviceTier) metadata.service_tier = opts.serviceTier;
|
|
@@ -1677,10 +1987,16 @@ function registerGen(program) {
|
|
|
1677
1987
|
ratio: opts.ratio,
|
|
1678
1988
|
aspectRatio: opts.aspectRatio,
|
|
1679
1989
|
seed: opts.seed,
|
|
1990
|
+
fps: opts.fps,
|
|
1991
|
+
safetyTolerance: opts.safetyTolerance,
|
|
1680
1992
|
serviceTier: opts.serviceTier,
|
|
1681
1993
|
priority: opts.priority,
|
|
1682
1994
|
executionExpiresAfter: opts.executionExpiresAfter,
|
|
1683
|
-
safetyIdentifier: opts.safetyIdentifier
|
|
1995
|
+
safetyIdentifier: opts.safetyIdentifier,
|
|
1996
|
+
imageCount: opts.image?.length,
|
|
1997
|
+
firstFrameCount: opts.firstFrame ? 1 : 0,
|
|
1998
|
+
generateAudio: opts.generateAudio,
|
|
1999
|
+
watermark: opts.watermark
|
|
1684
2000
|
});
|
|
1685
2001
|
if (Object.keys(metadata).length > 0) body.metadata = metadata;
|
|
1686
2002
|
const created = await withProgress("\u6B63\u5728\u63D0\u4EA4\u89C6\u9891\u4EFB\u52A1", () => request({
|
|
@@ -1699,7 +2015,7 @@ function registerGen(program) {
|
|
|
1699
2015
|
printJson({ model, task_id: taskId, submitted: true, next_command: `focalapi task status ${taskId} --json` });
|
|
1700
2016
|
} else {
|
|
1701
2017
|
process.stdout.write(taskId + "\n");
|
|
1702
|
-
info(`\u4EFB\u52A1\u5DF2\u63D0\u4EA4\u3002\u7EED\u53D6\uFF1Afocalapi task status ${taskId} / focalapi task download ${taskId}`);
|
|
2018
|
+
info(`\u4EFB\u52A1\u5DF2\u63D0\u4EA4\u3002\u7EED\u53D6\uFF1Afocalapi task status ${taskId} / focalapi task download ${taskId}\uFF1B\u6392\u961F\u4E2D\u53EF\u53D6\u6D88\uFF1Afocalapi task cancel ${taskId}`);
|
|
1703
2019
|
}
|
|
1704
2020
|
return;
|
|
1705
2021
|
}
|
|
@@ -1725,7 +2041,7 @@ function registerGen(program) {
|
|
|
1725
2041
|
|
|
1726
2042
|
// src/commands/task.ts
|
|
1727
2043
|
function registerTask(program) {
|
|
1728
|
-
const task = program.command("task").description("\u4EFB\u52A1\u67E5\u8BE2\u4E0E\u4EA7\u7269\u4E0B\u8F7D\uFF08\u89C6\u9891\u7B49\u4EFB\u52A1\u5236\u80FD\u529B\uFF09");
|
|
2044
|
+
const task = program.command("task").description("\u4EFB\u52A1\u67E5\u8BE2\u3001\u53D6\u6D88\u4E0E\u4EA7\u7269\u4E0B\u8F7D\uFF08\u89C6\u9891\u7B49\u4EFB\u52A1\u5236\u80FD\u529B\uFF09");
|
|
1729
2045
|
task.command("status").description("\u67E5\u8BE2\u4EFB\u52A1\u72B6\u6001").argument("<task_id>", "\u4EFB\u52A1 ID").action(async (taskId, _opts, cmd) => {
|
|
1730
2046
|
const g = cmd.optsWithGlobals();
|
|
1731
2047
|
const auth = resolveAuth(g);
|
|
@@ -1744,6 +2060,19 @@ function registerTask(program) {
|
|
|
1744
2060
|
if (info_.status === "success") {
|
|
1745
2061
|
info(`\u4EA7\u7269\u4E0B\u8F7D\uFF1Afocalapi task download ${taskId}`);
|
|
1746
2062
|
}
|
|
2063
|
+
if (info_.status === "pending" || info_.status === "running") {
|
|
2064
|
+
info(`\u5982\u9700\u505C\u6B62\u6392\u961F\u4E2D\u7684\u4EFB\u52A1\uFF1Afocalapi task cancel ${taskId}`);
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
});
|
|
2068
|
+
task.command("cancel").description("\u53D6\u6D88\u6392\u961F\u4E2D\u7684\u4EFB\u52A1\uFF08\u8FD0\u884C\u4E2D\u7684\u4EFB\u52A1\u4E0D\u53EF\u53D6\u6D88\uFF1B\u53D6\u6D88\u540E\u8D39\u7528\u81EA\u52A8\u9000\u8FD8\uFF09").argument("<task_id>", "\u4EFB\u52A1 ID").action(async (taskId, _opts, cmd) => {
|
|
2069
|
+
const g = cmd.optsWithGlobals();
|
|
2070
|
+
const auth = resolveAuth(g);
|
|
2071
|
+
await cancelTask(auth.baseUrl, auth.apiKey, taskId);
|
|
2072
|
+
if (g.json) {
|
|
2073
|
+
printJson({ task_id: taskId, status: "cancelled", cancelled: true });
|
|
2074
|
+
} else {
|
|
2075
|
+
info(`\u2713 \u4EFB\u52A1 ${taskId} \u5DF2\u53D6\u6D88`);
|
|
1747
2076
|
}
|
|
1748
2077
|
});
|
|
1749
2078
|
task.command("download").description("\u4E0B\u8F7D\u4EFB\u52A1\u4EA7\u7269\uFF08\u7ECF focalapi \u5185\u5BB9\u4EE3\u7406\uFF0C\u65E0\u9700\u4E0A\u6E38\u7B7E\u540D URL\uFF09").argument("<task_id>", "\u4EFB\u52A1 ID").option("-o, --out <dir>", "\u8F93\u51FA\u76EE\u5F55", "focalapi-out").action(async (taskId, opts, cmd) => {
|