@@ -73780,6 +73780,7 @@ var MODEL_ALIASES = {
7378073780 "nvidia/deepseek-v3.2": "free/deepseek-v3.2",
7378173781 "nvidia/mistral-large-3-675b": "free/mistral-large-3-675b",
7378273782 "nvidia/qwen3-coder-480b": "free/qwen3-coder-480b",
73783+ "qwen/qwen3-coder-480b-a35b-instruct": "free/qwen3-coder-480b",
7378373784 "nvidia/devstral-2-123b": "free/devstral-2-123b",
7378473785 "nvidia/glm-4.7": "free/glm-4.7",
7378573786 "nvidia/llama-4-maverick": "free/llama-4-maverick",
@@ -74379,6 +74380,39 @@ var BLOCKRUN_MODELS = [
7437974380 vision: true,
7438074381 toolCalling: true
7438174382 },
74383+ // xAI Grok 4.20 Family (hidden in picker; explicit-only — mirrors BlockRun hidden:true)
74384+ {
74385+ id: "xai/grok-4.20-reasoning",
74386+ name: "Grok 4.20 Reasoning",
74387+ version: "4.20",
74388+ inputPrice: 2,
74389+ outputPrice: 6,
74390+ contextWindow: 2e6,
74391+ maxOutput: 16384,
74392+ reasoning: true,
74393+ toolCalling: true
74394+ },
74395+ {
74396+ id: "xai/grok-4.20-non-reasoning",
74397+ name: "Grok 4.20",
74398+ version: "4.20",
74399+ inputPrice: 2,
74400+ outputPrice: 6,
74401+ contextWindow: 2e6,
74402+ maxOutput: 16384,
74403+ toolCalling: true
74404+ },
74405+ {
74406+ id: "xai/grok-4.20-multi-agent",
74407+ name: "Grok 4.20 Multi-Agent",
74408+ version: "4.20",
74409+ inputPrice: 2,
74410+ outputPrice: 6,
74411+ contextWindow: 2e6,
74412+ maxOutput: 16384,
74413+ reasoning: true,
74414+ toolCalling: true
74415+ },
7438274416 // MiniMax
7438374417 {
7438474418 id: "minimax/minimax-m2.7",
@@ -76535,6 +76569,7 @@ var BLOCKRUN_API = "https://blockrun.ai/api";
7653576569var BLOCKRUN_SOLANA_API = "https://sol.blockrun.ai/api";
7653676570var IMAGE_DIR = join8(homedir5(), ".openclaw", "blockrun", "images");
7653776571var AUDIO_DIR = join8(homedir5(), ".openclaw", "blockrun", "audio");
76572+ var VIDEO_DIR = join8(homedir5(), ".openclaw", "blockrun", "videos");
7653876573var AUTO_MODEL = "blockrun/auto";
7653976574var ROUTING_PROFILES = /* @__PURE__ */ new Set([
7654076575 "blockrun/eco",
@@ -77242,8 +77277,30 @@ var IMAGE_PRICING = {
7724277277 "google/nano-banana-pro": {
7724377278 default: 0.1,
7724477279 sizes: { "1024x1024": 0.1, "2048x2048": 0.1, "4096x4096": 0.15 }
77280+ },
77281+ "xai/grok-imagine-image": { default: 0.02, sizes: { "1024x1024": 0.02 } },
77282+ "xai/grok-imagine-image-pro": { default: 0.07, sizes: { "1024x1024": 0.07 } },
77283+ "zai/cogview-4": {
77284+ default: 0.015,
77285+ sizes: {
77286+ "512x512": 0.015,
77287+ "768x768": 0.015,
77288+ "1024x1024": 0.015,
77289+ "768x1344": 0.015,
77290+ "1344x768": 0.015,
77291+ "1440x1440": 0.02
77292+ }
7724577293 }
7724677294};
77295+ var VIDEO_PRICING = {
77296+ "xai/grok-imagine-video": { pricePerSecond: 0.05, defaultDurationSeconds: 8 }
77297+ };
77298+ function estimateVideoCost(model, durationSeconds) {
77299+ const p = VIDEO_PRICING[model];
77300+ if (!p) return 0.4 * 1.05;
77301+ const dur = durationSeconds ?? p.defaultDurationSeconds;
77302+ return p.pricePerSecond * dur * 1.05;
77303+ }
7724777304function estimateImageCost(model, size5, n = 1) {
7724877305 const pricing = IMAGE_PRICING[model];
7724977306 if (!pricing) return 0.04 * n * 1.05;
@@ -77630,6 +77687,35 @@ async function startProxy(options) {
7763077687 }
7763177688 return;
7763277689 }
77690+ if (req.url?.startsWith("/videos/") && req.method === "GET") {
77691+ const filename = req.url.slice("/videos/".length).split("?")[0].replace(/[^a-zA-Z0-9._-]/g, "");
77692+ if (!filename) {
77693+ res.writeHead(400);
77694+ res.end("Bad request");
77695+ return;
77696+ }
77697+ const filePath = join8(VIDEO_DIR, filename);
77698+ try {
77699+ const s3 = await fsStat(filePath);
77700+ if (!s3.isFile()) throw new Error("not a file");
77701+ const ext = filename.split(".").pop()?.toLowerCase() ?? "mp4";
77702+ const mime = {
77703+ mp4: "video/mp4",
77704+ webm: "video/webm",
77705+ mov: "video/quicktime"
77706+ };
77707+ const data = await readFile(filePath);
77708+ res.writeHead(200, {
77709+ "Content-Type": mime[ext] ?? "video/mp4",
77710+ "Content-Length": data.length
77711+ });
77712+ res.end(data);
77713+ } catch {
77714+ res.writeHead(404, { "Content-Type": "application/json" });
77715+ res.end(JSON.stringify({ error: "Video not found" }));
77716+ }
77717+ return;
77718+ }
7763377719 if (req.url === "/v1/images/generations" && req.method === "POST") {
7763477720 const imgStartTime = Date.now();
7763577721 const chunks = [];
@@ -77917,6 +78003,88 @@ async function startProxy(options) {
7791778003 }
7791878004 return;
7791978005 }
78006+ if (req.url === "/v1/videos/generations" && req.method === "POST") {
78007+ const videoStartTime = Date.now();
78008+ const chunks = [];
78009+ for await (const chunk of req) {
78010+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
78011+ }
78012+ const reqBody = Buffer.concat(chunks);
78013+ let videoModel = "xai/grok-imagine-video";
78014+ let videoDuration;
78015+ try {
78016+ const parsed = JSON.parse(reqBody.toString());
78017+ videoModel = parsed.model || videoModel;
78018+ videoDuration = typeof parsed.duration_seconds === "number" ? parsed.duration_seconds : void 0;
78019+ } catch {
78020+ }
78021+ try {
78022+ const upstream = await payFetch(`${apiBase}/v1/videos/generations`, {
78023+ method: "POST",
78024+ headers: { "content-type": "application/json", "user-agent": USER_AGENT },
78025+ body: reqBody
78026+ });
78027+ const text = await upstream.text();
78028+ if (!upstream.ok) {
78029+ res.writeHead(upstream.status, { "Content-Type": "application/json" });
78030+ res.end(text);
78031+ return;
78032+ }
78033+ let result;
78034+ try {
78035+ result = JSON.parse(text);
78036+ } catch {
78037+ res.writeHead(200, { "Content-Type": "application/json" });
78038+ res.end(text);
78039+ return;
78040+ }
78041+ if (result.data?.length) {
78042+ await mkdir3(VIDEO_DIR, { recursive: true });
78043+ const port2 = server.address()?.port ?? 8402;
78044+ for (const clip of result.data) {
78045+ if (clip.url?.startsWith("https://") || clip.url?.startsWith("http://")) {
78046+ try {
78047+ const videoResp = await fetch(clip.url);
78048+ if (videoResp.ok) {
78049+ const contentType = videoResp.headers.get("content-type") ?? "video/mp4";
78050+ const ext = contentType.includes("webm") ? "webm" : contentType.includes("quicktime") ? "mov" : "mp4";
78051+ const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
78052+ const buf = Buffer.from(await videoResp.arrayBuffer());
78053+ await writeFile2(join8(VIDEO_DIR, filename), buf);
78054+ clip.url = `http://localhost:${port2}/videos/${filename}`;
78055+ console.log(`[ClawRouter] Video saved \u2192 ${clip.url}`);
78056+ }
78057+ } catch (downloadErr) {
78058+ console.warn(
78059+ `[ClawRouter] Failed to download video, using original URL: ${downloadErr instanceof Error ? downloadErr.message : String(downloadErr)}`
78060+ );
78061+ }
78062+ }
78063+ }
78064+ }
78065+ const videoActualCost = paymentStore.getStore()?.amountUsd ?? estimateVideoCost(videoModel, videoDuration);
78066+ logUsage({
78067+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
78068+ model: videoModel,
78069+ tier: "VIDEO",
78070+ cost: videoActualCost,
78071+ baselineCost: videoActualCost,
78072+ savings: 0,
78073+ latencyMs: Date.now() - videoStartTime
78074+ }).catch(() => {
78075+ });
78076+ res.writeHead(200, { "Content-Type": "application/json" });
78077+ res.end(JSON.stringify(result));
78078+ } catch (err) {
78079+ const msg = err instanceof Error ? err.message : String(err);
78080+ console.error(`[ClawRouter] Video generation error: ${msg}`);
78081+ if (!res.headersSent) {
78082+ res.writeHead(502, { "Content-Type": "application/json" });
78083+ res.end(JSON.stringify({ error: "Video generation failed", details: msg }));
78084+ }
78085+ }
78086+ return;
78087+ }
7792078088 if (req.url?.match(/^\/v1\/(?:x|partner|pm|exa|modal)\//)) {
7792178089 try {
7792278090 await proxyPaidApiRequest(
0 commit comments