@flatkey-ai/cli 0.1.11 → 0.1.13
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/cli.js +147 -18
- package/src/config.js +34 -2
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -215,14 +215,17 @@ export async function runCommand(command, deps = {}) {
|
|
|
215
215
|
}
|
|
216
216
|
|
|
217
217
|
async function handleLogin(command, deps) {
|
|
218
|
-
const { ensureDeviceId, writeAuthConfig } = await import("./config.js");
|
|
218
|
+
const { ensureDeviceId, resolveOrigins, writeAuthConfig } = await import("./config.js");
|
|
219
219
|
const { createDeviceAuthorization, pollDeviceAuthorization } = await import("./api.js");
|
|
220
220
|
const deviceId = await ensureDeviceId({ configDir: deps.configDir });
|
|
221
221
|
const version = await readPackageVersion();
|
|
222
|
-
const
|
|
223
|
-
|
|
222
|
+
const { consoleOrigin } = await resolveOrigins({
|
|
223
|
+
consoleUrl: command.options.console_url,
|
|
224
|
+
env: deps.env ?? process.env,
|
|
225
|
+
configDir: deps.configDir,
|
|
226
|
+
});
|
|
224
227
|
const authorization = await createDeviceAuthorization({
|
|
225
|
-
consoleUrl,
|
|
228
|
+
consoleUrl: consoleOrigin,
|
|
226
229
|
deviceId,
|
|
227
230
|
clientName: "flatkey-cli",
|
|
228
231
|
clientVersion: version,
|
|
@@ -246,7 +249,7 @@ async function handleLogin(command, deps) {
|
|
|
246
249
|
while (Date.now() < deadline) {
|
|
247
250
|
await delay(nextLoginPollDelay(startedAt, initialIntervalMs), deps);
|
|
248
251
|
const poll = await pollDeviceAuthorization({
|
|
249
|
-
consoleUrl,
|
|
252
|
+
consoleUrl: consoleOrigin,
|
|
250
253
|
deviceCode: data.device_code,
|
|
251
254
|
fetch: deps.fetch,
|
|
252
255
|
});
|
|
@@ -352,10 +355,10 @@ async function handleAuthStatus(command, deps) {
|
|
|
352
355
|
authenticated: Boolean(apiKey),
|
|
353
356
|
source: command.options.api_key
|
|
354
357
|
? "option"
|
|
355
|
-
:
|
|
356
|
-
? "
|
|
357
|
-
:
|
|
358
|
-
? "
|
|
358
|
+
: saved?.apiKey
|
|
359
|
+
? "config"
|
|
360
|
+
: (deps.env ?? process.env).FLATKEY_API_KEY
|
|
361
|
+
? "env"
|
|
359
362
|
: "none",
|
|
360
363
|
key: maskKey(apiKey),
|
|
361
364
|
auth: saved?.auth ?? null,
|
|
@@ -364,7 +367,7 @@ async function handleAuthStatus(command, deps) {
|
|
|
364
367
|
}
|
|
365
368
|
|
|
366
369
|
async function handleGenerate(command, deps) {
|
|
367
|
-
const { resolveApiKey } = await import("./config.js");
|
|
370
|
+
const { resolveApiKey, resolveOrigins } = await import("./config.js");
|
|
368
371
|
const {
|
|
369
372
|
generateAudio,
|
|
370
373
|
generateAudioMusic,
|
|
@@ -386,11 +389,17 @@ async function handleGenerate(command, deps) {
|
|
|
386
389
|
: await resolveApiKey({
|
|
387
390
|
apiKey: command.options.api_key,
|
|
388
391
|
env: deps.env ?? process.env,
|
|
392
|
+
configDir: deps.configDir,
|
|
389
393
|
});
|
|
394
|
+
const { routerOrigin } = await resolveOrigins({
|
|
395
|
+
baseUrl: command.options.base_url,
|
|
396
|
+
env: deps.env ?? process.env,
|
|
397
|
+
configDir: deps.configDir,
|
|
398
|
+
});
|
|
390
399
|
const options = {
|
|
391
400
|
...command.options,
|
|
392
401
|
apiKey,
|
|
393
|
-
baseUrl:
|
|
402
|
+
baseUrl: routerOrigin,
|
|
394
403
|
fetch: deps.fetch,
|
|
395
404
|
};
|
|
396
405
|
if (command.options.dry_run) {
|
|
@@ -464,15 +473,21 @@ function scrubArtifactResponse(value) {
|
|
|
464
473
|
}
|
|
465
474
|
|
|
466
475
|
async function handleVoices(command, deps) {
|
|
467
|
-
const { resolveApiKey } = await import("./config.js");
|
|
476
|
+
const { resolveApiKey, resolveOrigins } = await import("./config.js");
|
|
468
477
|
const { getVoices } = await import("./api.js");
|
|
469
478
|
const apiKey = await resolveApiKey({
|
|
470
479
|
apiKey: command.options.api_key,
|
|
471
480
|
env: deps.env ?? process.env,
|
|
481
|
+
configDir: deps.configDir,
|
|
482
|
+
});
|
|
483
|
+
const { routerOrigin } = await resolveOrigins({
|
|
484
|
+
baseUrl: command.options.base_url,
|
|
485
|
+
env: deps.env ?? process.env,
|
|
486
|
+
configDir: deps.configDir,
|
|
472
487
|
});
|
|
473
488
|
return getVoices({
|
|
474
489
|
apiKey,
|
|
475
|
-
baseUrl:
|
|
490
|
+
baseUrl: routerOrigin,
|
|
476
491
|
fetch: deps.fetch,
|
|
477
492
|
});
|
|
478
493
|
}
|
|
@@ -516,32 +531,44 @@ function redactRequest(request) {
|
|
|
516
531
|
}
|
|
517
532
|
|
|
518
533
|
async function handleUtility(command, deps) {
|
|
519
|
-
const { resolveApiKey } = await import("./config.js");
|
|
534
|
+
const { resolveApiKey, resolveOrigins } = await import("./config.js");
|
|
520
535
|
const { getCredits, getStatus } = await import("./api.js");
|
|
521
536
|
const apiKey = await resolveApiKey({
|
|
522
537
|
apiKey: command.options.api_key,
|
|
523
538
|
env: deps.env ?? process.env,
|
|
539
|
+
configDir: deps.configDir,
|
|
540
|
+
});
|
|
541
|
+
const { consoleOrigin } = await resolveOrigins({
|
|
542
|
+
baseUrl: command.options.base_url,
|
|
543
|
+
env: deps.env ?? process.env,
|
|
544
|
+
configDir: deps.configDir,
|
|
524
545
|
});
|
|
525
546
|
const options = {
|
|
526
547
|
apiKey,
|
|
527
|
-
baseUrl:
|
|
548
|
+
baseUrl: consoleOrigin,
|
|
528
549
|
fetch: deps.fetch,
|
|
529
550
|
};
|
|
530
551
|
return command.group === "credits" ? getCredits(options) : getStatus(options);
|
|
531
552
|
}
|
|
532
553
|
|
|
533
554
|
async function handleModels(command, deps) {
|
|
534
|
-
const { resolveApiKey } = await import("./config.js");
|
|
555
|
+
const { resolveApiKey, resolveOrigins } = await import("./config.js");
|
|
535
556
|
const { getModels } = await import("./api.js");
|
|
536
557
|
const { normalizeModels } = await import("./models.js");
|
|
537
558
|
|
|
538
559
|
const apiKey = await resolveApiKey({
|
|
539
560
|
apiKey: command.options.api_key,
|
|
540
561
|
env: deps.env ?? process.env,
|
|
562
|
+
configDir: deps.configDir,
|
|
563
|
+
});
|
|
564
|
+
const { consoleOrigin } = await resolveOrigins({
|
|
565
|
+
baseUrl: command.options.base_url,
|
|
566
|
+
env: deps.env ?? process.env,
|
|
567
|
+
configDir: deps.configDir,
|
|
541
568
|
});
|
|
542
569
|
const response = await getModels({
|
|
543
570
|
apiKey,
|
|
544
|
-
baseUrl:
|
|
571
|
+
baseUrl: consoleOrigin,
|
|
545
572
|
fetch: deps.fetch,
|
|
546
573
|
});
|
|
547
574
|
return { models: normalizeModels(response, command.options.type) };
|
|
@@ -549,7 +576,109 @@ async function handleModels(command, deps) {
|
|
|
549
576
|
|
|
550
577
|
function formatHuman(result) {
|
|
551
578
|
if (typeof result === "string") return result;
|
|
552
|
-
|
|
579
|
+
if (!result || typeof result !== "object") return String(result);
|
|
580
|
+
if (result.dryRun && result.request) return formatDryRun(result);
|
|
581
|
+
if (result.kind) return formatGenerationResult(result);
|
|
582
|
+
if (Array.isArray(result.models)) return formatModels(result.models);
|
|
583
|
+
if (Array.isArray(result.voices)) return formatVoices(result.voices);
|
|
584
|
+
if (typeof result.authenticated === "boolean") return formatAuthStatus(result);
|
|
585
|
+
return formatKeyValues(result);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
function formatDryRun(result) {
|
|
589
|
+
const lines = [
|
|
590
|
+
`Dry run: ${result.kind}`,
|
|
591
|
+
`${result.request.method ?? "GET"} ${result.request.url}`,
|
|
592
|
+
];
|
|
593
|
+
if (result.request.body !== undefined) {
|
|
594
|
+
lines.push("Body:");
|
|
595
|
+
lines.push(JSON.stringify(result.request.body, null, 2));
|
|
596
|
+
}
|
|
597
|
+
return lines.join("\n");
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function formatGenerationResult(result) {
|
|
601
|
+
if (result.kind === "text") {
|
|
602
|
+
const lines = [result.text || "(empty text)"];
|
|
603
|
+
if (result.output) lines.push(`Saved: ${result.output}`);
|
|
604
|
+
return lines.join("\n");
|
|
605
|
+
}
|
|
606
|
+
const artifacts = Array.isArray(result.artifacts) ? result.artifacts : [];
|
|
607
|
+
if (artifacts.length === 0) return `${titleCase(result.kind)} generated. No artifacts returned.`;
|
|
608
|
+
const lines = [`${titleCase(result.kind)} generated:`];
|
|
609
|
+
for (const artifact of artifacts) {
|
|
610
|
+
if (artifact.path) lines.push(`- Saved: ${artifact.path}`);
|
|
611
|
+
else if (artifact.url) lines.push(`- URL: ${artifact.url}`);
|
|
612
|
+
else lines.push(`- ${JSON.stringify(artifact)}`);
|
|
613
|
+
}
|
|
614
|
+
return lines.join("\n");
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
function formatModels(models) {
|
|
618
|
+
if (models.length === 0) return "No models available.";
|
|
619
|
+
return [
|
|
620
|
+
`Available models (${models.length}):`,
|
|
621
|
+
formatTable(
|
|
622
|
+
["Model", "Type", "Source"],
|
|
623
|
+
models.map((model) => [
|
|
624
|
+
model.id,
|
|
625
|
+
model.type ?? "",
|
|
626
|
+
model.source ?? "",
|
|
627
|
+
]),
|
|
628
|
+
),
|
|
629
|
+
].join("\n");
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
function formatVoices(voices) {
|
|
633
|
+
if (voices.length === 0) return "No voices available.";
|
|
634
|
+
const lines = [`Available voices (${voices.length}):`];
|
|
635
|
+
for (const voice of voices) {
|
|
636
|
+
const id = voice.voice_id ?? voice.id;
|
|
637
|
+
const name = voice.name ? `${voice.name} ` : "";
|
|
638
|
+
lines.push(`- ${name}${id ? `(${id})` : ""}`.trim());
|
|
639
|
+
}
|
|
640
|
+
return lines.join("\n");
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
function formatKeyValues(result) {
|
|
644
|
+
const entries = Object.entries(result);
|
|
645
|
+
if (entries.length === 0) return "OK";
|
|
646
|
+
return entries
|
|
647
|
+
.map(([key, value]) => `${humanLabel(key)}: ${formatScalar(value)}`)
|
|
648
|
+
.join("\n");
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
function formatScalar(value) {
|
|
652
|
+
if (value === null || value === undefined) return "";
|
|
653
|
+
if (typeof value === "object") return JSON.stringify(value);
|
|
654
|
+
return String(value);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
function humanLabel(key) {
|
|
658
|
+
return key
|
|
659
|
+
.replaceAll("_", " ")
|
|
660
|
+
.replace(/([a-z])([A-Z])/g, "$1 $2")
|
|
661
|
+
.replace(/^./, (char) => char.toUpperCase());
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
function titleCase(value) {
|
|
665
|
+
return String(value).replace(/^./, (char) => char.toUpperCase());
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
function formatTable(headers, rows) {
|
|
669
|
+
const widths = headers.map((header, index) => Math.max(
|
|
670
|
+
header.length,
|
|
671
|
+
...rows.map((row) => String(row[index] ?? "").length),
|
|
672
|
+
));
|
|
673
|
+
const line = (cells) => cells
|
|
674
|
+
.map((cell, index) => String(cell ?? "").padEnd(widths[index]))
|
|
675
|
+
.join(" ")
|
|
676
|
+
.trimEnd();
|
|
677
|
+
return [
|
|
678
|
+
line(headers),
|
|
679
|
+
line(widths.map((width) => "-".repeat(width))),
|
|
680
|
+
...rows.map(line),
|
|
681
|
+
].join("\n");
|
|
553
682
|
}
|
|
554
683
|
|
|
555
684
|
async function readPackageVersion() {
|
package/src/config.js
CHANGED
|
@@ -3,6 +3,9 @@ import { randomUUID } from "node:crypto";
|
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
|
|
6
|
+
export const ROUTER_ORIGIN_KEY = "routerOrigin";
|
|
7
|
+
export const CONSOLE_ORIGIN_KEY = "consoleOrigin";
|
|
8
|
+
|
|
6
9
|
export function getDefaultConfigDir() {
|
|
7
10
|
return join(homedir(), ".config", "flatkey");
|
|
8
11
|
}
|
|
@@ -17,16 +20,40 @@ export async function resolveApiKey({
|
|
|
17
20
|
configDir = getDefaultConfigDir(),
|
|
18
21
|
} = {}) {
|
|
19
22
|
if (apiKey) return apiKey;
|
|
20
|
-
if (env.FLATKEY_API_KEY) return env.FLATKEY_API_KEY;
|
|
21
23
|
|
|
22
24
|
const saved = await readSavedConfig(configDir);
|
|
23
25
|
if (saved?.apiKey) return saved.apiKey;
|
|
26
|
+
if (env.FLATKEY_API_KEY) return env.FLATKEY_API_KEY;
|
|
24
27
|
|
|
25
28
|
throw new Error(
|
|
26
29
|
"Missing Flatkey API key. Create one at https://console.flatkey.ai/keys, then run `flatkey onboard --api-key <key>` or set FLATKEY_API_KEY.",
|
|
27
30
|
);
|
|
28
31
|
}
|
|
29
32
|
|
|
33
|
+
export async function resolveOrigins({
|
|
34
|
+
baseUrl,
|
|
35
|
+
consoleUrl,
|
|
36
|
+
env = process.env,
|
|
37
|
+
configDir = getDefaultConfigDir(),
|
|
38
|
+
} = {}) {
|
|
39
|
+
const saved = await readSavedConfig(configDir);
|
|
40
|
+
return {
|
|
41
|
+
routerOrigin: firstNonEmpty(
|
|
42
|
+
baseUrl,
|
|
43
|
+
saved?.[ROUTER_ORIGIN_KEY],
|
|
44
|
+
saved?.ROUTER_ORIGIN,
|
|
45
|
+
env.ROUTER_ORIGIN,
|
|
46
|
+
),
|
|
47
|
+
consoleOrigin: firstNonEmpty(
|
|
48
|
+
consoleUrl,
|
|
49
|
+
baseUrl,
|
|
50
|
+
saved?.[CONSOLE_ORIGIN_KEY],
|
|
51
|
+
saved?.CONSOLE_ORIGIN,
|
|
52
|
+
env.CONSOLE_ORIGIN,
|
|
53
|
+
),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
30
57
|
export async function writeConfig({ apiKey, configDir = getDefaultConfigDir() }) {
|
|
31
58
|
if (typeof apiKey !== "string" || apiKey.trim() === "") {
|
|
32
59
|
throw new Error("Missing --api-key value. Create a key at https://console.flatkey.ai/keys, then run `flatkey onboard --api-key <key>`.");
|
|
@@ -34,7 +61,8 @@ export async function writeConfig({ apiKey, configDir = getDefaultConfigDir() })
|
|
|
34
61
|
|
|
35
62
|
await mkdir(configDir, { recursive: true });
|
|
36
63
|
const configPath = getConfigPath(configDir);
|
|
37
|
-
|
|
64
|
+
const saved = await readSavedConfig(configDir) ?? {};
|
|
65
|
+
await writeFile(configPath, `${JSON.stringify({ ...saved, apiKey }, null, 2)}\n`, {
|
|
38
66
|
mode: 0o600,
|
|
39
67
|
});
|
|
40
68
|
try {
|
|
@@ -109,3 +137,7 @@ async function readSavedConfig(configDir) {
|
|
|
109
137
|
throw error;
|
|
110
138
|
}
|
|
111
139
|
}
|
|
140
|
+
|
|
141
|
+
function firstNonEmpty(...values) {
|
|
142
|
+
return values.find((value) => typeof value === "string" && value.trim() !== "");
|
|
143
|
+
}
|