@vibeframe/mcp-server 0.20.0 → 0.29.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/LICENSE +21 -0
- package/dist/index.js +823 -863
- package/package.json +14 -15
package/dist/index.js
CHANGED
|
@@ -60,6 +60,7 @@ __export(remotion_exports, {
|
|
|
60
60
|
compositeOverlay: () => compositeOverlay,
|
|
61
61
|
compositeWithImage: () => compositeWithImage,
|
|
62
62
|
ensureRemotionInstalled: () => ensureRemotionInstalled,
|
|
63
|
+
generateAnimatedCaptionComponent: () => generateAnimatedCaptionComponent,
|
|
63
64
|
generateCaptionComponent: () => generateCaptionComponent,
|
|
64
65
|
renderAndComposite: () => renderAndComposite,
|
|
65
66
|
renderMotion: () => renderMotion,
|
|
@@ -491,6 +492,190 @@ export const ${name} = () => {
|
|
|
491
492
|
`;
|
|
492
493
|
return { code, name };
|
|
493
494
|
}
|
|
495
|
+
function generateAnimatedCaptionComponent(options) {
|
|
496
|
+
const { groups, style, highlightColor, fontSize, position, width, fps, videoFileName } = options;
|
|
497
|
+
const name = videoFileName ? "VideoAnimatedCaption" : "AnimatedCaptionOverlay";
|
|
498
|
+
const groupsJSON = JSON.stringify(
|
|
499
|
+
groups.map((g) => ({
|
|
500
|
+
words: g.words.map((w) => ({ word: w.word, start: w.start, end: w.end })),
|
|
501
|
+
startTime: g.startTime,
|
|
502
|
+
endTime: g.endTime,
|
|
503
|
+
text: g.text
|
|
504
|
+
}))
|
|
505
|
+
);
|
|
506
|
+
const justifyContent = position === "top" ? "flex-start" : position === "center" ? "center" : "flex-end";
|
|
507
|
+
const paddingDir = position === "top" ? "paddingTop" : position === "bottom" ? "paddingBottom" : "";
|
|
508
|
+
const paddingVal = position === "center" ? "" : `${paddingDir}: 40,`;
|
|
509
|
+
const videoImport = videoFileName ? `, staticFile` : "";
|
|
510
|
+
const videoElement = videoFileName ? `<Video src={staticFile("${videoFileName}")} style={{ width: "100%", height: "100%" }} muted />` : "";
|
|
511
|
+
const videoMediaImport = videoFileName ? `import { Video } from "@remotion/media";
|
|
512
|
+
` : "";
|
|
513
|
+
let wordRenderer;
|
|
514
|
+
switch (style) {
|
|
515
|
+
case "highlight":
|
|
516
|
+
wordRenderer = `
|
|
517
|
+
const isActive = currentTime >= w.start && currentTime < w.end;
|
|
518
|
+
const bgOpacity = isActive ? 1 : 0;
|
|
519
|
+
return (
|
|
520
|
+
<span
|
|
521
|
+
key={wi}
|
|
522
|
+
style={{
|
|
523
|
+
display: "inline-block",
|
|
524
|
+
padding: "2px 6px",
|
|
525
|
+
margin: "0 2px",
|
|
526
|
+
borderRadius: 4,
|
|
527
|
+
backgroundColor: isActive ? "${highlightColor}" : "transparent",
|
|
528
|
+
color: isActive ? "#000000" : "#FFFFFF",
|
|
529
|
+
transition: "background-color 0.1s",
|
|
530
|
+
fontWeight: "bold",
|
|
531
|
+
textShadow: isActive ? "none" : "2px 2px 4px rgba(0,0,0,0.8)",
|
|
532
|
+
}}
|
|
533
|
+
>
|
|
534
|
+
{w.word}
|
|
535
|
+
</span>
|
|
536
|
+
);`;
|
|
537
|
+
break;
|
|
538
|
+
case "bounce":
|
|
539
|
+
wordRenderer = `
|
|
540
|
+
const isActive = currentTime >= w.start && currentTime < w.end;
|
|
541
|
+
const entryFrame = w.start * ${fps};
|
|
542
|
+
const progress = Math.min(1, Math.max(0, (frame - entryFrame) / 5));
|
|
543
|
+
const springVal = isActive
|
|
544
|
+
? 1 + Math.sin(progress * Math.PI) * 0.15
|
|
545
|
+
: 1;
|
|
546
|
+
const translateY = isActive
|
|
547
|
+
? -Math.sin(progress * Math.PI) * 8
|
|
548
|
+
: 0;
|
|
549
|
+
return (
|
|
550
|
+
<span
|
|
551
|
+
key={wi}
|
|
552
|
+
style={{
|
|
553
|
+
display: "inline-block",
|
|
554
|
+
margin: "0 3px",
|
|
555
|
+
transform: \`scale(\${springVal}) translateY(\${translateY}px)\`,
|
|
556
|
+
color: isActive ? "${highlightColor}" : "#FFFFFF",
|
|
557
|
+
fontWeight: "bold",
|
|
558
|
+
textShadow: "2px 2px 4px rgba(0,0,0,0.8)",
|
|
559
|
+
}}
|
|
560
|
+
>
|
|
561
|
+
{w.word}
|
|
562
|
+
</span>
|
|
563
|
+
);`;
|
|
564
|
+
break;
|
|
565
|
+
case "pop-in":
|
|
566
|
+
wordRenderer = `
|
|
567
|
+
const entryFrame = w.start * ${fps};
|
|
568
|
+
const scale = frame >= entryFrame
|
|
569
|
+
? Math.min(1, (frame - entryFrame) / 5)
|
|
570
|
+
: 0;
|
|
571
|
+
const isActive = currentTime >= w.start && currentTime < w.end;
|
|
572
|
+
return (
|
|
573
|
+
<span
|
|
574
|
+
key={wi}
|
|
575
|
+
style={{
|
|
576
|
+
display: "inline-block",
|
|
577
|
+
margin: "0 3px",
|
|
578
|
+
transform: \`scale(\${scale})\`,
|
|
579
|
+
opacity: scale,
|
|
580
|
+
color: isActive ? "${highlightColor}" : "#FFFFFF",
|
|
581
|
+
fontWeight: "bold",
|
|
582
|
+
textShadow: "2px 2px 4px rgba(0,0,0,0.8)",
|
|
583
|
+
}}
|
|
584
|
+
>
|
|
585
|
+
{w.word}
|
|
586
|
+
</span>
|
|
587
|
+
);`;
|
|
588
|
+
break;
|
|
589
|
+
case "neon":
|
|
590
|
+
wordRenderer = `
|
|
591
|
+
const isActive = currentTime >= w.start && currentTime < w.end;
|
|
592
|
+
const pulse = isActive ? 0.8 + Math.sin(frame * 0.3) * 0.2 : 0.5;
|
|
593
|
+
const glowSize = isActive ? 15 : 0;
|
|
594
|
+
return (
|
|
595
|
+
<span
|
|
596
|
+
key={wi}
|
|
597
|
+
style={{
|
|
598
|
+
display: "inline-block",
|
|
599
|
+
margin: "0 3px",
|
|
600
|
+
color: isActive ? "${highlightColor}" : "#FFFFFF",
|
|
601
|
+
fontWeight: "bold",
|
|
602
|
+
opacity: isActive ? 1 : pulse,
|
|
603
|
+
textShadow: isActive
|
|
604
|
+
? \`0 0 \${glowSize}px ${highlightColor}, 0 0 \${glowSize * 2}px ${highlightColor}, 0 0 \${glowSize * 3}px ${highlightColor}\`
|
|
605
|
+
: "2px 2px 4px rgba(0,0,0,0.8)",
|
|
606
|
+
}}
|
|
607
|
+
>
|
|
608
|
+
{w.word}
|
|
609
|
+
</span>
|
|
610
|
+
);`;
|
|
611
|
+
break;
|
|
612
|
+
}
|
|
613
|
+
const code = `import { AbsoluteFill, useCurrentFrame, useVideoConfig${videoImport} } from "remotion";
|
|
614
|
+
${videoMediaImport}
|
|
615
|
+
interface Word {
|
|
616
|
+
word: string;
|
|
617
|
+
start: number;
|
|
618
|
+
end: number;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
interface WordGroup {
|
|
622
|
+
words: Word[];
|
|
623
|
+
startTime: number;
|
|
624
|
+
endTime: number;
|
|
625
|
+
text: string;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
const groups: WordGroup[] = ${groupsJSON};
|
|
629
|
+
|
|
630
|
+
export const ${name} = () => {
|
|
631
|
+
const frame = useCurrentFrame();
|
|
632
|
+
const { fps } = useVideoConfig();
|
|
633
|
+
const currentTime = frame / fps;
|
|
634
|
+
|
|
635
|
+
const activeGroup = groups.find(
|
|
636
|
+
(g) => currentTime >= g.startTime && currentTime < g.endTime
|
|
637
|
+
);
|
|
638
|
+
|
|
639
|
+
const renderWord = (w: Word, wi: number) => {
|
|
640
|
+
${wordRenderer}
|
|
641
|
+
};
|
|
642
|
+
|
|
643
|
+
return (
|
|
644
|
+
<AbsoluteFill>
|
|
645
|
+
${videoElement}
|
|
646
|
+
{activeGroup && (
|
|
647
|
+
<AbsoluteFill
|
|
648
|
+
style={{
|
|
649
|
+
display: "flex",
|
|
650
|
+
justifyContent: "${justifyContent}",
|
|
651
|
+
alignItems: "center",
|
|
652
|
+
${paddingVal}
|
|
653
|
+
}}
|
|
654
|
+
>
|
|
655
|
+
<div
|
|
656
|
+
style={{
|
|
657
|
+
fontSize: ${fontSize},
|
|
658
|
+
fontFamily: "Arial, Helvetica, sans-serif",
|
|
659
|
+
textAlign: "center" as const,
|
|
660
|
+
maxWidth: "${Math.round(width * 0.9)}px",
|
|
661
|
+
lineHeight: 1.5,
|
|
662
|
+
padding: "8px 16px",
|
|
663
|
+
display: "flex",
|
|
664
|
+
flexWrap: "wrap" as const,
|
|
665
|
+
justifyContent: "center",
|
|
666
|
+
gap: "0px",
|
|
667
|
+
}}
|
|
668
|
+
>
|
|
669
|
+
{activeGroup.words.map((w, wi) => renderWord(w, wi))}
|
|
670
|
+
</div>
|
|
671
|
+
</AbsoluteFill>
|
|
672
|
+
)}
|
|
673
|
+
</AbsoluteFill>
|
|
674
|
+
);
|
|
675
|
+
};
|
|
676
|
+
`;
|
|
677
|
+
return { code, name };
|
|
678
|
+
}
|
|
494
679
|
async function compositeOverlay(options) {
|
|
495
680
|
try {
|
|
496
681
|
await execSafe("ffmpeg", [
|
|
@@ -1694,7 +1879,18 @@ function buildFFmpegArgs(clips, sources, presetSettings, outputPath, options, so
|
|
|
1694
1879
|
if (hasAudio) {
|
|
1695
1880
|
const audioTrimStart = clip.sourceStartOffset;
|
|
1696
1881
|
const audioTrimEnd = clip.sourceStartOffset + clip.duration;
|
|
1697
|
-
|
|
1882
|
+
const sourceDuration = source.duration || 0;
|
|
1883
|
+
const clipDuration = clip.duration;
|
|
1884
|
+
if (source.type === "audio" && sourceDuration > clipDuration && audioTrimStart === 0) {
|
|
1885
|
+
const tempo = sourceDuration / clipDuration;
|
|
1886
|
+
if (tempo <= 2) {
|
|
1887
|
+
audioFilter = `[${srcIdx}:a]atempo=${tempo.toFixed(4)},asetpts=PTS-STARTPTS`;
|
|
1888
|
+
} else {
|
|
1889
|
+
audioFilter = `[${srcIdx}:a]atrim=start=${audioTrimStart}:end=${audioTrimEnd},asetpts=PTS-STARTPTS`;
|
|
1890
|
+
}
|
|
1891
|
+
} else {
|
|
1892
|
+
audioFilter = `[${srcIdx}:a]atrim=start=${audioTrimStart}:end=${audioTrimEnd},asetpts=PTS-STARTPTS`;
|
|
1893
|
+
}
|
|
1698
1894
|
} else {
|
|
1699
1895
|
audioFilter = `anullsrc=r=48000:cl=stereo,atrim=0:${clip.duration},asetpts=PTS-STARTPTS`;
|
|
1700
1896
|
}
|
|
@@ -1742,7 +1938,7 @@ function buildFFmpegArgs(clips, sources, presetSettings, outputPath, options, so
|
|
|
1742
1938
|
return args;
|
|
1743
1939
|
}
|
|
1744
1940
|
function runFFmpegProcess(ffmpegPath, args, onProgress) {
|
|
1745
|
-
return new Promise((
|
|
1941
|
+
return new Promise((resolve12, reject) => {
|
|
1746
1942
|
const ffmpeg = spawn(ffmpegPath, args, {
|
|
1747
1943
|
stdio: ["pipe", "pipe", "pipe"]
|
|
1748
1944
|
});
|
|
@@ -1766,7 +1962,7 @@ function runFFmpegProcess(ffmpegPath, args, onProgress) {
|
|
|
1766
1962
|
});
|
|
1767
1963
|
ffmpeg.on("close", (code) => {
|
|
1768
1964
|
if (code === 0) {
|
|
1769
|
-
|
|
1965
|
+
resolve12();
|
|
1770
1966
|
} else {
|
|
1771
1967
|
const errorMatch = stderr.match(/Error.*$/m);
|
|
1772
1968
|
const errorMsg = errorMatch ? errorMatch[0] : `FFmpeg exited with code ${code}`;
|
|
@@ -2487,8 +2683,11 @@ async function analyzeContent(api, content, targetDuration, options) {
|
|
|
2487
2683
|
// ../ai-providers/dist/gemini/GeminiProvider.js
|
|
2488
2684
|
var MODEL_MAP = {
|
|
2489
2685
|
"flash": "gemini-2.5-flash-image",
|
|
2686
|
+
"3.1-flash": "gemini-3.1-flash-image-preview",
|
|
2687
|
+
"latest": "gemini-3.1-flash-image-preview",
|
|
2490
2688
|
"pro": "gemini-3-pro-image-preview",
|
|
2491
2689
|
"gemini-2.5-flash-image": "gemini-2.5-flash-image",
|
|
2690
|
+
"gemini-3.1-flash-image-preview": "gemini-3.1-flash-image-preview",
|
|
2492
2691
|
"gemini-3-pro-image-preview": "gemini-3-pro-image-preview"
|
|
2493
2692
|
};
|
|
2494
2693
|
var GeminiProvider = class {
|
|
@@ -2523,36 +2722,57 @@ var GeminiProvider = class {
|
|
|
2523
2722
|
};
|
|
2524
2723
|
}
|
|
2525
2724
|
try {
|
|
2526
|
-
const
|
|
2725
|
+
const veoOpts = options ?? {};
|
|
2726
|
+
const model = veoOpts.model || "veo-3.1-fast-generate-preview";
|
|
2527
2727
|
const aspectRatioMap = {
|
|
2528
2728
|
"16:9": "16:9",
|
|
2529
2729
|
"9:16": "9:16",
|
|
2530
2730
|
"1:1": "1:1"
|
|
2531
2731
|
};
|
|
2532
|
-
const
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
}],
|
|
2536
|
-
parameters: {
|
|
2537
|
-
aspectRatio: aspectRatioMap[options?.aspectRatio || "16:9"] || "16:9",
|
|
2538
|
-
durationSeconds: Math.max(4, Math.min(8, options?.duration || 8))
|
|
2539
|
-
}
|
|
2732
|
+
const parameters = {
|
|
2733
|
+
aspectRatio: aspectRatioMap[veoOpts.aspectRatio || "16:9"] || "16:9",
|
|
2734
|
+
durationSeconds: Math.max(4, Math.min(8, veoOpts.duration || 8))
|
|
2540
2735
|
};
|
|
2541
|
-
if (
|
|
2542
|
-
|
|
2736
|
+
if (veoOpts.negativePrompt) {
|
|
2737
|
+
parameters.negativePrompt = veoOpts.negativePrompt;
|
|
2738
|
+
}
|
|
2739
|
+
if (veoOpts.resolution) {
|
|
2740
|
+
parameters.resolution = veoOpts.resolution;
|
|
2741
|
+
}
|
|
2742
|
+
if (veoOpts.personGeneration) {
|
|
2743
|
+
parameters.personGeneration = veoOpts.personGeneration;
|
|
2744
|
+
}
|
|
2745
|
+
const instance = { prompt: prompt2 };
|
|
2746
|
+
if (veoOpts.referenceImage) {
|
|
2747
|
+
const imageData = veoOpts.referenceImage;
|
|
2543
2748
|
if (imageData.startsWith("data:")) {
|
|
2544
2749
|
const base64 = imageData.split(",")[1];
|
|
2545
2750
|
const mimeType = imageData.split(";")[0].split(":")[1];
|
|
2546
|
-
|
|
2547
|
-
bytesBase64Encoded: base64,
|
|
2548
|
-
mimeType
|
|
2549
|
-
};
|
|
2751
|
+
instance.image = { bytesBase64Encoded: base64, mimeType };
|
|
2550
2752
|
} else if (imageData.startsWith("http")) {
|
|
2551
|
-
|
|
2552
|
-
gcsUri: imageData
|
|
2553
|
-
};
|
|
2753
|
+
instance.image = { gcsUri: imageData };
|
|
2554
2754
|
}
|
|
2555
2755
|
}
|
|
2756
|
+
if (veoOpts.lastFrame) {
|
|
2757
|
+
const lastFrameData = veoOpts.lastFrame;
|
|
2758
|
+
if (lastFrameData.startsWith("data:")) {
|
|
2759
|
+
const base64 = lastFrameData.split(",")[1];
|
|
2760
|
+
const mimeType = lastFrameData.split(";")[0].split(":")[1];
|
|
2761
|
+
instance.lastFrame = { bytesBase64Encoded: base64, mimeType };
|
|
2762
|
+
} else if (lastFrameData.startsWith("http")) {
|
|
2763
|
+
instance.lastFrame = { gcsUri: lastFrameData };
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
if (veoOpts.referenceImages && veoOpts.referenceImages.length > 0) {
|
|
2767
|
+
instance.referenceImages = veoOpts.referenceImages.slice(0, 3).map((img) => ({
|
|
2768
|
+
bytesBase64Encoded: img.base64,
|
|
2769
|
+
mimeType: img.mimeType
|
|
2770
|
+
}));
|
|
2771
|
+
}
|
|
2772
|
+
const requestBody = {
|
|
2773
|
+
instances: [instance],
|
|
2774
|
+
parameters
|
|
2775
|
+
};
|
|
2556
2776
|
const response = await fetch(`${this.baseUrl}/models/${model}:predictLongRunning`, {
|
|
2557
2777
|
method: "POST",
|
|
2558
2778
|
headers: {
|
|
@@ -2687,7 +2907,70 @@ var GeminiProvider = class {
|
|
|
2687
2907
|
* Sleep helper
|
|
2688
2908
|
*/
|
|
2689
2909
|
sleep(ms) {
|
|
2690
|
-
return new Promise((
|
|
2910
|
+
return new Promise((resolve12) => setTimeout(resolve12, ms));
|
|
2911
|
+
}
|
|
2912
|
+
/**
|
|
2913
|
+
* Extend a previously generated Veo video
|
|
2914
|
+
* Uses the operation name from a completed generation to create a continuation
|
|
2915
|
+
*/
|
|
2916
|
+
async extendVideo(previousOperationName, prompt2, options) {
|
|
2917
|
+
if (!this.apiKey) {
|
|
2918
|
+
return {
|
|
2919
|
+
id: "",
|
|
2920
|
+
status: "failed",
|
|
2921
|
+
error: "Gemini API key not configured"
|
|
2922
|
+
};
|
|
2923
|
+
}
|
|
2924
|
+
try {
|
|
2925
|
+
const model = options?.model || "veo-3.1-generate-preview";
|
|
2926
|
+
const instance = {
|
|
2927
|
+
video: { previousOperationName }
|
|
2928
|
+
};
|
|
2929
|
+
if (prompt2) {
|
|
2930
|
+
instance.prompt = prompt2;
|
|
2931
|
+
}
|
|
2932
|
+
const requestBody = {
|
|
2933
|
+
instances: [instance],
|
|
2934
|
+
parameters: {
|
|
2935
|
+
durationSeconds: Math.max(4, Math.min(8, options?.duration || 6))
|
|
2936
|
+
}
|
|
2937
|
+
};
|
|
2938
|
+
const response = await fetch(`${this.baseUrl}/models/${model}:predictLongRunning`, {
|
|
2939
|
+
method: "POST",
|
|
2940
|
+
headers: {
|
|
2941
|
+
"Content-Type": "application/json",
|
|
2942
|
+
"x-goog-api-key": this.apiKey
|
|
2943
|
+
},
|
|
2944
|
+
body: JSON.stringify(requestBody)
|
|
2945
|
+
});
|
|
2946
|
+
if (!response.ok) {
|
|
2947
|
+
const errorText = await response.text();
|
|
2948
|
+
return {
|
|
2949
|
+
id: "",
|
|
2950
|
+
status: "failed",
|
|
2951
|
+
error: `Veo extend API error (${response.status}): ${errorText}`
|
|
2952
|
+
};
|
|
2953
|
+
}
|
|
2954
|
+
const data = await response.json();
|
|
2955
|
+
if (data.name) {
|
|
2956
|
+
return {
|
|
2957
|
+
id: data.name,
|
|
2958
|
+
status: "pending",
|
|
2959
|
+
progress: 0
|
|
2960
|
+
};
|
|
2961
|
+
}
|
|
2962
|
+
return {
|
|
2963
|
+
id: "",
|
|
2964
|
+
status: "failed",
|
|
2965
|
+
error: data.error?.message || "Unknown Veo extend error"
|
|
2966
|
+
};
|
|
2967
|
+
} catch (error) {
|
|
2968
|
+
return {
|
|
2969
|
+
id: "",
|
|
2970
|
+
status: "failed",
|
|
2971
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
2972
|
+
};
|
|
2973
|
+
}
|
|
2691
2974
|
}
|
|
2692
2975
|
async cancelGeneration(_id) {
|
|
2693
2976
|
return false;
|
|
@@ -2726,18 +3009,26 @@ var GeminiProvider = class {
|
|
|
2726
3009
|
} else {
|
|
2727
3010
|
imageConfig.aspectRatio = "1:1";
|
|
2728
3011
|
}
|
|
2729
|
-
if (options.resolution
|
|
2730
|
-
|
|
3012
|
+
if (options.resolution) {
|
|
3013
|
+
if (options.resolution === "512px" || options.resolution === "1K" || isPro) {
|
|
3014
|
+
imageConfig.imageSize = options.resolution;
|
|
3015
|
+
}
|
|
2731
3016
|
}
|
|
2732
3017
|
const generationConfig = {
|
|
2733
3018
|
responseModalities: ["TEXT", "IMAGE"],
|
|
2734
3019
|
imageConfig
|
|
2735
3020
|
};
|
|
3021
|
+
if (options.thinkingConfig) {
|
|
3022
|
+
generationConfig.thinkingConfig = options.thinkingConfig;
|
|
3023
|
+
}
|
|
2736
3024
|
const payload = {
|
|
2737
3025
|
contents: [{ parts: [{ text: prompt2 }] }],
|
|
2738
3026
|
generationConfig
|
|
2739
3027
|
};
|
|
2740
|
-
|
|
3028
|
+
const is31Flash = modelId === "gemini-3.1-flash-image-preview";
|
|
3029
|
+
if (options.imageSearchGrounding && is31Flash) {
|
|
3030
|
+
payload.tools = [{ googleSearch: { searchTypes: { webSearch: {}, imageSearch: {} } } }];
|
|
3031
|
+
} else if (options.grounding && isPro) {
|
|
2741
3032
|
payload.tools = [{ googleSearch: {} }];
|
|
2742
3033
|
}
|
|
2743
3034
|
const response = await fetch(`${this.baseUrl}/models/${modelId}:generateContent?key=${this.apiKey}`, {
|
|
@@ -2764,9 +3055,12 @@ var GeminiProvider = class {
|
|
|
2764
3055
|
const data = await response.json();
|
|
2765
3056
|
const parts = data.candidates?.[0]?.content?.parts;
|
|
2766
3057
|
if (!parts || parts.length === 0) {
|
|
3058
|
+
const finishReason = data.candidates?.[0]?.finishReason;
|
|
3059
|
+
const blockReason = data.promptFeedback?.blockReason;
|
|
3060
|
+
const detail = blockReason ? `Blocked by safety filter: ${blockReason}` : finishReason ? `Model returned no image (finishReason: ${finishReason})` : "No content in response";
|
|
2767
3061
|
return {
|
|
2768
3062
|
success: false,
|
|
2769
|
-
error:
|
|
3063
|
+
error: detail
|
|
2770
3064
|
};
|
|
2771
3065
|
}
|
|
2772
3066
|
const images = [];
|
|
@@ -2873,9 +3167,12 @@ var GeminiProvider = class {
|
|
|
2873
3167
|
const data = await response.json();
|
|
2874
3168
|
const responseParts = data.candidates?.[0]?.content?.parts;
|
|
2875
3169
|
if (!responseParts || responseParts.length === 0) {
|
|
3170
|
+
const finishReason = data.candidates?.[0]?.finishReason;
|
|
3171
|
+
const blockReason = data.promptFeedback?.blockReason;
|
|
3172
|
+
const detail = blockReason ? `Blocked by safety filter: ${blockReason}` : finishReason ? `Model returned no image (finishReason: ${finishReason})` : "No content in response";
|
|
2876
3173
|
return {
|
|
2877
3174
|
success: false,
|
|
2878
|
-
error:
|
|
3175
|
+
error: detail
|
|
2879
3176
|
};
|
|
2880
3177
|
}
|
|
2881
3178
|
const images = [];
|
|
@@ -3130,7 +3427,7 @@ Example response:
|
|
|
3130
3427
|
[{"type":"trim","description":"Trim intro to 3 seconds","clipIds":["clip-1"],"params":{"newDuration":3},"confidence":0.9}]
|
|
3131
3428
|
|
|
3132
3429
|
Respond with ONLY the JSON array, no other text.`;
|
|
3133
|
-
const response = await fetch(`${this.baseUrl}/models/gemini-
|
|
3430
|
+
const response = await fetch(`${this.baseUrl}/models/gemini-2.5-flash:generateContent?key=${this.apiKey}`, {
|
|
3134
3431
|
method: "POST",
|
|
3135
3432
|
headers: {
|
|
3136
3433
|
"Content-Type": "application/json"
|
|
@@ -3257,7 +3554,7 @@ async function analyzeContent2(apiKey, content, targetDuration, options) {
|
|
|
3257
3554
|
Authorization: `Bearer ${apiKey}`
|
|
3258
3555
|
},
|
|
3259
3556
|
body: JSON.stringify({
|
|
3260
|
-
model: "gpt-
|
|
3557
|
+
model: "gpt-5-mini",
|
|
3261
3558
|
messages: [
|
|
3262
3559
|
{ role: "system", content: systemPrompt },
|
|
3263
3560
|
{ role: "user", content: buildStoryboardUserMessage(content) }
|
|
@@ -3309,7 +3606,7 @@ var OpenAIProvider = class {
|
|
|
3309
3606
|
this.iconUrl = "/icons/openai.svg";
|
|
3310
3607
|
this.isAvailable = true;
|
|
3311
3608
|
this.baseUrl = "https://api.openai.com/v1";
|
|
3312
|
-
this.model = "gpt-
|
|
3609
|
+
this.model = "gpt-5-mini";
|
|
3313
3610
|
}
|
|
3314
3611
|
async initialize(config2) {
|
|
3315
3612
|
this.apiKey = config2.apiKey;
|
|
@@ -3616,7 +3913,7 @@ Each segment should be 3-10 seconds long.`;
|
|
|
3616
3913
|
Authorization: `Bearer ${this.apiKey}`
|
|
3617
3914
|
},
|
|
3618
3915
|
body: JSON.stringify({
|
|
3619
|
-
model: "gpt-
|
|
3916
|
+
model: "gpt-5-mini",
|
|
3620
3917
|
max_tokens: 4096,
|
|
3621
3918
|
response_format: { type: "json_object" },
|
|
3622
3919
|
messages: [
|
|
@@ -3650,7 +3947,7 @@ ${videoAnalysis}` }
|
|
|
3650
3947
|
}
|
|
3651
3948
|
}
|
|
3652
3949
|
/**
|
|
3653
|
-
* Generate a storyboard from script content using GPT-
|
|
3950
|
+
* Generate a storyboard from script content using GPT-5-mini.
|
|
3654
3951
|
* Alternative to ClaudeProvider.analyzeContent for when Claude is unavailable.
|
|
3655
3952
|
*/
|
|
3656
3953
|
async analyzeContent(content, targetDuration, options) {
|
|
@@ -5575,7 +5872,7 @@ var ElevenLabsProvider = class {
|
|
|
5575
5872
|
this.id = "elevenlabs";
|
|
5576
5873
|
this.name = "ElevenLabs";
|
|
5577
5874
|
this.description = "AI text-to-speech with natural voices and voice cloning";
|
|
5578
|
-
this.capabilities = ["text-to-speech", "sound-generation", "audio-isolation", "voice-clone"];
|
|
5875
|
+
this.capabilities = ["text-to-speech", "sound-generation", "music-generation", "audio-isolation", "voice-clone"];
|
|
5579
5876
|
this.iconUrl = "/icons/elevenlabs.svg";
|
|
5580
5877
|
this.isAvailable = true;
|
|
5581
5878
|
this.baseUrl = "https://api.elevenlabs.io/v1";
|
|
@@ -5633,7 +5930,7 @@ var ElevenLabsProvider = class {
|
|
|
5633
5930
|
error: voiceError instanceof Error ? voiceError.message : String(voiceError)
|
|
5634
5931
|
};
|
|
5635
5932
|
}
|
|
5636
|
-
const model = options.model || "
|
|
5933
|
+
const model = options.model || "eleven_v3";
|
|
5637
5934
|
const response = await fetch(`${this.baseUrl}/text-to-speech/${voiceId}`, {
|
|
5638
5935
|
method: "POST",
|
|
5639
5936
|
headers: {
|
|
@@ -5746,6 +6043,60 @@ var ElevenLabsProvider = class {
|
|
|
5746
6043
|
};
|
|
5747
6044
|
}
|
|
5748
6045
|
}
|
|
6046
|
+
/**
|
|
6047
|
+
* Generate music from text prompt
|
|
6048
|
+
*/
|
|
6049
|
+
async generateMusic(prompt2, options = {}) {
|
|
6050
|
+
if (!this.apiKey) {
|
|
6051
|
+
return {
|
|
6052
|
+
success: false,
|
|
6053
|
+
error: "ElevenLabs API key not configured"
|
|
6054
|
+
};
|
|
6055
|
+
}
|
|
6056
|
+
try {
|
|
6057
|
+
const body = {
|
|
6058
|
+
prompt: prompt2,
|
|
6059
|
+
model_id: "music_v1"
|
|
6060
|
+
};
|
|
6061
|
+
if (options.duration !== void 0) {
|
|
6062
|
+
const duration = Math.max(3, Math.min(600, options.duration));
|
|
6063
|
+
body.music_length_ms = duration * 1e3;
|
|
6064
|
+
}
|
|
6065
|
+
if (options.forceInstrumental !== void 0) {
|
|
6066
|
+
body.force_instrumental = options.forceInstrumental;
|
|
6067
|
+
}
|
|
6068
|
+
if (options.seed !== void 0) {
|
|
6069
|
+
body.seed = options.seed;
|
|
6070
|
+
}
|
|
6071
|
+
const response = await fetch(`${this.baseUrl}/music`, {
|
|
6072
|
+
method: "POST",
|
|
6073
|
+
headers: {
|
|
6074
|
+
"xi-api-key": this.apiKey,
|
|
6075
|
+
"Content-Type": "application/json",
|
|
6076
|
+
Accept: "audio/mpeg"
|
|
6077
|
+
},
|
|
6078
|
+
body: JSON.stringify(body)
|
|
6079
|
+
});
|
|
6080
|
+
if (!response.ok) {
|
|
6081
|
+
const error = await response.text();
|
|
6082
|
+
return {
|
|
6083
|
+
success: false,
|
|
6084
|
+
error: `Music generation failed: ${error}`
|
|
6085
|
+
};
|
|
6086
|
+
}
|
|
6087
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
6088
|
+
const audioBuffer = Buffer.from(arrayBuffer);
|
|
6089
|
+
return {
|
|
6090
|
+
success: true,
|
|
6091
|
+
audioBuffer
|
|
6092
|
+
};
|
|
6093
|
+
} catch (error) {
|
|
6094
|
+
return {
|
|
6095
|
+
success: false,
|
|
6096
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
6097
|
+
};
|
|
6098
|
+
}
|
|
6099
|
+
}
|
|
5749
6100
|
/**
|
|
5750
6101
|
* Isolate vocals from audio
|
|
5751
6102
|
* Separates the vocal track from the background music/noise
|
|
@@ -5896,7 +6247,7 @@ var OpenAIImageProvider = class {
|
|
|
5896
6247
|
this.id = "openai-image";
|
|
5897
6248
|
this.name = "OpenAI GPT Image";
|
|
5898
6249
|
this.description = "AI image generation with GPT Image 1.5 (fastest, best quality)";
|
|
5899
|
-
this.capabilities = ["text-to-image", "background-removal"];
|
|
6250
|
+
this.capabilities = ["text-to-image", "background-removal", "image-editing"];
|
|
5900
6251
|
this.iconUrl = "/icons/openai.svg";
|
|
5901
6252
|
this.isAvailable = true;
|
|
5902
6253
|
this.baseUrl = "https://api.openai.com/v1";
|
|
@@ -6026,6 +6377,76 @@ var OpenAIImageProvider = class {
|
|
|
6026
6377
|
quality: "high"
|
|
6027
6378
|
});
|
|
6028
6379
|
}
|
|
6380
|
+
/**
|
|
6381
|
+
* Edit images using GPT Image 1.5
|
|
6382
|
+
* Supports up to 16 input images with text instruction-based editing
|
|
6383
|
+
*/
|
|
6384
|
+
async editImage(imageBuffers, prompt2, options = {}) {
|
|
6385
|
+
if (!this.apiKey) {
|
|
6386
|
+
return {
|
|
6387
|
+
success: false,
|
|
6388
|
+
error: "OpenAI API key not configured"
|
|
6389
|
+
};
|
|
6390
|
+
}
|
|
6391
|
+
try {
|
|
6392
|
+
const formData = new FormData();
|
|
6393
|
+
formData.append("model", options.model || DEFAULT_MODEL);
|
|
6394
|
+
formData.append("prompt", prompt2);
|
|
6395
|
+
for (const buf of imageBuffers) {
|
|
6396
|
+
const uint8Array = new Uint8Array(buf);
|
|
6397
|
+
formData.append("image[]", new Blob([uint8Array], { type: "image/png" }), "image.png");
|
|
6398
|
+
}
|
|
6399
|
+
if (options.mask) {
|
|
6400
|
+
const maskUint8 = new Uint8Array(options.mask);
|
|
6401
|
+
formData.append("mask", new Blob([maskUint8], { type: "image/png" }), "mask.png");
|
|
6402
|
+
}
|
|
6403
|
+
if (options.quality) {
|
|
6404
|
+
formData.append("quality", options.quality);
|
|
6405
|
+
}
|
|
6406
|
+
if (options.size) {
|
|
6407
|
+
formData.append("size", options.size);
|
|
6408
|
+
}
|
|
6409
|
+
const response = await fetch(`${this.baseUrl}/images/edits`, {
|
|
6410
|
+
method: "POST",
|
|
6411
|
+
headers: {
|
|
6412
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
6413
|
+
},
|
|
6414
|
+
body: formData
|
|
6415
|
+
});
|
|
6416
|
+
if (!response.ok) {
|
|
6417
|
+
const errorText = await response.text();
|
|
6418
|
+
let errorMessage = `API error: ${response.status}`;
|
|
6419
|
+
try {
|
|
6420
|
+
const errorJson = JSON.parse(errorText);
|
|
6421
|
+
if (errorJson.error?.message) {
|
|
6422
|
+
errorMessage = errorJson.error.message;
|
|
6423
|
+
}
|
|
6424
|
+
} catch {
|
|
6425
|
+
if (errorText) {
|
|
6426
|
+
errorMessage = errorText.substring(0, 200);
|
|
6427
|
+
}
|
|
6428
|
+
}
|
|
6429
|
+
return {
|
|
6430
|
+
success: false,
|
|
6431
|
+
error: errorMessage
|
|
6432
|
+
};
|
|
6433
|
+
}
|
|
6434
|
+
const data = await response.json();
|
|
6435
|
+
return {
|
|
6436
|
+
success: true,
|
|
6437
|
+
images: data.data.map((img) => ({
|
|
6438
|
+
base64: img.b64_json,
|
|
6439
|
+
url: img.url,
|
|
6440
|
+
revisedPrompt: img.revised_prompt
|
|
6441
|
+
}))
|
|
6442
|
+
};
|
|
6443
|
+
} catch (error) {
|
|
6444
|
+
return {
|
|
6445
|
+
success: false,
|
|
6446
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
6447
|
+
};
|
|
6448
|
+
}
|
|
6449
|
+
}
|
|
6029
6450
|
/**
|
|
6030
6451
|
* Create image variations (uses DALL-E 2)
|
|
6031
6452
|
*/
|
|
@@ -6039,7 +6460,7 @@ var OpenAIImageProvider = class {
|
|
|
6039
6460
|
try {
|
|
6040
6461
|
const formData = new FormData();
|
|
6041
6462
|
const uint8Array = new Uint8Array(imageBuffer);
|
|
6042
|
-
formData.append("image", new Blob([uint8Array]), "image.png");
|
|
6463
|
+
formData.append("image", new Blob([uint8Array], { type: "image/png" }), "image.png");
|
|
6043
6464
|
formData.append("model", "dall-e-2");
|
|
6044
6465
|
formData.append("n", String(options.n || 1));
|
|
6045
6466
|
formData.append("size", options.size || "1024x1024");
|
|
@@ -6073,7 +6494,7 @@ var OpenAIImageProvider = class {
|
|
|
6073
6494
|
var openaiImageProvider = new OpenAIImageProvider();
|
|
6074
6495
|
|
|
6075
6496
|
// ../ai-providers/dist/runway/RunwayProvider.js
|
|
6076
|
-
var DEFAULT_MODEL2 = "
|
|
6497
|
+
var DEFAULT_MODEL2 = "gen4.5";
|
|
6077
6498
|
var RunwayProvider = class {
|
|
6078
6499
|
constructor() {
|
|
6079
6500
|
this.id = "runway";
|
|
@@ -6117,25 +6538,29 @@ var RunwayProvider = class {
|
|
|
6117
6538
|
};
|
|
6118
6539
|
const apiRatio = ratioMap[options?.aspectRatio || "16:9"] || "1280:720";
|
|
6119
6540
|
const model = options?.model || DEFAULT_MODEL2;
|
|
6120
|
-
if (!options?.referenceImage) {
|
|
6541
|
+
if (!options?.referenceImage && model !== "gen4.5") {
|
|
6121
6542
|
return {
|
|
6122
6543
|
id: "",
|
|
6123
6544
|
status: "failed",
|
|
6124
|
-
error:
|
|
6545
|
+
error: `Runway ${model} requires an input image. Use -i <image> or switch to gen4.5 for text-to-video.`
|
|
6125
6546
|
};
|
|
6126
6547
|
}
|
|
6127
|
-
const
|
|
6548
|
+
const hasImage = !!options?.referenceImage;
|
|
6549
|
+
const endpoint = hasImage ? "image_to_video" : "text_to_video";
|
|
6128
6550
|
const body = {
|
|
6129
6551
|
model,
|
|
6130
6552
|
promptText: prompt2,
|
|
6131
|
-
promptImage: imageData,
|
|
6132
6553
|
ratio: apiRatio,
|
|
6133
|
-
duration: options?.duration
|
|
6554
|
+
duration: this.clampDuration(options?.duration, model)
|
|
6134
6555
|
};
|
|
6556
|
+
if (hasImage) {
|
|
6557
|
+
const imageData = typeof options.referenceImage === "string" ? options.referenceImage : await this.blobToDataUri(options.referenceImage);
|
|
6558
|
+
body.promptImage = imageData;
|
|
6559
|
+
}
|
|
6135
6560
|
if (options?.seed !== void 0) {
|
|
6136
6561
|
body.seed = options.seed;
|
|
6137
6562
|
}
|
|
6138
|
-
const response = await fetch(`${this.baseUrl}
|
|
6563
|
+
const response = await fetch(`${this.baseUrl}/${endpoint}`, {
|
|
6139
6564
|
method: "POST",
|
|
6140
6565
|
headers: {
|
|
6141
6566
|
Authorization: `Bearer ${this.apiKey}`,
|
|
@@ -6302,6 +6727,16 @@ var RunwayProvider = class {
|
|
|
6302
6727
|
error: "Generation timed out"
|
|
6303
6728
|
};
|
|
6304
6729
|
}
|
|
6730
|
+
/**
|
|
6731
|
+
* Clamp duration to valid range for the given model
|
|
6732
|
+
*/
|
|
6733
|
+
clampDuration(duration, model) {
|
|
6734
|
+
if (model === "gen4.5") {
|
|
6735
|
+
const d = duration ?? 5;
|
|
6736
|
+
return Math.max(2, Math.min(10, Math.round(d)));
|
|
6737
|
+
}
|
|
6738
|
+
return duration === 10 ? 10 : 5;
|
|
6739
|
+
}
|
|
6305
6740
|
/**
|
|
6306
6741
|
* Convert Blob to data URI
|
|
6307
6742
|
*/
|
|
@@ -6315,7 +6750,7 @@ var RunwayProvider = class {
|
|
|
6315
6750
|
* Sleep helper
|
|
6316
6751
|
*/
|
|
6317
6752
|
sleep(ms) {
|
|
6318
|
-
return new Promise((
|
|
6753
|
+
return new Promise((resolve12) => setTimeout(resolve12, ms));
|
|
6319
6754
|
}
|
|
6320
6755
|
};
|
|
6321
6756
|
var runwayProvider = new RunwayProvider();
|
|
@@ -6323,7 +6758,7 @@ var runwayProvider = new RunwayProvider();
|
|
|
6323
6758
|
// ../ai-providers/dist/kling/KlingProvider.js
|
|
6324
6759
|
import { createHmac } from "node:crypto";
|
|
6325
6760
|
var DEFAULT_MODEL3 = "kling-v2-5-turbo";
|
|
6326
|
-
var STD_MODE_MODELS = ["kling-v2-5-turbo", "kling-v2-6"];
|
|
6761
|
+
var STD_MODE_MODELS = ["kling-v2-5-turbo", "kling-v2-6", "kling-v3", "kling-v3-omni"];
|
|
6327
6762
|
var KlingProvider = class {
|
|
6328
6763
|
constructor() {
|
|
6329
6764
|
this.id = "kling";
|
|
@@ -6419,14 +6854,14 @@ var KlingProvider = class {
|
|
|
6419
6854
|
return {
|
|
6420
6855
|
id: "",
|
|
6421
6856
|
status: "failed",
|
|
6422
|
-
error: "Kling v2.5
|
|
6857
|
+
error: "Kling v2.5+ requires image URL, not base64. Upload image to a hosting service first."
|
|
6423
6858
|
};
|
|
6424
6859
|
}
|
|
6425
6860
|
} else {
|
|
6426
6861
|
return {
|
|
6427
6862
|
id: "",
|
|
6428
6863
|
status: "failed",
|
|
6429
|
-
error: "Kling v2.5
|
|
6864
|
+
error: "Kling v2.5+ requires image URL, not Blob. Upload image to a hosting service first."
|
|
6430
6865
|
};
|
|
6431
6866
|
}
|
|
6432
6867
|
const response2 = await fetch(`${this.baseUrl}/videos/image2video`, {
|
|
@@ -6731,7 +7166,7 @@ var KlingProvider = class {
|
|
|
6731
7166
|
* Sleep helper
|
|
6732
7167
|
*/
|
|
6733
7168
|
sleep(ms) {
|
|
6734
|
-
return new Promise((
|
|
7169
|
+
return new Promise((resolve12) => setTimeout(resolve12, ms));
|
|
6735
7170
|
}
|
|
6736
7171
|
};
|
|
6737
7172
|
var klingProvider = new KlingProvider();
|
|
@@ -6743,7 +7178,7 @@ var GrokProvider = class {
|
|
|
6743
7178
|
this.id = "grok";
|
|
6744
7179
|
this.name = "xAI Grok Imagine";
|
|
6745
7180
|
this.description = "AI video generation with Grok Imagine (native audio, 1-15 sec)";
|
|
6746
|
-
this.capabilities = ["text-to-video", "image-to-video"];
|
|
7181
|
+
this.capabilities = ["text-to-video", "image-to-video", "text-to-image", "image-editing"];
|
|
6747
7182
|
this.iconUrl = "/icons/xai.svg";
|
|
6748
7183
|
this.isAvailable = true;
|
|
6749
7184
|
this.baseUrl = "https://api.x.ai/v1";
|
|
@@ -6759,33 +7194,29 @@ var GrokProvider = class {
|
|
|
6759
7194
|
return !!this.apiKey;
|
|
6760
7195
|
}
|
|
6761
7196
|
/**
|
|
6762
|
-
* Generate
|
|
7197
|
+
* Generate image using Grok Imagine
|
|
6763
7198
|
*/
|
|
6764
|
-
async
|
|
7199
|
+
async generateImage(prompt2, options = {}) {
|
|
6765
7200
|
if (!this.apiKey) {
|
|
6766
7201
|
return {
|
|
6767
|
-
|
|
6768
|
-
status: "failed",
|
|
7202
|
+
success: false,
|
|
6769
7203
|
error: "xAI API key not configured. Set XAI_API_KEY environment variable."
|
|
6770
7204
|
};
|
|
6771
7205
|
}
|
|
6772
7206
|
try {
|
|
6773
|
-
const duration = Math.min(15, Math.max(1, options?.duration || 5));
|
|
6774
7207
|
const body = {
|
|
6775
|
-
model:
|
|
7208
|
+
model: options.model || "grok-imagine-image",
|
|
6776
7209
|
prompt: prompt2,
|
|
6777
|
-
|
|
6778
|
-
|
|
7210
|
+
n: options.n || 1,
|
|
7211
|
+
response_format: options.responseFormat || "url"
|
|
6779
7212
|
};
|
|
6780
|
-
if (options
|
|
6781
|
-
|
|
6782
|
-
if (imageData.startsWith("http")) {
|
|
6783
|
-
body.image_url = imageData;
|
|
6784
|
-
} else if (imageData.startsWith("data:")) {
|
|
6785
|
-
body.image = imageData;
|
|
6786
|
-
}
|
|
7213
|
+
if (options.aspectRatio) {
|
|
7214
|
+
body.aspect_ratio = options.aspectRatio;
|
|
6787
7215
|
}
|
|
6788
|
-
|
|
7216
|
+
if (options.resolution) {
|
|
7217
|
+
body.resolution = options.resolution;
|
|
7218
|
+
}
|
|
7219
|
+
const response = await fetch(`${this.baseUrl}/images/generations`, {
|
|
6789
7220
|
method: "POST",
|
|
6790
7221
|
headers: {
|
|
6791
7222
|
"Content-Type": "application/json",
|
|
@@ -6795,210 +7226,29 @@ var GrokProvider = class {
|
|
|
6795
7226
|
});
|
|
6796
7227
|
if (!response.ok) {
|
|
6797
7228
|
const errorText = await response.text();
|
|
6798
|
-
|
|
6799
|
-
id: "",
|
|
6800
|
-
status: "failed",
|
|
6801
|
-
error: `Grok API error (${response.status}): ${errorText}`
|
|
6802
|
-
};
|
|
6803
|
-
}
|
|
6804
|
-
const data = await response.json();
|
|
6805
|
-
return {
|
|
6806
|
-
id: data.id,
|
|
6807
|
-
status: data.status === "completed" ? "completed" : "pending",
|
|
6808
|
-
videoUrl: data.video_url
|
|
6809
|
-
};
|
|
6810
|
-
} catch (error) {
|
|
6811
|
-
return {
|
|
6812
|
-
id: "",
|
|
6813
|
-
status: "failed",
|
|
6814
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
6815
|
-
};
|
|
6816
|
-
}
|
|
6817
|
-
}
|
|
6818
|
-
/**
|
|
6819
|
-
* Get generation status
|
|
6820
|
-
*/
|
|
6821
|
-
async getGenerationStatus(id) {
|
|
6822
|
-
if (!this.apiKey) {
|
|
6823
|
-
return {
|
|
6824
|
-
id,
|
|
6825
|
-
status: "failed",
|
|
6826
|
-
error: "xAI API key not configured"
|
|
6827
|
-
};
|
|
6828
|
-
}
|
|
6829
|
-
try {
|
|
6830
|
-
const response = await fetch(`${this.baseUrl}/video/generations/${id}`, {
|
|
6831
|
-
headers: {
|
|
6832
|
-
Authorization: `Bearer ${this.apiKey}`
|
|
6833
|
-
}
|
|
6834
|
-
});
|
|
6835
|
-
if (!response.ok) {
|
|
6836
|
-
const errorText = await response.text();
|
|
6837
|
-
return {
|
|
6838
|
-
id,
|
|
6839
|
-
status: "failed",
|
|
6840
|
-
error: `Failed to get status: ${errorText}`
|
|
6841
|
-
};
|
|
6842
|
-
}
|
|
6843
|
-
const data = await response.json();
|
|
6844
|
-
const statusMap = {
|
|
6845
|
-
pending: "pending",
|
|
6846
|
-
processing: "processing",
|
|
6847
|
-
completed: "completed",
|
|
6848
|
-
failed: "failed"
|
|
6849
|
-
};
|
|
6850
|
-
return {
|
|
6851
|
-
id: data.id,
|
|
6852
|
-
status: statusMap[data.status] || "pending",
|
|
6853
|
-
videoUrl: data.video_url,
|
|
6854
|
-
error: data.error
|
|
6855
|
-
};
|
|
6856
|
-
} catch (error) {
|
|
6857
|
-
return {
|
|
6858
|
-
id,
|
|
6859
|
-
status: "failed",
|
|
6860
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
6861
|
-
};
|
|
6862
|
-
}
|
|
6863
|
-
}
|
|
6864
|
-
/**
|
|
6865
|
-
* Wait for generation to complete
|
|
6866
|
-
*/
|
|
6867
|
-
async waitForCompletion(id, onProgress, maxWaitMs = 3e5) {
|
|
6868
|
-
const startTime = Date.now();
|
|
6869
|
-
while (Date.now() - startTime < maxWaitMs) {
|
|
6870
|
-
const result = await this.getGenerationStatus(id);
|
|
6871
|
-
if (onProgress) {
|
|
6872
|
-
onProgress(result);
|
|
6873
|
-
}
|
|
6874
|
-
if (result.status === "completed" || result.status === "failed") {
|
|
6875
|
-
return result;
|
|
6876
|
-
}
|
|
6877
|
-
await this.sleep(this.pollingInterval);
|
|
6878
|
-
}
|
|
6879
|
-
return {
|
|
6880
|
-
id,
|
|
6881
|
-
status: "failed",
|
|
6882
|
-
error: "Generation timed out"
|
|
6883
|
-
};
|
|
6884
|
-
}
|
|
6885
|
-
/**
|
|
6886
|
-
* Cancel generation (if supported)
|
|
6887
|
-
*/
|
|
6888
|
-
async cancelGeneration(id) {
|
|
6889
|
-
if (!this.apiKey)
|
|
6890
|
-
return false;
|
|
6891
|
-
try {
|
|
6892
|
-
const response = await fetch(`${this.baseUrl}/video/generations/${id}`, {
|
|
6893
|
-
method: "DELETE",
|
|
6894
|
-
headers: {
|
|
6895
|
-
Authorization: `Bearer ${this.apiKey}`
|
|
6896
|
-
}
|
|
6897
|
-
});
|
|
6898
|
-
return response.ok;
|
|
6899
|
-
} catch {
|
|
6900
|
-
return false;
|
|
6901
|
-
}
|
|
6902
|
-
}
|
|
6903
|
-
sleep(ms) {
|
|
6904
|
-
return new Promise((resolve13) => setTimeout(resolve13, ms));
|
|
6905
|
-
}
|
|
6906
|
-
};
|
|
6907
|
-
var grokProvider = new GrokProvider();
|
|
6908
|
-
|
|
6909
|
-
// ../ai-providers/dist/stability/StabilityProvider.js
|
|
6910
|
-
var StabilityProvider = class {
|
|
6911
|
-
constructor() {
|
|
6912
|
-
this.id = "stability";
|
|
6913
|
-
this.name = "Stability AI";
|
|
6914
|
-
this.description = "Stable Diffusion image generation with SD3.5 and SDXL";
|
|
6915
|
-
this.capabilities = ["background-removal", "upscale", "search-replace", "outpaint"];
|
|
6916
|
-
this.iconUrl = "/icons/stability.svg";
|
|
6917
|
-
this.isAvailable = true;
|
|
6918
|
-
this.baseUrl = "https://api.stability.ai";
|
|
6919
|
-
this.modelMapping = {
|
|
6920
|
-
"sd3.5-large": ["sd3", "sd3.5-large"],
|
|
6921
|
-
"sd3.5-large-turbo": ["sd3", "sd3.5-large-turbo"],
|
|
6922
|
-
"sd3.5-medium": ["sd3", "sd3.5-medium"],
|
|
6923
|
-
"sd3-large": ["sd3", "sd3-large"],
|
|
6924
|
-
"sd3-medium": ["sd3", "sd3-medium"],
|
|
6925
|
-
"stable-image-core": ["core", null],
|
|
6926
|
-
"stable-image-ultra": ["ultra", null]
|
|
6927
|
-
};
|
|
6928
|
-
}
|
|
6929
|
-
async initialize(config2) {
|
|
6930
|
-
this.apiKey = config2.apiKey;
|
|
6931
|
-
if (config2.baseUrl) {
|
|
6932
|
-
this.baseUrl = config2.baseUrl;
|
|
6933
|
-
}
|
|
6934
|
-
}
|
|
6935
|
-
isConfigured() {
|
|
6936
|
-
return !!this.apiKey;
|
|
6937
|
-
}
|
|
6938
|
-
/**
|
|
6939
|
-
* Generate images from text prompt using SD3.5 or newer models
|
|
6940
|
-
*/
|
|
6941
|
-
async generateImage(prompt2, options = {}) {
|
|
6942
|
-
if (!this.apiKey) {
|
|
6943
|
-
return {
|
|
6944
|
-
success: false,
|
|
6945
|
-
error: "Stability AI API key not configured"
|
|
6946
|
-
};
|
|
6947
|
-
}
|
|
6948
|
-
try {
|
|
6949
|
-
const model = options.model || "sd3.5-large";
|
|
6950
|
-
const [endpoint, modelParam] = this.modelMapping[model] || ["sd3", model];
|
|
6951
|
-
const formData = new FormData();
|
|
6952
|
-
formData.append("prompt", prompt2);
|
|
6953
|
-
formData.append("output_format", options.outputFormat || "png");
|
|
6954
|
-
if (modelParam) {
|
|
6955
|
-
formData.append("model", modelParam);
|
|
6956
|
-
}
|
|
6957
|
-
if (options.negativePrompt) {
|
|
6958
|
-
formData.append("negative_prompt", options.negativePrompt);
|
|
6959
|
-
}
|
|
6960
|
-
if (options.aspectRatio) {
|
|
6961
|
-
formData.append("aspect_ratio", options.aspectRatio);
|
|
6962
|
-
}
|
|
6963
|
-
if (options.seed !== void 0) {
|
|
6964
|
-
formData.append("seed", String(options.seed));
|
|
6965
|
-
}
|
|
6966
|
-
if (options.stylePreset) {
|
|
6967
|
-
formData.append("style_preset", options.stylePreset);
|
|
6968
|
-
}
|
|
6969
|
-
const response = await fetch(`${this.baseUrl}/v2beta/stable-image/generate/${endpoint}`, {
|
|
6970
|
-
method: "POST",
|
|
6971
|
-
headers: {
|
|
6972
|
-
Authorization: `Bearer ${this.apiKey}`,
|
|
6973
|
-
Accept: "image/*"
|
|
6974
|
-
},
|
|
6975
|
-
body: formData
|
|
6976
|
-
});
|
|
6977
|
-
if (!response.ok) {
|
|
6978
|
-
const errorText = await response.text();
|
|
6979
|
-
let errorMessage;
|
|
7229
|
+
let errorMessage = `API error: ${response.status}`;
|
|
6980
7230
|
try {
|
|
6981
|
-
const
|
|
6982
|
-
|
|
7231
|
+
const errorJson = JSON.parse(errorText);
|
|
7232
|
+
if (errorJson.error?.message) {
|
|
7233
|
+
errorMessage = errorJson.error.message;
|
|
7234
|
+
}
|
|
6983
7235
|
} catch {
|
|
6984
|
-
|
|
7236
|
+
if (errorText) {
|
|
7237
|
+
errorMessage = errorText.substring(0, 200);
|
|
7238
|
+
}
|
|
6985
7239
|
}
|
|
6986
7240
|
return {
|
|
6987
7241
|
success: false,
|
|
6988
|
-
error:
|
|
7242
|
+
error: errorMessage
|
|
6989
7243
|
};
|
|
6990
7244
|
}
|
|
6991
|
-
const
|
|
6992
|
-
const base64 = Buffer.from(arrayBuffer).toString("base64");
|
|
6993
|
-
const seed = response.headers.get("seed");
|
|
6994
|
-
const finishReason = response.headers.get("finish-reason");
|
|
7245
|
+
const data = await response.json();
|
|
6995
7246
|
return {
|
|
6996
7247
|
success: true,
|
|
6997
|
-
images:
|
|
6998
|
-
|
|
6999
|
-
|
|
7000
|
-
|
|
7001
|
-
}]
|
|
7248
|
+
images: data.data.map((img) => ({
|
|
7249
|
+
url: img.url,
|
|
7250
|
+
base64: img.b64_json
|
|
7251
|
+
}))
|
|
7002
7252
|
};
|
|
7003
7253
|
} catch (error) {
|
|
7004
7254
|
return {
|
|
@@ -7008,77 +7258,64 @@ var StabilityProvider = class {
|
|
|
7008
7258
|
}
|
|
7009
7259
|
}
|
|
7010
7260
|
/**
|
|
7011
|
-
*
|
|
7261
|
+
* Edit image using Grok Imagine
|
|
7262
|
+
* Supports single image input with text instruction-based editing
|
|
7012
7263
|
*/
|
|
7013
|
-
async
|
|
7264
|
+
async editImage(imageBuffer, prompt2, options = {}) {
|
|
7014
7265
|
if (!this.apiKey) {
|
|
7015
7266
|
return {
|
|
7016
7267
|
success: false,
|
|
7017
|
-
error: "
|
|
7268
|
+
error: "xAI API key not configured. Set XAI_API_KEY environment variable."
|
|
7018
7269
|
};
|
|
7019
7270
|
}
|
|
7020
7271
|
try {
|
|
7272
|
+
const base64 = imageBuffer.toString("base64");
|
|
7273
|
+
const dataUri = `data:image/png;base64,${base64}`;
|
|
7021
7274
|
const body = {
|
|
7022
|
-
|
|
7023
|
-
|
|
7024
|
-
|
|
7025
|
-
|
|
7026
|
-
|
|
7027
|
-
|
|
7028
|
-
|
|
7029
|
-
|
|
7030
|
-
|
|
7031
|
-
|
|
7032
|
-
|
|
7033
|
-
});
|
|
7034
|
-
}
|
|
7035
|
-
if (options.seed !== void 0) {
|
|
7036
|
-
body.seed = options.seed;
|
|
7037
|
-
}
|
|
7038
|
-
if (options.sampler) {
|
|
7039
|
-
body.sampler = options.sampler;
|
|
7040
|
-
}
|
|
7041
|
-
if (options.stylePreset) {
|
|
7042
|
-
body.style_preset = options.stylePreset;
|
|
7043
|
-
}
|
|
7044
|
-
if (options.size) {
|
|
7045
|
-
const [width, height] = options.size.split("x").map(Number);
|
|
7046
|
-
body.width = width;
|
|
7047
|
-
body.height = height;
|
|
7048
|
-
} else {
|
|
7049
|
-
body.width = 1024;
|
|
7050
|
-
body.height = 1024;
|
|
7275
|
+
model: options.model || "grok-imagine-image",
|
|
7276
|
+
prompt: prompt2,
|
|
7277
|
+
image: {
|
|
7278
|
+
url: dataUri,
|
|
7279
|
+
type: "image_url"
|
|
7280
|
+
},
|
|
7281
|
+
n: 1,
|
|
7282
|
+
response_format: options.responseFormat || "url"
|
|
7283
|
+
};
|
|
7284
|
+
if (options.aspectRatio) {
|
|
7285
|
+
body.aspect_ratio = options.aspectRatio;
|
|
7051
7286
|
}
|
|
7052
|
-
const response = await fetch(`${this.baseUrl}/
|
|
7287
|
+
const response = await fetch(`${this.baseUrl}/images/edits`, {
|
|
7053
7288
|
method: "POST",
|
|
7054
7289
|
headers: {
|
|
7055
7290
|
"Content-Type": "application/json",
|
|
7056
|
-
Authorization: `Bearer ${this.apiKey}
|
|
7057
|
-
Accept: "application/json"
|
|
7291
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
7058
7292
|
},
|
|
7059
7293
|
body: JSON.stringify(body)
|
|
7060
7294
|
});
|
|
7061
7295
|
if (!response.ok) {
|
|
7062
7296
|
const errorText = await response.text();
|
|
7063
|
-
let errorMessage
|
|
7297
|
+
let errorMessage = `API error: ${response.status}`;
|
|
7064
7298
|
try {
|
|
7065
|
-
const
|
|
7066
|
-
|
|
7299
|
+
const errorJson = JSON.parse(errorText);
|
|
7300
|
+
if (errorJson.error?.message) {
|
|
7301
|
+
errorMessage = errorJson.error.message;
|
|
7302
|
+
}
|
|
7067
7303
|
} catch {
|
|
7068
|
-
|
|
7304
|
+
if (errorText) {
|
|
7305
|
+
errorMessage = errorText.substring(0, 200);
|
|
7306
|
+
}
|
|
7069
7307
|
}
|
|
7070
7308
|
return {
|
|
7071
7309
|
success: false,
|
|
7072
|
-
error:
|
|
7310
|
+
error: errorMessage
|
|
7073
7311
|
};
|
|
7074
7312
|
}
|
|
7075
7313
|
const data = await response.json();
|
|
7076
7314
|
return {
|
|
7077
7315
|
success: true,
|
|
7078
|
-
images: data.
|
|
7079
|
-
|
|
7080
|
-
|
|
7081
|
-
finishReason: a.finishReason
|
|
7316
|
+
images: data.data.map((img) => ({
|
|
7317
|
+
url: img.url,
|
|
7318
|
+
base64: img.b64_json
|
|
7082
7319
|
}))
|
|
7083
7320
|
};
|
|
7084
7321
|
} catch (error) {
|
|
@@ -7089,350 +7326,145 @@ var StabilityProvider = class {
|
|
|
7089
7326
|
}
|
|
7090
7327
|
}
|
|
7091
7328
|
/**
|
|
7092
|
-
*
|
|
7093
|
-
*/
|
|
7094
|
-
async imageToImage(imageData, prompt2, options = {}) {
|
|
7095
|
-
if (!this.apiKey) {
|
|
7096
|
-
return {
|
|
7097
|
-
success: false,
|
|
7098
|
-
error: "Stability AI API key not configured"
|
|
7099
|
-
};
|
|
7100
|
-
}
|
|
7101
|
-
try {
|
|
7102
|
-
const formData = new FormData();
|
|
7103
|
-
const imageBlob = Buffer.isBuffer(imageData) ? new Blob([new Uint8Array(imageData)]) : imageData;
|
|
7104
|
-
formData.append("image", imageBlob, "image.png");
|
|
7105
|
-
formData.append("prompt", prompt2);
|
|
7106
|
-
formData.append("output_format", options.outputFormat || "png");
|
|
7107
|
-
formData.append("strength", String(options.strength || 0.35));
|
|
7108
|
-
formData.append("mode", options.mode || "image-to-image");
|
|
7109
|
-
if (options.negativePrompt) {
|
|
7110
|
-
formData.append("negative_prompt", options.negativePrompt);
|
|
7111
|
-
}
|
|
7112
|
-
if (options.seed !== void 0) {
|
|
7113
|
-
formData.append("seed", String(options.seed));
|
|
7114
|
-
}
|
|
7115
|
-
const response = await fetch(`${this.baseUrl}/v2beta/stable-image/generate/sd3`, {
|
|
7116
|
-
method: "POST",
|
|
7117
|
-
headers: {
|
|
7118
|
-
Authorization: `Bearer ${this.apiKey}`,
|
|
7119
|
-
Accept: "image/*"
|
|
7120
|
-
},
|
|
7121
|
-
body: formData
|
|
7122
|
-
});
|
|
7123
|
-
if (!response.ok) {
|
|
7124
|
-
const errorText = await response.text();
|
|
7125
|
-
return {
|
|
7126
|
-
success: false,
|
|
7127
|
-
error: `API error (${response.status}): ${errorText}`
|
|
7128
|
-
};
|
|
7129
|
-
}
|
|
7130
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
7131
|
-
const base64 = Buffer.from(arrayBuffer).toString("base64");
|
|
7132
|
-
return {
|
|
7133
|
-
success: true,
|
|
7134
|
-
images: [{ base64 }]
|
|
7135
|
-
};
|
|
7136
|
-
} catch (error) {
|
|
7137
|
-
return {
|
|
7138
|
-
success: false,
|
|
7139
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
7140
|
-
};
|
|
7141
|
-
}
|
|
7142
|
-
}
|
|
7143
|
-
/**
|
|
7144
|
-
* Upscale image
|
|
7145
|
-
*/
|
|
7146
|
-
async upscaleImage(imageData, options = {}) {
|
|
7147
|
-
if (!this.apiKey) {
|
|
7148
|
-
return {
|
|
7149
|
-
success: false,
|
|
7150
|
-
error: "Stability AI API key not configured"
|
|
7151
|
-
};
|
|
7152
|
-
}
|
|
7153
|
-
try {
|
|
7154
|
-
const formData = new FormData();
|
|
7155
|
-
const imageBlob = Buffer.isBuffer(imageData) ? new Blob([new Uint8Array(imageData)]) : imageData;
|
|
7156
|
-
formData.append("image", imageBlob, "image.png");
|
|
7157
|
-
formData.append("output_format", options.outputFormat || "png");
|
|
7158
|
-
const upscaleType = options.type || "fast";
|
|
7159
|
-
let endpoint;
|
|
7160
|
-
if (upscaleType === "creative") {
|
|
7161
|
-
endpoint = `${this.baseUrl}/v2beta/stable-image/upscale/creative`;
|
|
7162
|
-
if (options.creativity !== void 0) {
|
|
7163
|
-
formData.append("creativity", String(options.creativity));
|
|
7164
|
-
}
|
|
7165
|
-
} else if (upscaleType === "conservative") {
|
|
7166
|
-
endpoint = `${this.baseUrl}/v2beta/stable-image/upscale/conservative`;
|
|
7167
|
-
} else {
|
|
7168
|
-
endpoint = `${this.baseUrl}/v2beta/stable-image/upscale/fast`;
|
|
7169
|
-
}
|
|
7170
|
-
const response = await fetch(endpoint, {
|
|
7171
|
-
method: "POST",
|
|
7172
|
-
headers: {
|
|
7173
|
-
Authorization: `Bearer ${this.apiKey}`,
|
|
7174
|
-
Accept: "image/*"
|
|
7175
|
-
},
|
|
7176
|
-
body: formData
|
|
7177
|
-
});
|
|
7178
|
-
if (!response.ok) {
|
|
7179
|
-
const errorText = await response.text();
|
|
7180
|
-
return {
|
|
7181
|
-
success: false,
|
|
7182
|
-
error: `API error (${response.status}): ${errorText}`
|
|
7183
|
-
};
|
|
7184
|
-
}
|
|
7185
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
7186
|
-
const base64 = Buffer.from(arrayBuffer).toString("base64");
|
|
7187
|
-
return {
|
|
7188
|
-
success: true,
|
|
7189
|
-
images: [{ base64 }]
|
|
7190
|
-
};
|
|
7191
|
-
} catch (error) {
|
|
7192
|
-
return {
|
|
7193
|
-
success: false,
|
|
7194
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
7195
|
-
};
|
|
7196
|
-
}
|
|
7197
|
-
}
|
|
7198
|
-
/**
|
|
7199
|
-
* Remove background from image
|
|
7200
|
-
*/
|
|
7201
|
-
async removeBackground(imageData, outputFormat = "png") {
|
|
7202
|
-
if (!this.apiKey) {
|
|
7203
|
-
return {
|
|
7204
|
-
success: false,
|
|
7205
|
-
error: "Stability AI API key not configured"
|
|
7206
|
-
};
|
|
7207
|
-
}
|
|
7208
|
-
try {
|
|
7209
|
-
const formData = new FormData();
|
|
7210
|
-
const imageBlob = Buffer.isBuffer(imageData) ? new Blob([new Uint8Array(imageData)]) : imageData;
|
|
7211
|
-
formData.append("image", imageBlob, "image.png");
|
|
7212
|
-
formData.append("output_format", outputFormat);
|
|
7213
|
-
const response = await fetch(`${this.baseUrl}/v2beta/stable-image/edit/remove-background`, {
|
|
7214
|
-
method: "POST",
|
|
7215
|
-
headers: {
|
|
7216
|
-
Authorization: `Bearer ${this.apiKey}`,
|
|
7217
|
-
Accept: "image/*"
|
|
7218
|
-
},
|
|
7219
|
-
body: formData
|
|
7220
|
-
});
|
|
7221
|
-
if (!response.ok) {
|
|
7222
|
-
const errorText = await response.text();
|
|
7223
|
-
return {
|
|
7224
|
-
success: false,
|
|
7225
|
-
error: `API error (${response.status}): ${errorText}`
|
|
7226
|
-
};
|
|
7227
|
-
}
|
|
7228
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
7229
|
-
const base64 = Buffer.from(arrayBuffer).toString("base64");
|
|
7230
|
-
return {
|
|
7231
|
-
success: true,
|
|
7232
|
-
images: [{ base64 }]
|
|
7233
|
-
};
|
|
7234
|
-
} catch (error) {
|
|
7235
|
-
return {
|
|
7236
|
-
success: false,
|
|
7237
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
7238
|
-
};
|
|
7239
|
-
}
|
|
7240
|
-
}
|
|
7241
|
-
/**
|
|
7242
|
-
* Inpaint/outpaint image
|
|
7329
|
+
* Generate video using Grok Imagine
|
|
7243
7330
|
*/
|
|
7244
|
-
async
|
|
7331
|
+
async generateVideo(prompt2, options) {
|
|
7245
7332
|
if (!this.apiKey) {
|
|
7246
7333
|
return {
|
|
7247
|
-
|
|
7248
|
-
|
|
7334
|
+
id: "",
|
|
7335
|
+
status: "failed",
|
|
7336
|
+
error: "xAI API key not configured. Set XAI_API_KEY environment variable."
|
|
7249
7337
|
};
|
|
7250
7338
|
}
|
|
7251
7339
|
try {
|
|
7252
|
-
const
|
|
7253
|
-
const
|
|
7254
|
-
|
|
7255
|
-
|
|
7256
|
-
|
|
7257
|
-
|
|
7258
|
-
formData.append("output_format", options.outputFormat || "png");
|
|
7259
|
-
if (options.negativePrompt) {
|
|
7260
|
-
formData.append("negative_prompt", options.negativePrompt);
|
|
7261
|
-
}
|
|
7262
|
-
if (options.seed !== void 0) {
|
|
7263
|
-
formData.append("seed", String(options.seed));
|
|
7264
|
-
}
|
|
7265
|
-
const response = await fetch(`${this.baseUrl}/v2beta/stable-image/edit/inpaint`, {
|
|
7266
|
-
method: "POST",
|
|
7267
|
-
headers: {
|
|
7268
|
-
Authorization: `Bearer ${this.apiKey}`,
|
|
7269
|
-
Accept: "image/*"
|
|
7270
|
-
},
|
|
7271
|
-
body: formData
|
|
7272
|
-
});
|
|
7273
|
-
if (!response.ok) {
|
|
7274
|
-
const errorText = await response.text();
|
|
7275
|
-
return {
|
|
7276
|
-
success: false,
|
|
7277
|
-
error: `API error (${response.status}): ${errorText}`
|
|
7278
|
-
};
|
|
7279
|
-
}
|
|
7280
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
7281
|
-
const base64 = Buffer.from(arrayBuffer).toString("base64");
|
|
7282
|
-
return {
|
|
7283
|
-
success: true,
|
|
7284
|
-
images: [{ base64 }]
|
|
7285
|
-
};
|
|
7286
|
-
} catch (error) {
|
|
7287
|
-
return {
|
|
7288
|
-
success: false,
|
|
7289
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
7290
|
-
};
|
|
7291
|
-
}
|
|
7292
|
-
}
|
|
7293
|
-
/**
|
|
7294
|
-
* Search and replace objects in an image using AI
|
|
7295
|
-
*/
|
|
7296
|
-
async searchAndReplace(imageData, searchPrompt, replacePrompt, options = {}) {
|
|
7297
|
-
if (!this.apiKey) {
|
|
7298
|
-
return {
|
|
7299
|
-
success: false,
|
|
7300
|
-
error: "Stability AI API key not configured"
|
|
7340
|
+
const duration = Math.min(15, Math.max(1, options?.duration || 5));
|
|
7341
|
+
const body = {
|
|
7342
|
+
model: DEFAULT_MODEL4,
|
|
7343
|
+
prompt: prompt2,
|
|
7344
|
+
duration,
|
|
7345
|
+
aspect_ratio: options?.aspectRatio || "16:9"
|
|
7301
7346
|
};
|
|
7302
|
-
|
|
7303
|
-
|
|
7304
|
-
const formData = new FormData();
|
|
7305
|
-
const imageBlob = Buffer.isBuffer(imageData) ? new Blob([new Uint8Array(imageData)]) : imageData;
|
|
7306
|
-
formData.append("image", imageBlob, "image.png");
|
|
7307
|
-
formData.append("prompt", replacePrompt);
|
|
7308
|
-
formData.append("search_prompt", searchPrompt);
|
|
7309
|
-
formData.append("output_format", options.outputFormat || "png");
|
|
7310
|
-
if (options.negativePrompt) {
|
|
7311
|
-
formData.append("negative_prompt", options.negativePrompt);
|
|
7312
|
-
}
|
|
7313
|
-
if (options.seed !== void 0) {
|
|
7314
|
-
formData.append("seed", String(options.seed));
|
|
7347
|
+
if (options?.referenceImage) {
|
|
7348
|
+
body.image = { url: options.referenceImage };
|
|
7315
7349
|
}
|
|
7316
|
-
const response = await fetch(`${this.baseUrl}/
|
|
7350
|
+
const response = await fetch(`${this.baseUrl}/videos/generations`, {
|
|
7317
7351
|
method: "POST",
|
|
7318
7352
|
headers: {
|
|
7319
|
-
|
|
7320
|
-
|
|
7353
|
+
"Content-Type": "application/json",
|
|
7354
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
7321
7355
|
},
|
|
7322
|
-
body:
|
|
7356
|
+
body: JSON.stringify(body)
|
|
7323
7357
|
});
|
|
7324
7358
|
if (!response.ok) {
|
|
7325
7359
|
const errorText = await response.text();
|
|
7326
|
-
let errorMessage;
|
|
7327
|
-
try {
|
|
7328
|
-
const errorData = JSON.parse(errorText);
|
|
7329
|
-
errorMessage = errorData.message || errorData.error || errorText;
|
|
7330
|
-
} catch {
|
|
7331
|
-
errorMessage = errorText;
|
|
7332
|
-
}
|
|
7333
7360
|
return {
|
|
7334
|
-
|
|
7335
|
-
|
|
7361
|
+
id: "",
|
|
7362
|
+
status: "failed",
|
|
7363
|
+
error: `Grok API error (${response.status}): ${errorText}`
|
|
7336
7364
|
};
|
|
7337
7365
|
}
|
|
7338
|
-
const
|
|
7339
|
-
const base64 = Buffer.from(arrayBuffer).toString("base64");
|
|
7340
|
-
const seed = response.headers.get("seed");
|
|
7366
|
+
const data = await response.json();
|
|
7341
7367
|
return {
|
|
7342
|
-
|
|
7343
|
-
|
|
7344
|
-
base64,
|
|
7345
|
-
seed: seed ? parseInt(seed) : void 0
|
|
7346
|
-
}]
|
|
7368
|
+
id: data.request_id,
|
|
7369
|
+
status: "pending"
|
|
7347
7370
|
};
|
|
7348
7371
|
} catch (error) {
|
|
7349
7372
|
return {
|
|
7350
|
-
|
|
7373
|
+
id: "",
|
|
7374
|
+
status: "failed",
|
|
7351
7375
|
error: error instanceof Error ? error.message : "Unknown error"
|
|
7352
7376
|
};
|
|
7353
7377
|
}
|
|
7354
7378
|
}
|
|
7355
7379
|
/**
|
|
7356
|
-
*
|
|
7380
|
+
* Get generation status
|
|
7357
7381
|
*/
|
|
7358
|
-
async
|
|
7382
|
+
async getGenerationStatus(id) {
|
|
7359
7383
|
if (!this.apiKey) {
|
|
7360
7384
|
return {
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
}
|
|
7365
|
-
const { left = 0, right = 0, up = 0, down = 0 } = options;
|
|
7366
|
-
if (left === 0 && right === 0 && up === 0 && down === 0) {
|
|
7367
|
-
return {
|
|
7368
|
-
success: false,
|
|
7369
|
-
error: "At least one direction (left, right, up, down) must be specified"
|
|
7385
|
+
id,
|
|
7386
|
+
status: "failed",
|
|
7387
|
+
error: "xAI API key not configured"
|
|
7370
7388
|
};
|
|
7371
7389
|
}
|
|
7372
7390
|
try {
|
|
7373
|
-
const
|
|
7374
|
-
const imageBlob = Buffer.isBuffer(imageData) ? new Blob([new Uint8Array(imageData)]) : imageData;
|
|
7375
|
-
formData.append("image", imageBlob, "image.png");
|
|
7376
|
-
formData.append("output_format", options.outputFormat || "png");
|
|
7377
|
-
if (left > 0) {
|
|
7378
|
-
formData.append("left", String(Math.min(2e3, Math.max(0, left))));
|
|
7379
|
-
}
|
|
7380
|
-
if (right > 0) {
|
|
7381
|
-
formData.append("right", String(Math.min(2e3, Math.max(0, right))));
|
|
7382
|
-
}
|
|
7383
|
-
if (up > 0) {
|
|
7384
|
-
formData.append("up", String(Math.min(2e3, Math.max(0, up))));
|
|
7385
|
-
}
|
|
7386
|
-
if (down > 0) {
|
|
7387
|
-
formData.append("down", String(Math.min(2e3, Math.max(0, down))));
|
|
7388
|
-
}
|
|
7389
|
-
if (options.prompt) {
|
|
7390
|
-
formData.append("prompt", options.prompt);
|
|
7391
|
-
}
|
|
7392
|
-
if (options.creativity !== void 0) {
|
|
7393
|
-
formData.append("creativity", String(Math.min(1, Math.max(0, options.creativity))));
|
|
7394
|
-
}
|
|
7395
|
-
const response = await fetch(`${this.baseUrl}/v2beta/stable-image/edit/outpaint`, {
|
|
7396
|
-
method: "POST",
|
|
7391
|
+
const response = await fetch(`${this.baseUrl}/videos/${id}`, {
|
|
7397
7392
|
headers: {
|
|
7398
|
-
Authorization: `Bearer ${this.apiKey}
|
|
7399
|
-
|
|
7400
|
-
},
|
|
7401
|
-
body: formData
|
|
7393
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
7394
|
+
}
|
|
7402
7395
|
});
|
|
7403
7396
|
if (!response.ok) {
|
|
7404
7397
|
const errorText = await response.text();
|
|
7405
|
-
let errorMessage;
|
|
7406
|
-
try {
|
|
7407
|
-
const errorData = JSON.parse(errorText);
|
|
7408
|
-
errorMessage = errorData.message || errorData.error || errorText;
|
|
7409
|
-
} catch {
|
|
7410
|
-
errorMessage = errorText;
|
|
7411
|
-
}
|
|
7412
7398
|
return {
|
|
7413
|
-
|
|
7414
|
-
|
|
7399
|
+
id,
|
|
7400
|
+
status: "failed",
|
|
7401
|
+
error: `Failed to get status: ${errorText}`
|
|
7415
7402
|
};
|
|
7416
7403
|
}
|
|
7417
|
-
const
|
|
7418
|
-
const
|
|
7419
|
-
|
|
7404
|
+
const data = await response.json();
|
|
7405
|
+
const statusMap = {
|
|
7406
|
+
pending: "pending",
|
|
7407
|
+
done: "completed",
|
|
7408
|
+
expired: "failed"
|
|
7409
|
+
};
|
|
7420
7410
|
return {
|
|
7421
|
-
|
|
7422
|
-
|
|
7423
|
-
|
|
7424
|
-
|
|
7425
|
-
}]
|
|
7411
|
+
id,
|
|
7412
|
+
status: statusMap[data.status] || "pending",
|
|
7413
|
+
videoUrl: data.video?.url,
|
|
7414
|
+
error: data.status === "expired" ? "Generation expired" : void 0
|
|
7426
7415
|
};
|
|
7427
7416
|
} catch (error) {
|
|
7428
7417
|
return {
|
|
7429
|
-
|
|
7418
|
+
id,
|
|
7419
|
+
status: "failed",
|
|
7430
7420
|
error: error instanceof Error ? error.message : "Unknown error"
|
|
7431
7421
|
};
|
|
7432
7422
|
}
|
|
7433
7423
|
}
|
|
7424
|
+
/**
|
|
7425
|
+
* Wait for generation to complete
|
|
7426
|
+
*/
|
|
7427
|
+
async waitForCompletion(id, onProgress, maxWaitMs = 3e5) {
|
|
7428
|
+
const startTime = Date.now();
|
|
7429
|
+
while (Date.now() - startTime < maxWaitMs) {
|
|
7430
|
+
const result = await this.getGenerationStatus(id);
|
|
7431
|
+
if (onProgress) {
|
|
7432
|
+
onProgress(result);
|
|
7433
|
+
}
|
|
7434
|
+
if (result.status === "completed" || result.status === "failed") {
|
|
7435
|
+
return result;
|
|
7436
|
+
}
|
|
7437
|
+
await this.sleep(this.pollingInterval);
|
|
7438
|
+
}
|
|
7439
|
+
return {
|
|
7440
|
+
id,
|
|
7441
|
+
status: "failed",
|
|
7442
|
+
error: "Generation timed out"
|
|
7443
|
+
};
|
|
7444
|
+
}
|
|
7445
|
+
/**
|
|
7446
|
+
* Cancel generation (if supported)
|
|
7447
|
+
*/
|
|
7448
|
+
async cancelGeneration(id) {
|
|
7449
|
+
if (!this.apiKey)
|
|
7450
|
+
return false;
|
|
7451
|
+
try {
|
|
7452
|
+
const response = await fetch(`${this.baseUrl}/videos/${id}`, {
|
|
7453
|
+
method: "DELETE",
|
|
7454
|
+
headers: {
|
|
7455
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
7456
|
+
}
|
|
7457
|
+
});
|
|
7458
|
+
return response.ok;
|
|
7459
|
+
} catch {
|
|
7460
|
+
return false;
|
|
7461
|
+
}
|
|
7462
|
+
}
|
|
7463
|
+
sleep(ms) {
|
|
7464
|
+
return new Promise((resolve12) => setTimeout(resolve12, ms));
|
|
7465
|
+
}
|
|
7434
7466
|
};
|
|
7435
|
-
var
|
|
7467
|
+
var grokProvider = new GrokProvider();
|
|
7436
7468
|
|
|
7437
7469
|
// ../ai-providers/dist/replicate/ReplicateProvider.js
|
|
7438
7470
|
var ReplicateProvider = class {
|
|
@@ -7688,7 +7720,7 @@ var ReplicateProvider = class {
|
|
|
7688
7720
|
* Sleep helper
|
|
7689
7721
|
*/
|
|
7690
7722
|
sleep(ms) {
|
|
7691
|
-
return new Promise((
|
|
7723
|
+
return new Promise((resolve12) => setTimeout(resolve12, ms));
|
|
7692
7724
|
}
|
|
7693
7725
|
/**
|
|
7694
7726
|
* Generate music from text prompt using MusicGen
|
|
@@ -7829,8 +7861,7 @@ var ReplicateProvider = class {
|
|
|
7829
7861
|
Authorization: `Bearer ${this.apiToken}`
|
|
7830
7862
|
},
|
|
7831
7863
|
body: JSON.stringify({
|
|
7832
|
-
|
|
7833
|
-
// resemble-enhance
|
|
7864
|
+
model: "lucataco/resemble-enhance",
|
|
7834
7865
|
input
|
|
7835
7866
|
})
|
|
7836
7867
|
});
|
|
@@ -7993,8 +8024,7 @@ var ReplicateProvider = class {
|
|
|
7993
8024
|
let modelVersion;
|
|
7994
8025
|
if (point) {
|
|
7995
8026
|
input.query_points = [[point[0], point[1], 0]];
|
|
7996
|
-
modelVersion = "facebookresearch/co-tracker
|
|
7997
|
-
modelVersion = "0a0dcaf0c51e8d6a8b1a3e4c5f6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5";
|
|
8027
|
+
modelVersion = "facebookresearch/co-tracker";
|
|
7998
8028
|
} else if (box) {
|
|
7999
8029
|
input.box = [box[0], box[1], box[0] + box[2], box[1] + box[3]];
|
|
8000
8030
|
modelVersion = "meta/sam-2:fe97b453a6455861e3bac769b441ca1f1086110da7466dbb65cf1eecfd60dc83";
|
|
@@ -8015,7 +8045,7 @@ var ReplicateProvider = class {
|
|
|
8015
8045
|
Authorization: `Bearer ${this.apiToken}`
|
|
8016
8046
|
},
|
|
8017
8047
|
body: JSON.stringify({
|
|
8018
|
-
version: modelVersion.split(":")[1]
|
|
8048
|
+
...modelVersion.includes(":") ? { version: modelVersion.split(":")[1] } : { model: modelVersion },
|
|
8019
8049
|
input
|
|
8020
8050
|
})
|
|
8021
8051
|
});
|
|
@@ -8093,9 +8123,9 @@ var PROVIDER_ENV_VARS = {
|
|
|
8093
8123
|
runway: "RUNWAY_API_SECRET",
|
|
8094
8124
|
kling: "KLING_API_KEY",
|
|
8095
8125
|
imgbb: "IMGBB_API_KEY",
|
|
8096
|
-
stability: "STABILITY_API_KEY",
|
|
8097
8126
|
replicate: "REPLICATE_API_TOKEN",
|
|
8098
|
-
xai: "XAI_API_KEY"
|
|
8127
|
+
xai: "XAI_API_KEY",
|
|
8128
|
+
openrouter: "OPENROUTER_API_KEY"
|
|
8099
8129
|
};
|
|
8100
8130
|
function createDefaultConfig() {
|
|
8101
8131
|
return {
|
|
@@ -8148,7 +8178,14 @@ async function getApiKeyFromConfig(providerKey) {
|
|
|
8148
8178
|
}
|
|
8149
8179
|
|
|
8150
8180
|
// ../cli/src/utils/api-key.ts
|
|
8151
|
-
function
|
|
8181
|
+
function loadEnv() {
|
|
8182
|
+
config({ path: resolve5(process.cwd(), ".env"), debug: false });
|
|
8183
|
+
const monorepoRoot = findMonorepoRoot();
|
|
8184
|
+
if (monorepoRoot && monorepoRoot !== process.cwd()) {
|
|
8185
|
+
config({ path: resolve5(monorepoRoot, ".env"), debug: false });
|
|
8186
|
+
}
|
|
8187
|
+
}
|
|
8188
|
+
function findMonorepoRoot() {
|
|
8152
8189
|
let dir = process.cwd();
|
|
8153
8190
|
while (dir !== "/") {
|
|
8154
8191
|
try {
|
|
@@ -8158,18 +8195,14 @@ function findProjectRoot() {
|
|
|
8158
8195
|
dir = resolve5(dir, "..");
|
|
8159
8196
|
}
|
|
8160
8197
|
}
|
|
8161
|
-
return
|
|
8162
|
-
}
|
|
8163
|
-
function loadEnv() {
|
|
8164
|
-
const projectRoot = findProjectRoot();
|
|
8165
|
-
config({ path: resolve5(projectRoot, ".env") });
|
|
8198
|
+
return null;
|
|
8166
8199
|
}
|
|
8167
8200
|
async function prompt(question, hidden = false) {
|
|
8168
8201
|
const rl = createInterface({
|
|
8169
8202
|
input: process.stdin,
|
|
8170
8203
|
output: process.stdout
|
|
8171
8204
|
});
|
|
8172
|
-
return new Promise((
|
|
8205
|
+
return new Promise((resolve12) => {
|
|
8173
8206
|
if (hidden && process.stdin.isTTY) {
|
|
8174
8207
|
process.stdout.write(question);
|
|
8175
8208
|
let input = "";
|
|
@@ -8183,7 +8216,7 @@ async function prompt(question, hidden = false) {
|
|
|
8183
8216
|
process.stdin.removeListener("data", onData);
|
|
8184
8217
|
process.stdout.write("\n");
|
|
8185
8218
|
rl.close();
|
|
8186
|
-
|
|
8219
|
+
resolve12(input);
|
|
8187
8220
|
} else if (char === "") {
|
|
8188
8221
|
process.exit(1);
|
|
8189
8222
|
} else if (char === "\x7F" || char === "\b") {
|
|
@@ -8198,7 +8231,7 @@ async function prompt(question, hidden = false) {
|
|
|
8198
8231
|
} else {
|
|
8199
8232
|
rl.question(question, (answer) => {
|
|
8200
8233
|
rl.close();
|
|
8201
|
-
|
|
8234
|
+
resolve12(answer);
|
|
8202
8235
|
});
|
|
8203
8236
|
}
|
|
8204
8237
|
});
|
|
@@ -8215,7 +8248,6 @@ async function getApiKey(envVar, providerName, optionValue) {
|
|
|
8215
8248
|
RUNWAY_API_SECRET: "runway",
|
|
8216
8249
|
KLING_API_KEY: "kling",
|
|
8217
8250
|
IMGBB_API_KEY: "imgbb",
|
|
8218
|
-
STABILITY_API_KEY: "stability",
|
|
8219
8251
|
REPLICATE_API_TOKEN: "replicate"
|
|
8220
8252
|
};
|
|
8221
8253
|
const providerKey = providerKeyMap[envVar];
|
|
@@ -8235,7 +8267,7 @@ async function getApiKey(envVar, providerName, optionValue) {
|
|
|
8235
8267
|
}
|
|
8236
8268
|
console.log();
|
|
8237
8269
|
console.log(chalk2.yellow(`${providerName} API key not found.`));
|
|
8238
|
-
console.log(chalk2.dim(`Set ${envVar} in .env, run 'vibe setup', or enter below.`));
|
|
8270
|
+
console.log(chalk2.dim(`Set ${envVar} in .env (current directory), run 'vibe setup', or enter below.`));
|
|
8239
8271
|
console.log();
|
|
8240
8272
|
const apiKey = await prompt(chalk2.cyan(`Enter ${providerName} API key: `), true);
|
|
8241
8273
|
if (!apiKey || apiKey.trim() === "") {
|
|
@@ -8249,8 +8281,7 @@ async function getApiKey(envVar, providerName, optionValue) {
|
|
|
8249
8281
|
return apiKey.trim();
|
|
8250
8282
|
}
|
|
8251
8283
|
async function saveApiKeyToEnv(envVar, apiKey) {
|
|
8252
|
-
const
|
|
8253
|
-
const envPath = resolve5(projectRoot, ".env");
|
|
8284
|
+
const envPath = resolve5(process.cwd(), ".env");
|
|
8254
8285
|
let content = "";
|
|
8255
8286
|
try {
|
|
8256
8287
|
await access3(envPath);
|
|
@@ -8409,7 +8440,7 @@ async function detectSilencePeriodsWithGemini(videoPath, minDuration, options) {
|
|
|
8409
8440
|
const totalDuration = await getVideoDuration(videoPath);
|
|
8410
8441
|
const geminiApiKey = options.apiKey || await getApiKey("GOOGLE_API_KEY", "Google");
|
|
8411
8442
|
if (!geminiApiKey) {
|
|
8412
|
-
throw new Error("Google API key required for Gemini Video Understanding.
|
|
8443
|
+
throw new Error("Google API key required for Gemini Video Understanding. Run 'vibe setup' or set GOOGLE_API_KEY in .env");
|
|
8413
8444
|
}
|
|
8414
8445
|
const gemini = new GeminiProvider();
|
|
8415
8446
|
await gemini.initialize({ apiKey: geminiApiKey });
|
|
@@ -8666,7 +8697,7 @@ async function executeJumpCut(options) {
|
|
|
8666
8697
|
}
|
|
8667
8698
|
const openaiKey = apiKey || process.env.OPENAI_API_KEY;
|
|
8668
8699
|
if (!openaiKey) {
|
|
8669
|
-
return { success: false, error: "OpenAI API key required for Whisper transcription." };
|
|
8700
|
+
return { success: false, error: "OpenAI API key required for Whisper transcription. Run 'vibe setup' or set OPENAI_API_KEY in .env" };
|
|
8670
8701
|
}
|
|
8671
8702
|
try {
|
|
8672
8703
|
const tmpDir = `/tmp/vibe_jumpcut_${Date.now()}`;
|
|
@@ -8811,7 +8842,7 @@ async function executeCaption(options) {
|
|
|
8811
8842
|
}
|
|
8812
8843
|
const openaiKey = apiKey || process.env.OPENAI_API_KEY;
|
|
8813
8844
|
if (!openaiKey) {
|
|
8814
|
-
return { success: false, error: "OpenAI API key required for Whisper transcription." };
|
|
8845
|
+
return { success: false, error: "OpenAI API key required for Whisper transcription. Run 'vibe setup' or set OPENAI_API_KEY in .env" };
|
|
8815
8846
|
}
|
|
8816
8847
|
try {
|
|
8817
8848
|
const tmpDir = `/tmp/vibe_caption_${Date.now()}`;
|
|
@@ -9080,7 +9111,7 @@ ${textsToTranslate}`;
|
|
|
9080
9111
|
if (provider === "openai") {
|
|
9081
9112
|
const openaiKey = apiKey || process.env.OPENAI_API_KEY;
|
|
9082
9113
|
if (!openaiKey) {
|
|
9083
|
-
return { success: false, error: "OpenAI API key required for translation." };
|
|
9114
|
+
return { success: false, error: "OpenAI API key required for translation. Run 'vibe setup' or set OPENAI_API_KEY in .env" };
|
|
9084
9115
|
}
|
|
9085
9116
|
const response = await fetch("https://api.openai.com/v1/chat/completions", {
|
|
9086
9117
|
method: "POST",
|
|
@@ -9089,7 +9120,7 @@ ${textsToTranslate}`;
|
|
|
9089
9120
|
Authorization: `Bearer ${openaiKey}`
|
|
9090
9121
|
},
|
|
9091
9122
|
body: JSON.stringify({
|
|
9092
|
-
model: "gpt-
|
|
9123
|
+
model: "gpt-5-mini",
|
|
9093
9124
|
messages: [{ role: "user", content: translatePrompt }],
|
|
9094
9125
|
temperature: 0.3
|
|
9095
9126
|
})
|
|
@@ -9102,7 +9133,7 @@ ${textsToTranslate}`;
|
|
|
9102
9133
|
} else {
|
|
9103
9134
|
const claudeKey = apiKey || process.env.ANTHROPIC_API_KEY;
|
|
9104
9135
|
if (!claudeKey) {
|
|
9105
|
-
return { success: false, error: "Anthropic API key required for translation." };
|
|
9136
|
+
return { success: false, error: "Anthropic API key required for translation. Run 'vibe setup' or set ANTHROPIC_API_KEY in .env" };
|
|
9106
9137
|
}
|
|
9107
9138
|
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
9108
9139
|
method: "POST",
|
|
@@ -9599,7 +9630,7 @@ async function executeGeminiVideo(options) {
|
|
|
9599
9630
|
try {
|
|
9600
9631
|
const apiKey = await getApiKey("GOOGLE_API_KEY", "Google");
|
|
9601
9632
|
if (!apiKey) {
|
|
9602
|
-
return { success: false, error: "Google API key required" };
|
|
9633
|
+
return { success: false, error: "Google API key required. Run 'vibe setup' or set GOOGLE_API_KEY in .env" };
|
|
9603
9634
|
}
|
|
9604
9635
|
const isYouTube = options.source.includes("youtube.com") || options.source.includes("youtu.be");
|
|
9605
9636
|
const modelMap = {
|
|
@@ -9649,7 +9680,7 @@ async function executeAnalyze(options) {
|
|
|
9649
9680
|
try {
|
|
9650
9681
|
const apiKey = await getApiKey("GOOGLE_API_KEY", "Google");
|
|
9651
9682
|
if (!apiKey) {
|
|
9652
|
-
return { success: false, error: "Google API key required" };
|
|
9683
|
+
return { success: false, error: "Google API key required. Run 'vibe setup' or set GOOGLE_API_KEY in .env" };
|
|
9653
9684
|
}
|
|
9654
9685
|
const source = options.source;
|
|
9655
9686
|
const isYouTube = source.includes("youtube.com") || source.includes("youtu.be");
|
|
@@ -9789,7 +9820,7 @@ async function executeReview(options) {
|
|
|
9789
9820
|
}
|
|
9790
9821
|
const apiKey = process.env.GOOGLE_API_KEY || await getApiKey("GOOGLE_API_KEY", "Google");
|
|
9791
9822
|
if (!apiKey) {
|
|
9792
|
-
return { success: false, error: "Google API key required for Gemini video review" };
|
|
9823
|
+
return { success: false, error: "Google API key required for Gemini video review. Run 'vibe setup' or set GOOGLE_API_KEY in .env" };
|
|
9793
9824
|
}
|
|
9794
9825
|
let storyboardContext = "";
|
|
9795
9826
|
if (storyboardPath) {
|
|
@@ -9896,7 +9927,7 @@ Score each category 1-10. For fixable issues, provide an FFmpeg filter in autoFi
|
|
|
9896
9927
|
}
|
|
9897
9928
|
|
|
9898
9929
|
// ../cli/src/commands/ai-image.ts
|
|
9899
|
-
import { readFile as readFile8, writeFile as writeFile6 } from "node:fs/promises";
|
|
9930
|
+
import { readFile as readFile8, writeFile as writeFile6, mkdir as mkdir4 } from "node:fs/promises";
|
|
9900
9931
|
import { existsSync as existsSync5 } from "node:fs";
|
|
9901
9932
|
import chalk5 from "chalk";
|
|
9902
9933
|
import ora4 from "ora";
|
|
@@ -9917,7 +9948,7 @@ async function executeThumbnailBestFrame(options) {
|
|
|
9917
9948
|
}
|
|
9918
9949
|
const googleKey = apiKey || process.env.GOOGLE_API_KEY;
|
|
9919
9950
|
if (!googleKey) {
|
|
9920
|
-
return { success: false, error: "Google API key required for Gemini video analysis." };
|
|
9951
|
+
return { success: false, error: "Google API key required for Gemini video analysis. Run 'vibe setup' or set GOOGLE_API_KEY in .env" };
|
|
9921
9952
|
}
|
|
9922
9953
|
try {
|
|
9923
9954
|
const gemini = new GeminiProvider();
|
|
@@ -9926,7 +9957,9 @@ async function executeThumbnailBestFrame(options) {
|
|
|
9926
9957
|
const analysisPrompt = prompt2 || 'Analyze this video and find the single best frame for a thumbnail. Look for frames that are visually striking, well-composed, emotionally engaging, and representative of the video content. Avoid blurry frames, transitions, or dark scenes. Return ONLY a JSON object: {"timestamp": <seconds as number>, "reason": "<brief explanation>"}';
|
|
9927
9958
|
const modelMap = {
|
|
9928
9959
|
flash: "gemini-3-flash-preview",
|
|
9960
|
+
latest: "gemini-2.5-flash",
|
|
9929
9961
|
"flash-2.5": "gemini-2.5-flash",
|
|
9962
|
+
// backward compat
|
|
9930
9963
|
pro: "gemini-2.5-pro"
|
|
9931
9964
|
};
|
|
9932
9965
|
const modelId = modelMap[model] || "gemini-3-flash-preview";
|
|
@@ -9968,7 +10001,7 @@ async function executeThumbnailBestFrame(options) {
|
|
|
9968
10001
|
// src/tools/ai-analysis.ts
|
|
9969
10002
|
var aiAnalysisTools = [
|
|
9970
10003
|
{
|
|
9971
|
-
name: "
|
|
10004
|
+
name: "analyze_media",
|
|
9972
10005
|
description: "Analyze media (image, video, or YouTube URL) using Gemini AI. Requires GOOGLE_API_KEY.",
|
|
9973
10006
|
inputSchema: {
|
|
9974
10007
|
type: "object",
|
|
@@ -9989,7 +10022,7 @@ var aiAnalysisTools = [
|
|
|
9989
10022
|
}
|
|
9990
10023
|
},
|
|
9991
10024
|
{
|
|
9992
|
-
name: "
|
|
10025
|
+
name: "analyze_video",
|
|
9993
10026
|
description: "Analyze video content using Gemini AI with temporal understanding. Requires GOOGLE_API_KEY.",
|
|
9994
10027
|
inputSchema: {
|
|
9995
10028
|
type: "object",
|
|
@@ -10010,7 +10043,7 @@ var aiAnalysisTools = [
|
|
|
10010
10043
|
}
|
|
10011
10044
|
},
|
|
10012
10045
|
{
|
|
10013
|
-
name: "
|
|
10046
|
+
name: "analyze_review",
|
|
10014
10047
|
description: "AI video review: analyzes quality, suggests fixes, and optionally auto-applies them. Requires GOOGLE_API_KEY.",
|
|
10015
10048
|
inputSchema: {
|
|
10016
10049
|
type: "object",
|
|
@@ -10030,7 +10063,7 @@ var aiAnalysisTools = [
|
|
|
10030
10063
|
}
|
|
10031
10064
|
},
|
|
10032
10065
|
{
|
|
10033
|
-
name: "
|
|
10066
|
+
name: "generate_thumbnail",
|
|
10034
10067
|
description: "Extract the best thumbnail frame from a video using Gemini AI analysis. Requires GOOGLE_API_KEY.",
|
|
10035
10068
|
inputSchema: {
|
|
10036
10069
|
type: "object",
|
|
@@ -10046,7 +10079,7 @@ var aiAnalysisTools = [
|
|
|
10046
10079
|
];
|
|
10047
10080
|
async function handleAiAnalysisToolCall(name, args) {
|
|
10048
10081
|
switch (name) {
|
|
10049
|
-
case "
|
|
10082
|
+
case "analyze_media": {
|
|
10050
10083
|
const result = await executeAnalyze({
|
|
10051
10084
|
source: args.source,
|
|
10052
10085
|
prompt: args.prompt,
|
|
@@ -10064,7 +10097,7 @@ async function handleAiAnalysisToolCall(name, args) {
|
|
|
10064
10097
|
totalTokens: result.totalTokens
|
|
10065
10098
|
});
|
|
10066
10099
|
}
|
|
10067
|
-
case "
|
|
10100
|
+
case "analyze_video": {
|
|
10068
10101
|
const result = await executeGeminiVideo({
|
|
10069
10102
|
source: args.source,
|
|
10070
10103
|
prompt: args.prompt,
|
|
@@ -10081,7 +10114,7 @@ async function handleAiAnalysisToolCall(name, args) {
|
|
|
10081
10114
|
totalTokens: result.totalTokens
|
|
10082
10115
|
});
|
|
10083
10116
|
}
|
|
10084
|
-
case "
|
|
10117
|
+
case "analyze_review": {
|
|
10085
10118
|
const result = await executeReview({
|
|
10086
10119
|
videoPath: args.videoPath,
|
|
10087
10120
|
storyboardPath: args.storyboardPath,
|
|
@@ -10098,7 +10131,7 @@ async function handleAiAnalysisToolCall(name, args) {
|
|
|
10098
10131
|
outputPath: result.outputPath
|
|
10099
10132
|
});
|
|
10100
10133
|
}
|
|
10101
|
-
case "
|
|
10134
|
+
case "generate_thumbnail": {
|
|
10102
10135
|
const result = await executeThumbnailBestFrame({
|
|
10103
10136
|
videoPath: args.videoPath,
|
|
10104
10137
|
outputPath: args.outputPath,
|
|
@@ -10118,15 +10151,30 @@ async function handleAiAnalysisToolCall(name, args) {
|
|
|
10118
10151
|
}
|
|
10119
10152
|
|
|
10120
10153
|
// ../cli/src/commands/ai-script-pipeline.ts
|
|
10121
|
-
import { readFile as readFile9, writeFile as writeFile7, mkdir as
|
|
10154
|
+
import { readFile as readFile9, writeFile as writeFile7, mkdir as mkdir5, unlink, rename as rename2 } from "node:fs/promises";
|
|
10122
10155
|
import { resolve as resolve9, basename as basename3, extname as extname3 } from "node:path";
|
|
10123
10156
|
import { existsSync as existsSync6 } from "node:fs";
|
|
10124
10157
|
import chalk6 from "chalk";
|
|
10125
10158
|
init_exec_safe();
|
|
10159
|
+
|
|
10160
|
+
// ../cli/src/commands/ai-helpers.ts
|
|
10161
|
+
async function downloadVideo(url, apiKey) {
|
|
10162
|
+
const headers = {};
|
|
10163
|
+
if (url.includes("generativelanguage.googleapis.com") && apiKey) {
|
|
10164
|
+
headers["x-goog-api-key"] = apiKey;
|
|
10165
|
+
}
|
|
10166
|
+
const response = await fetch(url, { headers, redirect: "follow" });
|
|
10167
|
+
if (!response.ok) {
|
|
10168
|
+
throw new Error(`Download failed (${response.status}): ${response.statusText}`);
|
|
10169
|
+
}
|
|
10170
|
+
return Buffer.from(await response.arrayBuffer());
|
|
10171
|
+
}
|
|
10172
|
+
|
|
10173
|
+
// ../cli/src/commands/ai-script-pipeline.ts
|
|
10126
10174
|
var DEFAULT_VIDEO_RETRIES = 2;
|
|
10127
10175
|
var RETRY_DELAY_MS = 5e3;
|
|
10128
10176
|
function sleep(ms) {
|
|
10129
|
-
return new Promise((
|
|
10177
|
+
return new Promise((resolve12) => setTimeout(resolve12, ms));
|
|
10130
10178
|
}
|
|
10131
10179
|
async function generateVideoWithRetryKling(kling, segment, options, maxRetries, onProgress) {
|
|
10132
10180
|
const prompt2 = segment.visualStyle ? `${segment.visuals}. Style: ${segment.visualStyle}` : segment.visuals;
|
|
@@ -10193,6 +10241,37 @@ async function generateVideoWithRetryRunway(runway, segment, referenceImage, opt
|
|
|
10193
10241
|
}
|
|
10194
10242
|
return null;
|
|
10195
10243
|
}
|
|
10244
|
+
async function generateVideoWithRetryVeo(gemini, segment, options, maxRetries, onProgress) {
|
|
10245
|
+
const prompt2 = segment.visualStyle ? `${segment.visuals}. Style: ${segment.visualStyle}` : segment.visuals;
|
|
10246
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
10247
|
+
try {
|
|
10248
|
+
const result = await gemini.generateVideo(prompt2, {
|
|
10249
|
+
prompt: prompt2,
|
|
10250
|
+
referenceImage: options.referenceImage,
|
|
10251
|
+
duration: options.duration,
|
|
10252
|
+
aspectRatio: options.aspectRatio,
|
|
10253
|
+
model: "veo-3.1-fast-generate-preview"
|
|
10254
|
+
});
|
|
10255
|
+
if (result.status !== "failed" && result.id) {
|
|
10256
|
+
return { operationName: result.id };
|
|
10257
|
+
}
|
|
10258
|
+
if (attempt < maxRetries) {
|
|
10259
|
+
onProgress?.(`\u26A0 Retry ${attempt + 1}/${maxRetries}...`);
|
|
10260
|
+
await sleep(RETRY_DELAY_MS);
|
|
10261
|
+
}
|
|
10262
|
+
} catch (err) {
|
|
10263
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
10264
|
+
if (attempt < maxRetries) {
|
|
10265
|
+
onProgress?.(`\u26A0 Error: ${errMsg.slice(0, 50)}... retry ${attempt + 1}/${maxRetries}`);
|
|
10266
|
+
await sleep(RETRY_DELAY_MS);
|
|
10267
|
+
} else {
|
|
10268
|
+
console.error(chalk6.dim(`
|
|
10269
|
+
[Veo error: ${errMsg}]`));
|
|
10270
|
+
}
|
|
10271
|
+
}
|
|
10272
|
+
}
|
|
10273
|
+
return null;
|
|
10274
|
+
}
|
|
10196
10275
|
async function executeScriptToVideo(options) {
|
|
10197
10276
|
const outputDir = options.outputDir || "script-video-output";
|
|
10198
10277
|
try {
|
|
@@ -10201,17 +10280,17 @@ async function executeScriptToVideo(options) {
|
|
|
10201
10280
|
if (storyboardProvider === "openai") {
|
|
10202
10281
|
storyboardApiKey = await getApiKey("OPENAI_API_KEY", "OpenAI") ?? void 0;
|
|
10203
10282
|
if (!storyboardApiKey) {
|
|
10204
|
-
return { success: false, outputDir, scenes: 0, error: "OpenAI API key required for storyboard generation (--storyboard-provider openai)" };
|
|
10283
|
+
return { success: false, outputDir, scenes: 0, error: "OpenAI API key required for storyboard generation (--storyboard-provider openai). Run 'vibe setup' or set OPENAI_API_KEY in .env" };
|
|
10205
10284
|
}
|
|
10206
10285
|
} else if (storyboardProvider === "gemini") {
|
|
10207
10286
|
storyboardApiKey = await getApiKey("GOOGLE_API_KEY", "Google") ?? void 0;
|
|
10208
10287
|
if (!storyboardApiKey) {
|
|
10209
|
-
return { success: false, outputDir, scenes: 0, error: "Google API key required for storyboard generation (--storyboard-provider gemini)" };
|
|
10288
|
+
return { success: false, outputDir, scenes: 0, error: "Google API key required for storyboard generation (--storyboard-provider gemini). Run 'vibe setup' or set GOOGLE_API_KEY in .env" };
|
|
10210
10289
|
}
|
|
10211
10290
|
} else {
|
|
10212
10291
|
storyboardApiKey = await getApiKey("ANTHROPIC_API_KEY", "Anthropic") ?? void 0;
|
|
10213
10292
|
if (!storyboardApiKey) {
|
|
10214
|
-
return { success: false, outputDir, scenes: 0, error: "Anthropic API key required for storyboard generation" };
|
|
10293
|
+
return { success: false, outputDir, scenes: 0, error: "Anthropic API key required for storyboard generation. Run 'vibe setup' or set ANTHROPIC_API_KEY in .env" };
|
|
10215
10294
|
}
|
|
10216
10295
|
}
|
|
10217
10296
|
let imageApiKey;
|
|
@@ -10219,24 +10298,24 @@ async function executeScriptToVideo(options) {
|
|
|
10219
10298
|
if (imageProvider === "openai" || imageProvider === "dalle") {
|
|
10220
10299
|
imageApiKey = await getApiKey("OPENAI_API_KEY", "OpenAI") ?? void 0;
|
|
10221
10300
|
if (!imageApiKey) {
|
|
10222
|
-
return { success: false, outputDir, scenes: 0, error: "OpenAI API key required for image generation" };
|
|
10223
|
-
}
|
|
10224
|
-
} else if (imageProvider === "stability") {
|
|
10225
|
-
imageApiKey = await getApiKey("STABILITY_API_KEY", "Stability AI") ?? void 0;
|
|
10226
|
-
if (!imageApiKey) {
|
|
10227
|
-
return { success: false, outputDir, scenes: 0, error: "Stability API key required for image generation" };
|
|
10301
|
+
return { success: false, outputDir, scenes: 0, error: "OpenAI API key required for image generation. Run 'vibe setup' or set OPENAI_API_KEY in .env" };
|
|
10228
10302
|
}
|
|
10229
10303
|
} else if (imageProvider === "gemini") {
|
|
10230
10304
|
imageApiKey = await getApiKey("GOOGLE_API_KEY", "Google") ?? void 0;
|
|
10231
10305
|
if (!imageApiKey) {
|
|
10232
|
-
return { success: false, outputDir, scenes: 0, error: "Google API key required for Gemini image generation" };
|
|
10306
|
+
return { success: false, outputDir, scenes: 0, error: "Google API key required for Gemini image generation. Run 'vibe setup' or set GOOGLE_API_KEY in .env" };
|
|
10307
|
+
}
|
|
10308
|
+
} else if (imageProvider === "grok") {
|
|
10309
|
+
imageApiKey = await getApiKey("XAI_API_KEY", "xAI") ?? void 0;
|
|
10310
|
+
if (!imageApiKey) {
|
|
10311
|
+
return { success: false, outputDir, scenes: 0, error: "xAI API key required for Grok image generation. Run 'vibe setup' or set XAI_API_KEY in .env" };
|
|
10233
10312
|
}
|
|
10234
10313
|
}
|
|
10235
10314
|
let elevenlabsApiKey;
|
|
10236
10315
|
if (!options.noVoiceover) {
|
|
10237
10316
|
elevenlabsApiKey = await getApiKey("ELEVENLABS_API_KEY", "ElevenLabs") ?? void 0;
|
|
10238
10317
|
if (!elevenlabsApiKey) {
|
|
10239
|
-
return { success: false, outputDir, scenes: 0, error: "ElevenLabs API key required for voiceover (or use noVoiceover option)" };
|
|
10318
|
+
return { success: false, outputDir, scenes: 0, error: "ElevenLabs API key required for voiceover (or use noVoiceover option). Run 'vibe setup' or set ELEVENLABS_API_KEY in .env" };
|
|
10240
10319
|
}
|
|
10241
10320
|
}
|
|
10242
10321
|
let videoApiKey;
|
|
@@ -10244,18 +10323,23 @@ async function executeScriptToVideo(options) {
|
|
|
10244
10323
|
if (options.generator === "kling") {
|
|
10245
10324
|
videoApiKey = await getApiKey("KLING_API_KEY", "Kling") ?? void 0;
|
|
10246
10325
|
if (!videoApiKey) {
|
|
10247
|
-
return { success: false, outputDir, scenes: 0, error: "Kling API key required (or use imagesOnly option)" };
|
|
10326
|
+
return { success: false, outputDir, scenes: 0, error: "Kling API key required (or use imagesOnly option). Run 'vibe setup' or set KLING_API_KEY in .env" };
|
|
10327
|
+
}
|
|
10328
|
+
} else if (options.generator === "veo") {
|
|
10329
|
+
videoApiKey = await getApiKey("GOOGLE_API_KEY", "Google") ?? void 0;
|
|
10330
|
+
if (!videoApiKey) {
|
|
10331
|
+
return { success: false, outputDir, scenes: 0, error: "Google API key required for Veo video generation (or use imagesOnly option). Run 'vibe setup' or set GOOGLE_API_KEY in .env" };
|
|
10248
10332
|
}
|
|
10249
10333
|
} else {
|
|
10250
10334
|
videoApiKey = await getApiKey("RUNWAY_API_SECRET", "Runway") ?? void 0;
|
|
10251
10335
|
if (!videoApiKey) {
|
|
10252
|
-
return { success: false, outputDir, scenes: 0, error: "Runway API key required (or use imagesOnly option)" };
|
|
10336
|
+
return { success: false, outputDir, scenes: 0, error: "Runway API key required (or use imagesOnly option). Run 'vibe setup' or set RUNWAY_API_SECRET in .env" };
|
|
10253
10337
|
}
|
|
10254
10338
|
}
|
|
10255
10339
|
}
|
|
10256
10340
|
const absOutputDir = resolve9(process.cwd(), outputDir);
|
|
10257
10341
|
if (!existsSync6(absOutputDir)) {
|
|
10258
|
-
await
|
|
10342
|
+
await mkdir5(absOutputDir, { recursive: true });
|
|
10259
10343
|
}
|
|
10260
10344
|
let segments;
|
|
10261
10345
|
const creativityOpts = { creativity: options.creativity };
|
|
@@ -10344,23 +10428,18 @@ async function executeScriptToVideo(options) {
|
|
|
10344
10428
|
"9:16": "1024x1536",
|
|
10345
10429
|
"1:1": "1024x1024"
|
|
10346
10430
|
};
|
|
10347
|
-
const stabilityAspectRatios = {
|
|
10348
|
-
"16:9": "16:9",
|
|
10349
|
-
"9:16": "9:16",
|
|
10350
|
-
"1:1": "1:1"
|
|
10351
|
-
};
|
|
10352
10431
|
let openaiImageInstance;
|
|
10353
|
-
let stabilityInstance;
|
|
10354
10432
|
let geminiInstance;
|
|
10433
|
+
let grokInstance;
|
|
10355
10434
|
if (imageProvider === "openai" || imageProvider === "dalle") {
|
|
10356
10435
|
openaiImageInstance = new OpenAIImageProvider();
|
|
10357
10436
|
await openaiImageInstance.initialize({ apiKey: imageApiKey });
|
|
10358
|
-
} else if (imageProvider === "stability") {
|
|
10359
|
-
stabilityInstance = new StabilityProvider();
|
|
10360
|
-
await stabilityInstance.initialize({ apiKey: imageApiKey });
|
|
10361
10437
|
} else if (imageProvider === "gemini") {
|
|
10362
10438
|
geminiInstance = new GeminiProvider();
|
|
10363
10439
|
await geminiInstance.initialize({ apiKey: imageApiKey });
|
|
10440
|
+
} else if (imageProvider === "grok") {
|
|
10441
|
+
grokInstance = new GrokProvider();
|
|
10442
|
+
await grokInstance.initialize({ apiKey: imageApiKey });
|
|
10364
10443
|
}
|
|
10365
10444
|
const imagePaths = [];
|
|
10366
10445
|
for (let i = 0; i < segments.length; i++) {
|
|
@@ -10382,12 +10461,18 @@ async function executeScriptToVideo(options) {
|
|
|
10382
10461
|
imageUrl = img.url;
|
|
10383
10462
|
}
|
|
10384
10463
|
}
|
|
10385
|
-
} else if (imageProvider === "
|
|
10386
|
-
const imageResult = await
|
|
10387
|
-
aspectRatio:
|
|
10388
|
-
|
|
10464
|
+
} else if (imageProvider === "gemini" && geminiInstance) {
|
|
10465
|
+
const imageResult = await geminiInstance.generateImage(imagePrompt, {
|
|
10466
|
+
aspectRatio: options.aspectRatio || "16:9"
|
|
10467
|
+
});
|
|
10468
|
+
if (imageResult.success && imageResult.images?.[0]?.base64) {
|
|
10469
|
+
imageBuffer = Buffer.from(imageResult.images[0].base64, "base64");
|
|
10470
|
+
}
|
|
10471
|
+
} else if (imageProvider === "grok" && grokInstance) {
|
|
10472
|
+
const imageResult = await grokInstance.generateImage(imagePrompt, {
|
|
10473
|
+
aspectRatio: options.aspectRatio || "16:9"
|
|
10389
10474
|
});
|
|
10390
|
-
if (imageResult.success && imageResult.images
|
|
10475
|
+
if (imageResult.success && imageResult.images && imageResult.images.length > 0) {
|
|
10391
10476
|
const img = imageResult.images[0];
|
|
10392
10477
|
if (img.base64) {
|
|
10393
10478
|
imageBuffer = Buffer.from(img.base64, "base64");
|
|
@@ -10395,13 +10480,6 @@ async function executeScriptToVideo(options) {
|
|
|
10395
10480
|
imageUrl = img.url;
|
|
10396
10481
|
}
|
|
10397
10482
|
}
|
|
10398
|
-
} else if (imageProvider === "gemini" && geminiInstance) {
|
|
10399
|
-
const imageResult = await geminiInstance.generateImage(imagePrompt, {
|
|
10400
|
-
aspectRatio: options.aspectRatio || "16:9"
|
|
10401
|
-
});
|
|
10402
|
-
if (imageResult.success && imageResult.images?.[0]?.base64) {
|
|
10403
|
-
imageBuffer = Buffer.from(imageResult.images[0].base64, "base64");
|
|
10404
|
-
}
|
|
10405
10483
|
}
|
|
10406
10484
|
const imagePath = resolve9(absOutputDir, `scene-${i + 1}.png`);
|
|
10407
10485
|
if (imageBuffer) {
|
|
@@ -10451,8 +10529,53 @@ async function executeScriptToVideo(options) {
|
|
|
10451
10529
|
const waitResult = await kling.waitForCompletion(taskResult.taskId, taskResult.type, void 0, 6e5);
|
|
10452
10530
|
if (waitResult.status === "completed" && waitResult.videoUrl) {
|
|
10453
10531
|
const videoPath = resolve9(absOutputDir, `scene-${i + 1}.mp4`);
|
|
10454
|
-
const
|
|
10455
|
-
|
|
10532
|
+
const buffer = await downloadVideo(waitResult.videoUrl, videoApiKey);
|
|
10533
|
+
await writeFile7(videoPath, buffer);
|
|
10534
|
+
const targetDuration = segment.duration;
|
|
10535
|
+
const actualVideoDuration = await getVideoDuration(videoPath);
|
|
10536
|
+
if (actualVideoDuration < targetDuration - 0.1) {
|
|
10537
|
+
const extendedPath = resolve9(absOutputDir, `scene-${i + 1}-extended.mp4`);
|
|
10538
|
+
await extendVideoNaturally(videoPath, targetDuration, extendedPath);
|
|
10539
|
+
await unlink(videoPath);
|
|
10540
|
+
await rename2(extendedPath, videoPath);
|
|
10541
|
+
}
|
|
10542
|
+
videoPaths.push(videoPath);
|
|
10543
|
+
result.videos.push(videoPath);
|
|
10544
|
+
} else {
|
|
10545
|
+
videoPaths.push("");
|
|
10546
|
+
result.failedScenes.push(i + 1);
|
|
10547
|
+
}
|
|
10548
|
+
} catch {
|
|
10549
|
+
videoPaths.push("");
|
|
10550
|
+
result.failedScenes.push(i + 1);
|
|
10551
|
+
}
|
|
10552
|
+
} else {
|
|
10553
|
+
videoPaths.push("");
|
|
10554
|
+
result.failedScenes.push(i + 1);
|
|
10555
|
+
}
|
|
10556
|
+
}
|
|
10557
|
+
} else if (options.generator === "veo") {
|
|
10558
|
+
const veo = new GeminiProvider();
|
|
10559
|
+
await veo.initialize({ apiKey: videoApiKey });
|
|
10560
|
+
for (let i = 0; i < segments.length; i++) {
|
|
10561
|
+
if (!imagePaths[i]) {
|
|
10562
|
+
videoPaths.push("");
|
|
10563
|
+
continue;
|
|
10564
|
+
}
|
|
10565
|
+
const segment = segments[i];
|
|
10566
|
+
const veoDuration = segment.duration > 6 ? 8 : segment.duration > 4 ? 6 : 4;
|
|
10567
|
+
const taskResult = await generateVideoWithRetryVeo(
|
|
10568
|
+
veo,
|
|
10569
|
+
segment,
|
|
10570
|
+
{ duration: veoDuration, aspectRatio: options.aspectRatio || "16:9" },
|
|
10571
|
+
maxRetries
|
|
10572
|
+
);
|
|
10573
|
+
if (taskResult) {
|
|
10574
|
+
try {
|
|
10575
|
+
const waitResult = await veo.waitForVideoCompletion(taskResult.operationName, void 0, 3e5);
|
|
10576
|
+
if (waitResult.status === "completed" && waitResult.videoUrl) {
|
|
10577
|
+
const videoPath = resolve9(absOutputDir, `scene-${i + 1}.mp4`);
|
|
10578
|
+
const buffer = await downloadVideo(waitResult.videoUrl, videoApiKey);
|
|
10456
10579
|
await writeFile7(videoPath, buffer);
|
|
10457
10580
|
const targetDuration = segment.duration;
|
|
10458
10581
|
const actualVideoDuration = await getVideoDuration(videoPath);
|
|
@@ -10504,8 +10627,7 @@ async function executeScriptToVideo(options) {
|
|
|
10504
10627
|
const waitResult = await runway.waitForCompletion(taskResult.taskId, void 0, 3e5);
|
|
10505
10628
|
if (waitResult.status === "completed" && waitResult.videoUrl) {
|
|
10506
10629
|
const videoPath = resolve9(absOutputDir, `scene-${i + 1}.mp4`);
|
|
10507
|
-
const
|
|
10508
|
-
const buffer = Buffer.from(await response.arrayBuffer());
|
|
10630
|
+
const buffer = await downloadVideo(waitResult.videoUrl, videoApiKey);
|
|
10509
10631
|
await writeFile7(videoPath, buffer);
|
|
10510
10632
|
const targetDuration = segment.duration;
|
|
10511
10633
|
const actualVideoDuration = await getVideoDuration(videoPath);
|
|
@@ -10659,7 +10781,7 @@ async function executeScriptToVideo(options) {
|
|
|
10659
10781
|
}
|
|
10660
10782
|
|
|
10661
10783
|
// ../cli/src/commands/ai-highlights.ts
|
|
10662
|
-
import { readFile as readFile10, writeFile as writeFile8, mkdir as
|
|
10784
|
+
import { readFile as readFile10, writeFile as writeFile8, mkdir as mkdir6 } from "node:fs/promises";
|
|
10663
10785
|
import { resolve as resolve10, dirname as dirname2, basename as basename4, extname as extname4 } from "node:path";
|
|
10664
10786
|
import { existsSync as existsSync7 } from "node:fs";
|
|
10665
10787
|
import chalk7 from "chalk";
|
|
@@ -10702,7 +10824,7 @@ async function executeHighlights(options) {
|
|
|
10702
10824
|
if (options.useGemini && isVideo) {
|
|
10703
10825
|
const geminiApiKey = await getApiKey("GOOGLE_API_KEY", "Google");
|
|
10704
10826
|
if (!geminiApiKey) {
|
|
10705
|
-
return { success: false, highlights: [], totalDuration: 0, totalHighlightDuration: 0, error: "Google API key required for Gemini Video Understanding" };
|
|
10827
|
+
return { success: false, highlights: [], totalDuration: 0, totalHighlightDuration: 0, error: "Google API key required for Gemini Video Understanding. Run 'vibe setup' or set GOOGLE_API_KEY in .env" };
|
|
10706
10828
|
}
|
|
10707
10829
|
sourceDuration = await ffprobeDuration(absPath);
|
|
10708
10830
|
const gemini = new GeminiProvider();
|
|
@@ -10770,11 +10892,11 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
|
|
|
10770
10892
|
} else {
|
|
10771
10893
|
const openaiApiKey = await getApiKey("OPENAI_API_KEY", "OpenAI");
|
|
10772
10894
|
if (!openaiApiKey) {
|
|
10773
|
-
return { success: false, highlights: [], totalDuration: 0, totalHighlightDuration: 0, error: "OpenAI API key required for Whisper transcription" };
|
|
10895
|
+
return { success: false, highlights: [], totalDuration: 0, totalHighlightDuration: 0, error: "OpenAI API key required for Whisper transcription. Run 'vibe setup' or set OPENAI_API_KEY in .env" };
|
|
10774
10896
|
}
|
|
10775
10897
|
const claudeApiKey = await getApiKey("ANTHROPIC_API_KEY", "Anthropic");
|
|
10776
10898
|
if (!claudeApiKey) {
|
|
10777
|
-
return { success: false, highlights: [], totalDuration: 0, totalHighlightDuration: 0, error: "Anthropic API key required for highlight analysis" };
|
|
10899
|
+
return { success: false, highlights: [], totalDuration: 0, totalHighlightDuration: 0, error: "Anthropic API key required for highlight analysis. Run 'vibe setup' or set ANTHROPIC_API_KEY in .env" };
|
|
10778
10900
|
}
|
|
10779
10901
|
let audioPath = absPath;
|
|
10780
10902
|
let tempAudioPath = null;
|
|
@@ -10900,7 +11022,7 @@ async function executeAutoShorts(options) {
|
|
|
10900
11022
|
if (options.useGemini) {
|
|
10901
11023
|
const geminiApiKey = await getApiKey("GOOGLE_API_KEY", "Google");
|
|
10902
11024
|
if (!geminiApiKey) {
|
|
10903
|
-
return { success: false, shorts: [], error: "Google API key required for Gemini Video Understanding" };
|
|
11025
|
+
return { success: false, shorts: [], error: "Google API key required for Gemini Video Understanding. Run 'vibe setup' or set GOOGLE_API_KEY in .env" };
|
|
10904
11026
|
}
|
|
10905
11027
|
const gemini = new GeminiProvider();
|
|
10906
11028
|
await gemini.initialize({ apiKey: geminiApiKey });
|
|
@@ -10971,11 +11093,11 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
|
|
|
10971
11093
|
} else {
|
|
10972
11094
|
const openaiApiKey = await getApiKey("OPENAI_API_KEY", "OpenAI");
|
|
10973
11095
|
if (!openaiApiKey) {
|
|
10974
|
-
return { success: false, shorts: [], error: "OpenAI API key required for transcription" };
|
|
11096
|
+
return { success: false, shorts: [], error: "OpenAI API key required for transcription. Run 'vibe setup' or set OPENAI_API_KEY in .env" };
|
|
10975
11097
|
}
|
|
10976
11098
|
const claudeApiKey = await getApiKey("ANTHROPIC_API_KEY", "Anthropic");
|
|
10977
11099
|
if (!claudeApiKey) {
|
|
10978
|
-
return { success: false, shorts: [], error: "Anthropic API key required for highlight detection" };
|
|
11100
|
+
return { success: false, shorts: [], error: "Anthropic API key required for highlight detection. Run 'vibe setup' or set ANTHROPIC_API_KEY in .env" };
|
|
10979
11101
|
}
|
|
10980
11102
|
const tempAudio = absPath.replace(/(\.[^.]+)$/, "-temp-audio.mp3");
|
|
10981
11103
|
await execSafe("ffmpeg", ["-i", absPath, "-vn", "-acodec", "libmp3lame", "-q:a", "2", tempAudio, "-y"]);
|
|
@@ -11020,7 +11142,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
|
|
|
11020
11142
|
}
|
|
11021
11143
|
const outputDir = options.outputDir ? resolve10(process.cwd(), options.outputDir) : dirname2(absPath);
|
|
11022
11144
|
if (options.outputDir && !existsSync7(outputDir)) {
|
|
11023
|
-
await
|
|
11145
|
+
await mkdir6(outputDir, { recursive: true });
|
|
11024
11146
|
}
|
|
11025
11147
|
const result = {
|
|
11026
11148
|
success: true,
|
|
@@ -11095,136 +11217,10 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
|
|
|
11095
11217
|
}
|
|
11096
11218
|
}
|
|
11097
11219
|
|
|
11098
|
-
// ../cli/src/commands/ai-narrate.ts
|
|
11099
|
-
import { readFile as readFile11, writeFile as writeFile9, mkdir as mkdir6 } from "node:fs/promises";
|
|
11100
|
-
import { resolve as resolve11, dirname as dirname3, basename as basename5 } from "node:path";
|
|
11101
|
-
import { existsSync as existsSync8 } from "node:fs";
|
|
11102
|
-
import chalk8 from "chalk";
|
|
11103
|
-
import ora6 from "ora";
|
|
11104
|
-
init_exec_safe();
|
|
11105
|
-
async function autoNarrate(options) {
|
|
11106
|
-
const {
|
|
11107
|
-
videoPath,
|
|
11108
|
-
duration,
|
|
11109
|
-
outputDir,
|
|
11110
|
-
voice = "rachel",
|
|
11111
|
-
style = "informative",
|
|
11112
|
-
language = "en",
|
|
11113
|
-
scriptProvider = "claude"
|
|
11114
|
-
} = options;
|
|
11115
|
-
const geminiApiKey = await getApiKey("GOOGLE_API_KEY", "Google");
|
|
11116
|
-
if (!geminiApiKey) {
|
|
11117
|
-
return { success: false, error: "GOOGLE_API_KEY required for video analysis" };
|
|
11118
|
-
}
|
|
11119
|
-
let claudeApiKey = null;
|
|
11120
|
-
let openaiScriptApiKey = null;
|
|
11121
|
-
if (scriptProvider === "openai") {
|
|
11122
|
-
openaiScriptApiKey = await getApiKey("OPENAI_API_KEY", "OpenAI");
|
|
11123
|
-
if (!openaiScriptApiKey) {
|
|
11124
|
-
return { success: false, error: "OPENAI_API_KEY required for script generation" };
|
|
11125
|
-
}
|
|
11126
|
-
} else {
|
|
11127
|
-
claudeApiKey = await getApiKey("ANTHROPIC_API_KEY", "Anthropic");
|
|
11128
|
-
if (!claudeApiKey) {
|
|
11129
|
-
return { success: false, error: "ANTHROPIC_API_KEY required for script generation" };
|
|
11130
|
-
}
|
|
11131
|
-
}
|
|
11132
|
-
const elevenlabsApiKey = await getApiKey("ELEVENLABS_API_KEY", "ElevenLabs");
|
|
11133
|
-
if (!elevenlabsApiKey) {
|
|
11134
|
-
return { success: false, error: "ELEVENLABS_API_KEY required for TTS" };
|
|
11135
|
-
}
|
|
11136
|
-
try {
|
|
11137
|
-
const gemini = new GeminiProvider();
|
|
11138
|
-
await gemini.initialize({ apiKey: geminiApiKey });
|
|
11139
|
-
const videoBuffer = await readFile11(videoPath);
|
|
11140
|
-
const analysisPrompt = `Analyze this video in detail for narration purposes. Describe:
|
|
11141
|
-
1. What is happening visually (actions, movements, subjects)
|
|
11142
|
-
2. The setting and environment
|
|
11143
|
-
3. Any text or graphics visible
|
|
11144
|
-
4. The mood and tone of the content
|
|
11145
|
-
5. Key moments and their approximate timestamps
|
|
11146
|
-
|
|
11147
|
-
Provide a detailed description that could be used to write a voiceover narration.
|
|
11148
|
-
Focus on what viewers need to know to understand and appreciate the video.`;
|
|
11149
|
-
const analysisResult = await gemini.analyzeVideo(videoBuffer, analysisPrompt, {
|
|
11150
|
-
fps: 0.5,
|
|
11151
|
-
// Lower FPS for cost optimization
|
|
11152
|
-
lowResolution: duration > 60
|
|
11153
|
-
// Use low res for longer videos
|
|
11154
|
-
});
|
|
11155
|
-
if (!analysisResult.success || !analysisResult.response) {
|
|
11156
|
-
return { success: false, error: `Video analysis failed: ${analysisResult.error}` };
|
|
11157
|
-
}
|
|
11158
|
-
let scriptResult;
|
|
11159
|
-
if (scriptProvider === "openai") {
|
|
11160
|
-
const gpt = new OpenAIProvider();
|
|
11161
|
-
await gpt.initialize({ apiKey: openaiScriptApiKey });
|
|
11162
|
-
scriptResult = await gpt.generateNarrationScript(
|
|
11163
|
-
analysisResult.response,
|
|
11164
|
-
duration,
|
|
11165
|
-
style,
|
|
11166
|
-
language
|
|
11167
|
-
);
|
|
11168
|
-
} else {
|
|
11169
|
-
const claude = new ClaudeProvider();
|
|
11170
|
-
await claude.initialize({ apiKey: claudeApiKey });
|
|
11171
|
-
scriptResult = await claude.generateNarrationScript(
|
|
11172
|
-
analysisResult.response,
|
|
11173
|
-
duration,
|
|
11174
|
-
style,
|
|
11175
|
-
language
|
|
11176
|
-
);
|
|
11177
|
-
if (!scriptResult.success && scriptResult.error?.includes("529")) {
|
|
11178
|
-
const fallbackKey = await getApiKey("OPENAI_API_KEY", "OpenAI");
|
|
11179
|
-
if (fallbackKey) {
|
|
11180
|
-
console.error("\u26A0\uFE0F Claude overloaded, falling back to OpenAI...");
|
|
11181
|
-
const gpt = new OpenAIProvider();
|
|
11182
|
-
await gpt.initialize({ apiKey: fallbackKey });
|
|
11183
|
-
scriptResult = await gpt.generateNarrationScript(
|
|
11184
|
-
analysisResult.response,
|
|
11185
|
-
duration,
|
|
11186
|
-
style,
|
|
11187
|
-
language
|
|
11188
|
-
);
|
|
11189
|
-
}
|
|
11190
|
-
}
|
|
11191
|
-
}
|
|
11192
|
-
if (!scriptResult.success || !scriptResult.script) {
|
|
11193
|
-
return { success: false, error: `Script generation failed: ${scriptResult.error}` };
|
|
11194
|
-
}
|
|
11195
|
-
const elevenlabs = new ElevenLabsProvider();
|
|
11196
|
-
await elevenlabs.initialize({ apiKey: elevenlabsApiKey });
|
|
11197
|
-
const ttsResult = await elevenlabs.textToSpeech(scriptResult.script, {
|
|
11198
|
-
voiceId: voice
|
|
11199
|
-
});
|
|
11200
|
-
if (!ttsResult.success || !ttsResult.audioBuffer) {
|
|
11201
|
-
return { success: false, error: `TTS generation failed: ${ttsResult.error}` };
|
|
11202
|
-
}
|
|
11203
|
-
if (!existsSync8(outputDir)) {
|
|
11204
|
-
await mkdir6(outputDir, { recursive: true });
|
|
11205
|
-
}
|
|
11206
|
-
const audioPath = resolve11(outputDir, "auto-narration.mp3");
|
|
11207
|
-
await writeFile9(audioPath, ttsResult.audioBuffer);
|
|
11208
|
-
const scriptPath = resolve11(outputDir, "narration-script.txt");
|
|
11209
|
-
await writeFile9(scriptPath, scriptResult.script, "utf-8");
|
|
11210
|
-
return {
|
|
11211
|
-
success: true,
|
|
11212
|
-
audioPath,
|
|
11213
|
-
script: scriptResult.script,
|
|
11214
|
-
segments: scriptResult.segments
|
|
11215
|
-
};
|
|
11216
|
-
} catch (error) {
|
|
11217
|
-
return {
|
|
11218
|
-
success: false,
|
|
11219
|
-
error: error instanceof Error ? error.message : "Unknown error in autoNarrate"
|
|
11220
|
-
};
|
|
11221
|
-
}
|
|
11222
|
-
}
|
|
11223
|
-
|
|
11224
11220
|
// src/tools/ai-pipelines.ts
|
|
11225
11221
|
var aiPipelineTools = [
|
|
11226
11222
|
{
|
|
11227
|
-
name: "
|
|
11223
|
+
name: "pipeline_script_to_video",
|
|
11228
11224
|
description: "Full script-to-video pipeline: script -> storyboard -> images -> voiceover -> video. Requires multiple API keys depending on providers chosen.",
|
|
11229
11225
|
inputSchema: {
|
|
11230
11226
|
type: "object",
|
|
@@ -11240,7 +11236,7 @@ var aiPipelineTools = [
|
|
|
11240
11236
|
},
|
|
11241
11237
|
imageProvider: {
|
|
11242
11238
|
type: "string",
|
|
11243
|
-
enum: ["openai", "
|
|
11239
|
+
enum: ["openai", "gemini", "grok"],
|
|
11244
11240
|
description: "Image generation provider (default: gemini)"
|
|
11245
11241
|
},
|
|
11246
11242
|
aspectRatio: {
|
|
@@ -11273,7 +11269,7 @@ var aiPipelineTools = [
|
|
|
11273
11269
|
}
|
|
11274
11270
|
},
|
|
11275
11271
|
{
|
|
11276
|
-
name: "
|
|
11272
|
+
name: "pipeline_highlights",
|
|
11277
11273
|
description: "Extract highlight clips from a longer video using AI analysis. Requires OPENAI_API_KEY+ANTHROPIC_API_KEY or GOOGLE_API_KEY (with --use-gemini).",
|
|
11278
11274
|
inputSchema: {
|
|
11279
11275
|
type: "object",
|
|
@@ -11297,8 +11293,8 @@ var aiPipelineTools = [
|
|
|
11297
11293
|
}
|
|
11298
11294
|
},
|
|
11299
11295
|
{
|
|
11300
|
-
name: "
|
|
11301
|
-
description: "Automatically generate short-form content (Reels/TikTok/Shorts) from a longer video. Same API key requirements as
|
|
11296
|
+
name: "pipeline_auto_shorts",
|
|
11297
|
+
description: "Automatically generate short-form content (Reels/TikTok/Shorts) from a longer video. Same API key requirements as pipeline_highlights.",
|
|
11302
11298
|
inputSchema: {
|
|
11303
11299
|
type: "object",
|
|
11304
11300
|
properties: {
|
|
@@ -11324,31 +11320,11 @@ var aiPipelineTools = [
|
|
|
11324
11320
|
},
|
|
11325
11321
|
required: ["video"]
|
|
11326
11322
|
}
|
|
11327
|
-
},
|
|
11328
|
-
{
|
|
11329
|
-
name: "ai_narrate",
|
|
11330
|
-
description: "Auto-generate narration for a video: analyze content with Gemini, generate script, produce voiceover with ElevenLabs. Requires GOOGLE_API_KEY + ELEVENLABS_API_KEY.",
|
|
11331
|
-
inputSchema: {
|
|
11332
|
-
type: "object",
|
|
11333
|
-
properties: {
|
|
11334
|
-
videoPath: { type: "string", description: "Path to the input video file" },
|
|
11335
|
-
duration: { type: "number", description: "Video duration in seconds (auto-detected if omitted)" },
|
|
11336
|
-
outputDir: { type: "string", description: "Output directory for narration audio" },
|
|
11337
|
-
voice: { type: "string", description: "ElevenLabs voice name (default: Rachel)" },
|
|
11338
|
-
style: {
|
|
11339
|
-
type: "string",
|
|
11340
|
-
enum: ["informative", "energetic", "calm", "dramatic"],
|
|
11341
|
-
description: "Narration style (default: informative)"
|
|
11342
|
-
},
|
|
11343
|
-
language: { type: "string", description: "Language code (default: en)" }
|
|
11344
|
-
},
|
|
11345
|
-
required: ["videoPath", "duration", "outputDir"]
|
|
11346
|
-
}
|
|
11347
11323
|
}
|
|
11348
11324
|
];
|
|
11349
11325
|
async function handleAiPipelineToolCall(name, args) {
|
|
11350
11326
|
switch (name) {
|
|
11351
|
-
case "
|
|
11327
|
+
case "pipeline_script_to_video": {
|
|
11352
11328
|
const result = await executeScriptToVideo({
|
|
11353
11329
|
script: args.script,
|
|
11354
11330
|
outputDir: args.outputDir,
|
|
@@ -11378,7 +11354,7 @@ async function handleAiPipelineToolCall(name, args) {
|
|
|
11378
11354
|
failedScenes: result.failedScenes
|
|
11379
11355
|
});
|
|
11380
11356
|
}
|
|
11381
|
-
case "
|
|
11357
|
+
case "pipeline_highlights": {
|
|
11382
11358
|
const result = await executeHighlights({
|
|
11383
11359
|
media: args.media,
|
|
11384
11360
|
output: args.output,
|
|
@@ -11400,7 +11376,7 @@ async function handleAiPipelineToolCall(name, args) {
|
|
|
11400
11376
|
projectPath: result.projectPath
|
|
11401
11377
|
});
|
|
11402
11378
|
}
|
|
11403
|
-
case "
|
|
11379
|
+
case "pipeline_auto_shorts": {
|
|
11404
11380
|
const result = await executeAutoShorts({
|
|
11405
11381
|
video: args.video,
|
|
11406
11382
|
outputDir: args.outputDir,
|
|
@@ -11426,22 +11402,6 @@ async function handleAiPipelineToolCall(name, args) {
|
|
|
11426
11402
|
}))
|
|
11427
11403
|
});
|
|
11428
11404
|
}
|
|
11429
|
-
case "ai_narrate": {
|
|
11430
|
-
const result = await autoNarrate({
|
|
11431
|
-
videoPath: args.videoPath,
|
|
11432
|
-
duration: args.duration,
|
|
11433
|
-
outputDir: args.outputDir,
|
|
11434
|
-
voice: args.voice,
|
|
11435
|
-
style: args.style,
|
|
11436
|
-
language: args.language
|
|
11437
|
-
});
|
|
11438
|
-
if (!result.success) return `Narration failed: ${result.error}`;
|
|
11439
|
-
return JSON.stringify({
|
|
11440
|
-
audioPath: result.audioPath,
|
|
11441
|
-
script: result.script,
|
|
11442
|
-
segments: result.segments?.length
|
|
11443
|
-
});
|
|
11444
|
-
}
|
|
11445
11405
|
default:
|
|
11446
11406
|
throw new Error(`Unknown AI pipeline tool: ${name}`);
|
|
11447
11407
|
}
|
|
@@ -11482,8 +11442,8 @@ async function handleToolCall(name, args) {
|
|
|
11482
11442
|
}
|
|
11483
11443
|
|
|
11484
11444
|
// src/resources/index.ts
|
|
11485
|
-
import { readFile as
|
|
11486
|
-
import { resolve as
|
|
11445
|
+
import { readFile as readFile11 } from "node:fs/promises";
|
|
11446
|
+
import { resolve as resolve11 } from "node:path";
|
|
11487
11447
|
var resources = [
|
|
11488
11448
|
{
|
|
11489
11449
|
uri: "vibe://project/current",
|
|
@@ -11518,8 +11478,8 @@ var resources = [
|
|
|
11518
11478
|
];
|
|
11519
11479
|
var currentProjectPath = process.env.VIBE_PROJECT_PATH || null;
|
|
11520
11480
|
async function loadProject2(projectPath) {
|
|
11521
|
-
const absPath =
|
|
11522
|
-
const content = await
|
|
11481
|
+
const absPath = resolve11(process.cwd(), projectPath);
|
|
11482
|
+
const content = await readFile11(absPath, "utf-8");
|
|
11523
11483
|
const data = JSON.parse(content);
|
|
11524
11484
|
return Project.fromJSON(data);
|
|
11525
11485
|
}
|