focalapi-cli 0.4.1 → 0.5.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 +10 -0
- package/dist/cli.js +137 -8
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.0 - 2026-08-25
|
|
4
|
+
|
|
5
|
+
- Fixed `krea-2-medium/-turbo/large` reference-image validation: the local table said 0 while the platform contract accepts up to 10 reference images (each maps to a Krea style reference), so every `gen image --image` edit was rejected locally without reaching the server (2026-08-25 live audit P01).
|
|
6
|
+
- Added local constraints for the August image wave so invalid parameters are intercepted before submission: `topaz-image-reimagine`/`-bloom-2`/`-wonder-3-5`, `wavespeed-seedvr2-upscale`/`-ultimate-upscale`, `quiver-text-to-svg`/`-image-to-svg`, `hitpaw-image-enhance`/`-portrait-enhance`, `beeble-switchx-image-720p`/`-1080p`, `recraft-v4`/`-v4-pro`.
|
|
7
|
+
- Enforce the exact-one-input-image requirement locally for the enhance/upscale/vectorize/switch families instead of surfacing the server 400.
|
|
8
|
+
- Map `--resolution` to the `target_resolution` wire field for WaveSpeed upscales (the generic `resolution` field is silently ignored by that family).
|
|
9
|
+
- Send Topaz `--creativity` as an integer 1-9 (the platform contract takes a number; the Krea string modes are unchanged).
|
|
10
|
+
- Validate Recraft sizes against the official V4 tables (non-pro caps at ~1.5K, pro at ~3K) and reject reference images locally (the channel is text-to-image only).
|
|
11
|
+
- Intercept `gen image` on Gemini image models with a direct pointer to `gen gemini-image` (native generateContent endpoint) instead of an opaque upstream failure (audit P07).
|
|
12
|
+
|
|
3
13
|
## 0.4.1 - 2026-08-18
|
|
4
14
|
|
|
5
15
|
- Added `@file` syntax for local reference media on all gen paths (`--image @C:/path/ref.jpg`): the file is read and inlined as a data URI with per-file 8MB and per-command 12MB guards — local files no longer require manual hosting.
|
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.
|
|
179
|
+
var VERSION = true ? "0.5.0" : "0.0.0-dev";
|
|
180
180
|
|
|
181
181
|
// src/commands/auth.ts
|
|
182
182
|
import { createInterface } from "readline/promises";
|
|
@@ -1050,7 +1050,7 @@ var IMAGE_CONSTRAINTS = {
|
|
|
1050
1050
|
},
|
|
1051
1051
|
"krea-2-medium": {
|
|
1052
1052
|
maxN: 1,
|
|
1053
|
-
maxReferenceImages:
|
|
1053
|
+
maxReferenceImages: 10,
|
|
1054
1054
|
aspectRatios: KREA_IMAGE_ASPECT_RATIOS,
|
|
1055
1055
|
resolutions: ["1k"],
|
|
1056
1056
|
supportsSeed: true,
|
|
@@ -1061,7 +1061,7 @@ var IMAGE_CONSTRAINTS = {
|
|
|
1061
1061
|
},
|
|
1062
1062
|
"krea-2-medium-turbo": {
|
|
1063
1063
|
maxN: 1,
|
|
1064
|
-
maxReferenceImages:
|
|
1064
|
+
maxReferenceImages: 10,
|
|
1065
1065
|
aspectRatios: KREA_IMAGE_ASPECT_RATIOS,
|
|
1066
1066
|
resolutions: ["1k"],
|
|
1067
1067
|
supportsSeed: true,
|
|
@@ -1072,7 +1072,7 @@ var IMAGE_CONSTRAINTS = {
|
|
|
1072
1072
|
},
|
|
1073
1073
|
"krea-2-large": {
|
|
1074
1074
|
maxN: 1,
|
|
1075
|
-
maxReferenceImages:
|
|
1075
|
+
maxReferenceImages: 10,
|
|
1076
1076
|
aspectRatios: KREA_IMAGE_ASPECT_RATIOS,
|
|
1077
1077
|
resolutions: ["1k"],
|
|
1078
1078
|
supportsSeed: true,
|
|
@@ -1106,8 +1106,112 @@ var IMAGE_CONSTRAINTS = {
|
|
|
1106
1106
|
supportsNegativePrompt: true,
|
|
1107
1107
|
supportsPromptExtend: true,
|
|
1108
1108
|
supportsWatermark: true
|
|
1109
|
+
},
|
|
1110
|
+
"topaz-image-reimagine": {
|
|
1111
|
+
maxN: 1,
|
|
1112
|
+
minImageCount: 1,
|
|
1113
|
+
maxReferenceImages: 1,
|
|
1114
|
+
integerCreativity: [1, 9]
|
|
1115
|
+
},
|
|
1116
|
+
"topaz-image-bloom-2": {
|
|
1117
|
+
maxN: 1,
|
|
1118
|
+
minImageCount: 1,
|
|
1119
|
+
maxReferenceImages: 1
|
|
1120
|
+
},
|
|
1121
|
+
"topaz-image-wonder-3-5": {
|
|
1122
|
+
maxN: 1,
|
|
1123
|
+
minImageCount: 1,
|
|
1124
|
+
maxReferenceImages: 1
|
|
1125
|
+
},
|
|
1126
|
+
"wavespeed-seedvr2-upscale": {
|
|
1127
|
+
maxN: 1,
|
|
1128
|
+
minImageCount: 1,
|
|
1129
|
+
maxReferenceImages: 1,
|
|
1130
|
+
resolutions: ["2k", "4k", "8k"],
|
|
1131
|
+
resolutionParam: "target_resolution"
|
|
1132
|
+
},
|
|
1133
|
+
"wavespeed-ultimate-upscale": {
|
|
1134
|
+
maxN: 1,
|
|
1135
|
+
minImageCount: 1,
|
|
1136
|
+
maxReferenceImages: 1,
|
|
1137
|
+
resolutions: ["2k", "4k", "8k"],
|
|
1138
|
+
resolutionParam: "target_resolution"
|
|
1139
|
+
},
|
|
1140
|
+
"quiver-text-to-svg": {
|
|
1141
|
+
maxN: 1,
|
|
1142
|
+
maxReferenceImages: 4
|
|
1143
|
+
},
|
|
1144
|
+
"quiver-image-to-svg": {
|
|
1145
|
+
maxN: 1,
|
|
1146
|
+
minImageCount: 1,
|
|
1147
|
+
maxReferenceImages: 1
|
|
1148
|
+
},
|
|
1149
|
+
"hitpaw-image-enhance": {
|
|
1150
|
+
maxN: 1,
|
|
1151
|
+
minImageCount: 1,
|
|
1152
|
+
maxReferenceImages: 1
|
|
1153
|
+
},
|
|
1154
|
+
"hitpaw-image-portrait-enhance": {
|
|
1155
|
+
maxN: 1,
|
|
1156
|
+
minImageCount: 1,
|
|
1157
|
+
maxReferenceImages: 1
|
|
1158
|
+
},
|
|
1159
|
+
"beeble-switchx-image-720p": {
|
|
1160
|
+
maxN: 1,
|
|
1161
|
+
minImageCount: 1,
|
|
1162
|
+
maxReferenceImages: 2
|
|
1163
|
+
},
|
|
1164
|
+
"beeble-switchx-image-1080p": {
|
|
1165
|
+
maxN: 1,
|
|
1166
|
+
minImageCount: 1,
|
|
1167
|
+
maxReferenceImages: 2
|
|
1168
|
+
},
|
|
1169
|
+
"recraft-v4": {
|
|
1170
|
+
defaultSize: "1024x1024",
|
|
1171
|
+
maxN: 6,
|
|
1172
|
+
maxReferenceImages: 0,
|
|
1173
|
+
allowedSizes: [
|
|
1174
|
+
"1024x1024",
|
|
1175
|
+
"1280x832",
|
|
1176
|
+
"832x1280",
|
|
1177
|
+
"1216x896",
|
|
1178
|
+
"896x1216",
|
|
1179
|
+
"1152x896",
|
|
1180
|
+
"896x1152",
|
|
1181
|
+
"1536x768",
|
|
1182
|
+
"768x1536",
|
|
1183
|
+
"1344x768",
|
|
1184
|
+
"768x1344",
|
|
1185
|
+
"832x1344",
|
|
1186
|
+
"1280x896",
|
|
1187
|
+
"896x1280"
|
|
1188
|
+
]
|
|
1189
|
+
},
|
|
1190
|
+
"recraft-v4-pro": {
|
|
1191
|
+
defaultSize: "2048x2048",
|
|
1192
|
+
maxN: 6,
|
|
1193
|
+
maxReferenceImages: 0,
|
|
1194
|
+
allowedSizes: [
|
|
1195
|
+
"2048x2048",
|
|
1196
|
+
"2560x1664",
|
|
1197
|
+
"1664x2560",
|
|
1198
|
+
"2432x1792",
|
|
1199
|
+
"1792x2432",
|
|
1200
|
+
"2304x1792",
|
|
1201
|
+
"1792x2304",
|
|
1202
|
+
"3072x1536",
|
|
1203
|
+
"1536x3072",
|
|
1204
|
+
"2688x1536",
|
|
1205
|
+
"1536x2688",
|
|
1206
|
+
"1664x2688",
|
|
1207
|
+
"2560x1792",
|
|
1208
|
+
"1792x2560"
|
|
1209
|
+
]
|
|
1109
1210
|
}
|
|
1110
1211
|
};
|
|
1212
|
+
function getImageConstraint(model) {
|
|
1213
|
+
return IMAGE_CONSTRAINTS[model.trim()];
|
|
1214
|
+
}
|
|
1111
1215
|
var SEEDANCE_RATIOS = ["adaptive", "16:9", "4:3", "1:1", "3:4", "9:16", "21:9"];
|
|
1112
1216
|
var GROK_VIDEO_ASPECT_RATIOS = ["auto", "16:9", "4:3", "3:2", "1:1", "2:3", "3:4", "9:16"];
|
|
1113
1217
|
var KLING_VIDEO_ASPECT_RATIOS = ["16:9", "9:16", "1:1"];
|
|
@@ -1285,11 +1389,18 @@ function validateImageGeneration(model, input) {
|
|
|
1285
1389
|
if (input.responseFormat && input.responseFormat !== "url" && input.responseFormat !== "b64_json") {
|
|
1286
1390
|
throw new ApiError("invalid_request", `response_format must be url or b64_json (received: ${input.responseFormat})`);
|
|
1287
1391
|
}
|
|
1288
|
-
const
|
|
1392
|
+
const trimmedModel = model.trim();
|
|
1393
|
+
if (GEMINI_IMAGE_CONSTRAINTS[trimmedModel]) {
|
|
1394
|
+
throw new ApiError("invalid_request", `${model} is a Gemini image model; use \`focalapi gen gemini-image\` (native generateContent endpoint) instead of \`gen image\``);
|
|
1395
|
+
}
|
|
1396
|
+
const constraint = IMAGE_CONSTRAINTS[trimmedModel];
|
|
1289
1397
|
if (!constraint) return;
|
|
1290
1398
|
if (input.n < 1 || input.n > constraint.maxN) {
|
|
1291
1399
|
throw new ApiError("invalid_request", `${model} n must be 1-${constraint.maxN} (received: ${input.n})`);
|
|
1292
1400
|
}
|
|
1401
|
+
if (input.imageCount !== void 0 && constraint.minImageCount !== void 0 && input.imageCount < constraint.minImageCount) {
|
|
1402
|
+
throw new ApiError("invalid_request", `${model} requires at least ${constraint.minImageCount} input image${constraint.minImageCount === 1 ? "" : "s"}`);
|
|
1403
|
+
}
|
|
1293
1404
|
if (input.imageCount !== void 0 && constraint.maxReferenceImages !== void 0 && input.imageCount > constraint.maxReferenceImages) {
|
|
1294
1405
|
throw new ApiError("invalid_request", `${model} supports at most ${constraint.maxReferenceImages} reference images`);
|
|
1295
1406
|
}
|
|
@@ -1305,7 +1416,16 @@ function validateImageGeneration(model, input) {
|
|
|
1305
1416
|
validateOptionalChoice(model, "resolution", input.resolution, constraint.resolutions);
|
|
1306
1417
|
validateOptionalChoice(model, "output_format", input.outputFormat, constraint.outputFormats);
|
|
1307
1418
|
validateOptionalChoice(model, "optimize_prompt", input.optimizePrompt, constraint.optimizePromptModes);
|
|
1308
|
-
|
|
1419
|
+
if (!constraint.integerCreativity) {
|
|
1420
|
+
validateOptionalChoice(model, "creativity", input.creativity, constraint.creativityModes);
|
|
1421
|
+
}
|
|
1422
|
+
if (input.creativity !== void 0 && constraint.integerCreativity) {
|
|
1423
|
+
const [minimum, maximum] = constraint.integerCreativity;
|
|
1424
|
+
const creativity = Number(input.creativity);
|
|
1425
|
+
if (!Number.isInteger(creativity) || creativity < minimum || creativity > maximum) {
|
|
1426
|
+
throw new ApiError("invalid_request", `${model} creativity must be an integer ${minimum}-${maximum} (received: ${input.creativity})`);
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1309
1429
|
if (input.negativePrompt !== void 0 && !constraint.supportsNegativePrompt) {
|
|
1310
1430
|
throw new ApiError("invalid_request", `${model} does not support negative_prompt`);
|
|
1311
1431
|
}
|
|
@@ -1340,6 +1460,12 @@ function validateImageGeneration(model, input) {
|
|
|
1340
1460
|
return;
|
|
1341
1461
|
}
|
|
1342
1462
|
const suppliedSize = input.size ?? constraint.defaultSize;
|
|
1463
|
+
if (constraint.allowedSizes) {
|
|
1464
|
+
if (!constraint.allowedSizes.includes(suppliedSize.toLowerCase())) {
|
|
1465
|
+
throw new ApiError("invalid_request", `${model} size must be one of ${constraint.allowedSizes.join(", ")} (received: ${suppliedSize})`);
|
|
1466
|
+
}
|
|
1467
|
+
return;
|
|
1468
|
+
}
|
|
1343
1469
|
if (constraint.sizeTiers?.includes(suppliedSize.toLowerCase())) return;
|
|
1344
1470
|
if (constraint.sizeTiers && !/^\d+x\d+$/i.test(suppliedSize)) {
|
|
1345
1471
|
throw new ApiError("invalid_request", `${model} size must be one of ${constraint.sizeTiers.join(", ")} or WIDTHxHEIGHT (received: ${suppliedSize})`);
|
|
@@ -1981,14 +2107,17 @@ function registerGen(program) {
|
|
|
1981
2107
|
throw new ApiError("invalid_request", "--response-format b64_json cannot be used with --no-wait; use url");
|
|
1982
2108
|
}
|
|
1983
2109
|
const body = { model, prompt: promptParts.join(" "), n };
|
|
2110
|
+
const constraint = getImageConstraint(model);
|
|
1984
2111
|
if (opts.size) body.size = opts.size;
|
|
1985
2112
|
if (opts.aspectRatio) body.aspect_ratio = opts.aspectRatio;
|
|
1986
|
-
if (opts.resolution) body
|
|
2113
|
+
if (opts.resolution) body[constraint?.resolutionParam ?? "resolution"] = opts.resolution.toLowerCase();
|
|
1987
2114
|
if (opts.seed !== void 0) body.seed = opts.seed;
|
|
1988
2115
|
if (opts.quality) body.quality = opts.quality;
|
|
1989
2116
|
if (opts.background) body.background = opts.background;
|
|
1990
2117
|
if (opts.negativePrompt) body.negative_prompt = opts.negativePrompt;
|
|
1991
|
-
if (opts.creativity)
|
|
2118
|
+
if (opts.creativity) {
|
|
2119
|
+
body.creativity = constraint?.integerCreativity ? Number(opts.creativity) : opts.creativity.toLowerCase();
|
|
2120
|
+
}
|
|
1992
2121
|
if (opts.promptExtend !== void 0) body.prompt_extend = opts.promptExtend;
|
|
1993
2122
|
if (styleReferences) body.image_style_references = styleReferences;
|
|
1994
2123
|
if (moodboards) body.moodboards = moodboards;
|