ima2-gen 1.1.20 → 1.1.21

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.
Files changed (93) hide show
  1. package/README.md +15 -25
  2. package/bin/commands/capabilities.js +2 -2
  3. package/bin/commands/capabilities.ts +2 -2
  4. package/bin/commands/defaults.js +2 -2
  5. package/bin/commands/defaults.ts +2 -2
  6. package/bin/commands/doctor.js +3 -3
  7. package/bin/commands/doctor.ts +3 -3
  8. package/bin/commands/edit.js +1 -1
  9. package/bin/commands/edit.ts +1 -1
  10. package/bin/commands/gen.js +1 -1
  11. package/bin/commands/gen.ts +1 -1
  12. package/bin/commands/grok.js +16 -11
  13. package/bin/commands/grok.ts +16 -11
  14. package/bin/commands/multimode.js +1 -1
  15. package/bin/commands/multimode.ts +1 -1
  16. package/bin/commands/observability.js +2 -2
  17. package/bin/commands/observability.ts +2 -2
  18. package/bin/commands/video.js +335 -13
  19. package/bin/commands/video.ts +249 -12
  20. package/bin/ima2.js +9 -9
  21. package/bin/ima2.ts +9 -9
  22. package/bin/lib/error-hints.js +2 -2
  23. package/bin/lib/error-hints.ts +2 -2
  24. package/docs/API.md +112 -3
  25. package/docs/CLI.md +61 -7
  26. package/docs/FAQ.ko.md +15 -20
  27. package/docs/FAQ.md +14 -19
  28. package/docs/NPX_QUICKSTART.md +40 -0
  29. package/docs/PROMPT_STUDIO.ko.md +1 -1
  30. package/docs/PROMPT_STUDIO.md +1 -1
  31. package/docs/README.ja.md +6 -16
  32. package/docs/README.ko.md +10 -20
  33. package/docs/README.zh-CN.md +7 -17
  34. package/docs/migration/runtime-test-inventory.md +8 -1
  35. package/lib/agentRuntime.js +19 -5
  36. package/lib/agentRuntime.ts +17 -5
  37. package/lib/capabilities.js +1 -1
  38. package/lib/capabilities.ts +1 -1
  39. package/lib/generationErrors.js +1 -1
  40. package/lib/generationErrors.ts +1 -1
  41. package/lib/grokProxyLauncher.js +26 -3
  42. package/lib/grokProxyLauncher.ts +27 -3
  43. package/lib/grokVideoAdapter.js +18 -89
  44. package/lib/grokVideoAdapter.ts +27 -88
  45. package/lib/grokVideoCanvas.js +25 -0
  46. package/lib/grokVideoCanvas.ts +26 -0
  47. package/lib/grokVideoDownload.js +58 -0
  48. package/lib/grokVideoDownload.ts +59 -0
  49. package/lib/grokVideoPlannerPrompt.js +64 -0
  50. package/lib/grokVideoPlannerPrompt.ts +67 -0
  51. package/lib/historyList.js +7 -1
  52. package/lib/historyList.ts +5 -1
  53. package/lib/oauthLauncher.js +21 -6
  54. package/lib/oauthLauncher.ts +22 -6
  55. package/lib/videoContinuity.js +149 -0
  56. package/lib/videoContinuity.ts +180 -0
  57. package/lib/videoFrameExtract.js +80 -0
  58. package/lib/videoFrameExtract.ts +78 -0
  59. package/node_modules/progrok/dist/index.js +187 -88
  60. package/node_modules/progrok/dist/index.js.map +1 -1
  61. package/node_modules/progrok/package.json +1 -1
  62. package/node_modules/progrok/skills/progrok/SKILL.md +33 -4
  63. package/package.json +2 -2
  64. package/routes/index.js +4 -0
  65. package/routes/index.ts +4 -0
  66. package/routes/quota.js +66 -0
  67. package/routes/quota.ts +89 -0
  68. package/routes/video.js +77 -15
  69. package/routes/video.ts +82 -14
  70. package/routes/videoExtended.js +293 -0
  71. package/routes/videoExtended.ts +284 -0
  72. package/server.js +6 -2
  73. package/server.ts +5 -2
  74. package/skills/ima2/SKILL.md +320 -7
  75. package/ui/dist/.vite/manifest.json +12 -12
  76. package/ui/dist/assets/{AgentWorkspace-DS8uvoLI.js → AgentWorkspace-B_hq9CLg.js} +2 -2
  77. package/ui/dist/assets/{CardNewsWorkspace-CYxMsE67.js → CardNewsWorkspace-wD12J7qk.js} +1 -1
  78. package/ui/dist/assets/{NodeCanvas-DccIc347.js → NodeCanvas-CI_wuPMf.js} +1 -1
  79. package/ui/dist/assets/{PromptBuilderPanel-BvxxwSJp.js → PromptBuilderPanel-CUTujJUV.js} +1 -1
  80. package/ui/dist/assets/{PromptImportDialog-u1_BFDRd.js → PromptImportDialog-CUi66jPK.js} +2 -2
  81. package/ui/dist/assets/{PromptImportDiscoverySection-C5uvkVSz.js → PromptImportDiscoverySection-Cm3vrjY4.js} +1 -1
  82. package/ui/dist/assets/{PromptImportFolderSection-D3E_O1SD.js → PromptImportFolderSection-DOtWTD9n.js} +1 -1
  83. package/ui/dist/assets/{PromptLibraryPanel-4gyf9CB9.js → PromptLibraryPanel-BMjQegRa.js} +2 -2
  84. package/ui/dist/assets/SettingsWorkspace-PiaVnsdA.js +1 -0
  85. package/ui/dist/assets/{index-DoKtXbod.js → index-31uVIdt4.js} +1 -1
  86. package/ui/dist/assets/index-CjgnNtgt.css +1 -0
  87. package/ui/dist/assets/index-Da2s4_-5.js +36 -0
  88. package/ui/dist/index.html +2 -2
  89. package/vendor/progrok-0.2.0.tgz +0 -0
  90. package/ui/dist/assets/SettingsWorkspace-F3eNu3mJ.js +0 -1
  91. package/ui/dist/assets/index-B6tcw_UF.css +0 -1
  92. package/ui/dist/assets/index-DYOh6gQD.js +0 -32
  93. package/vendor/progrok-0.1.1.tgz +0 -0
