veogent 1.5.1 → 1.6.0

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/README.md CHANGED
@@ -98,20 +98,23 @@ veogent create-scene --project <projectId> --chapter <chapterId> \
98
98
  --lang English --material CINEMATIC --scenes 5 --wait
99
99
  ```
100
100
 
101
- #### Phase 3: Generate Images + Videos
101
+ #### Phase 3: Generate Images + Videos (Async Polling)
102
102
 
103
103
  ```bash
104
- # Generate all images
104
+ # 1. Trigger generation (without --wait) to avoid locking your terminal
105
105
  veogent batch-request --type GENERATE_IMAGES \
106
- --project <projectId> --chapter <chapterId> --all \
107
- --orientation VERTICAL --wait
106
+ --project <projectId> --chapter <chapterId> --all --orientation VERTICAL
108
107
 
109
- # Generate all videos
110
- veogent batch-request --type GENERATE_VIDEO \
111
- --project <projectId> --chapter <chapterId> --all \
112
- --orientation VERTICAL --wait
108
+ # 2. Extract `requestId` from the output
109
+ # 3. Poll status asynchronously:
110
+ veogent request-status --id <requestId>
111
+ # ... loop until requestStatus === "COMPLETED"
112
+
113
+ # Repeat for GENERATE_VIDEO once images are ready
113
114
  ```
114
115
 
116
+ > **Tip for Agents:** While the `--wait` flag exists, using `request-status` is recommended so you don't lock your main process for the 5-10 minutes it takes to render a video.
117
+
115
118
  #### Phase 4: QA + Fix Loop
116
119
 
117
120
  ```bash
@@ -187,13 +190,15 @@ veogent request --type GENERATE_VIDEO \
187
190
 
188
191
  | Command | Description |
189
192
  |---------|-------------|
190
- | `request` | Create a single generation job (`GENERATE_IMAGES` / `GENERATE_VIDEO`) |
193
+ | `request` | Create a single generation job (`GENERATE_IMAGES` / `GENERATE_VIDEO` / `UPSCALE_VIDEO`) |
191
194
  | `batch-request` | Batch generation for multiple/all scenes (`--all`) |
192
195
  | `requests` | List existing generation jobs |
196
+ | `request-status` | Check a specific job by request ID (includes asset URL) |
193
197
  | `failed-requests` | List failed requests for a chapter |
194
198
  | `wait-images` | Poll until all image jobs finish |
195
199
  | `wait-videos` | Poll until all video jobs finish |
196
200
  | `queue-status` | Current queue/concurrency status |
201
+ | `download-upscale` | Download 4K upscaled video as `.mp4` (decodes base64 from flow-media) |
197
202
 
198
203
  ### Monitoring
199
204
 
@@ -201,6 +206,7 @@ veogent request --type GENERATE_VIDEO \
201
206
  |---------|-------------|
202
207
  | `scene-status` | Scene-level image+video status with asset URLs |
203
208
  | `workflow-status` | Full snapshot: scenes + requests + assets (best for agents) |
209
+ | `request-status` | Check a specific job by request ID |
204
210
 
205
211
  ### YouTube
206
212
 
@@ -245,6 +251,27 @@ Uses sceneA's image as start frame, sceneB's image as end frame. Both scenes mus
245
251
 
246
252
  ---
247
253
 
254
+ ## Video Upscale (4K)
255
+
256
+ ```bash
257
+ # 1. Trigger upscale
258
+ veogent request --type UPSCALE_VIDEO \
259
+ --project X --chapter Y --scene Z \
260
+ --orientation VERTICAL --resolution VIDEO_RESOLUTION_4K
261
+
262
+ # 2. Poll status by request ID
263
+ veogent request-status --id <requestId>
264
+
265
+ # 3. Download the upscaled video
266
+ veogent download-upscale \
267
+ --project X --chapter Y --scene Z \
268
+ --orientation VERTICAL --out ./scene_4k.mp4
269
+ ```
270
+
271
+ > `download-upscale` calls `/app/scene/flow-media`, decodes the base64 `encodedVideo`, and writes a `.mp4` file.
272
+
273
+ ---
274
+
248
275
  ## Concurrency
