feat(xai): add Grok Imagine video generation (/v1/videos) + CLI

Async video job proxy mirroring the existing image-generation layer split:
Next routes → src/sse/handlers/videoGeneration.js (auth gate, account
fallback loop, refresh persistence) → open-sse/handlers/videoCore.js
(transparent upstream proxy, 401 refresh-once/retry-once, secret sanitization).

- POST /v1/videos/{generations,edits,extensions}: byte-exact body forward
  (JSON + multipart), request_id passthrough, Idempotency-Key forwarded
- GET /v1/videos/{request_id}: status/progress/video.url passthrough
- Register grok-imagine-video (kind: "video"); add "video" to MODEL_TYPE_TO_KIND
  so video models stay out of chat lists (also fixes runwayml leak)
- 9router xai video CLI: submit → poll → atomic MP4 download
- No auto-retry of creation POSTs (billable jobs); rotate accounts only on
  401/403/429; sanitize Bearer tokens + credential values from errors/logs

Closes #1285
This commit is contained in:
ann
2026-07-16 15:29:52 +07:00
committed by decolua
parent 02ccdc2d22
commit d6761c6fb0
17 changed files with 1652 additions and 3 deletions
+1
View File
@@ -94,6 +94,7 @@ const MODEL_TYPE_TO_KIND = {
embedding: "embedding",
stt: "stt",
imageToText: "imageToText",
video: "video",
};
function modelKind(model) {
+17
View File
@@ -0,0 +1,17 @@
import { handleVideoGet } from "@/sse/handlers/videoGeneration.js";
export async function OPTIONS() {
return new Response(null, {
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, OPTIONS",
"Access-Control-Allow-Headers": "*",
},
});
}
/** GET /v1/videos/{request_id} - poll async video job status (xAI Grok Imagine) */
export async function GET(request, { params }) {
const { id } = await params;
return await handleVideoGet(request, id);
}
+16
View File
@@ -0,0 +1,16 @@
import { handleVideoCreate } from "@/sse/handlers/videoGeneration.js";
export async function OPTIONS() {
return new Response(null, {
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "POST, OPTIONS",
"Access-Control-Allow-Headers": "*",
},
});
}
/** POST /v1/videos/edits - async video edit (xAI Grok Imagine) */
export async function POST(request) {
return await handleVideoCreate(request, "edits");
}
+16
View File
@@ -0,0 +1,16 @@
import { handleVideoCreate } from "@/sse/handlers/videoGeneration.js";
export async function OPTIONS() {
return new Response(null, {
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "POST, OPTIONS",
"Access-Control-Allow-Headers": "*",
},
});
}
/** POST /v1/videos/extensions - async video extension (xAI Grok Imagine) */
export async function POST(request) {
return await handleVideoCreate(request, "extensions");
}
@@ -0,0 +1,16 @@
import { handleVideoCreate } from "@/sse/handlers/videoGeneration.js";
export async function OPTIONS() {
return new Response(null, {
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "POST, OPTIONS",
"Access-Control-Allow-Headers": "*",
},
});
}
/** POST /v1/videos/generations - async video generation (xAI Grok Imagine) */
export async function POST(request) {
return await handleVideoCreate(request, "generations");
}