varg.ai-sdk 0.1.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.
Files changed (48) hide show
  1. package/.claude/settings.local.json +7 -0
  2. package/.env.example +24 -0
  3. package/CLAUDE.md +118 -0
  4. package/README.md +231 -0
  5. package/SKILLS.md +157 -0
  6. package/STRUCTURE.md +92 -0
  7. package/TEST_RESULTS.md +122 -0
  8. package/action/captions/SKILL.md +170 -0
  9. package/action/captions/index.ts +227 -0
  10. package/action/edit/SKILL.md +235 -0
  11. package/action/edit/index.ts +493 -0
  12. package/action/image/SKILL.md +140 -0
  13. package/action/image/index.ts +112 -0
  14. package/action/sync/SKILL.md +136 -0
  15. package/action/sync/index.ts +187 -0
  16. package/action/transcribe/SKILL.md +179 -0
  17. package/action/transcribe/index.ts +227 -0
  18. package/action/video/SKILL.md +116 -0
  19. package/action/video/index.ts +135 -0
  20. package/action/voice/SKILL.md +125 -0
  21. package/action/voice/index.ts +201 -0
  22. package/biome.json +33 -0
  23. package/index.ts +38 -0
  24. package/lib/README.md +144 -0
  25. package/lib/ai-sdk/fal.ts +106 -0
  26. package/lib/ai-sdk/replicate.ts +107 -0
  27. package/lib/elevenlabs.ts +382 -0
  28. package/lib/fal.ts +478 -0
  29. package/lib/ffmpeg.ts +467 -0
  30. package/lib/fireworks.ts +235 -0
  31. package/lib/groq.ts +246 -0
  32. package/lib/higgsfield.ts +176 -0
  33. package/lib/remotion/SKILL.md +823 -0
  34. package/lib/remotion/cli.ts +115 -0
  35. package/lib/remotion/functions.ts +283 -0
  36. package/lib/remotion/index.ts +19 -0
  37. package/lib/remotion/templates.ts +73 -0
  38. package/lib/replicate.ts +304 -0
  39. package/output.txt +1 -0
  40. package/package.json +35 -0
  41. package/pipeline/cookbooks/SKILL.md +285 -0
  42. package/pipeline/cookbooks/remotion-video.md +585 -0
  43. package/pipeline/cookbooks/round-video-character.md +337 -0
  44. package/pipeline/cookbooks/talking-character.md +59 -0
  45. package/test-import.ts +7 -0
  46. package/test-services.ts +97 -0
  47. package/tsconfig.json +29 -0
  48. package/utilities/s3.ts +147 -0