@@ -1247,101 +1247,200 @@ function imageToDataUri(filePath) {
1247
1247
  const ext = abs.toLowerCase().endsWith(".png") ? "png" : "jpeg";
1248
1248
  return `data:image/${ext};base64,${buf.toString("base64")}`;
1249
1249
  }
1250
+ async function pollUntilDone(requestId, bearer, timeout, json) {
1251
+ const deadline = Date.now() + timeout;
1252
+ let lastProgress = -1;
1253
+ while (Date.now() < deadline) {
1254
+ await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
1255
+ const poll = await fetch(`${XAI_API_BASE_URL}/videos/${requestId}`, {
1256
+ headers: { Authorization: `Bearer ${bearer}` }
1257
+ });
1258
+ if (!poll.ok) {
1259
+ throw new Error(`Poll HTTP ${poll.status}: ${(await poll.text()).slice(0, 200)}`);
1260
+ }
1261
+ const data = await poll.json();
1262
+ const status = data.status;
1263
+ if (status === "done") return data;
1264
+ if (status === "failed") {
1265
+ const err = data.error;
1266
+ throw new Error(`Generation failed: ${err?.code ?? "unknown"} \u2014 ${err?.message ?? ""}`);
1267
+ }
1268
+ if (status === "expired") throw new Error("Generation expired");
1269
+ if (!json && typeof data.progress === "number" && data.progress !== lastProgress) {
1270
+ lastProgress = data.progress;
1271
+ process.stdout.write(`\r Progress: ${Math.round(lastProgress * 100)}%`);
1272
+ }
1273
+ }
1274
+ throw new Error(`Timeout after ${timeout / 1e3}s \u2014 video still pending`);
1275
+ }
1276
+ async function downloadAndSave(videoUrl, outPath) {
1277
+ const videoRes = await fetch(videoUrl);
1278
+ if (!videoRes.ok) throw new Error(`Download failed: HTTP ${videoRes.status}`);
1279
+ const videoBuffer = Buffer.from(await videoRes.arrayBuffer());
1280
+ writeFileSync2(outPath, videoBuffer);
1281
+ }
1250
1282
  function videoCommand() {
1251
- return new Command10("video").description(
1252
- `Generate video with Grok Imagine Video (T2V / I2V).
1253
- Async: submits generation, polls until done, downloads result.
1283
+ const cmd = new Command10("video").description(
1284
+ `Generate, edit, or extend video with Grok Imagine Video.
1285
+
1286
+ Subcommands:
1287
+ generate (default) Text-to-video or image-to-video
1288
+ edit Edit existing video with text prompt (V2V)
1289
+ extend Continue video from its last frame
1254
1290
 
1255
1291
  Examples:
1256
1292
  $ progrok video "A cat playing piano"
1257
- $ progrok video "Animate this photo" --image photo.jpg
1258
- $ progrok video "prompt" --model grok-imagine-video-1.5-preview --duration 10`
1259
- ).argument("<prompt>", "video generation prompt").option("--model <id>", "video model", DEFAULT_VIDEO_MODEL).option("--duration <s>", "duration in seconds (1-15)", "5").option("--aspect <ratio>", "aspect ratio", "16:9").option("--resolution <r>", "480p or 720p", "480p").option("--image <path>", "source image for image-to-video").option("--output <path>", "output file path").option("--json", "output structured JSON").option("--timeout <s>", "polling timeout in seconds", "600").action(async (prompt, opts) => {
1260
- try {
1261
- const bearer = await getValidBearer();
1262
- const duration = parseInt(opts.duration ?? "5", 10);
1263
- const timeout = parseInt(opts.timeout ?? "600", 10) * 1e3;
1264
- const body = {
1265
- model: opts.model ?? DEFAULT_VIDEO_MODEL,
1266
- prompt,
1267
- duration,
1268
- aspect_ratio: opts.aspect ?? "16:9",
1269
- resolution: opts.resolution ?? "480p"
1270
- };
1271
- if (opts.image) {
1272
- const uri = opts.image.startsWith("data:") ? opts.image : imageToDataUri(opts.image);
1273
- body.image = { url: uri };
1274
- } else if (opts.model?.includes("1.5") && !opts.image) {
1275
- body.image = { url: WHITE_PIXEL_PNG };
1276
- body.prompt = `${prompt}
1293
+ $ progrok video "Animate this" --image photo.jpg
1294
+ $ progrok video edit "Make it sunset colors" --video input.mp4
1295
+ $ progrok video extend "Camera pulls back slowly" --video input.mp4 --duration 5`
1296
+ );
1297
+ cmd.argument("[prompt]", "video generation prompt").option("--model <id>", "video model", DEFAULT_VIDEO_MODEL).option("--duration <s>", "duration in seconds (1-15)", "5").option("--aspect <ratio>", "aspect ratio", "16:9").option("--resolution <r>", "480p or 720p", "480p").option("--image <path>", "source image for image-to-video").option("--output <path>", "output file path").option("--json", "output structured JSON").option("--timeout <s>", "polling timeout in seconds", "600").action(async (prompt, opts) => {
1298
+ if (!prompt) {
1299
+ cmd.help();
1300
+ return;
1301
+ }
1302
+ await generateAction(prompt, opts);
1303
+ });
1304
+ cmd.command("edit <prompt>").description("Edit existing video with a text prompt (real V2V). Model: grok-imagine-video only.").requiredOption("--video <url-or-path>", "source video URL or local .mp4 path").option("--model <id>", "video model (must be grok-imagine-video)", DEFAULT_VIDEO_MODEL).option("--output <path>", "output file path").option("--json", "output structured JSON").option("--timeout <s>", "polling timeout in seconds", "600").action(async (prompt, opts) => {
1305
+ await editAction(prompt, opts);
1306
+ });
1307
+ cmd.command("extend <prompt>").description("Extend video from its last frame. Model: grok-imagine-video only.").requiredOption("--video <url-or-path>", "source video URL or local .mp4 path").option("--model <id>", "video model (must be grok-imagine-video)", DEFAULT_VIDEO_MODEL).option("--duration <s>", "extension duration 2-10s (default 6)", "6").option("--output <path>", "output file path").option("--json", "output structured JSON").option("--timeout <s>", "polling timeout in seconds", "600").action(async (prompt, opts) => {
1308
+ await extendAction(prompt, opts);
1309
+ });
1310
+ return cmd;
1311
+ }
1312
+ async function generateAction(prompt, opts) {
1313
+ try {
1314
+ const bearer = await getValidBearer();
1315
+ const duration = parseInt(opts.duration ?? "5", 10);
1316
+ const timeout = parseInt(opts.timeout ?? "600", 10) * 1e3;
1317
+ const body = {
1318
+ model: opts.model ?? DEFAULT_VIDEO_MODEL,
1319
+ prompt,
1320
+ duration,
1321
+ aspect_ratio: opts.aspect ?? "16:9",
1322
+ resolution: opts.resolution ?? "480p"
1323
+ };
1324
+ if (opts.image) {
1325
+ const uri = opts.image.startsWith("data:") ? opts.image : imageToDataUri(opts.image);
1326
+ body.image = { url: uri };
1327
+ } else if (opts.model?.includes("1.5") && !opts.image) {
1328
+ body.image = { url: WHITE_PIXEL_PNG };
1329
+ body.prompt = `${prompt}
1277
1330
 
1278
1331
  This is not a start frame \u2014 generate freely as a new video.`;
1279
- }
1280
- const res = await fetch(`${XAI_API_BASE_URL}/videos/generations`, {
1281
- method: "POST",
1282
- headers: {
1283
- Authorization: `Bearer ${bearer}`,
1284
- "Content-Type": "application/json"
1285
- },
1286
- body: JSON.stringify(body)
1287
- });
1288
- if (!res.ok) {
1289
- const errBody = await res.text();
1290
- throw new Error(`HTTP ${res.status}: ${errBody.slice(0, 300)}`);
1291
- }
1292
- const { request_id } = await res.json();
1293
- if (!opts.json) {
1294
- log.info(`Video generation started (${request_id})`);
1295
- log.dim(`Model: ${body.model} | ${duration}s | ${opts.aspect} | ${opts.resolution}`);
1296
- }
1297
- const deadline = Date.now() + timeout;
1298
- let lastProgress = -1;
1299
- while (Date.now() < deadline) {
1300
- await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
1301
- const poll = await fetch(`${XAI_API_BASE_URL}/videos/${request_id}`, {
1302
- headers: { Authorization: `Bearer ${bearer}` }
1303
- });
1304
- if (!poll.ok) {
1305
- throw new Error(`Poll HTTP ${poll.status}: ${(await poll.text()).slice(0, 200)}`);
1306
- }
1307
- const data = await poll.json();
1308
- if (data.status === "done" && data.video) {
1309
- if (opts.json) {
1310
- console.log(JSON.stringify(data, null, 2));
1311
- return;
1312
- }
1313
- const videoRes = await fetch(data.video.url);
1314
- if (!videoRes.ok) throw new Error(`Download failed: HTTP ${videoRes.status}`);
1315
- const videoBuffer = Buffer.from(await videoRes.arrayBuffer());
1316
- const outPath = opts.output ?? `progrok-video-${request_id.slice(0, 8)}.mp4`;
1317
- writeFileSync2(outPath, videoBuffer);
1318
- log.success(`
1319
- Video saved: ${outPath}`);
1320
- log.info(`Duration: ${data.video.duration}s`);
1321
- if (data.usage?.cost_in_usd_ticks) {
1322
- log.dim(`Cost: $${(data.usage.cost_in_usd_ticks / 1e10).toFixed(4)}`);
1323
- }
1324
- return;
1325
- }
1326
- if (data.status === "failed") {
1327
- throw new Error(
1328
- `Generation failed: ${data.error?.code ?? "unknown"} \u2014 ${data.error?.message ?? ""}`
1329
- );
1330
- }
1331
- if (data.status === "expired") {
1332
- throw new Error("Generation expired");
1333
- }
1334
- if (!opts.json && data.progress !== void 0 && data.progress !== lastProgress) {
1335
- lastProgress = data.progress;
1336
- process.stdout.write(`\r Progress: ${Math.round(data.progress * 100)}%`);
1337
- }
1338
- }
1339
- throw new Error(`Timeout after ${timeout / 1e3}s \u2014 video still pending`);
1340
- } catch (err) {
1341
- log.error(err.message);
1342
- process.exit(1);
1343
1332
  }
1344
- });
1333
+ const res = await fetch(`${XAI_API_BASE_URL}/videos/generations`, {
1334
+ method: "POST",
1335
+ headers: { Authorization: `Bearer ${bearer}`, "Content-Type": "application/json" },
1336
+ body: JSON.stringify(body)
1337
+ });
1338
+ if (!res.ok) throw new Error(`HTTP ${res.status}: ${(await res.text()).slice(0, 300)}`);
1339
+ const { request_id } = await res.json();
1340
+ if (!opts.json) {
1341
+ log.info(`Video generation started (${request_id})`);
1342
+ log.dim(`Model: ${body.model} | ${duration}s | ${opts.aspect} | ${opts.resolution}`);
1343
+ }
1344
+ const data = await pollUntilDone(request_id, bearer, timeout, opts.json);
1345
+ if (opts.json) {
1346
+ console.log(JSON.stringify(data, null, 2));
1347
+ return;
1348
+ }
1349
+ const video = data.video;
1350
+ if (!video) throw new Error("No video in response");
1351
+ const outPath = opts.output ?? `progrok-video-${request_id.slice(0, 8)}.mp4`;
1352
+ await downloadAndSave(video.url, outPath);
1353
+ if (!opts.json) process.stdout.write("\n");
1354
+ log.success(`Video saved: ${outPath}`);
1355
+ log.info(`Duration: ${video.duration}s`);
1356
+ const usage = data.usage;
1357
+ if (usage?.cost_in_usd_ticks) log.dim(`Cost: $${(usage.cost_in_usd_ticks / 1e10).toFixed(4)}`);
1358
+ } catch (err) {
1359
+ log.error(err.message);
1360
+ process.exit(1);
1361
+ }
1362
+ }
1363
+ async function editAction(prompt, opts) {
1364
+ try {
1365
+ const bearer = await getValidBearer();
1366
+ const timeout = parseInt(opts.timeout ?? "600", 10) * 1e3;
1367
+ const model = opts.model ?? DEFAULT_VIDEO_MODEL;
1368
+ if (model.includes("1.5")) {
1369
+ throw new Error("Video editing is only supported by grok-imagine-video (not 1.5-preview).");
1370
+ }
1371
+ const videoUrl = opts.video.startsWith("http") ? opts.video : (() => {
1372
+ throw new Error("--video must be a URL (local file upload not yet supported for editing).");
1373
+ })();
1374
+ const res = await fetch(`${XAI_API_BASE_URL}/videos/edits`, {
1375
+ method: "POST",
1376
+ headers: { Authorization: `Bearer ${bearer}`, "Content-Type": "application/json" },
1377
+ body: JSON.stringify({ model, prompt, video: { url: videoUrl } })
1378
+ });
1379
+ if (!res.ok) throw new Error(`HTTP ${res.status}: ${(await res.text()).slice(0, 300)}`);
1380
+ const { request_id } = await res.json();
1381
+ if (!opts.json) {
1382
+ log.info(`Video edit started (${request_id})`);
1383
+ log.dim(`Model: ${model} | Edit: "${prompt.slice(0, 60)}"`);
1384
+ }
1385
+ const data = await pollUntilDone(request_id, bearer, timeout, opts.json);
1386
+ if (opts.json) {
1387
+ console.log(JSON.stringify(data, null, 2));
1388
+ return;
1389
+ }
1390
+ const video = data.video;
1391
+ if (!video) throw new Error("No video in response");
1392
+ const outPath = opts.output ?? `progrok-edit-${request_id.slice(0, 8)}.mp4`;
1393
+ await downloadAndSave(video.url, outPath);
1394
+ if (!opts.json) process.stdout.write("\n");
1395
+ log.success(`Edited video saved: ${outPath}`);
1396
+ log.info(`Duration: ${video.duration}s`);
1397
+ } catch (err) {
1398
+ log.error(err.message);
1399
+ process.exit(1);
1400
+ }
1401
+ }
1402
+ async function extendAction(prompt, opts) {
1403
+ try {
1404
+ const bearer = await getValidBearer();
1405
+ const timeout = parseInt(opts.timeout ?? "600", 10) * 1e3;
1406
+ const duration = parseInt(opts.duration ?? "6", 10);
1407
+ const model = opts.model ?? DEFAULT_VIDEO_MODEL;
1408
+ if (model.includes("1.5")) {
1409
+ throw new Error("Video extension is only supported by grok-imagine-video (not 1.5-preview).");
1410
+ }
1411
+ if (duration < 2 || duration > 10) {
1412
+ throw new Error("Extension duration must be 2-10 seconds.");
1413
+ }
1414
+ const videoUrl = opts.video.startsWith("http") ? opts.video : (() => {
1415
+ throw new Error("--video must be a URL (local file upload not yet supported for extension).");
1416
+ })();
1417
+ const res = await fetch(`${XAI_API_BASE_URL}/videos/extensions`, {
1418
+ method: "POST",
1419
+ headers: { Authorization: `Bearer ${bearer}`, "Content-Type": "application/json" },
1420
+ body: JSON.stringify({ model, prompt, duration, video: { url: videoUrl } })
1421
+ });
1422
+ if (!res.ok) throw new Error(`HTTP ${res.status}: ${(await res.text()).slice(0, 300)}`);
1423
+ const { request_id } = await res.json();
1424
+ if (!opts.json) {
1425
+ log.info(`Video extension started (${request_id})`);
1426
+ log.dim(`Model: ${model} | Extend: +${duration}s | "${prompt.slice(0, 60)}"`);
1427
+ }
1428
+ const data = await pollUntilDone(request_id, bearer, timeout, opts.json);
1429
+ if (opts.json) {
1430
+ console.log(JSON.stringify(data, null, 2));
1431
+ return;
1432
+ }
1433
+ const video = data.video;
1434
+ if (!video) throw new Error("No video in response");
1435
+ const outPath = opts.output ?? `progrok-extend-${request_id.slice(0, 8)}.mp4`;
1436
+ await downloadAndSave(video.url, outPath);
1437
+ if (!opts.json) process.stdout.write("\n");
1438
+ log.success(`Extended video saved: ${outPath}`);
1439
+ log.info(`Total duration: ${video.duration}s (original + ${duration}s extension)`);
1440
+ } catch (err) {
1441
+ log.error(err.message);
1442
+ process.exit(1);
1443
+ }
1345
1444
  }
1346
1445
 
1347
1446
  // src/commands/image.ts