249
276
 
250
277
  Maximum **5** concurrent requests. If queue is full (E10071), CLI auto-retries once after 30s.
package/index.js CHANGED
@@ -747,8 +747,8 @@ program
747
747
  // --- Execution Request (Generate Image / Video) ---
748
748
  program
749
749
  .command('request')
750
- .description('Create a job request (GENERATE_IMAGES, GENERATE_VIDEO)')
751
- .requiredOption('--type <type>', 'Request type (e.g., GENERATE_IMAGES, GENERATE_VIDEO)')
750
+ .description('Create a job request (GENERATE_IMAGES, GENERATE_VIDEO, UPSCALE_VIDEO). Use UPSCALE_VIDEO to upscale a rendered video to 4K resolution.')
751
+ .requiredOption('--type <type>', 'Request type (e.g., GENERATE_IMAGES, GENERATE_VIDEO, UPSCALE_VIDEO)')
752
752
  .requiredOption('--project <project>', 'Project ID')
753
753
  .requiredOption('--chapter <chapter>', 'Chapter ID')
754
754
  .requiredOption('--scene <scene>', 'Scene ID')
@@ -757,6 +757,7 @@ program
757
757
  .option('--videomodel <videomodel>', 'Video Model (veo_3_1_fast, veo_3_1_fast_r2v). Default: veo_3_1_fast', 'veo_3_1_fast')
758
758
  .option('--speed <speed>', 'Video Speed (normal, timelapse, slowmotion)', 'normal')
759
759
  .option('--endscene <endscene>', 'End Scene ID for continuous video generation')
760
+ .option('--resolution <resolution>', 'Resolution for UPSCALE_VIDEO (e.g. VIDEO_RESOLUTION_4K)', 'VIDEO_RESOLUTION_4K')
760
761
  .option('--wait', 'Wait for request to complete before returning')