@@ -0,0 +1,115 @@
1
+ import {
2
+ createComposition,
3
+ getCompositionsList,
4
+ render,
5
+ renderStill,
6
+ } from "./functions";
7
+
8
+ // cli
9
+ export async function cli() {
10
+ const args = process.argv.slice(2);
11
+ const command = args[0];
12
+
13
+ if (!command || command === "help") {
14
+ console.log(`
15
+ usage:
16
+ bun run lib/remotion.ts <command> [args]
17
+
18
+ commands:
19
+ create <name> setup composition directory
20
+ compositions <root-file.tsx> list all compositions
21
+ render <root-file.tsx> <comp-id> <output.mp4> render video
22
+ still <root-file.tsx> <comp-id> <frame> <out.png> render still frame
23
+ help show this help
24
+
25
+ examples:
26
+ bun run lib/remotion/index.ts create MyVideo
27
+ bun run lib/remotion/index.ts compositions lib/remotion/compositions/MyVideo.root.tsx
28
+ bun run lib/remotion/index.ts render lib/remotion/compositions/MyVideo.root.tsx Demo output.mp4
29
+ bun run lib/remotion/index.ts still lib/remotion/compositions/MyVideo.root.tsx Demo 30 frame.png
30
+
31
+ requirements:
32
+ remotion and @remotion/cli must be installed
33
+ bun install remotion @remotion/cli
34
+ `);
35
+ process.exit(0);
36
+ }
37
+
38
+ try {
39
+ switch (command) {
40
+ case "create": {
41
+ const name = args[1];
42
+
43
+ if (!name) {
44
+ throw new Error("composition name is required");
45
+ }
46
+
47
+ await createComposition({ name });
48
+ console.log("\ncomposition setup complete!");
49
+ break;
50
+ }
51
+
52
+ case "compositions": {
53
+ const entryPoint = args[1];
54
+
55
+ if (!entryPoint) {
56
+ throw new Error("entry point is required");
57
+ }
58
+
59
+ const compositions = await getCompositionsList(entryPoint);
60
+
61
+ console.log("\navailable compositions:");
62
+ for (const comp of compositions) {
63
+ console.log(
64
+ ` ${comp.id}: ${comp.width}x${comp.height} @ ${comp.fps}fps (${comp.durationInFrames} frames)`,
65
+ );
66
+ }
67
+ break;
68
+ }
69
+
70
+ case "render": {
71
+ const entryPoint = args[1];
72
+ const compositionId = args[2];
73
+ const outputPath = args[3];
74
+
75
+ if (!entryPoint || !compositionId || !outputPath) {
76
+ throw new Error(
77
+ "entryPoint, compositionId, and outputPath are required",
78
+ );
79
+ }
80
+
81
+ await render({ entryPoint, compositionId, outputPath });
82
+ break;
83
+ }
84
+
85
+ case "still": {
86
+ const entryPoint = args[1];
87
+ const compositionId = args[2];
88
+ const frameArg = args[3];
89
+ const outputPath = args[4];
90
+
91
+ if (!entryPoint || !compositionId || !frameArg || !outputPath) {
92
+ throw new Error(
93
+ "entryPoint, compositionId, frame, and outputPath are required",
94
+ );
95
+ }
96
+
97
+ const frame = Number.parseInt(frameArg, 10);
98
+ if (Number.isNaN(frame)) {
99
+ throw new Error("frame must be a valid number");
100
+ }
101
+
102
+ await renderStill(entryPoint, compositionId, frame, outputPath);
103
+ break;
104
+ }
105
+
106
+ default:
107
+ console.error(`unknown command: ${command}`);
108
+ console.log("run 'bun run lib/remotion.ts help' for usage");
109
+ process.exit(1);
110
+ }
111
+ } catch (error) {
112
+ console.error("[remotion] error:", error);
113
+ process.exit(1);
114
+ }
115
+ }
@@ -0,0 +1,283 @@
1
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { bundle } from "@remotion/bundler";
4
+ import { getCompositions, renderMedia } from "@remotion/renderer";
5
+ import { getCompositionTemplate, getRootTemplate } from "./templates";
6
+
7
+ export interface RenderOptions {
8
+ entryPoint: string;
9
+ compositionId: string;
10
+ outputPath: string;
11
+ props?: Record<string, unknown>;
12
+ concurrency?: number;
13
+ frameRange?: [number, number];
14
+ }
15
+
16
+ export interface CompositionInfo {
17
+ id: string;
18
+ width: number;
19
+ height: number;
20
+ fps: number;
21
+ durationInFrames: number;
22
+ }
23
+
24
+ export interface CreateCompositionOptions {
25
+ name: string;
26
+ dir?: string;
27
+ }
28
+
29
+ export interface CreateCompositionResult {
30
+ compositionPath: string;
31
+ rootPath: string;
32
+ compositionsDir: string;
33
+ }
34
+
35
+ /**
36
+ * create composition directory structure and template files
37
+ *
38
+ * flow:
39
+ * 1. ensure lib/remotion/compositions/ exists
40
+ * 2. create template composition tsx file
41
+ * 3. create template root tsx file with registerRoot
42
+ * 4. files are ready to customize
43
+ */
44
+ export async function createComposition(
45
+ options: CreateCompositionOptions,
46
+ ): Promise<CreateCompositionResult> {
47
+ const { name, dir } = options;
48
+
49
+ // use provided dir or default to lib/remotion/compositions
50
+ const compositionsDir =
51
+ dir || join(process.cwd(), "lib/remotion/compositions");
52
+
53
+ // ensure directory exists
54
+ if (!existsSync(compositionsDir)) {
55
+ mkdirSync(compositionsDir, { recursive: true });
56
+ console.log(`[remotion] created directory: ${compositionsDir}`);
57
+ }
58
+
59
+ const compositionPath = join(compositionsDir, `${name}.tsx`);
60
+ const rootPath = join(compositionsDir, `${name}.root.tsx`);
61
+
62
+ // create template composition file
63
+ const compositionTemplate = getCompositionTemplate(name);
64
+
65
+ // create template root file
66
+ const rootTemplate = getRootTemplate(name);
67
+
68
+ // write files
69
+ writeFileSync(compositionPath, compositionTemplate);
70
+ writeFileSync(rootPath, rootTemplate);
71
+
72
+ console.log(`[remotion] created composition: ${compositionPath}`);
73
+ console.log(`[remotion] created root: ${rootPath}`);
74
+ console.log(`\n[remotion] next steps:`);
75
+ console.log(` 1. mkdir -p lib/remotion/public`);
76
+ console.log(` 2. cp media/video.mp4 media/audio.mp3 lib/remotion/public/`);
77
+ console.log(` 3. edit composition to use staticFile("filename.ext")`);
78
+ console.log(
79
+ ` 4. bun run lib/remotion/index.ts render ${rootPath} ${name} output.mp4`,
80
+ );
81
+
82
+ return {
83
+ compositionPath,
84
+ rootPath,
85
+ compositionsDir,
86
+ };
87
+ }
88
+
89
+ /**
90
+ * render a composition to video file
91
+ *
92
+ * flow:
93
+ * 1. bundle the remotion project using webpack
94
+ * 2. get composition metadata (verify it exists)
95
+ * 3. render each frame using chrome headless
96
+ * 4. encode frames to video using ffmpeg
97
+ * 5. save final video to outputPath
98
+ *
99
+ * this is the main rendering function that converts
100
+ * react components into actual video files
101
+ */
102
+ export async function render(options: RenderOptions): Promise<string> {
103
+ const {
104
+ entryPoint,
105
+ compositionId,
106
+ outputPath,
107
+ props = {},
108
+ concurrency,
109
+ frameRange,
110
+ } = options;
111
+
112
+ if (!entryPoint || !compositionId || !outputPath) {
113
+ throw new Error("entryPoint, compositionId, and outputPath are required");
114
+ }
115
+
116
+ if (!existsSync(entryPoint)) {
117
+ throw new Error(`entry point not found: ${entryPoint}`);
118
+ }
119
+
120
+ console.log(`[remotion] bundling ${entryPoint}...`);
121
+
122
+ // step 1: bundle project with webpack
123
+ const bundleLocation = await bundle({
124
+ entryPoint,
125
+ webpackOverride: (config) => config,
126
+ publicDir: join(process.cwd(), "lib/remotion/public"),
127
+ });
128
+
129
+ console.log("[remotion] getting composition info...");
130
+
131
+ // step 2: extract composition metadata
132
+ const compositions = await getCompositions(bundleLocation, {
133
+ inputProps: props,
134
+ });
135
+
136
+ // verify composition exists
137
+ const composition = compositions.find((c) => c.id === compositionId);
138
+ if (!composition) {
139
+ throw new Error(
140
+ `composition '${compositionId}' not found. available: ${compositions
141
+ .map((c) => c.id)
142
+ .join(", ")}`,
143
+ );
144
+ }
145
+
146
+ console.log(
147
+ `[remotion] rendering ${composition.id} (${composition.durationInFrames} frames @ ${composition.fps}fps)...`,
148
+ );
149
+
150
+ // step 3-5: render frames and encode to video
151
+ // - launches chrome headless to render each frame
152
+ // - uses ffmpeg to encode frames into h264 video
153
+ // - displays progress as it renders
154
+ await renderMedia({
155
+ composition,
156
+ serveUrl: bundleLocation,
157
+ codec: "h264",
158
+ outputLocation: outputPath,
159
+ inputProps: props,
160
+ concurrency: concurrency || undefined,
161
+ frameRange: frameRange || undefined,
162
+ onProgress: ({ progress, renderedFrames, encodedFrames }) => {
163
+ console.log(
164
+ `[remotion] progress: ${(progress * 100).toFixed(
165
+ 1,
166
+ )}% | rendered: ${renderedFrames} | encoded: ${encodedFrames}`,
167
+ );
168
+ },
169
+ });
170
+
171
+ console.log(`[remotion] saved to ${outputPath}`);
172
+ return outputPath;
173
+ }
174
+
175
+ /**
176
+ * render a single frame from composition as image
177
+ *
178
+ * flow:
179
+ * 1. bundle the remotion project
180
+ * 2. get composition metadata
181
+ * 3. render specified frame number using chrome
182
+ * 4. save as png/jpeg image
183
+ *
184
+ * useful for creating thumbnails or previews
185
+ */
186
+ export async function renderStill(
187
+ entryPoint: string,
188
+ compositionId: string,
189
+ frame: number,
190
+ outputPath: string,
191
+ props?: Record<string, unknown>,
192
+ ): Promise<string> {
193
+ if (!entryPoint || !compositionId || !outputPath) {
194
+ throw new Error("entryPoint, compositionId, and outputPath are required");
195
+ }
196
+
197
+ if (!existsSync(entryPoint)) {
198
+ throw new Error(`entry point not found: ${entryPoint}`);
199
+ }
200
+
201
+ console.log(`[remotion] bundling ${entryPoint}...`);
202
+
203
+ // step 1: bundle project
204
+ const bundleLocation = await bundle({
205
+ entryPoint,
206
+ webpackOverride: (config) => config,
207
+ });
208
+
209
+ console.log("[remotion] getting composition info...");
210
+
211
+ // step 2: get composition metadata
212
+ const compositions = await getCompositions(bundleLocation, {
213
+ inputProps: props || {},
214
+ });
215
+
216
+ const composition = compositions.find((c) => c.id === compositionId);
217
+ if (!composition) {
218
+ throw new Error(
219
+ `composition '${compositionId}' not found. available: ${compositions
220
+ .map((c) => c.id)
221
+ .join(", ")}`,
222
+ );
223
+ }
224
+
225
+ console.log(`[remotion] rendering frame ${frame}...`);
226
+
227
+ // dynamic import to avoid loading unless needed
228
+ const { renderStill: renderStillFrame } = await import("@remotion/renderer");
229
+
230
+ // step 3-4: render single frame and save as image
231
+ await renderStillFrame({
232
+ composition,
233
+ serveUrl: bundleLocation,
234
+ output: outputPath,
235
+ frame,
236
+ inputProps: props || {},
237
+ });
238
+
239
+ console.log(`[remotion] saved to ${outputPath}`);
240
+ return outputPath;
241
+ }
242
+ /**
243
+ * get list of compositions from entry point
244
+ *
245
+ * flow:
246
+ * 1. bundle the remotion project using webpack
247
+ * 2. extract composition metadata (id, dimensions, fps, duration)
248
+ * 3. return array of composition info
249
+ *
250
+ * useful for listing available compositions before rendering
251
+ */
252
+
253
+ export async function getCompositionsList(
254
+ entryPoint: string,
255
+ ): Promise<CompositionInfo[]> {
256
+ if (!existsSync(entryPoint)) {
257
+ throw new Error(`entry point not found: ${entryPoint}`);
258
+ }
259
+
260
+ console.log("[remotion] bundling compositions...");
261
+
262
+ // step 1: bundle project with webpack (creates serve url)
263
+ const bundleLocation = await bundle({
264
+ entryPoint,
265
+ webpackOverride: (config) => config,
266
+ });
267
+
268
+ console.log("[remotion] fetching compositions...");
269
+
270
+ // step 2: extract composition metadata from bundle
271
+ const compositions = await getCompositions(bundleLocation, {
272
+ inputProps: {},
273
+ });
274
+
275
+ // step 3: map to simple info objects
276
+ return compositions.map((comp) => ({
277
+ id: comp.id,
278
+ width: comp.width,
279
+ height: comp.height,
280
+ fps: comp.fps,
281
+ durationInFrames: comp.durationInFrames,
282
+ }));
283
+ }
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env bun
2
+
3
+ /**
4
+ * remotion wrapper for programmatic video creation
5
+ * requires @remotion/cli and remotion packages
6
+ *
7
+ * usage: bun run lib/remotion/index.ts <command> <args>
8
+ *
9
+ * simplified workflow:
10
+ * 1. create composition with: bun run lib/remotion/index.ts create <name>
11
+ * 2. copy media files to lib/remotion/public/
12
+ * 3. customize composition files (use staticFile() for media paths)
13
+ * 4. render with: bun run lib/remotion/index.ts render <root.tsx> <id> <output.mp4>
14
+ */
15
+ import { cli } from "./cli";
16
+
17
+ if (import.meta.main) {
18
+ cli();
19
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * remotion composition templates
3
+ * used by createComposition to generate starter files
4
+ */
5
+
6
+ export function getCompositionTemplate(name: string): string {
7
+ return `import React from "react";
8
+ import {
9
+ AbsoluteFill,
10
+ OffthreadVideo,
11
+ Audio,
12
+ Img,
13
+ useCurrentFrame,
14
+ useVideoConfig,
15
+ interpolate,
16
+ staticFile,
17
+ } from "remotion";
18
+
19
+ export const ${name}: React.FC = () => {
20
+ const frame = useCurrentFrame();
21
+ const { fps } = useVideoConfig();
22
+
23
+ // TODO: customize your composition
24
+ // Example: const video = staticFile("video.mp4");
25
+ // Example: const audio = staticFile("audio.mp3");
26
+
27
+ return (
28
+ <AbsoluteFill style={{ backgroundColor: "black" }}>
29
+ {/* Add your content here */}
30
+ <div
31
+ style={{
32
+ display: "flex",
33
+ alignItems: "center",
34
+ justifyContent: "center",
35
+ fontSize: 60,
36
+ color: "white",
37
+ }}
38
+ >
39
+ Frame {frame}
40
+ </div>
41
+ </AbsoluteFill>
42
+ );
43
+ };
44
+ `;
45
+ }
46
+
47
+ export function getRootTemplate(name: string): string {
48
+ return `import React from "react";
49
+ import { Composition, registerRoot } from "remotion";
50
+ import { ${name} } from "./${name}";
51
+
52
+ // TODO: configure your composition settings
53
+ const fps = 30;
54
+ const durationInFrames = 150; // 5 seconds at 30fps
55
+ const width = 1920;
56
+ const height = 1080;
57
+
58
+ registerRoot(() => {
59
+ return (
60
+ <>
61
+ <Composition
62
+ id="${name}"
63
+ component={${name}}
64
+ durationInFrames={durationInFrames}
65
+ fps={fps}
66
+ width={width}
67
+ height={height}
68
+ />
69
+ </>
70
+ );
71
+ });
72
+ `;
73
+ }