@flatkey-ai/cli 0.1.21 → 0.1.22
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/package.json +1 -1
- package/src/api.js +4 -2
- package/src/cli.js +62 -9
package/package.json
CHANGED
package/src/api.js
CHANGED
|
@@ -16,7 +16,9 @@ export async function generateImage(options) {
|
|
|
16
16
|
|
|
17
17
|
export async function uploadTempMediaImage(options) {
|
|
18
18
|
const form = new FormData();
|
|
19
|
-
form.append("file", new Blob([options.file]
|
|
19
|
+
form.append("file", new Blob([options.file], cleanObject({
|
|
20
|
+
type: options.contentType,
|
|
21
|
+
})), options.filename ?? "image");
|
|
20
22
|
return requestJsonFromPlan(options, planRequest(options, "/v1/temp-media/images", {
|
|
21
23
|
method: "POST",
|
|
22
24
|
headers: authHeaders(options.apiKey),
|
|
@@ -240,7 +242,7 @@ async function requestJsonFromPlan(options, plan) {
|
|
|
240
242
|
: JSON.stringify(plan.body),
|
|
241
243
|
});
|
|
242
244
|
const body = await readJson(response);
|
|
243
|
-
if (!response.ok) {
|
|
245
|
+
if (!response.ok || body?.success === false) {
|
|
244
246
|
throw new FlatkeyError(extractErrorMessage(body, response.status), {
|
|
245
247
|
status: response.status,
|
|
246
248
|
});
|
package/src/cli.js
CHANGED
|
@@ -404,7 +404,7 @@ async function handleImageUpload(command, deps) {
|
|
|
404
404
|
const { basename } = await import("node:path");
|
|
405
405
|
const { readFile } = await import("node:fs/promises");
|
|
406
406
|
const { resolveApiKey, resolveOrigins } = await import("./config.js");
|
|
407
|
-
const { uploadTempMediaImage } = await import("./api.js");
|
|
407
|
+
const { DEFAULT_CONSOLE_URL, uploadTempMediaImage } = await import("./api.js");
|
|
408
408
|
const file = command.options.file;
|
|
409
409
|
if (typeof file !== "string" || file.trim() === "") {
|
|
410
410
|
throw new Error("Missing --file value. Run `flatkey image upload --help` to see supported options.");
|
|
@@ -421,18 +421,20 @@ async function handleImageUpload(command, deps) {
|
|
|
421
421
|
});
|
|
422
422
|
const response = await uploadTempMediaImage({
|
|
423
423
|
apiKey,
|
|
424
|
-
baseUrl: consoleOrigin,
|
|
424
|
+
baseUrl: consoleOrigin ?? DEFAULT_CONSOLE_URL,
|
|
425
425
|
file: await readFile(await expandHomePath(file)),
|
|
426
426
|
filename: basename(file),
|
|
427
|
+
contentType: imageContentTypeFromPath(file),
|
|
427
428
|
fetch: deps.fetch,
|
|
428
429
|
});
|
|
429
430
|
const data = response?.data ?? response;
|
|
430
|
-
|
|
431
|
+
const signedUrl = extractUploadedImageUrl(response);
|
|
432
|
+
if (!signedUrl) {
|
|
431
433
|
throw new Error("Image upload failed: missing signed_url.");
|
|
432
434
|
}
|
|
433
435
|
return {
|
|
434
436
|
kind: "upload",
|
|
435
|
-
url:
|
|
437
|
+
url: signedUrl,
|
|
436
438
|
objectKey: data?.object_key,
|
|
437
439
|
expiresAt: data?.expires_at,
|
|
438
440
|
expiresIn: data?.expires_in,
|
|
@@ -449,6 +451,7 @@ async function handleGenerate(command, deps) {
|
|
|
449
451
|
generateImage,
|
|
450
452
|
generateText,
|
|
451
453
|
generateVideo,
|
|
454
|
+
DEFAULT_CONSOLE_URL,
|
|
452
455
|
getVideo,
|
|
453
456
|
planAudioMusicRequest,
|
|
454
457
|
planAudioRequest,
|
|
@@ -467,7 +470,7 @@ async function handleGenerate(command, deps) {
|
|
|
467
470
|
env: deps.env ?? process.env,
|
|
468
471
|
configDir: deps.configDir,
|
|
469
472
|
});
|
|
470
|
-
const { routerOrigin
|
|
473
|
+
const { routerOrigin } = await resolveOrigins({
|
|
471
474
|
baseUrl: command.options.base_url,
|
|
472
475
|
consoleUrl: command.options.console_url,
|
|
473
476
|
env: deps.env ?? process.env,
|
|
@@ -482,7 +485,7 @@ async function handleGenerate(command, deps) {
|
|
|
482
485
|
...command.options,
|
|
483
486
|
apiKey,
|
|
484
487
|
baseUrl: routerOrigin,
|
|
485
|
-
tempMediaBaseUrl: tempMediaBaseUrl ??
|
|
488
|
+
tempMediaBaseUrl: tempMediaBaseUrl ?? DEFAULT_CONSOLE_URL,
|
|
486
489
|
env: deps.env ?? process.env,
|
|
487
490
|
fetch: deps.fetch,
|
|
488
491
|
};
|
|
@@ -570,13 +573,27 @@ async function uploadLocalImage(file, options, deps) {
|
|
|
570
573
|
baseUrl: options.tempMediaBaseUrl ?? options.baseUrl,
|
|
571
574
|
file: await readFile(await expandHomePath(file)),
|
|
572
575
|
filename: basename(file),
|
|
576
|
+
contentType: imageContentTypeFromPath(file),
|
|
573
577
|
fetch: options.fetch,
|
|
574
578
|
});
|
|
575
|
-
const url = response
|
|
579
|
+
const url = extractUploadedImageUrl(response);
|
|
576
580
|
if (!url) throw new Error(`Image upload failed: missing signed_url for ${file}`);
|
|
577
581
|
return url;
|
|
578
582
|
}
|
|
579
583
|
|
|
584
|
+
function extractUploadedImageUrl(response) {
|
|
585
|
+
const data = response?.data ?? response;
|
|
586
|
+
return data?.signed_url ?? data?.signedUrl ?? data?.url ?? response?.signed_url ?? response?.signedUrl ?? response?.url;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
function imageContentTypeFromPath(file) {
|
|
590
|
+
const lower = String(file).toLowerCase();
|
|
591
|
+
if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
|
|
592
|
+
if (lower.endsWith(".png")) return "image/png";
|
|
593
|
+
if (lower.endsWith(".webp")) return "image/webp";
|
|
594
|
+
return undefined;
|
|
595
|
+
}
|
|
596
|
+
|
|
580
597
|
async function waitForVideoResult(response, options) {
|
|
581
598
|
if (hasArtifact(response)) return response;
|
|
582
599
|
const taskId = videoTaskId(response);
|
|
@@ -835,9 +852,45 @@ function formatVoices(voices) {
|
|
|
835
852
|
function formatKeyValues(result) {
|
|
836
853
|
const entries = Object.entries(result);
|
|
837
854
|
if (entries.length === 0) return "OK";
|
|
838
|
-
|
|
855
|
+
const lines = entries
|
|
839
856
|
.map(([key, value]) => `${humanLabel(key)}: ${formatScalar(value)}`)
|
|
840
|
-
|
|
857
|
+
const warning = lowCreditWarning(result);
|
|
858
|
+
if (warning) lines.push(warning);
|
|
859
|
+
return lines.join("\n");
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
function lowCreditWarning(result) {
|
|
863
|
+
const credits = extractCreditAmount(result);
|
|
864
|
+
if (credits === undefined || credits >= 10) return "";
|
|
865
|
+
return `Low credit warning: remaining credits are ${credits}. Top up soon.`;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
function extractCreditAmount(result) {
|
|
869
|
+
const creditKeys = new Set([
|
|
870
|
+
"credit",
|
|
871
|
+
"credits",
|
|
872
|
+
"remaining",
|
|
873
|
+
"balance",
|
|
874
|
+
"total_available",
|
|
875
|
+
"totalAvailable",
|
|
876
|
+
"available_balance",
|
|
877
|
+
"availableBalance",
|
|
878
|
+
]);
|
|
879
|
+
const stack = [result];
|
|
880
|
+
const seen = new Set();
|
|
881
|
+
while (stack.length > 0) {
|
|
882
|
+
const value = stack.shift();
|
|
883
|
+
if (!value || typeof value !== "object" || seen.has(value)) continue;
|
|
884
|
+
seen.add(value);
|
|
885
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
886
|
+
if (creditKeys.has(key)) {
|
|
887
|
+
const number = Number(entry);
|
|
888
|
+
if (Number.isFinite(number)) return number;
|
|
889
|
+
}
|
|
890
|
+
if (entry && typeof entry === "object") stack.push(entry);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
return undefined;
|
|
841
894
|
}
|
|
842
895
|
|
|
843
896
|
function formatScalar(value) {
|