761
762
  .action(async (options) => {
762
763
  try {
@@ -804,8 +805,9 @@ program
804
805
  payload.orientation = options.orientation || 'HORIZONTAL';
805
806
  payload.imageModel = options.imagemodel;
806
807
  }
807
- if (['VIDEO_UPSCALE'].includes(options.type)) {
808
- payload.orientation = options.orientation || 'HORIZONTAL'; // Provide fallback
808
+ if (['UPSCALE_VIDEO'].includes(options.type)) {
809
+ payload.orientation = options.orientation || 'VERTICAL'; // Upscale defaults to VERTICAL
810
+ payload.resolution = options.resolution || 'VIDEO_RESOLUTION_4K';
809
811
  }
810
812
 
811
813
  const data = await api.post('/app/request', payload);
@@ -1057,35 +1059,64 @@ program
1057
1059
 
1058
1060
  program
1059
1061
  .command('request-status')
1060
- .description('Get status of a specific request by ID')
1062
+ .description('Get status of a specific request by ID. For GENERATE_VIDEO/GENERATE_IMAGES, falls back to assets endpoint for output URL. UPSCALE_VIDEO output URL is read directly from the request object.')
1061
1063
  .requiredOption('--id <requestId>', 'Request ID to check')
1062
1064
  .action(async (options) => {
1063
1065
  try {
1064
- const data = await api.get('/app/requests');
1065
- let items = unwrapData(data);
1066
- items = Array.isArray(items) ? items : (items?.items || []);
1066
+ const data = await api.get(`/app/standalone/request/${options.id}`);
1067
+ const found = unwrapData(data);
1067
1068
 
1068
- const found = items.find((r) => r?.id === options.id || r?.requestId === options.id);
1069
1069
  if (!found) {
1070
1070
  emitJson({ status: 'error', code: 'REQUEST_NOT_FOUND', message: `No request found with ID: ${options.id}` });
1071
1071
  return;
1072
1072
  }
1073
1073
 
1074
- const status = String(found?.status || '').toUpperCase();
1074
+ const requestStatus = String(found?.status || '').toUpperCase();
1075
+ const type = found?.type || null;
1076
+ const projectId = found?.projectId || found?.project_id || null;
1077
+ const chapterId = found?.chapterId || found?.chapter_id || null;
1078
+ const sceneId = found?.sceneId || found?.scene_id || null;
1079
+
1080
+ let imageAsset = { url: found?.imageVerticalUri || found?.imageHorizontalUri || found?.imageUrl || null };
1081
+ // UPSCALE_VIDEO: output URL lives directly on the request object (not in assets endpoint)
1082
+ let videoAsset = { url: found?.videoVerticalUri || found?.videoHorizontalUri || found?.videoUrl || found?.outputUrl || null };
1083
+
1084
+ // For GENERATE_VIDEO only: fallback to assets endpoint if URL not on request object
1085
+ if (type === 'GENERATE_VIDEO' && projectId && chapterId && sceneId && !videoAsset.url) {
1086
+ try {
1087
+ const assetData = unwrapData(await api.get(`/app/request/assets/${projectId}/${chapterId}/${sceneId}?type=GENERATE_VIDEO`));
1088
+ const assetItems = Array.isArray(assetData) ? assetData : (assetData?.items || []);
1089
+ const matched = assetItems.find((a) => a?.id === options.id || a?.requestId === options.id)
1090
+ || assetItems.find((a) => String(a?.status || '').toUpperCase() === 'COMPLETED');
1091
+ if (matched) {
1092
+ videoAsset = { url: matched?.videoVerticalUri || matched?.videoHorizontalUri || matched?.videoUrl || matched?.outputUrl || null };
1093
+ }
1094
+ } catch { /* asset fetch optional */ }
1095
+ }
1096
+
1097
+ // For image type: fetch asset URL if not present
1098
+ if (type === 'GENERATE_IMAGES' && projectId && chapterId && sceneId && !imageAsset.url) {
1099
+ try {
1100
+ const assetData = unwrapData(await api.get(`/app/request/assets/${projectId}/${chapterId}/${sceneId}?type=GENERATE_IMAGES`));
1101
+ const assetItems = Array.isArray(assetData) ? assetData : (assetData?.items || []);
1102
+ const matched = assetItems.find((a) => a?.id === options.id || a?.requestId === options.id)
1103
+ || assetItems.find((a) => String(a?.status || '').toUpperCase() === 'COMPLETED');
1104
+ if (matched) {
1105
+ imageAsset = { url: matched?.imageVerticalUri || matched?.imageHorizontalUri || matched?.imageUrl || matched?.outputUrl || null };
1106
+ }
1107
+ } catch { /* asset fetch optional */ }
1108
+ }
1109
+
1075
1110
  const result = {
1076
1111
  status: 'success',
1077
1112
  requestId: found?.id || found?.requestId,
1078
- type: found?.type || null,
1079
- requestStatus: status,
1080
- sceneId: found?.sceneId || found?.scene_id || null,
1081
- chapterId: found?.chapterId || found?.chapter_id || null,
1082
- projectId: found?.projectId || found?.project_id || null,
1083
- image: {
1084
- url: found?.imageVerticalUri || found?.imageHorizontalUri || found?.imageUrl || found?.outputUrl || null,
1085
- },
1086
- video: {
1087
- url: found?.videoVerticalUri || found?.videoHorizontalUri || found?.videoUrl || found?.outputUrl || null,
1088
- },
1113
+ type,
1114
+ requestStatus,
1115
+ sceneId,
1116
+ chapterId,
1117
+ projectId,
1118
+ image: imageAsset,
1119
+ video: videoAsset,
1089
1120
  createdAt: found?.createdAt || found?.created_at || null,
1090
1121
  completedAt: found?.completedAt || found?.updatedAt || null,
1091
1122
  raw: found,
@@ -1109,6 +1140,41 @@ program
1109
1140
  }
1110
1141
  });
1111
1142
 
1143
+ program
1144
+ .command('download-upscale')
1145
+ .description('Download the 4K upscaled video for a scene. Calls /app/scene/flow-media to retrieve the base64-encoded video and saves it as an .mp4 file.')
1146
+ .requiredOption('--project <project>', 'Project ID')
1147
+ .requiredOption('--chapter <chapter>', 'Chapter ID')
1148
+ .requiredOption('--scene <scene>', 'Scene ID')
1149
+ .option('--orientation <orientation>', 'Orientation to download (VERTICAL, HORIZONTAL)', 'VERTICAL')
1150
+ .option('--out <path>', 'Output file path (default: ./<sceneId>_upscale.mp4)')
1151
+ .action(async (options) => {
1152
+ try {
1153
+ const orientation = options.orientation.toLowerCase(); // Backend expects lowercase (e.g. 'vertical')
1154
+ humanLog(`⏳ Fetching upscaled video from flow-media...`);
1155
+
1156
+ const data = await api.get(`/app/scene/flow-media/${options.project}/${options.chapter}/${options.scene}/${orientation}`);
1157
+ const raw = unwrapData(data);
1158
+
1159
+ // Backend returns { video: { encodedVideo, ... } } or flat { encodedVideo }
1160
+ const encodedVideo = raw?.video?.encodedVideo || raw?.encodedVideo || null;
1161
+
1162
+ if (!encodedVideo) {
1163
+ emitJson({ status: 'error', code: 'NO_VIDEO_DATA', message: 'No encodedVideo in response. The upscale may not be ready yet.', raw });
1164
+ return;
1165
+ }
1166
+
1167
+ const outPath = options.out || `./${options.scene}_upscale.mp4`;
1168
+ const { writeFileSync } = await import('fs');
1169
+ writeFileSync(outPath, Buffer.from(encodedVideo, 'base64'));
1170
+
1171
+ humanLog(`✅ Saved upscaled video to: ${outPath}`);
1172
+ emitJson({ status: 'success', file: outPath, sceneId: options.scene, orientation });
1173
+ } catch (error) {
1174
+ emitJson({ status: 'error', ...formatCliError(error) });
1175
+ }
1176
+ });
1177
+
1112
1178
  program
1113
1179
  .command('failed-requests')
1114
1180
  .description('List failed requests for a project/chapter (agent helper)')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veogent",
3
- "version": "1.5.1",
3
+ "version": "1.6.0",
4
4
  "description": "The official CLI to interact with the VEOGENT API - AI Video and Image generation platform",
5
5
  "main": "index.js",
6
6
  "bin": {
package/skills/SKILL.md CHANGED
@@ -57,15 +57,20 @@ veogent characters <projectId> # poll until allReady === true
57
57
 
58
58
  NEVER skip `create-chapter-content` — always let VEOGENT AI generate the storyboard.
59
59
 
60
- ### Phase 3: Generate Images + Videos
60
+ ### Phase 3: Generate Images + Videos (Async)
61
61
 
62
62
  ```bash
63
- veogent batch-request --type GENERATE_IMAGES \
64
- --project X --chapter Y --all --orientation VERTICAL --wait
65
- veogent batch-request --type GENERATE_VIDEO \
66
- --project X --chapter Y --all --orientation VERTICAL --wait
63
+ # 1. Trigger batch generation (without --wait)
64
+ veogent batch-request --type GENERATE_IMAGES --project X --chapter Y --all --orientation VERTICAL
65
+
66
+ # 2. Extract the request IDs from the output
67
+ # 3. Poll their status asynchronously without blocking your main process
68
+ veogent request-status --id <requestId>
69
+ # ... loop until requestStatus === "COMPLETED"
67
70
  ```
68
71
 
72
+ *Note: You CAN use the `--wait` flag to make the CLI block until finished, but using `request-status` is better for true asynchronous agent pipelines so you don't lock your process.*
73
+
69
74
  ### Phase 4: QA + Fix Loop
70
75
 
71
76
  ```bash
@@ -196,6 +201,25 @@ veogent request --type GENERATE_VIDEO --project X --chapter Y --scene sceneA \
196
201
  ```
197
202
  Uses sceneA image → sceneB image as start/end frames. Both must have successful images. Only `veo_3_1_fast`.
198
203
 
204
+ ### Video Upscale (4K)
205
+
206
+ ```bash
207
+ # 1. Trigger upscale
208
+ veogent request --type UPSCALE_VIDEO \
209
+ --project X --chapter Y --scene Z \
210
+ --orientation VERTICAL --resolution VIDEO_RESOLUTION_4K
211
+
212
+ # 2. Poll until COMPLETED
213
+ veogent request-status --id <requestId>
214
+
215
+ # 3. Download the upscaled .mp4 (reads from flow-media, decodes base64)
216
+ veogent download-upscale \
217
+ --project X --chapter Y --scene Z \
218
+ --orientation VERTICAL --out ./scene_4k.mp4
219
+ ```
220
+
221
+ > `download-upscale` calls `GET /app/scene/flow-media/{project}/{chapter}/{scene}/{orientation}`, decodes the base64 `encodedVideo` response, and saves it as an `.mp4` file.
222
+
199
223
  ---
200
224
 
201
225
  ## Monitoring & Status
@@ -205,6 +229,7 @@ Uses sceneA image → sceneB image as start/end frames. Both must have successfu
205
229
  | `workflow-status --project X --chapter Y` | Full snapshot: scenes + requests + assets (best for agents) |
206
230
  | `scene-status --project X --chapter Y` | Quick image/video status per scene |
207
231
  | `requests --project X --limit 10` | Check recent generation jobs |
232
+ | `request-status --id <requestId>` | Check a specific job by request ID (with asset URL) |
208
233
  | `failed-requests --project X --chapter Y` | Debug why gen failed |
209
234
  | `wait-images --project X --chapter Y --require-success` | Block until all images done |
210
235
  | `wait-videos --project X --chapter Y --require-success` | Block until all videos done |
@@ -217,7 +242,7 @@ Uses sceneA image → sceneB image as start/end frames. Both must have successfu
217
242
 
218
243
  1. Verify auth before any command (`veogent status`).
219
244
  2. Wait for character readiness (`allReady`) before requesting images.
220
- 3. Use `--wait` flag on `request`/`batch-request`/`edit-scene` to block until completion.
245
+ 3. **Polling vs Blocking:** For true asynchronous workflows, trigger jobs *without* `--wait`, capture the `requestId`, and poll `request-status --id <requestId>`. Only use `--wait` if you are okay with the CLI locking the process for 5-10 minutes.
221
246
  4. Use `workflow-status` as the main status view.
222
247
  5. Respect queue limits (max 5 concurrent). `queue-status` to check.
223
248
  6. **QA image BEFORE video** — bad image = bad video. Each video gen costs 8-10 min.
@@ -359,8 +384,9 @@ veogent request --type GENERATE_VIDEO --project X --chapter Y --scene Z \
359
384
 
360
385
  | Command | Description | Key Options |
361
386
  |---------|-------------|-------------|
362
- | `request` | Single generation job | `--type`, `--project`, `--chapter`, `--scene`, `--orientation`, `--speed`, `--endscene`, `--wait` |
387
+ | `request` | Single generation job | `--type` (GENERATE_IMAGES, GENERATE_VIDEO, UPSCALE_VIDEO), `--project`, `--chapter`, `--scene`, `--orientation`, `--speed`, `--endscene`, `--resolution` (upscale), `--wait` |
363
388
  | `batch-request` | Batch generation | `--type`, `--project`, `--chapter`, `--all`/`--scenes`, `--orientation`, `--speed`, `--wait` |
389
+ | `download-upscale` | Download 4K upscaled video as .mp4 | `--project`, `--chapter`, `--scene`, `--orientation`, `--out` |
364
390
 
365
391
  ### Monitoring Commands
366
392
 
@@ -369,6 +395,7 @@ veogent request --type GENERATE_VIDEO --project X --chapter Y --scene Z \
369
395
  | `workflow-status` | Full snapshot (best for agents) | `--project`, `--chapter` |
370
396
  | `scene-status` | Per-scene image/video status | `--project`, `--chapter` |
371
397
  | `requests` | List generation jobs | `--project`, `--chapter`, `--status`, `--limit` |
398
+ | `request-status` | Check a specific job by ID (with asset URL) | `--id` |
372
399
  | `failed-requests` | Failed jobs only | `--project`, `--chapter` |
373
400
  | `wait-images` | Poll until images done | `--project`, `--chapter`, `--require-success` |
374
401
  | `wait-videos` | Poll until videos done | `--project`, `--chapter`, `--require-success` |