@flatkey-ai/cli 0.1.16 → 0.1.17

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flatkey-ai/cli",
3
- "version": "0.1.16",
3
+ "version": "0.1.17",
4
4
  "description": "Flatkey media generation CLI for image, video, audio, credits, status, and models.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/api.js CHANGED
@@ -44,6 +44,15 @@ export function generateVideo(options) {
44
44
  }
45
45
 
46
46
  export function planVideoRequest(options) {
47
+ const model = options.model ?? "seedance-2.0-pro";
48
+ const imageUrls = [
49
+ ...arrayOption(options.image_url),
50
+ ...arrayOption(options.imageUrl),
51
+ ...arrayOption(options.first_frame_url),
52
+ ...arrayOption(options.firstFrameUrl),
53
+ ...arrayOption(options.last_frame_url),
54
+ ...arrayOption(options.lastFrameUrl),
55
+ ];
47
56
  const ratio = validateOptionalValue(
48
57
  optionValue(options, "ratio", "aspect"),
49
58
  ["16:9", "9:16", "4:3", "3:4", "21:9", "1:1"],
@@ -54,8 +63,8 @@ export function planVideoRequest(options) {
54
63
  ["480p", "720p", "1080p"],
55
64
  "resolution",
56
65
  );
57
- return planJsonPost(options, "/v1/video/generations", cleanObject({
58
- model: options.model ?? "veo-3",
66
+ const basePayload = cleanObject({
67
+ model,
59
68
  prompt: options.prompt,
60
69
  duration: parseOptionalInteger(options.duration),
61
70
  aspect: ratio,
@@ -63,7 +72,16 @@ export function planVideoRequest(options) {
63
72
  resolution,
64
73
  quality: resolution,
65
74
  fps: parseOptionalInteger(options.fps),
66
- }));
75
+ images: !isSeedanceModel(model) && imageUrls.length > 0 ? imageUrls : undefined,
76
+ });
77
+ const seedanceContent = buildSeedanceContent(options);
78
+ if (isSeedanceModel(model) && seedanceContent.length > 0) {
79
+ return planJsonPost(options, "/v1/video/generations", cleanObject({
80
+ ...basePayload,
81
+ content: seedanceContent,
82
+ }));
83
+ }
84
+ return planJsonPost(options, "/v1/video/generations", basePayload);
67
85
  }
68
86
 
69
87
  export function generateAudio(options) {
@@ -275,6 +293,47 @@ function validateOptionalValue(value, allowed, name) {
275
293
  throw new Error(`Invalid ${name}: ${value}. Allowed values: ${allowed.join(", ")}`);
276
294
  }
277
295
 
296
+ function isSeedanceModel(model) {
297
+ return /seedance/i.test(model);
298
+ }
299
+
300
+ function buildSeedanceContent(options) {
301
+ const content = [];
302
+ if (options.prompt !== undefined) {
303
+ content.push({ type: "text", text: options.prompt });
304
+ }
305
+ for (const url of arrayOption(options.image_url)) {
306
+ content.push({ type: "image_url", image_url: { url }, role: "reference_image" });
307
+ }
308
+ for (const url of arrayOption(options.imageUrl)) {
309
+ content.push({ type: "image_url", image_url: { url }, role: "reference_image" });
310
+ }
311
+ for (const url of arrayOption(options.first_frame_url)) {
312
+ content.push({ type: "image_url", image_url: { url }, role: "first_frame" });
313
+ }
314
+ for (const url of arrayOption(options.firstFrameUrl)) {
315
+ content.push({ type: "image_url", image_url: { url }, role: "first_frame" });
316
+ }
317
+ for (const url of arrayOption(options.last_frame_url)) {
318
+ content.push({ type: "image_url", image_url: { url }, role: "last_frame" });
319
+ }
320
+ for (const url of arrayOption(options.lastFrameUrl)) {
321
+ content.push({ type: "image_url", image_url: { url }, role: "last_frame" });
322
+ }
323
+ for (const url of arrayOption(options.video_url)) {
324
+ content.push({ type: "video_url", video_url: { url }, role: "reference_video" });
325
+ }
326
+ for (const url of arrayOption(options.videoUrl)) {
327
+ content.push({ type: "video_url", video_url: { url }, role: "reference_video" });
328
+ }
329
+ return content;
330
+ }
331
+
332
+ function arrayOption(value) {
333
+ if (value === undefined) return [];
334
+ return Array.isArray(value) ? value : [value];
335
+ }
336
+
278
337
  function cleanObject(value) {
279
338
  return Object.fromEntries(
280
339
  Object.entries(value).filter(([, entry]) => entry !== undefined),
package/src/cli.js CHANGED
@@ -16,6 +16,7 @@ const COMMANDS = new Set([
16
16
 
17
17
  const GROUP_ACTIONS = new Set(["audio", "auth", "image", "text", "video"]);
18
18
  const GLOBAL_OPTIONS = new Set(["api_key", "base_url", "dry_run", "help", "json", "output", "out"]);
19
+ const REPEATABLE_OPTIONS = new Set(["image_url", "video_url"]);
19
20
  const COMMAND_OPTIONS = {
20
21
  "audio generate": new Set(["model", "prompt", "similarity_boost", "stability", "style", "voice_id"]),
21
22
  "audio music": new Set(["music_length_ms", "prompt"]),
@@ -35,7 +36,7 @@ const COMMAND_OPTIONS = {
35
36
  "text generate": new Set(["model", "prompt"]),
36
37
  version: new Set([]),
37
38
  video: new Set([]),
38
- "video generate": new Set(["aspect", "duration", "fps", "model", "prompt", "ratio", "resolution"]),
39
+ "video generate": new Set(["aspect", "duration", "first_frame_url", "fps", "image_url", "last_frame_url", "model", "prompt", "ratio", "resolution", "video_url"]),
39
40
  };
40
41
 
41
42
  export function parseArgv(argv) {
@@ -115,12 +116,21 @@ function parseOptions(tokens) {
115
116
  options[name] = true;
116
117
  continue;
117
118
  }
118
- options[name] = next;
119
+ if (REPEATABLE_OPTIONS.has(name)) {
120
+ options[name] = [...asArray(options[name]), next];
121
+ } else {
122
+ options[name] = next;
123
+ }
119
124
  index += 1;
120
125
  }
121
126
  return options;
122
127
  }
123
128
 
129
+ function asArray(value) {
130
+ if (value === undefined) return [];
131
+ return Array.isArray(value) ? value : [value];
132
+ }
133
+
124
134
  function validateCommandOptions(command) {
125
135
  const key = command.action ? `${command.group} ${command.action}` : command.group;
126
136
  const allowed = COMMAND_OPTIONS[key] ?? new Set();
@@ -215,15 +225,11 @@ export async function runCommand(command, deps = {}) {
215
225
  }
216
226
 
217
227
  async function handleLogin(command, deps) {
218
- const { ensureDeviceId, readConfig, resolveOrigins, writeAuthConfig } = await import("./config.js");
219
- const { createDeviceAuthorization, pollDeviceAuthorization } = await import("./api.js");
228
+ const { ensureDeviceId, readConfig, writeAuthConfig } = await import("./config.js");
229
+ const { DEFAULT_CONSOLE_URL, createDeviceAuthorization, pollDeviceAuthorization } = await import("./api.js");
220
230
  const deviceId = await ensureDeviceId({ configDir: deps.configDir });
221
231
  const version = await readPackageVersion();
222
- const { consoleOrigin } = await resolveOrigins({
223
- consoleUrl: command.options.console_url,
224
- env: deps.env ?? process.env,
225
- configDir: deps.configDir,
226
- });
232
+ const consoleOrigin = firstNonEmpty(command.options.console_url, DEFAULT_CONSOLE_URL);
227
233
  const authorization = await createDeviceAuthorization({
228
234
  consoleUrl: consoleOrigin,
229
235
  deviceId,
package/src/help.js CHANGED
@@ -208,20 +208,28 @@ Options:
208
208
  video: `Usage: flatkey video generate --prompt <txt> [options]
209
209
 
210
210
  Options:
211
- --model <model> Model id, default veo-3
211
+ --model <model> Model id, default seedance-2.0-pro
212
212
  --duration <seconds> Video duration
213
213
  --ratio <value> 16:9, 9:16, 4:3, 3:4, 21:9, or 1:1
214
214
  --resolution <value> 480p, 720p, or 1080p
215
+ --image-url <url> Add reference image URL, repeatable
216
+ --video-url <url> Add reference video URL, repeatable
217
+ --first-frame-url <url> Add first frame image URL
218
+ --last-frame-url <url> Add last frame image URL
215
219
  --fps <fps> Frames per second
216
220
  --output, -o <file> Write video file
217
221
  --json Print machine-readable JSON`,
218
222
  "video generate": `Usage: flatkey video generate --prompt <txt> [options]
219
223
 
220
224
  Options:
221
- --model <model> Model id, default veo-3
225
+ --model <model> Model id, default seedance-2.0-pro
222
226
  --duration <seconds> Video duration
223
227
  --ratio <value> 16:9, 9:16, 4:3, 3:4, 21:9, or 1:1
224
228
  --resolution <value> 480p, 720p, or 1080p
229
+ --image-url <url> Add reference image URL, repeatable
230
+ --video-url <url> Add reference video URL, repeatable
231
+ --first-frame-url <url> Add first frame image URL
232
+ --last-frame-url <url> Add last frame image URL
225
233
  --fps <fps> Frames per second
226
234
  --output, -o <file> Write video file
227
235
  --json Print machine-readable JSON`,