veogent 1.0.21 → 1.0.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/index.js +254 -18
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -170,9 +170,9 @@ program
|
|
|
170
170
|
try {
|
|
171
171
|
const data = await api.get('/app/capabilities');
|
|
172
172
|
const caps = unwrapData(data);
|
|
173
|
-
emitJson({
|
|
173
|
+
emitJson({ status: 'success', models: caps?.imageModels || IMAGE_MODELS });
|
|
174
174
|
} catch {
|
|
175
|
-
emitJson({
|
|
175
|
+
emitJson({ status: 'success', models: IMAGE_MODELS });
|
|
176
176
|
}
|
|
177
177
|
});
|
|
178
178
|
|
|
@@ -183,9 +183,9 @@ program
|
|
|
183
183
|
try {
|
|
184
184
|
const data = await api.get('/app/capabilities');
|
|
185
185
|
const caps = unwrapData(data);
|
|
186
|
-
emitJson({
|
|
186
|
+
emitJson({ status: 'success', models: caps?.videoModels || VIDEO_MODELS });
|
|
187
187
|
} catch {
|
|
188
|
-
emitJson({
|
|
188
|
+
emitJson({ status: 'success', models: VIDEO_MODELS });
|
|
189
189
|
}
|
|
190
190
|
});
|
|
191
191
|
|
|
@@ -316,7 +316,7 @@ program
|
|
|
316
316
|
|
|
317
317
|
program
|
|
318
318
|
.command('create-chapter-content')
|
|
319
|
-
.description('Generate content for a specific chapter')
|
|
319
|
+
.description('Generate content for a specific chapter. This is a synchronous AI generation call — use the returned chapterContent directly.')
|
|
320
320
|
.requiredOption('-p, --project <project>', 'Project ID')
|
|
321
321
|
.requiredOption('-c, --chapter <chapter>', 'Chapter ID')
|
|
322
322
|
.option('-s, --scenes <count>', 'Number of scenes', '1')
|
|
@@ -328,9 +328,15 @@ program
|
|
|
328
328
|
numberScene: parseInt(options.scenes),
|
|
329
329
|
};
|
|
330
330
|
const data = await api.post('/app/chapter/content', payload);
|
|
331
|
-
|
|
331
|
+
const raw = unwrapData(data);
|
|
332
|
+
// Normalize: always return chapterContent as array of strings
|
|
333
|
+
let chapterContent = raw;
|
|
334
|
+
if (raw?.chapterContent) chapterContent = raw.chapterContent;
|
|
335
|
+
else if (raw?.content) chapterContent = raw.content;
|
|
336
|
+
if (!Array.isArray(chapterContent)) chapterContent = [chapterContent];
|
|
337
|
+
emitJson({ status: "success", chapterContent });
|
|
332
338
|
} catch (error) {
|
|
333
|
-
|
|
339
|
+
emitJson({ status: "error", ...formatCliError(error) });
|
|
334
340
|
}
|
|
335
341
|
});
|
|
336
342
|
|
|
@@ -338,13 +344,34 @@ program
|
|
|
338
344
|
// --- Characters ---
|
|
339
345
|
program
|
|
340
346
|
.command('characters <projectId>')
|
|
341
|
-
.description('Get all characters for a specific project')
|
|
347
|
+
.description('Get all characters for a specific project (includes readiness info)')
|
|
342
348
|
.action(async (projectId) => {
|
|
343
349
|
try {
|
|
344
350
|
const data = await api.get(`/app/characters/${projectId}`);
|
|
345
|
-
|
|
351
|
+
const raw = unwrapData(data);
|
|
352
|
+
const chars = Array.isArray(raw) ? raw : (raw?.characters || raw?.items || []);
|
|
353
|
+
|
|
354
|
+
const characters = chars.map((ch) => ({
|
|
355
|
+
id: ch?.id || ch?.characterId || null,
|
|
356
|
+
name: ch?.name || ch?.characterName || null,
|
|
357
|
+
imageUri: ch?.imageUri || ch?.imageUrl || ch?.image || null,
|
|
358
|
+
ready: !!(ch?.imageUri || ch?.imageUrl || ch?.image),
|
|
359
|
+
...ch,
|
|
360
|
+
}));
|
|
361
|
+
|
|
362
|
+
const readyCount = characters.filter((c) => c.ready).length;
|
|
363
|
+
const result = {
|
|
364
|
+
status: 'success',
|
|
365
|
+
characters,
|
|
366
|
+
characterReadiness: {
|
|
367
|
+
total: characters.length,
|
|
368
|
+
ready: readyCount,
|
|
369
|
+
allReady: characters.length > 0 && readyCount === characters.length,
|
|
370
|
+
},
|
|
371
|
+
};
|
|
372
|
+
emitJson(result);
|
|
346
373
|
} catch (error) {
|
|
347
|
-
|
|
374
|
+
emitJson({ status: "error", ...formatCliError(error) });
|
|
348
375
|
}
|
|
349
376
|
});
|
|
350
377
|
|
|
@@ -414,13 +441,66 @@ program
|
|
|
414
441
|
|
|
415
442
|
program
|
|
416
443
|
.command('scene-status')
|
|
417
|
-
.description('Get scene status snapshot by chapter')
|
|
444
|
+
.description('Get scene status snapshot by chapter (with embedded asset URLs)')
|
|
418
445
|
.requiredOption('-p, --project <project>', 'Project ID')
|
|
419
446
|
.requiredOption('-c, --chapter <chapter>', 'Chapter ID')
|
|
420
447
|
.action(async (options) => {
|
|
421
448
|
try {
|
|
422
449
|
const data = await api.get(`/app/scene-status/${options.project}/${options.chapter}`);
|
|
423
|
-
|
|
450
|
+
const rawData = unwrapData(data);
|
|
451
|
+
|
|
452
|
+
// Try to enrich each scene with asset URLs
|
|
453
|
+
const scenes = Array.isArray(rawData) ? rawData : (rawData?.scenes || rawData?.items || [rawData]);
|
|
454
|
+
const enriched = await Promise.all(scenes.map(async (scene) => {
|
|
455
|
+
const sceneId = scene?.id || scene?.sceneId;
|
|
456
|
+
if (!sceneId) return scene;
|
|
457
|
+
|
|
458
|
+
let imageAsset = { status: scene?.imageStatus || null, url: scene?.imageUrl || scene?.imageVerticalUri || scene?.imageHorizontalUri || null };
|
|
459
|
+
let videoAsset = { status: scene?.videoStatus || null, url: scene?.videoUrl || scene?.videoVerticalUri || scene?.videoHorizontalUri || null };
|
|
460
|
+
|
|
461
|
+
// Fetch assets if URLs not already present
|
|
462
|
+
try {
|
|
463
|
+
if (!imageAsset.url) {
|
|
464
|
+
const imgData = unwrapData(await api.get(`/app/request/assets/${options.project}/${options.chapter}/${sceneId}?type=GENERATE_IMAGES`));
|
|
465
|
+
const imgItems = Array.isArray(imgData) ? imgData : (imgData?.items || []);
|
|
466
|
+
const completed = imgItems.find((r) => String(r?.status || '').toUpperCase() === 'COMPLETED');
|
|
467
|
+
if (completed) {
|
|
468
|
+
imageAsset = {
|
|
469
|
+
status: 'COMPLETED',
|
|
470
|
+
url: completed?.imageVerticalUri || completed?.imageHorizontalUri || completed?.imageUrl || completed?.outputUrl || null,
|
|
471
|
+
createdAt: completed?.createdAt || null,
|
|
472
|
+
completedAt: completed?.completedAt || completed?.updatedAt || null,
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
} catch { /* asset fetch optional */ }
|
|
477
|
+
|
|
478
|
+
try {
|
|
479
|
+
if (!videoAsset.url) {
|
|
480
|
+
const vidData = unwrapData(await api.get(`/app/request/assets/${options.project}/${options.chapter}/${sceneId}?type=GENERATE_VIDEO`));
|
|
481
|
+
const vidItems = Array.isArray(vidData) ? vidData : (vidData?.items || []);
|
|
482
|
+
const completed = vidItems.find((r) => String(r?.status || '').toUpperCase() === 'COMPLETED');
|
|
483
|
+
if (completed) {
|
|
484
|
+
videoAsset = {
|
|
485
|
+
status: 'COMPLETED',
|
|
486
|
+
url: completed?.videoVerticalUri || completed?.videoHorizontalUri || completed?.videoUrl || completed?.outputUrl || null,
|
|
487
|
+
createdAt: completed?.createdAt || null,
|
|
488
|
+
completedAt: completed?.completedAt || completed?.updatedAt || null,
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
} catch { /* asset fetch optional */ }
|
|
493
|
+
|
|
494
|
+
return {
|
|
495
|
+
sceneId,
|
|
496
|
+
displayOrder: scene?.displayOrder ?? null,
|
|
497
|
+
image: imageAsset,
|
|
498
|
+
video: videoAsset,
|
|
499
|
+
raw: scene,
|
|
500
|
+
};
|
|
501
|
+
}));
|
|
502
|
+
|
|
503
|
+
emitJson({ status: 'success', data: enriched });
|
|
424
504
|
} catch (error) {
|
|
425
505
|
emitJson({ status: 'error', ...formatCliError(error) });
|
|
426
506
|
}
|
|
@@ -428,13 +508,49 @@ program
|
|
|
428
508
|
|
|
429
509
|
program
|
|
430
510
|
.command('workflow-status')
|
|
431
|
-
.description('Export workflow snapshot for a project/chapter (agent helper)')
|
|
511
|
+
.description('Export workflow snapshot for a project/chapter with embedded asset URLs (agent helper)')
|
|
432
512
|
.requiredOption('-p, --project <project>', 'Project ID')
|
|
433
513
|
.requiredOption('-c, --chapter <chapter>', 'Chapter ID')
|
|
434
514
|
.action(async (options) => {
|
|
435
515
|
try {
|
|
436
516
|
const data = await api.get(`/app/workflow-status/${options.project}/${options.chapter}`);
|
|
437
|
-
|
|
517
|
+
const rawData = unwrapData(data);
|
|
518
|
+
|
|
519
|
+
// Enrich scenes with asset URLs
|
|
520
|
+
const scenes = Array.isArray(rawData?.scenes) ? rawData.scenes : (Array.isArray(rawData) ? rawData : []);
|
|
521
|
+
const enrichedScenes = await Promise.all(scenes.map(async (scene) => {
|
|
522
|
+
const sceneId = scene?.id || scene?.sceneId;
|
|
523
|
+
if (!sceneId) return scene;
|
|
524
|
+
|
|
525
|
+
let imageAsset = { status: scene?.imageStatus || null, url: scene?.imageUrl || scene?.imageVerticalUri || scene?.imageHorizontalUri || null };
|
|
526
|
+
let videoAsset = { status: scene?.videoStatus || null, url: scene?.videoUrl || scene?.videoVerticalUri || scene?.videoHorizontalUri || null };
|
|
527
|
+
|
|
528
|
+
try {
|
|
529
|
+
if (!imageAsset.url) {
|
|
530
|
+
const imgData = unwrapData(await api.get(`/app/request/assets/${options.project}/${options.chapter}/${sceneId}?type=GENERATE_IMAGES`));
|
|
531
|
+
const imgItems = Array.isArray(imgData) ? imgData : (imgData?.items || []);
|
|
532
|
+
const completed = imgItems.find((r) => String(r?.status || '').toUpperCase() === 'COMPLETED');
|
|
533
|
+
if (completed) {
|
|
534
|
+
imageAsset = { status: 'COMPLETED', url: completed?.imageVerticalUri || completed?.imageHorizontalUri || completed?.imageUrl || completed?.outputUrl || null };
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
} catch { /* optional */ }
|
|
538
|
+
|
|
539
|
+
try {
|
|
540
|
+
if (!videoAsset.url) {
|
|
541
|
+
const vidData = unwrapData(await api.get(`/app/request/assets/${options.project}/${options.chapter}/${sceneId}?type=GENERATE_VIDEO`));
|
|
542
|
+
const vidItems = Array.isArray(vidData) ? vidData : (vidData?.items || []);
|
|
543
|
+
const completed = vidItems.find((r) => String(r?.status || '').toUpperCase() === 'COMPLETED');
|
|
544
|
+
if (completed) {
|
|
545
|
+
videoAsset = { status: 'COMPLETED', url: completed?.videoVerticalUri || completed?.videoHorizontalUri || completed?.videoUrl || completed?.outputUrl || null };
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
} catch { /* optional */ }
|
|
549
|
+
|
|
550
|
+
return { sceneId, image: imageAsset, video: videoAsset, raw: scene };
|
|
551
|
+
}));
|
|
552
|
+
|
|
553
|
+
emitJson({ status: 'success', data: { ...rawData, scenes: enrichedScenes } });
|
|
438
554
|
} catch (error) {
|
|
439
555
|
// Backward-compatible fallback for old backend
|
|
440
556
|
try {
|
|
@@ -451,13 +567,77 @@ program
|
|
|
451
567
|
(r?.chapter_id === options.chapter || r?.chapterId === options.chapter)
|
|
452
568
|
);
|
|
453
569
|
|
|
454
|
-
|
|
570
|
+
// Enrich fallback scenes too
|
|
571
|
+
const enrichedFallback = await Promise.all(scenes.map(async (scene) => {
|
|
572
|
+
const sceneId = scene?.id || scene?.sceneId;
|
|
573
|
+
if (!sceneId) return scene;
|
|
574
|
+
|
|
575
|
+
let imageAsset = { status: null, url: null };
|
|
576
|
+
let videoAsset = { status: null, url: null };
|
|
577
|
+
|
|
578
|
+
try {
|
|
579
|
+
const imgData = unwrapData(await api.get(`/app/request/assets/${options.project}/${options.chapter}/${sceneId}?type=GENERATE_IMAGES`));
|
|
580
|
+
const imgItems = Array.isArray(imgData) ? imgData : (imgData?.items || []);
|
|
581
|
+
const completed = imgItems.find((r) => String(r?.status || '').toUpperCase() === 'COMPLETED');
|
|
582
|
+
if (completed) {
|
|
583
|
+
imageAsset = { status: 'COMPLETED', url: completed?.imageVerticalUri || completed?.imageHorizontalUri || completed?.imageUrl || null };
|
|
584
|
+
}
|
|
585
|
+
} catch { /* optional */ }
|
|
586
|
+
|
|
587
|
+
try {
|
|
588
|
+
const vidData = unwrapData(await api.get(`/app/request/assets/${options.project}/${options.chapter}/${sceneId}?type=GENERATE_VIDEO`));
|
|
589
|
+
const vidItems = Array.isArray(vidData) ? vidData : (vidData?.items || []);
|
|
590
|
+
const completed = vidItems.find((r) => String(r?.status || '').toUpperCase() === 'COMPLETED');
|
|
591
|
+
if (completed) {
|
|
592
|
+
videoAsset = { status: 'COMPLETED', url: completed?.videoVerticalUri || completed?.videoHorizontalUri || completed?.videoUrl || null };
|
|
593
|
+
}
|
|
594
|
+
} catch { /* optional */ }
|
|
595
|
+
|
|
596
|
+
return { sceneId, image: imageAsset, video: videoAsset, raw: scene };
|
|
597
|
+
}));
|
|
598
|
+
|
|
599
|
+
emitJson({ status: 'success', data: { projectId: options.project, chapterId: options.chapter, scenes: enrichedFallback, requests: chapterRequests } });
|
|
455
600
|
} catch (fallbackError) {
|
|
456
601
|
emitJson({ status: 'error', ...formatCliError(fallbackError) });
|
|
457
602
|
}
|
|
458
603
|
}
|
|
459
604
|
});
|
|
460
605
|
|
|
606
|
+
program
|
|
607
|
+
.command('scene-materialization-status')
|
|
608
|
+
.description('Check how many scenes have been materialized for a chapter')
|
|
609
|
+
.requiredOption('-p, --project <project>', 'Project ID')
|
|
610
|
+
.requiredOption('-c, --chapter <chapter>', 'Chapter ID')
|
|
611
|
+
.action(async (options) => {
|
|
612
|
+
try {
|
|
613
|
+
// Get chapter data to determine expected scenes
|
|
614
|
+
let expectedScenes = 0;
|
|
615
|
+
try {
|
|
616
|
+
const chapterData = unwrapData(await api.get(`/app/chapters/${options.project}`));
|
|
617
|
+
const chapters = Array.isArray(chapterData) ? chapterData : (chapterData?.chapters || chapterData?.items || []);
|
|
618
|
+
const chapter = chapters.find((ch) => (ch?.id || ch?.chapterId) === options.chapter);
|
|
619
|
+
expectedScenes = chapter?.numberScene || chapter?.sceneCount || chapter?.expectedScenes || 0;
|
|
620
|
+
} catch { /* fallback: expectedScenes stays 0 */ }
|
|
621
|
+
|
|
622
|
+
// Get actual scenes
|
|
623
|
+
const scenesRaw = unwrapData(await api.get(`/app/scenes/${options.project}/${options.chapter}`));
|
|
624
|
+
const scenes = Array.isArray(scenesRaw) ? scenesRaw : (scenesRaw?.scenes || scenesRaw?.items || []);
|
|
625
|
+
const materializedScenes = scenes.length;
|
|
626
|
+
|
|
627
|
+
// If we couldn't get expectedScenes from chapter, use materialized as fallback
|
|
628
|
+
if (expectedScenes === 0) expectedScenes = materializedScenes;
|
|
629
|
+
|
|
630
|
+
let status;
|
|
631
|
+
if (materializedScenes === 0) status = 'EMPTY';
|
|
632
|
+
else if (materializedScenes >= expectedScenes) status = 'READY';
|
|
633
|
+
else status = 'PROCESSING';
|
|
634
|
+
|
|
635
|
+
emitJson({ status: 'success', expectedScenes, materializedScenes, materialization: status });
|
|
636
|
+
} catch (error) {
|
|
637
|
+
emitJson({ status: 'error', ...formatCliError(error) });
|
|
638
|
+
}
|
|
639
|
+
});
|
|
640
|
+
|
|
461
641
|
program
|
|
462
642
|
.command('create-scene')
|
|
463
643
|
.description('Create a new scene from text content')
|
|
@@ -564,7 +744,13 @@ program
|
|
|
564
744
|
}
|
|
565
745
|
|
|
566
746
|
const data = await api.post('/app/request', payload);
|
|
567
|
-
|
|
747
|
+
const requestResult = unwrapData(data);
|
|
748
|
+
// P2-7: Persist endScene metadata when chained video generation
|
|
749
|
+
if (options.endscene) {
|
|
750
|
+
requestResult.end_scene_id = options.endscene;
|
|
751
|
+
requestResult.generationMode = 'CHAINED_VIDEO';
|
|
752
|
+
}
|
|
753
|
+
console.log(JSON.stringify({ status: "success", request: requestResult }, null, 2));
|
|
568
754
|
} catch (error) {
|
|
569
755
|
console.log(JSON.stringify({ status: "error", ...formatCliError(error) }));
|
|
570
756
|
}
|
|
@@ -622,6 +808,7 @@ program
|
|
|
622
808
|
.requiredOption('-c, --chapter <chapter>', 'Chapter ID')
|
|
623
809
|
.option('-i, --interval <sec>', 'Polling interval in seconds', '10')
|
|
624
810
|
.option('-t, --timeout <sec>', 'Timeout in seconds', '1800')
|
|
811
|
+
.option('--require-success', 'Exit non-zero if any scene lacks a successful image asset')
|
|
625
812
|
.action(async (options) => {
|
|
626
813
|
const intervalMs = Math.max(1, Number(options.interval || 10)) * 1000;
|
|
627
814
|
const timeoutMs = Math.max(30, Number(options.timeout || 1800)) * 1000;
|
|
@@ -640,6 +827,24 @@ program
|
|
|
640
827
|
|
|
641
828
|
const pending = filtered.filter((r) => ['PENDING', 'PROCESSING', 'RUNNING'].includes(String(r?.status || '').toUpperCase()));
|
|
642
829
|
if (pending.length === 0) {
|
|
830
|
+
// --require-success: verify each scene has at least one COMPLETED request with asset URL
|
|
831
|
+
if (options.requireSuccess) {
|
|
832
|
+
const sceneIds = [...new Set(filtered.map((r) => r?.scene || r?.sceneId).filter(Boolean))];
|
|
833
|
+
const failedScenes = [];
|
|
834
|
+
for (const sid of sceneIds) {
|
|
835
|
+
const sceneReqs = filtered.filter((r) => (r?.scene === sid || r?.sceneId === sid));
|
|
836
|
+
const hasSuccess = sceneReqs.some((r) => {
|
|
837
|
+
const st = String(r?.status || '').toUpperCase();
|
|
838
|
+
const hasUrl = !!(r?.imageVerticalUri || r?.imageHorizontalUri || r?.imageUrl || r?.outputUrl);
|
|
839
|
+
return st === 'COMPLETED' && hasUrl;
|
|
840
|
+
});
|
|
841
|
+
if (!hasSuccess) failedScenes.push(sid);
|
|
842
|
+
}
|
|
843
|
+
if (failedScenes.length > 0) {
|
|
844
|
+
console.log(JSON.stringify({ status: 'error', code: 'ASSETS_NOT_SUCCESS', message: 'Some scenes did not produce successful assets', failedScenes }, null, 2));
|
|
845
|
+
process.exit(1);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
643
848
|
console.log(JSON.stringify({ status: 'success', data: filtered, message: 'All image requests finished' }, null, 2));
|
|
644
849
|
return;
|
|
645
850
|
}
|
|
@@ -660,6 +865,7 @@ program
|
|
|
660
865
|
.requiredOption('-c, --chapter <chapter>', 'Chapter ID')
|
|
661
866
|
.option('-i, --interval <sec>', 'Polling interval in seconds', '10')
|
|
662
867
|
.option('-t, --timeout <sec>', 'Timeout in seconds', '3600')
|
|
868
|
+
.option('--require-success', 'Exit non-zero if any scene lacks a successful video asset')
|
|
663
869
|
.action(async (options) => {
|
|
664
870
|
const intervalMs = Math.max(1, Number(options.interval || 10)) * 1000;
|
|
665
871
|
const timeoutMs = Math.max(30, Number(options.timeout || 3600)) * 1000;
|
|
@@ -678,6 +884,24 @@ program
|
|
|
678
884
|
|
|
679
885
|
const pending = filtered.filter((r) => ['PENDING', 'PROCESSING', 'RUNNING'].includes(String(r?.status || '').toUpperCase()));
|
|
680
886
|
if (pending.length === 0) {
|
|
887
|
+
// --require-success: verify each scene has at least one COMPLETED request with asset URL
|
|
888
|
+
if (options.requireSuccess) {
|
|
889
|
+
const sceneIds = [...new Set(filtered.map((r) => r?.scene || r?.sceneId).filter(Boolean))];
|
|
890
|
+
const failedScenes = [];
|
|
891
|
+
for (const sid of sceneIds) {
|
|
892
|
+
const sceneReqs = filtered.filter((r) => (r?.scene === sid || r?.sceneId === sid));
|
|
893
|
+
const hasSuccess = sceneReqs.some((r) => {
|
|
894
|
+
const st = String(r?.status || '').toUpperCase();
|
|
895
|
+
const hasUrl = !!(r?.videoVerticalUri || r?.videoHorizontalUri || r?.videoUrl || r?.outputUrl);
|
|
896
|
+
return st === 'COMPLETED' && hasUrl;
|
|
897
|
+
});
|
|
898
|
+
if (!hasSuccess) failedScenes.push(sid);
|
|
899
|
+
}
|
|
900
|
+
if (failedScenes.length > 0) {
|
|
901
|
+
console.log(JSON.stringify({ status: 'error', code: 'ASSETS_NOT_SUCCESS', message: 'Some scenes did not produce successful assets', failedScenes }, null, 2));
|
|
902
|
+
process.exit(1);
|
|
903
|
+
}
|
|
904
|
+
}
|
|
681
905
|
console.log(JSON.stringify({ status: 'success', data: filtered, message: 'All video requests finished' }, null, 2));
|
|
682
906
|
return;
|
|
683
907
|
}
|
|
@@ -697,9 +921,21 @@ program
|
|
|
697
921
|
.action(async (projectId, chapterId, sceneId, type) => {
|
|
698
922
|
try {
|
|
699
923
|
const data = await api.get(`/app/request/assets/${projectId}/${chapterId}/${sceneId}?type=${type}`);
|
|
700
|
-
|
|
924
|
+
const raw = unwrapData(data);
|
|
925
|
+
// Enrich with lifecycle metadata
|
|
926
|
+
const items = Array.isArray(raw) ? raw : (raw?.items || [raw]);
|
|
927
|
+
const enriched = items.map((r) => ({
|
|
928
|
+
...r,
|
|
929
|
+
createdAt: r?.createdAt || null,
|
|
930
|
+
startedAt: r?.startedAt || null,
|
|
931
|
+
updatedAt: r?.updatedAt || null,
|
|
932
|
+
completedAt: r?.completedAt || null,
|
|
933
|
+
deducted: r?.deducted ?? null,
|
|
934
|
+
retryable: typeof r?.retryable === 'boolean' ? r.retryable : (r?.status && ['FAILED', 'ERROR'].includes(String(r.status).toUpperCase())),
|
|
935
|
+
}));
|
|
936
|
+
emitJson({ status: 'success', data: enriched });
|
|
701
937
|
} catch (error) {
|
|
702
|
-
|
|
938
|
+
emitJson({ status: "error", ...formatCliError(error) });
|
|
703
939
|
}
|
|
704
940
|
});
|
|
705
941
|
|