@flatkey-ai/cli 0.1.6 → 0.1.7
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 +46 -4
- package/src/artifacts.js +9 -0
- package/src/cli.js +195 -2
- package/src/config.js +57 -0
- package/src/help.js +30 -1
package/package.json
CHANGED
package/src/api.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export const DEFAULT_BASE_URL = "https://router.flatkey.ai";
|
|
2
2
|
export const DEFAULT_MODELS_BASE_URL = "https://console.flatkey.ai";
|
|
3
|
+
export const DEFAULT_CONSOLE_URL = "https://console.flatkey.ai";
|
|
3
4
|
|
|
4
5
|
export class FlatkeyError extends Error {
|
|
5
6
|
constructor(message, { status } = {}) {
|
|
@@ -124,11 +125,17 @@ export function planTextRequest(options) {
|
|
|
124
125
|
}
|
|
125
126
|
|
|
126
127
|
export function getCredits(options) {
|
|
127
|
-
return requestJson(
|
|
128
|
+
return requestJson({
|
|
129
|
+
...options,
|
|
130
|
+
baseUrl: options.baseUrl ?? DEFAULT_CONSOLE_URL,
|
|
131
|
+
}, "/v1/credits");
|
|
128
132
|
}
|
|
129
133
|
|
|
130
134
|
export function getStatus(options) {
|
|
131
|
-
return requestJson(
|
|
135
|
+
return requestJson({
|
|
136
|
+
...options,
|
|
137
|
+
baseUrl: options.baseUrl ?? DEFAULT_CONSOLE_URL,
|
|
138
|
+
}, "/v1/status");
|
|
132
139
|
}
|
|
133
140
|
|
|
134
141
|
export function getModels(options) {
|
|
@@ -138,6 +145,32 @@ export function getModels(options) {
|
|
|
138
145
|
}, "/v1/available_models");
|
|
139
146
|
}
|
|
140
147
|
|
|
148
|
+
export async function createDeviceAuthorization(options) {
|
|
149
|
+
return requestJson({
|
|
150
|
+
...options,
|
|
151
|
+
baseUrl: options.consoleUrl ?? DEFAULT_CONSOLE_URL,
|
|
152
|
+
}, "/api/cli/device_authorizations", {
|
|
153
|
+
method: "POST",
|
|
154
|
+
body: JSON.stringify({
|
|
155
|
+
client_name: options.clientName ?? "flatkey-cli",
|
|
156
|
+
client_version: options.clientVersion,
|
|
157
|
+
device_id: options.deviceId,
|
|
158
|
+
}),
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export async function pollDeviceAuthorization(options) {
|
|
163
|
+
return requestJson({
|
|
164
|
+
...options,
|
|
165
|
+
baseUrl: options.consoleUrl ?? DEFAULT_CONSOLE_URL,
|
|
166
|
+
}, "/api/cli/device_authorizations/token", {
|
|
167
|
+
method: "POST",
|
|
168
|
+
body: JSON.stringify({
|
|
169
|
+
device_code: options.deviceCode,
|
|
170
|
+
}),
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
141
174
|
async function postJson(options, path, payload) {
|
|
142
175
|
return requestJsonFromPlan(options, planJsonPost(options, path, payload));
|
|
143
176
|
}
|
|
@@ -257,7 +290,16 @@ async function readJson(response) {
|
|
|
257
290
|
}
|
|
258
291
|
|
|
259
292
|
function extractErrorMessage(body, status) {
|
|
260
|
-
|
|
261
|
-
|
|
293
|
+
const message = typeof body?.error?.message === "string"
|
|
294
|
+
? body.error.message
|
|
295
|
+
: typeof body?.message === "string"
|
|
296
|
+
? body.message
|
|
297
|
+
: undefined;
|
|
298
|
+
if (message === "Token not provided") return missingApiKeyMessage();
|
|
299
|
+
if (message) return message;
|
|
262
300
|
return `Flatkey API request failed with HTTP ${status}`;
|
|
263
301
|
}
|
|
302
|
+
|
|
303
|
+
function missingApiKeyMessage() {
|
|
304
|
+
return "Missing Flatkey API key. Create one at https://console.flatkey.ai/keys, then run `flatkey onboard --api-key <key>` or set FLATKEY_API_KEY.";
|
|
305
|
+
}
|
package/src/artifacts.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
2
3
|
import { dirname, extname, join } from "node:path";
|
|
3
4
|
|
|
4
5
|
const DEFAULT_EXTENSIONS = {
|
|
@@ -25,6 +26,7 @@ export async function persistArtifacts({
|
|
|
25
26
|
output,
|
|
26
27
|
fetch: fetchImpl = fetch,
|
|
27
28
|
}) {
|
|
29
|
+
output = expandHomePath(output);
|
|
28
30
|
const items = extractItems(response);
|
|
29
31
|
const artifacts = [];
|
|
30
32
|
await mkdir(output ? dirname(output) : outDir, { recursive: true });
|
|
@@ -37,6 +39,13 @@ export async function persistArtifacts({
|
|
|
37
39
|
return artifacts;
|
|
38
40
|
}
|
|
39
41
|
|
|
42
|
+
function expandHomePath(path) {
|
|
43
|
+
if (typeof path !== "string") return path;
|
|
44
|
+
if (path === "~") return homedir();
|
|
45
|
+
if (path.startsWith("~/")) return join(homedir(), path.slice(2));
|
|
46
|
+
return path;
|
|
47
|
+
}
|
|
48
|
+
|
|
40
49
|
async function persistItem({ kind, item, outDir, output, index, fetchImpl }) {
|
|
41
50
|
const dataUrl = getString(item, ["url", "data_url", "dataUrl"]);
|
|
42
51
|
if (dataUrl?.startsWith("data:")) {
|
package/src/cli.js
CHANGED
|
@@ -3,15 +3,18 @@ const COMMANDS = new Set([
|
|
|
3
3
|
"credits",
|
|
4
4
|
"help",
|
|
5
5
|
"image",
|
|
6
|
+
"login",
|
|
6
7
|
"models",
|
|
7
8
|
"onboard",
|
|
9
|
+
"logout",
|
|
10
|
+
"auth",
|
|
8
11
|
"status",
|
|
9
12
|
"text",
|
|
10
13
|
"version",
|
|
11
14
|
"video",
|
|
12
15
|
]);
|
|
13
16
|
|
|
14
|
-
const GROUP_ACTIONS = new Set(["audio", "image", "text", "video"]);
|
|
17
|
+
const GROUP_ACTIONS = new Set(["audio", "auth", "image", "text", "video"]);
|
|
15
18
|
|
|
16
19
|
export function parseArgv(argv) {
|
|
17
20
|
const [group, maybeAction, ...rest] = argv;
|
|
@@ -140,6 +143,21 @@ export async function runCommand(command, deps = {}) {
|
|
|
140
143
|
return handleModels(command, deps);
|
|
141
144
|
}
|
|
142
145
|
|
|
146
|
+
if (command.group === "login") {
|
|
147
|
+
return handleLogin(command, { ...deps, stdout, stderr });
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (command.group === "logout") {
|
|
151
|
+
return handleLogout(command, deps);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (command.group === "auth") {
|
|
155
|
+
if (command.action !== "status") {
|
|
156
|
+
throw new Error(`Unknown action for auth: ${command.action}`);
|
|
157
|
+
}
|
|
158
|
+
return handleAuthStatus(command, deps);
|
|
159
|
+
}
|
|
160
|
+
|
|
143
161
|
if (command.group === "credits" || command.group === "status") {
|
|
144
162
|
return handleUtility(command, deps);
|
|
145
163
|
}
|
|
@@ -160,6 +178,150 @@ export async function runCommand(command, deps = {}) {
|
|
|
160
178
|
throw new Error(`Unknown command: ${command.group}`);
|
|
161
179
|
}
|
|
162
180
|
|
|
181
|
+
async function handleLogin(command, deps) {
|
|
182
|
+
const { ensureDeviceId, writeAuthConfig } = await import("./config.js");
|
|
183
|
+
const { createDeviceAuthorization, pollDeviceAuthorization } = await import("./api.js");
|
|
184
|
+
const deviceId = await ensureDeviceId({ configDir: deps.configDir });
|
|
185
|
+
const version = await readPackageVersion();
|
|
186
|
+
const consoleUrl = command.options.console_url;
|
|
187
|
+
const authorization = await createDeviceAuthorization({
|
|
188
|
+
consoleUrl,
|
|
189
|
+
deviceId,
|
|
190
|
+
clientName: "flatkey-cli",
|
|
191
|
+
clientVersion: version,
|
|
192
|
+
fetch: deps.fetch,
|
|
193
|
+
});
|
|
194
|
+
const data = authorization?.data ?? authorization;
|
|
195
|
+
if (!data?.device_code || !data?.verification_uri_complete) {
|
|
196
|
+
throw new Error("Flatkey login failed: missing device authorization response.");
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (!command.options.json) {
|
|
200
|
+
deps.stdout?.write?.(`Open this URL to approve Flatkey CLI:\n${data.verification_uri_complete}\n\n`);
|
|
201
|
+
}
|
|
202
|
+
if (command.options.open !== false && command.options.no_open !== true) {
|
|
203
|
+
await openBrowser(data.verification_uri_complete, deps);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const initialIntervalMs = Math.max(Number(data.interval ?? 5), 5) * 1000;
|
|
207
|
+
const deadline = Date.now() + Math.max(Number(data.expires_in ?? 600), 1) * 1000;
|
|
208
|
+
const startedAt = Date.now();
|
|
209
|
+
while (Date.now() < deadline) {
|
|
210
|
+
await delay(nextLoginPollDelay(startedAt, initialIntervalMs), deps);
|
|
211
|
+
const poll = await pollDeviceAuthorization({
|
|
212
|
+
consoleUrl,
|
|
213
|
+
deviceCode: data.device_code,
|
|
214
|
+
fetch: deps.fetch,
|
|
215
|
+
});
|
|
216
|
+
const pollData = poll?.data ?? poll;
|
|
217
|
+
if (pollData?.status === "approved") {
|
|
218
|
+
if (!pollData.api_key) {
|
|
219
|
+
throw new Error("Flatkey login approved but no API key was returned.");
|
|
220
|
+
}
|
|
221
|
+
const configPath = await writeAuthConfig({
|
|
222
|
+
apiKey: pollData.api_key,
|
|
223
|
+
auth: {
|
|
224
|
+
deviceId,
|
|
225
|
+
userId: pollData.user_id,
|
|
226
|
+
tokenId: pollData.token_id,
|
|
227
|
+
loginAt: Math.floor(Date.now() / 1000),
|
|
228
|
+
},
|
|
229
|
+
configDir: deps.configDir,
|
|
230
|
+
});
|
|
231
|
+
return command.options.json
|
|
232
|
+
? { success: true, configPath, tokenId: pollData.token_id, userId: pollData.user_id }
|
|
233
|
+
: `Flatkey CLI authorized. Saved config: ${configPath}`;
|
|
234
|
+
}
|
|
235
|
+
if (pollData?.status === "denied") {
|
|
236
|
+
throw new Error("Flatkey login denied.");
|
|
237
|
+
}
|
|
238
|
+
if (pollData?.status === "expired") {
|
|
239
|
+
throw new Error("Flatkey login expired. Run `flatkey login` again.");
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
throw new Error("Flatkey login timed out. Run `flatkey login` again.");
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function nextLoginPollDelay(startedAt, initialIntervalMs) {
|
|
246
|
+
const elapsed = Date.now() - startedAt;
|
|
247
|
+
const base = elapsed < 30_000
|
|
248
|
+
? initialIntervalMs
|
|
249
|
+
: elapsed < 120_000
|
|
250
|
+
? Math.max(initialIntervalMs, 10_000)
|
|
251
|
+
: Math.max(initialIntervalMs, 15_000);
|
|
252
|
+
return base + Math.floor(Math.random() * 800);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async function delay(ms, deps = {}) {
|
|
256
|
+
const sleep = deps.sleep ?? ((duration) => new Promise((resolve) => setTimeout(resolve, duration)));
|
|
257
|
+
return sleep(ms);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async function openBrowser(url, deps = {}) {
|
|
261
|
+
if (deps.openBrowser) return deps.openBrowser(url);
|
|
262
|
+
const { spawn } = await import("node:child_process");
|
|
263
|
+
const platform = deps.platform ?? process.platform;
|
|
264
|
+
const command = platform === "darwin"
|
|
265
|
+
? "open"
|
|
266
|
+
: platform === "win32"
|
|
267
|
+
? "cmd"
|
|
268
|
+
: "xdg-open";
|
|
269
|
+
const args = platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
270
|
+
try {
|
|
271
|
+
const child = spawn(command, args, { stdio: "ignore", detached: true });
|
|
272
|
+
child.unref();
|
|
273
|
+
} catch {
|
|
274
|
+
// Printed URL is enough when open is unavailable.
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function maskKey(key) {
|
|
279
|
+
if (!key) return "";
|
|
280
|
+
if (key.length <= 8) return `${key.slice(0, 2)}****${key.slice(-2)}`;
|
|
281
|
+
return `${key.slice(0, 6)}...${key.slice(-4)}`;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function formatAuthStatus(status) {
|
|
285
|
+
if (!status.authenticated) return "Not authenticated";
|
|
286
|
+
return `Authenticated via ${status.source}: ${status.key}`;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
async function handleLogout(command, deps = {}) {
|
|
290
|
+
const { clearSavedApiKey } = await import("./config.js");
|
|
291
|
+
const configPath = await clearSavedApiKey({ configDir: deps.configDir });
|
|
292
|
+
return command.options.json
|
|
293
|
+
? { success: true, configPath }
|
|
294
|
+
: `Removed saved Flatkey API key from ${configPath}`;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async function handleAuthStatus(command, deps) {
|
|
298
|
+
const { readConfig, resolveApiKey } = await import("./config.js");
|
|
299
|
+
const saved = await readConfig(deps.configDir);
|
|
300
|
+
let apiKey;
|
|
301
|
+
try {
|
|
302
|
+
apiKey = await resolveApiKey({
|
|
303
|
+
apiKey: command.options.api_key,
|
|
304
|
+
env: deps.env ?? process.env,
|
|
305
|
+
configDir: deps.configDir,
|
|
306
|
+
});
|
|
307
|
+
} catch {
|
|
308
|
+
apiKey = "";
|
|
309
|
+
}
|
|
310
|
+
const status = {
|
|
311
|
+
authenticated: Boolean(apiKey),
|
|
312
|
+
source: command.options.api_key
|
|
313
|
+
? "option"
|
|
314
|
+
: (deps.env ?? process.env).FLATKEY_API_KEY
|
|
315
|
+
? "env"
|
|
316
|
+
: saved?.apiKey
|
|
317
|
+
? "config"
|
|
318
|
+
: "none",
|
|
319
|
+
key: maskKey(apiKey),
|
|
320
|
+
auth: saved?.auth ?? null,
|
|
321
|
+
};
|
|
322
|
+
return command.options.json ? status : formatAuthStatus(status);
|
|
323
|
+
}
|
|
324
|
+
|
|
163
325
|
async function handleGenerate(command, deps) {
|
|
164
326
|
const { resolveApiKey } = await import("./config.js");
|
|
165
327
|
const {
|
|
@@ -233,12 +395,33 @@ async function handleGenerate(command, deps) {
|
|
|
233
395
|
output: command.options.output,
|
|
234
396
|
fetch: deps.fetch,
|
|
235
397
|
});
|
|
236
|
-
return { kind: command.group, artifacts, response };
|
|
398
|
+
return { kind: command.group, artifacts, response: scrubArtifactResponse(response) };
|
|
237
399
|
} finally {
|
|
238
400
|
animation.stop();
|
|
239
401
|
}
|
|
240
402
|
}
|
|
241
403
|
|
|
404
|
+
function scrubArtifactResponse(value) {
|
|
405
|
+
if (Array.isArray(value)) return value.map(scrubArtifactResponse);
|
|
406
|
+
if (!value || typeof value !== "object") {
|
|
407
|
+
if (typeof value === "string" && value.startsWith("data:")) return "<artifact omitted>";
|
|
408
|
+
return value;
|
|
409
|
+
}
|
|
410
|
+
return Object.fromEntries(
|
|
411
|
+
Object.entries(value).map(([key, entry]) => {
|
|
412
|
+
if (["b64_json", "base64", "data"].includes(key) && typeof entry === "string") {
|
|
413
|
+
return [key, "<artifact omitted>"];
|
|
414
|
+
}
|
|
415
|
+
if (["url", "data_url", "dataUrl"].includes(key)
|
|
416
|
+
&& typeof entry === "string"
|
|
417
|
+
&& entry.startsWith("data:")) {
|
|
418
|
+
return [key, "<artifact omitted>"];
|
|
419
|
+
}
|
|
420
|
+
return [key, scrubArtifactResponse(entry)];
|
|
421
|
+
}),
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
|
|
242
425
|
async function handleVoices(command, deps) {
|
|
243
426
|
const { resolveApiKey } = await import("./config.js");
|
|
244
427
|
const { getVoices } = await import("./api.js");
|
|
@@ -262,6 +445,7 @@ function extractText(response) {
|
|
|
262
445
|
|
|
263
446
|
async function writeTextOutput(text, output) {
|
|
264
447
|
if (!output) return undefined;
|
|
448
|
+
output = await expandHomePath(output);
|
|
265
449
|
const { mkdir, writeFile } = await import("node:fs/promises");
|
|
266
450
|
const { dirname } = await import("node:path");
|
|
267
451
|
await mkdir(dirname(output), { recursive: true });
|
|
@@ -269,6 +453,15 @@ async function writeTextOutput(text, output) {
|
|
|
269
453
|
return output;
|
|
270
454
|
}
|
|
271
455
|
|
|
456
|
+
async function expandHomePath(path) {
|
|
457
|
+
if (typeof path !== "string") return path;
|
|
458
|
+
if (path !== "~" && !path.startsWith("~/")) return path;
|
|
459
|
+
const { homedir } = await import("node:os");
|
|
460
|
+
const { join } = await import("node:path");
|
|
461
|
+
if (path === "~") return homedir();
|
|
462
|
+
return join(homedir(), path.slice(2));
|
|
463
|
+
}
|
|
464
|
+
|
|
272
465
|
function redactRequest(request) {
|
|
273
466
|
return {
|
|
274
467
|
...request,
|
package/src/config.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { mkdir, readFile, writeFile, chmod } from "node:fs/promises";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
2
3
|
import { homedir } from "node:os";
|
|
3
4
|
import { join } from "node:path";
|
|
4
5
|
|
|
@@ -44,6 +45,62 @@ export async function writeConfig({ apiKey, configDir = getDefaultConfigDir() })
|
|
|
44
45
|
return configPath;
|
|
45
46
|
}
|
|
46
47
|
|
|
48
|
+
export async function writeAuthConfig({
|
|
49
|
+
apiKey,
|
|
50
|
+
auth,
|
|
51
|
+
configDir = getDefaultConfigDir(),
|
|
52
|
+
}) {
|
|
53
|
+
if (typeof apiKey !== "string" || apiKey.trim() === "") {
|
|
54
|
+
throw new Error("Missing API key from Flatkey login response.");
|
|
55
|
+
}
|
|
56
|
+
const saved = await readSavedConfig(configDir) ?? {};
|
|
57
|
+
const next = {
|
|
58
|
+
...saved,
|
|
59
|
+
apiKey,
|
|
60
|
+
auth: {
|
|
61
|
+
...(saved.auth ?? {}),
|
|
62
|
+
...auth,
|
|
63
|
+
type: "device",
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
await mkdir(configDir, { recursive: true });
|
|
67
|
+
const configPath = getConfigPath(configDir);
|
|
68
|
+
await writeFile(configPath, `${JSON.stringify(next, null, 2)}\n`, {
|
|
69
|
+
mode: 0o600,
|
|
70
|
+
});
|
|
71
|
+
try {
|
|
72
|
+
await chmod(configPath, 0o600);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
if (process.platform !== "win32") throw error;
|
|
75
|
+
}
|
|
76
|
+
return configPath;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export async function ensureDeviceId({ configDir = getDefaultConfigDir() } = {}) {
|
|
80
|
+
const saved = await readSavedConfig(configDir);
|
|
81
|
+
if (typeof saved?.auth?.deviceId === "string" && saved.auth.deviceId) {
|
|
82
|
+
return saved.auth.deviceId;
|
|
83
|
+
}
|
|
84
|
+
return randomUUID();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export async function clearSavedApiKey({ configDir = getDefaultConfigDir() } = {}) {
|
|
88
|
+
const saved = await readSavedConfig(configDir);
|
|
89
|
+
if (!saved) return getConfigPath(configDir);
|
|
90
|
+
const next = { ...saved };
|
|
91
|
+
delete next.apiKey;
|
|
92
|
+
await mkdir(configDir, { recursive: true });
|
|
93
|
+
const configPath = getConfigPath(configDir);
|
|
94
|
+
await writeFile(configPath, `${JSON.stringify(next, null, 2)}\n`, {
|
|
95
|
+
mode: 0o600,
|
|
96
|
+
});
|
|
97
|
+
return configPath;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export async function readConfig(configDir = getDefaultConfigDir()) {
|
|
101
|
+
return readSavedConfig(configDir);
|
|
102
|
+
}
|
|
103
|
+
|
|
47
104
|
async function readSavedConfig(configDir) {
|
|
48
105
|
try {
|
|
49
106
|
return JSON.parse(await readFile(getConfigPath(configDir), "utf8"));
|
package/src/help.js
CHANGED
|
@@ -2,7 +2,8 @@ export function getAiHelp() {
|
|
|
2
2
|
return `Flatkey CLI protocol for agents
|
|
3
3
|
|
|
4
4
|
Setup:
|
|
5
|
-
- Prefer
|
|
5
|
+
- Prefer browser auth: flatkey login
|
|
6
|
+
- Or env: FLATKEY_API_KEY=<key>
|
|
6
7
|
- Create a key at https://console.flatkey.ai/keys
|
|
7
8
|
- Or save key: flatkey onboard --api-key <key>
|
|
8
9
|
- Use --json for machine-readable output.
|
|
@@ -23,6 +24,9 @@ Commands:
|
|
|
23
24
|
- flatkey credits --json
|
|
24
25
|
- flatkey status --json
|
|
25
26
|
- flatkey models --json [--type image|video|audio|text]
|
|
27
|
+
- flatkey login [--no-open] [--console-url <url>]
|
|
28
|
+
- flatkey logout
|
|
29
|
+
- flatkey auth status --json
|
|
26
30
|
- flatkey help --ai
|
|
27
31
|
|
|
28
32
|
Environment:
|
|
@@ -41,6 +45,9 @@ export function getHumanHelp() {
|
|
|
41
45
|
return `Usage: flatkey <command> [options]
|
|
42
46
|
|
|
43
47
|
Commands:
|
|
48
|
+
login Authorize CLI in browser
|
|
49
|
+
logout Remove saved Flatkey API key
|
|
50
|
+
auth status Show saved auth state
|
|
44
51
|
onboard --api-key <key> Save Flatkey API key from https://console.flatkey.ai/keys
|
|
45
52
|
image generate --prompt <txt> Generate image
|
|
46
53
|
video generate --prompt <txt> Generate video
|
|
@@ -58,6 +65,7 @@ Global options:
|
|
|
58
65
|
--json Print machine-readable JSON
|
|
59
66
|
--output, -o <file> Write generated output to a local file
|
|
60
67
|
--base-url <url> Override Flatkey router URL
|
|
68
|
+
--console-url <url> Override Flatkey console URL for login
|
|
61
69
|
|
|
62
70
|
Video options:
|
|
63
71
|
--ratio <value> 16:9, 9:16, 4:3, 3:4, 21:9, or 1:1
|
|
@@ -110,6 +118,17 @@ Options:
|
|
|
110
118
|
--json Print machine-readable JSON`,
|
|
111
119
|
credits: `Usage: flatkey credits [options]
|
|
112
120
|
|
|
121
|
+
Options:
|
|
122
|
+
--json Print machine-readable JSON`,
|
|
123
|
+
auth: `Usage: flatkey auth <action> [options]
|
|
124
|
+
|
|
125
|
+
Actions:
|
|
126
|
+
status Show saved auth state
|
|
127
|
+
|
|
128
|
+
Options:
|
|
129
|
+
--json Print machine-readable JSON`,
|
|
130
|
+
"auth status": `Usage: flatkey auth status [options]
|
|
131
|
+
|
|
113
132
|
Options:
|
|
114
133
|
--json Print machine-readable JSON`,
|
|
115
134
|
help: `Usage: flatkey help [command] [options]
|
|
@@ -138,6 +157,16 @@ Options:
|
|
|
138
157
|
|
|
139
158
|
Options:
|
|
140
159
|
--type <type> Filter: image, video, audio, or text
|
|
160
|
+
--json Print machine-readable JSON`,
|
|
161
|
+
login: `Usage: flatkey login [options]
|
|
162
|
+
|
|
163
|
+
Options:
|
|
164
|
+
--no-open Print approval URL without opening a browser
|
|
165
|
+
--console-url <url> Override Flatkey console URL
|
|
166
|
+
--json Print machine-readable JSON`,
|
|
167
|
+
logout: `Usage: flatkey logout [options]
|
|
168
|
+
|
|
169
|
+
Options:
|
|
141
170
|
--json Print machine-readable JSON`,
|
|
142
171
|
onboard: `Usage: flatkey onboard --api-key <key>
|
|
143
172
|
|