@mevdragon/vidfarm-devcli 0.1.0 → 0.2.1

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 (69) hide show
  1. package/.env.example +12 -5
  2. package/PLATFORM_SPEC.md +143 -2
  3. package/README.md +165 -16
  4. package/SKILL.developer.md +258 -0
  5. package/SKILL.director.md +599 -0
  6. package/dist/infra/cdk/bin/vidfarm-prod.js +59 -0
  7. package/dist/infra/cdk/lib/vidfarm-prod-stack.js +212 -0
  8. package/dist/src/account-pages.js +630 -0
  9. package/dist/src/app.js +897 -66
  10. package/dist/src/cli.js +284 -5
  11. package/dist/src/config.js +25 -5
  12. package/dist/src/context.js +1 -1
  13. package/dist/src/db.js +427 -18
  14. package/dist/src/dev-app.js +59 -12
  15. package/dist/src/homepage.js +441 -0
  16. package/dist/src/index.js +12 -7
  17. package/dist/src/lib/crypto.js +14 -0
  18. package/dist/src/lib/template-dna.js +542 -0
  19. package/dist/src/lib/template-style-options.js +49 -0
  20. package/dist/src/registry.js +54 -7
  21. package/dist/src/runtime.js +3 -1
  22. package/dist/src/services/auth.js +69 -5
  23. package/dist/src/services/jobs.js +23 -4
  24. package/dist/src/services/providers.js +74 -12
  25. package/dist/src/services/storage.js +74 -18
  26. package/dist/src/services/template-certification.js +160 -0
  27. package/dist/src/services/template-loader.js +37 -0
  28. package/dist/src/services/template-sources.js +135 -0
  29. package/dist/src/worker.js +19 -7
  30. package/dist/templates/template_0000/src/lib/images.js +242 -0
  31. package/dist/templates/template_0000/src/remotion/Root.js +33 -0
  32. package/dist/templates/template_0000/src/sdk.js +3 -0
  33. package/dist/templates/template_0000/src/style-options.js +51 -0
  34. package/dist/templates/template_0000/src/template-dna.js +9 -0
  35. package/dist/templates/template_0000/src/template.js +1217 -0
  36. package/package.json +10 -1
  37. package/templates/template_0000/README.md +121 -0
  38. package/templates/template_0000/SKILL.md +193 -0
  39. package/templates/template_0000/assets/Abel-Regular.ttf +0 -0
  40. package/templates/template_0000/assets/DMSerifDisplay-Regular.ttf +0 -0
  41. package/templates/template_0000/assets/Montserrat[wght].ttf +0 -0
  42. package/templates/template_0000/assets/SourceCodePro[wght].ttf +0 -0
  43. package/templates/template_0000/assets/TikTokSans-SemiBold.ttf +0 -0
  44. package/templates/template_0000/assets/Yesteryear-Regular.ttf +0 -0
  45. package/templates/template_0000/composition.json +11 -0
  46. package/templates/template_0000/package-lock.json +5505 -0
  47. package/templates/template_0000/package.json +31 -0
  48. package/templates/template_0000/research/preview/.gitkeep +1 -0
  49. package/templates/template_0000/research/source_notes.md +7 -0
  50. package/templates/template_0000/scripts/create-site.mjs +27 -0
  51. package/templates/template_0000/scripts/render-cloud.mjs +72 -0
  52. package/templates/template_0000/src/lib/images.js +242 -0
  53. package/templates/template_0000/src/lib/images.ts +284 -0
  54. package/templates/template_0000/src/remotion/Root.js +33 -0
  55. package/templates/template_0000/src/remotion/Root.tsx +75 -0
  56. package/templates/template_0000/src/remotion/index.js +3 -0
  57. package/templates/template_0000/src/remotion/index.tsx +4 -0
  58. package/templates/template_0000/src/sdk.js +3 -0
  59. package/templates/template_0000/src/sdk.ts +122 -0
  60. package/templates/template_0000/src/style-options.js +51 -0
  61. package/templates/template_0000/src/style-options.ts +60 -0
  62. package/templates/template_0000/src/template-dna.ts +15 -0
  63. package/templates/template_0000/src/template.js +1117 -0
  64. package/templates/template_0000/src/template.ts +1747 -0
  65. package/templates/template_0000/template.config.json +26 -0
  66. package/templates/template_0000/tsconfig.json +19 -0
  67. package/dist/templates/template_0000/demo-template.js +0 -196
  68. package/dist/templates/template_0000/remotion/Root.js +0 -66
  69. /package/dist/templates/template_0000/{remotion → src/remotion}/index.js +0 -0
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { AbsoluteFill, Composition, Img, Sequence, interpolate, spring, useCurrentFrame, useVideoConfig } from "remotion";
3
+ export const RemotionRoot = () => {
4
+ return (_jsx(Composition, { id: "template-0000", component: TemplateVideo, width: 1080, height: 1920, fps: 30, durationInFrames: 120, defaultProps: { slides: [] }, calculateMetadata: ({ props }) => ({
5
+ width: 1080,
6
+ height: 1920,
7
+ durationInFrames: Math.max(1, sumSlideFrames(props.slides, 30))
8
+ }) }));
9
+ };
10
+ const TemplateVideo = ({ slides }) => {
11
+ const { fps } = useVideoConfig();
12
+ let currentFrame = 0;
13
+ return (_jsx(AbsoluteFill, { style: { backgroundColor: "#120f0b" }, children: slides.map((slide, index) => {
14
+ const durationInFrames = msToFrames(slide.durationMs, fps);
15
+ const sequence = (_jsx(Sequence, { from: currentFrame, durationInFrames: durationInFrames, children: _jsx(SlideFrame, { slide: slide }) }, `${slide.imageUrl}-${index}`));
16
+ currentFrame += durationInFrames;
17
+ return sequence;
18
+ }) }));
19
+ };
20
+ const SlideFrame = ({ slide }) => {
21
+ const frame = useCurrentFrame();
22
+ const { fps } = useVideoConfig();
23
+ const entrance = spring({ fps, frame, config: { damping: 200, stiffness: 140 } });
24
+ const opacity = interpolate(entrance, [0, 1], [0.35, 1]);
25
+ const lift = interpolate(entrance, [0, 1], [22, 0]);
26
+ return (_jsxs(AbsoluteFill, { children: [_jsx(AbsoluteFill, { style: { opacity, transform: `translateY(${lift}px)` }, children: _jsx(Img, { src: slide.imageUrl, style: { width: "100%", height: "100%", objectFit: "fill" } }) }), _jsx(AbsoluteFill, { style: { background: "linear-gradient(180deg, rgba(0,0,0,0.18) 0%, rgba(0,0,0,0.06) 36%, rgba(0,0,0,0.34) 100%)" } })] }));
27
+ };
28
+ function msToFrames(durationMs, fps) {
29
+ return Math.max(1, Math.round((durationMs / 1000) * fps));
30
+ }
31
+ function sumSlideFrames(slides, fps) {
32
+ return slides.reduce((total, slide) => total + msToFrames(slide.durationMs, fps), 0);
33
+ }
@@ -0,0 +1,75 @@
1
+ import React from "react";
2
+ import { AbsoluteFill, Composition, Img, Sequence, interpolate, spring, useCurrentFrame, useVideoConfig } from "remotion";
3
+
4
+ type Slide = {
5
+ imageUrl: string;
6
+ durationMs: number;
7
+ };
8
+
9
+ type TemplateVideoProps = {
10
+ slides: Slide[];
11
+ };
12
+
13
+ export const RemotionRoot: React.FC = () => {
14
+ return (
15
+ <Composition
16
+ id="template-0000"
17
+ component={TemplateVideo}
18
+ width={1080}
19
+ height={1920}
20
+ fps={30}
21
+ durationInFrames={120}
22
+ defaultProps={{ slides: [] }}
23
+ calculateMetadata={({ props }) => ({
24
+ width: 1080,
25
+ height: 1920,
26
+ durationInFrames: Math.max(1, sumSlideFrames(props.slides, 30))
27
+ })}
28
+ />
29
+ );
30
+ };
31
+
32
+ const TemplateVideo: React.FC<TemplateVideoProps> = ({ slides }) => {
33
+ const { fps } = useVideoConfig();
34
+ let currentFrame = 0;
35
+
36
+ return (
37
+ <AbsoluteFill style={{ backgroundColor: "#120f0b" }}>
38
+ {slides.map((slide, index) => {
39
+ const durationInFrames = msToFrames(slide.durationMs, fps);
40
+ const sequence = (
41
+ <Sequence key={`${slide.imageUrl}-${index}`} from={currentFrame} durationInFrames={durationInFrames}>
42
+ <SlideFrame slide={slide} />
43
+ </Sequence>
44
+ );
45
+ currentFrame += durationInFrames;
46
+ return sequence;
47
+ })}
48
+ </AbsoluteFill>
49
+ );
50
+ };
51
+
52
+ const SlideFrame: React.FC<{ slide: Slide }> = ({ slide }) => {
53
+ const frame = useCurrentFrame();
54
+ const { fps } = useVideoConfig();
55
+ const entrance = spring({ fps, frame, config: { damping: 200, stiffness: 140 } });
56
+ const opacity = interpolate(entrance, [0, 1], [0.35, 1]);
57
+ const lift = interpolate(entrance, [0, 1], [22, 0]);
58
+
59
+ return (
60
+ <AbsoluteFill>
61
+ <AbsoluteFill style={{ opacity, transform: `translateY(${lift}px)` }}>
62
+ <Img src={slide.imageUrl} style={{ width: "100%", height: "100%", objectFit: "fill" }} />
63
+ </AbsoluteFill>
64
+ <AbsoluteFill style={{ background: "linear-gradient(180deg, rgba(0,0,0,0.18) 0%, rgba(0,0,0,0.06) 36%, rgba(0,0,0,0.34) 100%)" }} />
65
+ </AbsoluteFill>
66
+ );
67
+ };
68
+
69
+ function msToFrames(durationMs: number, fps: number) {
70
+ return Math.max(1, Math.round((durationMs / 1000) * fps));
71
+ }
72
+
73
+ function sumSlideFrames(slides: Slide[], fps: number) {
74
+ return slides.reduce((total, slide) => total + msToFrames(slide.durationMs, fps), 0);
75
+ }
@@ -0,0 +1,3 @@
1
+ import { registerRoot } from "remotion";
2
+ import { RemotionRoot } from "./Root.js";
3
+ registerRoot(RemotionRoot);
@@ -0,0 +1,4 @@
1
+ import { registerRoot } from "remotion";
2
+ import { RemotionRoot } from "./Root.js";
3
+
4
+ registerRoot(RemotionRoot);
@@ -0,0 +1,3 @@
1
+ export function defineTemplate(definition) {
2
+ return definition;
3
+ }
@@ -0,0 +1,122 @@
1
+ import type { ZodTypeAny } from "zod";
2
+
3
+ export type ProviderType = "openai" | "gemini" | "openrouter" | "perplexity";
4
+
5
+ export interface TemplateOperationDefinition {
6
+ description: string;
7
+ inputSchema: ZodTypeAny;
8
+ workflow: string;
9
+ providerHint?: ProviderType;
10
+ webhookSupport?: boolean;
11
+ smokeTestPayload?: Record<string, unknown>;
12
+ }
13
+
14
+ export interface TemplateAboutMetadata {
15
+ title: string;
16
+ description: string;
17
+ viral_dna: string;
18
+ visual_dna: string;
19
+ preview_media: string[];
20
+ link_to_original: string;
21
+ }
22
+
23
+ export interface JobExecutionResult {
24
+ progress?: number;
25
+ output?: Record<string, unknown>;
26
+ }
27
+
28
+ export interface TemplateDefinition {
29
+ id: string;
30
+ slugId: string;
31
+ version: string;
32
+ about: TemplateAboutMetadata;
33
+ configSchema: ZodTypeAny;
34
+ skillPath?: string;
35
+ operations: Record<string, TemplateOperationDefinition>;
36
+ jobs: Record<string, (ctx: TemplateJobContext, input: Record<string, unknown>) => Promise<JobExecutionResult>>;
37
+ }
38
+
39
+ export interface TemplateJobContext {
40
+ env: "development" | "production";
41
+ customer: {
42
+ id: string;
43
+ email: string;
44
+ name: string;
45
+ defaultWebhookUrl: string | null;
46
+ };
47
+ templateConfig: Record<string, unknown>;
48
+ logger: {
49
+ debug(message: string, metadata?: Record<string, unknown>): void;
50
+ info(message: string, metadata?: Record<string, unknown>): void;
51
+ warn(message: string, metadata?: Record<string, unknown>): void;
52
+ error(message: string, metadata?: Record<string, unknown>): void;
53
+ progress(progress: number, message: string, metadata?: Record<string, unknown>): void;
54
+ };
55
+ jobs: {
56
+ enqueueChild(input: {
57
+ operationName: string;
58
+ workflowName: string;
59
+ payload: Record<string, unknown>;
60
+ providerHint?: ProviderType;
61
+ }): Promise<{ jobId: string }>;
62
+ };
63
+ storage: {
64
+ putJson(key: string, value: unknown): Promise<{ key: string; url: string | null }>;
65
+ putText(key: string, value: string, contentType?: string): Promise<{ key: string; url: string | null }>;
66
+ putBuffer(
67
+ key: string,
68
+ value: Uint8Array,
69
+ options?: { contentType?: string; kind?: string; metadata?: Record<string, unknown> }
70
+ ): Promise<{ key: string; url: string | null }>;
71
+ getPublicUrl(key: string): string | null;
72
+ };
73
+ billing: {
74
+ record(input: {
75
+ type: "ai_generation" | "render" | "storage_write" | "cpu_estimate";
76
+ costUsd: number;
77
+ chargeUsd?: number;
78
+ metadata?: Record<string, unknown>;
79
+ }): Promise<void>;
80
+ };
81
+ providers: {
82
+ generateText(input: {
83
+ provider: ProviderType;
84
+ model: string;
85
+ prompt: string;
86
+ temperature?: number;
87
+ }): Promise<{ text: string; usage: { inputTokens: number; outputTokens: number; costUsd: number } }>;
88
+ generateImage(input: {
89
+ provider: ProviderType;
90
+ model: string;
91
+ prompt: string;
92
+ promptAttachments?: string[];
93
+ size?: string;
94
+ aspectRatio?: string;
95
+ imageSize?: "1K" | "2K" | "4K";
96
+ }): Promise<{ bytes: Uint8Array; contentType: string; revisedPrompt: string | null }>;
97
+ analyzeImageLayout(input: {
98
+ provider: ProviderType;
99
+ model: string;
100
+ imageUrl: string;
101
+ overlayText: string;
102
+ }): Promise<{
103
+ zone: "top" | "center" | "bottom";
104
+ align: "left" | "center" | "right";
105
+ maxWidthPercent: number;
106
+ justification: string;
107
+ }>;
108
+ };
109
+ remotion: {
110
+ render(input: {
111
+ compositionId: string;
112
+ serveUrl?: string;
113
+ entryPoint?: string;
114
+ outputKey?: string;
115
+ inputProps: Record<string, unknown>;
116
+ }): Promise<{ renderId: string; outputUrl: string | null; metadata: Record<string, unknown> }>;
117
+ };
118
+ }
119
+
120
+ export function defineTemplate(definition: TemplateDefinition): TemplateDefinition {
121
+ return definition;
122
+ }
@@ -0,0 +1,51 @@
1
+ export const TEMPLATE_FONT_OPTIONS = [
2
+ {
3
+ id: "source_code_pro",
4
+ label: "Source Code Pro",
5
+ family: "Source Code Pro",
6
+ assetFile: "SourceCodePro[wght].ttf"
7
+ },
8
+ {
9
+ id: "montserrat",
10
+ label: "Montserrat",
11
+ family: "Montserrat",
12
+ assetFile: "Montserrat[wght].ttf"
13
+ },
14
+ {
15
+ id: "yesteryear",
16
+ label: "Yesteryear",
17
+ family: "Yesteryear",
18
+ assetFile: "Yesteryear-Regular.ttf"
19
+ },
20
+ {
21
+ id: "dm_serif_display",
22
+ label: "DM Serif Display",
23
+ family: "DM Serif Display",
24
+ assetFile: "DMSerifDisplay-Regular.ttf"
25
+ },
26
+ {
27
+ id: "abel",
28
+ label: "Abel",
29
+ family: "Abel",
30
+ assetFile: "Abel-Regular.ttf"
31
+ }
32
+ ];
33
+ export const TEMPLATE_TEXT_BACKGROUND_COLOR_OPTIONS = [
34
+ { id: "black", label: "Black", hex: "#000000" },
35
+ { id: "red", label: "Red", hex: "#EA403F" },
36
+ { id: "orange", label: "Orange", hex: "#FF933D" },
37
+ { id: "yellow", label: "Yellow", hex: "#F2CD46" },
38
+ { id: "lime_green", label: "Lime Green", hex: "#78C25E" },
39
+ { id: "teal", label: "Teal", hex: "#77C8A6" },
40
+ { id: "light_blue", label: "Light Blue", hex: "#3496F0" },
41
+ { id: "dark_blue", label: "Dark Blue", hex: "#3496F0" },
42
+ { id: "violet", label: "Violet", hex: "#5756D4" },
43
+ { id: "pink", label: "Pink", hex: "#F7D7E9" },
44
+ { id: "brown", label: "Brown", hex: "#A3895B" },
45
+ { id: "dark_green", label: "Dark Green", hex: "#32523B" },
46
+ { id: "blue_gray", label: "Blue Gray", hex: "#2F688C" },
47
+ { id: "light_gray", label: "Light Gray", hex: "#92979E" },
48
+ { id: "dark_gray", label: "Dark Gray", hex: "#333333" }
49
+ ];
50
+ export const TEMPLATE_FONT_IDS = TEMPLATE_FONT_OPTIONS.map((option) => option.id);
51
+ export const TEMPLATE_TEXT_BACKGROUND_COLOR_IDS = TEMPLATE_TEXT_BACKGROUND_COLOR_OPTIONS.map((option) => option.id);
@@ -0,0 +1,60 @@
1
+ export const TEMPLATE_FONT_OPTIONS = [
2
+ {
3
+ id: "source_code_pro",
4
+ label: "Source Code Pro",
5
+ family: "Source Code Pro",
6
+ assetFile: "SourceCodePro[wght].ttf"
7
+ },
8
+ {
9
+ id: "montserrat",
10
+ label: "Montserrat",
11
+ family: "Montserrat",
12
+ assetFile: "Montserrat[wght].ttf"
13
+ },
14
+ {
15
+ id: "yesteryear",
16
+ label: "Yesteryear",
17
+ family: "Yesteryear",
18
+ assetFile: "Yesteryear-Regular.ttf"
19
+ },
20
+ {
21
+ id: "dm_serif_display",
22
+ label: "DM Serif Display",
23
+ family: "DM Serif Display",
24
+ assetFile: "DMSerifDisplay-Regular.ttf"
25
+ },
26
+ {
27
+ id: "abel",
28
+ label: "Abel",
29
+ family: "Abel",
30
+ assetFile: "Abel-Regular.ttf"
31
+ }
32
+ ] as const;
33
+
34
+ export const TEMPLATE_TEXT_BACKGROUND_COLOR_OPTIONS = [
35
+ { id: "black", label: "Black", hex: "#000000" },
36
+ { id: "red", label: "Red", hex: "#EA403F" },
37
+ { id: "orange", label: "Orange", hex: "#FF933D" },
38
+ { id: "yellow", label: "Yellow", hex: "#F2CD46" },
39
+ { id: "lime_green", label: "Lime Green", hex: "#78C25E" },
40
+ { id: "teal", label: "Teal", hex: "#77C8A6" },
41
+ { id: "light_blue", label: "Light Blue", hex: "#3496F0" },
42
+ { id: "dark_blue", label: "Dark Blue", hex: "#3496F0" },
43
+ { id: "violet", label: "Violet", hex: "#5756D4" },
44
+ { id: "pink", label: "Pink", hex: "#F7D7E9" },
45
+ { id: "brown", label: "Brown", hex: "#A3895B" },
46
+ { id: "dark_green", label: "Dark Green", hex: "#32523B" },
47
+ { id: "blue_gray", label: "Blue Gray", hex: "#2F688C" },
48
+ { id: "light_gray", label: "Light Gray", hex: "#92979E" },
49
+ { id: "dark_gray", label: "Dark Gray", hex: "#333333" }
50
+ ] as const;
51
+
52
+ export type TemplateFontOption = (typeof TEMPLATE_FONT_OPTIONS)[number];
53
+ export type TemplateFontId = TemplateFontOption["id"];
54
+ export type TemplateTextBackgroundColorOption = (typeof TEMPLATE_TEXT_BACKGROUND_COLOR_OPTIONS)[number];
55
+ export type TemplateTextBackgroundColorId = TemplateTextBackgroundColorOption["id"];
56
+
57
+ export const TEMPLATE_FONT_IDS = TEMPLATE_FONT_OPTIONS.map((option) => option.id) as [TemplateFontId, ...TemplateFontId[]];
58
+ export const TEMPLATE_TEXT_BACKGROUND_COLOR_IDS = TEMPLATE_TEXT_BACKGROUND_COLOR_OPTIONS.map(
59
+ (option) => option.id
60
+ ) as [TemplateTextBackgroundColorId, ...TemplateTextBackgroundColorId[]];
@@ -0,0 +1,15 @@
1
+ // Generated by `vidfarm analyze-viral-dna` and `vidfarm analyze-visual-dna`.
2
+ // Keep source notes in `research/source_notes.md` and reference media in `research/preview/`.
3
+
4
+ export const templateLinkToOriginal = "";
5
+ export const templateSourceNotesPath = "research/source_notes.md";
6
+ export const templatePreviewMediaRelativePaths = [] as const;
7
+
8
+ export const templateViralDna =
9
+ "Run `vidfarm analyze-viral-dna --template-dir .` after adding source notes and preview media.";
10
+ export const templateVisualDna =
11
+ "Run `vidfarm analyze-visual-dna --template-dir .` after adding source notes and preview media.";
12
+
13
+ export const templateViralDnaAnalysis = null;
14
+
15
+ export const templateVisualDnaAnalysis = null;