@mevdragon/vidfarm-devcli 0.2.1 → 0.2.3

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 (40) hide show
  1. package/.env.example +6 -39
  2. package/GETTING_STARTED.developers.md +87 -0
  3. package/README.md +94 -238
  4. package/SKILL.developer.md +430 -104
  5. package/dist/src/account-pages.js +1 -1
  6. package/dist/src/app.js +93 -5
  7. package/dist/src/cli.js +456 -8
  8. package/dist/src/config.js +3 -2
  9. package/dist/src/context.js +30 -11
  10. package/dist/src/db.js +2 -57
  11. package/dist/src/dev-app.js +0 -1
  12. package/dist/src/index.js +4 -2
  13. package/dist/src/lib/template-paths.js +21 -0
  14. package/dist/src/runtime.js +3 -1
  15. package/dist/src/services/auth.js +4 -4
  16. package/dist/src/services/job-logs.js +186 -0
  17. package/dist/src/services/jobs.js +3 -2
  18. package/dist/src/services/providers.js +14 -6
  19. package/dist/src/services/storage.js +85 -2
  20. package/dist/src/services/template-sources.js +29 -3
  21. package/dist/templates/template_0000/src/lib/images.js +46 -86
  22. package/dist/templates/template_0000/src/template.js +277 -53
  23. package/package.json +5 -6
  24. package/templates/template_0000/README.md +8 -52
  25. package/templates/template_0000/SKILL.md +35 -3
  26. package/templates/template_0000/package.json +3 -6
  27. package/templates/template_0000/src/lib/images.js +46 -86
  28. package/templates/template_0000/src/lib/images.ts +55 -98
  29. package/templates/template_0000/src/template-dna.js +9 -0
  30. package/templates/template_0000/src/template.js +523 -199
  31. package/templates/template_0000/src/template.ts +356 -61
  32. package/templates/template_0000/template.config.json +7 -12
  33. package/AWS_REMOTION_HANDOFF.md +0 -311
  34. package/PLATFORM_SPEC.md +0 -1039
  35. package/SKILL.director.md +0 -599
  36. package/dist/infra/cdk/bin/vidfarm-prod.js +0 -59
  37. package/dist/infra/cdk/lib/vidfarm-prod-stack.js +0 -212
  38. package/templates/template_0000/package-lock.json +0 -5505
  39. package/templates/template_0000/scripts/create-site.mjs +0 -27
  40. package/templates/template_0000/scripts/render-cloud.mjs +0 -72
@@ -39,6 +39,32 @@ const NATIVE_UI_TEXT_ZONE = {
39
39
  maxCenterYPercent: 0.58,
40
40
  } as const;
41
41
  const TEXT_BACKGROUND_NONE = "none" as const;
42
+ // Documentation-only dependency map for humans and agents.
43
+ // The current template standard has no first-class dependencies field, so
44
+ // provider/model requirements must be declared in source and SKILL.md.
45
+ const TEMPLATE_PROVIDER_REQUIREMENTS = {
46
+ image: [
47
+ { provider: "openai", models: ["gpt-image-1", "gpt-image-2"], strict: false },
48
+ { provider: "gemini", models: ["gemini-3.1-flash-image-preview", "gemini-2.5-flash-image"], strict: false },
49
+ { provider: "openrouter", models: ["bytedance/seedance-2.0", "bytedance-seed/seedream-4.5"], strict: false, planned: true },
50
+ ],
51
+ text: [
52
+ { provider: "openai", models: ["gpt-5.4"], strict: false },
53
+ { provider: "gemini", models: ["gemini-3.1-flash-lite", "gemini-2.5-flash-lite"], strict: false },
54
+ { provider: "openrouter", models: ["qwen/qwen3.6-flash"], strict: false },
55
+ ],
56
+ layout_analysis: [
57
+ { provider: "openai", models: ["gpt-5.4"], strict: false },
58
+ { provider: "gemini", models: ["gemini-3.1-flash-lite", "gemini-2.5-flash-lite"], strict: false },
59
+ { provider: "openrouter", models: ["qwen/qwen3.6-flash"], strict: false },
60
+ ],
61
+ video: [
62
+ { provider: "openai", models: ["sora-2"], strict: false, planned: true },
63
+ { provider: "gemini", models: ["veo-3.0-generate-001"], strict: false, planned: true },
64
+ ],
65
+ } as const;
66
+
67
+ void TEMPLATE_PROVIDER_REQUIREMENTS;
42
68
 
43
69
  const legacySlideInputSchema = z.union([
44
70
  z.tuple([z.string().min(3), z.string().min(1)]),
@@ -81,8 +107,21 @@ type Layout = {
81
107
  justification: string;
82
108
  };
83
109
 
110
+ type SlideSourceType = "ai_prompt" | "image_source";
111
+
112
+ type NormalizedSlideInput = {
113
+ sourceType: SlideSourceType;
114
+ sourceValue: string;
115
+ imagePrompt: string;
116
+ imagePromptAttachments: string[];
117
+ overlayText: string;
118
+ durationMs: number;
119
+ };
120
+
84
121
  type SlideOutput = {
85
122
  index: number;
123
+ sourceType: SlideSourceType;
124
+ sourceValue: string;
86
125
  imagePrompt: string;
87
126
  imagePromptAttachments: string[];
88
127
  overlayText: string;
@@ -213,38 +252,61 @@ export const template0000Definition = defineTemplate({
213
252
  const payload = createSlideshowInputSchema.parse(input);
214
253
  ctx.logger.progress(0.04, "Starting template_0000 slideshow image stage");
215
254
  const provider = parseImageProvider(ctx.templateConfig.defaultProvider);
255
+ const configuredTextModel =
256
+ typeof ctx.templateConfig.textModel === "string"
257
+ ? ctx.templateConfig.textModel
258
+ : null;
259
+ const configuredImageModel =
260
+ typeof ctx.templateConfig.imageModel === "string"
261
+ ? ctx.templateConfig.imageModel
262
+ : null;
216
263
  const textModel = String(
217
- ctx.templateConfig.textModel ?? defaultTextModelForProvider(provider),
264
+ configuredTextModel ?? defaultTextModelForProvider(provider),
218
265
  );
219
266
  const imageModel = String(
220
- ctx.templateConfig.imageModel ?? defaultImageModelForProvider(provider),
267
+ configuredImageModel ?? defaultImageModelForProvider(provider),
221
268
  );
222
269
  const textStyle = resolveTextStyleSpec(ctx.templateConfig);
223
270
 
224
271
  const slides: SlideOutput[] = [];
225
272
  for (const [index, rawSlide] of payload.slides.entries()) {
226
- const { imagePrompt, imagePromptAttachments, overlayText, durationMs } =
227
- normalizeSlideInput(rawSlide);
228
- ctx.logger.progress(
229
- 0.08 + (index / payload.slides.length) * 0.42,
230
- `Generating slide ${index + 1} image`,
231
- );
232
- const image = await generatePortraitCandidateImage(ctx, {
233
- provider,
234
- imageModel,
273
+ const {
274
+ sourceType,
275
+ sourceValue,
235
276
  imagePrompt,
236
277
  imagePromptAttachments,
237
278
  overlayText,
238
- });
239
- await ctx.billing.record({
240
- type: "ai_generation",
241
- costUsd: 0.04,
242
- metadata: {
243
- stage: "image_generation",
244
- slideIndex: index,
245
- model: imageModel,
246
- },
247
- });
279
+ durationMs,
280
+ } = normalizeSlideInput(rawSlide);
281
+ ctx.logger.progress(
282
+ 0.08 + (index / payload.slides.length) * 0.42,
283
+ sourceType === "image_source"
284
+ ? `Loading slide ${index + 1} source image`
285
+ : `Generating slide ${index + 1} image`,
286
+ );
287
+ const image =
288
+ sourceType === "image_source"
289
+ ? await loadExistingSlideImage(sourceValue)
290
+ : await generatePortraitCandidateImage(ctx, {
291
+ provider,
292
+ imageModel,
293
+ configuredProvider: provider,
294
+ configuredImageModel,
295
+ imagePrompt,
296
+ imagePromptAttachments,
297
+ overlayText,
298
+ });
299
+ if (sourceType === "ai_prompt") {
300
+ await ctx.billing.record({
301
+ type: "ai_generation",
302
+ costUsd: 0.04,
303
+ metadata: {
304
+ stage: "image_generation",
305
+ slideIndex: index,
306
+ model: imageModel,
307
+ },
308
+ });
309
+ }
248
310
 
249
311
  ctx.logger.progress(
250
312
  0.13 + (index / payload.slides.length) * 0.3,
@@ -312,6 +374,8 @@ export const template0000Definition = defineTemplate({
312
374
 
313
375
  slides.push({
314
376
  index,
377
+ sourceType,
378
+ sourceValue,
315
379
  imagePrompt,
316
380
  imagePromptAttachments,
317
381
  overlayText,
@@ -328,6 +392,8 @@ export const template0000Definition = defineTemplate({
328
392
  const metaDetails = await generateMetaDetails(ctx, {
329
393
  provider,
330
394
  textModel,
395
+ configuredProvider: provider,
396
+ configuredTextModel,
331
397
  slides,
332
398
  metaDetailsPrompt: payload.meta_details_prompt,
333
399
  });
@@ -381,6 +447,8 @@ export const template0000Definition = defineTemplate({
381
447
  meta_details_prompt: payload.meta_details_prompt ?? null,
382
448
  slides: slides.map((slide) => ({
383
449
  index: slide.index,
450
+ sourceType: slide.sourceType,
451
+ sourceValue: slide.sourceValue,
384
452
  imagePrompt: slide.imagePrompt,
385
453
  imagePromptAttachments: slide.imagePromptAttachments,
386
454
  overlayText: slide.overlayText,
@@ -499,6 +567,8 @@ async function generatePortraitCandidateImage(
499
567
  input: {
500
568
  provider: ImageProvider;
501
569
  imageModel: string;
570
+ configuredProvider: ImageProvider;
571
+ configuredImageModel: string | null;
502
572
  imagePrompt: string;
503
573
  imagePromptAttachments: string[];
504
574
  overlayText: string;
@@ -553,31 +623,48 @@ async function generatePortraitCandidateImage(
553
623
  prompt: string;
554
624
  score: number;
555
625
  } | null = null;
626
+ let lastError: Error | null = null;
556
627
 
557
- for (const prompt of attempts) {
558
- const image = await ctx.providers.generateImage({
559
- provider: input.provider,
560
- model: input.imageModel,
561
- prompt,
562
- promptAttachments: input.imagePromptAttachments,
563
- size: sourceImageSizeForProvider(input.provider),
564
- aspectRatio: "9:16",
565
- imageSize: sourceImageOutputSizeForProvider(
566
- input.provider,
567
- input.imageModel,
568
- ),
569
- });
570
- const score = await portraitScore(image.bytes);
571
- if (!best || score < best.score) {
572
- best = { ...image, prompt, score };
573
- }
574
- if (score <= 0.16) {
575
- break;
628
+ for (const provider of providerFailoverOrder(input.provider)) {
629
+ const imageModel = resolveImageModelForAttempt(
630
+ provider,
631
+ input.configuredProvider,
632
+ input.configuredImageModel,
633
+ );
634
+ try {
635
+ for (const prompt of attempts) {
636
+ const image = await ctx.providers.generateImage({
637
+ provider,
638
+ model: imageModel,
639
+ prompt,
640
+ promptAttachments: input.imagePromptAttachments,
641
+ size: sourceImageSizeForProvider(provider),
642
+ aspectRatio: "9:16",
643
+ imageSize: sourceImageOutputSizeForProvider(provider, imageModel),
644
+ });
645
+ const score = await portraitScore(image.bytes);
646
+ if (!best || score < best.score) {
647
+ best = { ...image, prompt, score };
648
+ }
649
+ if (score <= 0.16) {
650
+ return best;
651
+ }
652
+ }
653
+ if (best) {
654
+ return best;
655
+ }
656
+ } catch (error) {
657
+ lastError = error instanceof Error ? error : new Error(String(error));
658
+ ctx.logger.warn("Image generation provider failed, trying next provider", {
659
+ provider,
660
+ model: imageModel,
661
+ message: lastError.message,
662
+ });
576
663
  }
577
664
  }
578
665
 
579
666
  if (!best) {
580
- throw new Error("No image candidate was generated.");
667
+ throw lastError ?? new Error("No image candidate was generated.");
581
668
  }
582
669
 
583
670
  return best;
@@ -611,7 +698,7 @@ function isImageProvider(value: unknown): value is ImageProvider {
611
698
 
612
699
  function sourceImageSizeForProvider(provider: ImageProvider) {
613
700
  if (provider === "openai") {
614
- return "1024x1792";
701
+ return "1024x1536";
615
702
  }
616
703
  return "1080x1920";
617
704
  }
@@ -623,7 +710,7 @@ function defaultImageModelForProvider(provider: ImageProvider) {
623
710
  if (provider === "openrouter") {
624
711
  return "bytedance-seed/seedream-4.5";
625
712
  }
626
- return "gemini-3.1-flash-image-preview";
713
+ return "gemini-2.5-flash-image";
627
714
  }
628
715
 
629
716
  function defaultTextModelForProvider(provider: ImageProvider) {
@@ -646,15 +733,41 @@ function sourceImageOutputSizeForProvider(
646
733
  if (provider !== "gemini") {
647
734
  return undefined;
648
735
  }
649
- if (
650
- model === "gemini-3.1-flash-image-preview" ||
651
- model === "gemini-3-pro-image-preview"
652
- ) {
736
+ if (model === "gemini-3-pro-image-preview") {
653
737
  return "1K" as const;
654
738
  }
655
739
  return undefined;
656
740
  }
657
741
 
742
+ function providerFailoverOrder(primary: ImageProvider): ImageProvider[] {
743
+ return [
744
+ primary,
745
+ ...supportedImageProviders.filter((provider) => provider !== primary),
746
+ ];
747
+ }
748
+
749
+ function resolveImageModelForAttempt(
750
+ provider: ImageProvider,
751
+ configuredProvider: ImageProvider,
752
+ configuredImageModel: string | null,
753
+ ) {
754
+ if (provider === configuredProvider && configuredImageModel) {
755
+ return configuredImageModel;
756
+ }
757
+ return defaultImageModelForProvider(provider);
758
+ }
759
+
760
+ function resolveTextModelForAttempt(
761
+ provider: ImageProvider,
762
+ configuredProvider: ImageProvider,
763
+ configuredTextModel: string | null,
764
+ ) {
765
+ if (provider === configuredProvider && configuredTextModel) {
766
+ return configuredTextModel;
767
+ }
768
+ return defaultTextModelForProvider(provider);
769
+ }
770
+
658
771
  function resolveTextStyleSpec(config: Record<string, unknown>): TextStyleSpec {
659
772
  const fontId = isTemplateFontId(config.captionFont)
660
773
  ? config.captionFont
@@ -759,6 +872,8 @@ const slideshowManifestSchema = z.object({
759
872
  slides: z.array(
760
873
  z.object({
761
874
  index: z.number(),
875
+ sourceType: z.enum(["ai_prompt", "image_source"]),
876
+ sourceValue: z.string(),
762
877
  imagePrompt: z.string(),
763
878
  imagePromptAttachments: z.array(z.string().url()),
764
879
  overlayText: z.string(),
@@ -779,41 +894,196 @@ const slideshowManifestSchema = z.object({
779
894
  ),
780
895
  });
781
896
 
782
- function normalizeSlideInput(input: z.infer<typeof slideInputSchema>) {
897
+ function normalizeSlideInput(
898
+ input: z.infer<typeof slideInputSchema>,
899
+ ): NormalizedSlideInput {
783
900
  if (Array.isArray(input)) {
784
- return {
785
- imagePrompt: input[0],
901
+ return normalizeSlideFields({
902
+ primarySource: input[0],
786
903
  imagePromptAttachments: [],
787
904
  overlayText: input[1],
788
905
  durationMs: input[2] ?? 4000,
789
- };
906
+ });
790
907
  }
791
908
 
792
- return {
793
- imagePrompt: input.image_prompt,
909
+ return normalizeSlideFields({
910
+ primarySource: input.image_prompt,
794
911
  imagePromptAttachments: input.image_prompt_attachments,
795
912
  overlayText: input.caption,
796
913
  durationMs: input.duration_ms,
914
+ });
915
+ }
916
+
917
+ function normalizeSlideFields(input: {
918
+ primarySource: string;
919
+ imagePromptAttachments: string[];
920
+ overlayText: string;
921
+ durationMs: number;
922
+ }): NormalizedSlideInput {
923
+ const sourceValue = input.primarySource.trim();
924
+ const existingImageSource = resolveExistingImageSource(sourceValue);
925
+ return {
926
+ sourceType: existingImageSource ? "image_source" : "ai_prompt",
927
+ sourceValue: existingImageSource ?? sourceValue,
928
+ imagePrompt: sourceValue,
929
+ imagePromptAttachments: input.imagePromptAttachments,
930
+ overlayText: input.overlayText,
931
+ durationMs: input.durationMs,
797
932
  };
798
933
  }
799
934
 
935
+ async function loadExistingSlideImage(sourceValue: string) {
936
+ const resolved = resolveExistingImageSource(sourceValue);
937
+ if (!resolved) {
938
+ throw new Error(`Slide source is not a supported image URL or file path: ${sourceValue}`);
939
+ }
940
+
941
+ const bytes =
942
+ isLocalFileSource(resolved)
943
+ ? readFileSync(resolveLocalFileSourcePath(resolved))
944
+ : await fetchExternalImageBytes(resolved);
945
+ const contentType = await detectImageContentType(bytes);
946
+
947
+ return {
948
+ bytes,
949
+ contentType,
950
+ revisedPrompt: null,
951
+ prompt: sourceValue,
952
+ };
953
+ }
954
+
955
+ function resolveExistingImageSource(value: string) {
956
+ const trimmed = value.trim();
957
+ if (!trimmed) {
958
+ return null;
959
+ }
960
+
961
+ const parsedUrl = parseUrl(trimmed);
962
+ if (
963
+ parsedUrl &&
964
+ ["http:", "https:", "file:", "data:"].includes(parsedUrl.protocol)
965
+ ) {
966
+ return trimmed;
967
+ }
968
+
969
+ const localPath = resolveLocalPathIfPresent(trimmed);
970
+ if (localPath) {
971
+ return localPath;
972
+ }
973
+
974
+ return null;
975
+ }
976
+
977
+ function parseUrl(value: string) {
978
+ try {
979
+ return new URL(value);
980
+ } catch {
981
+ return null;
982
+ }
983
+ }
984
+
985
+ function resolveLocalPathIfPresent(value: string) {
986
+ const expandedPath = value.startsWith("~/")
987
+ ? path.join(process.env.HOME ?? "", value.slice(2))
988
+ : value;
989
+ const candidate = path.resolve(expandedPath);
990
+ return existsSync(candidate) ? candidate : null;
991
+ }
992
+
993
+ function isLocalFileSource(sourceValue: string) {
994
+ const parsedUrl = parseUrl(sourceValue);
995
+ return !parsedUrl || parsedUrl.protocol === "file:";
996
+ }
997
+
998
+ function resolveLocalFileSourcePath(sourceValue: string) {
999
+ const parsedUrl = parseUrl(sourceValue);
1000
+ return parsedUrl?.protocol === "file:"
1001
+ ? fileURLToPath(parsedUrl)
1002
+ : path.resolve(sourceValue);
1003
+ }
1004
+
1005
+ async function fetchExternalImageBytes(sourceValue: string) {
1006
+ const response = await fetch(sourceValue);
1007
+ if (!response.ok) {
1008
+ throw new Error(`Could not fetch slide source image: ${response.status} ${response.statusText}`);
1009
+ }
1010
+ return new Uint8Array(await response.arrayBuffer());
1011
+ }
1012
+
1013
+ async function detectImageContentType(bytes: Uint8Array) {
1014
+ try {
1015
+ const metadata = await sharp(bytes).metadata();
1016
+ if (metadata.format) {
1017
+ return contentTypeForSharpFormat(metadata.format);
1018
+ }
1019
+ } catch (error) {
1020
+ throw new Error(
1021
+ `Slide source could not be decoded as an image: ${error instanceof Error ? error.message : String(error)}`,
1022
+ );
1023
+ }
1024
+ return "image/png";
1025
+ }
1026
+
1027
+ function contentTypeForSharpFormat(format: string) {
1028
+ switch (format) {
1029
+ case "jpeg":
1030
+ return "image/jpeg";
1031
+ case "png":
1032
+ return "image/png";
1033
+ case "webp":
1034
+ return "image/webp";
1035
+ case "gif":
1036
+ return "image/gif";
1037
+ case "avif":
1038
+ return "image/avif";
1039
+ case "tiff":
1040
+ return "image/tiff";
1041
+ case "svg":
1042
+ return "image/svg+xml";
1043
+ case "heif":
1044
+ return "image/heif";
1045
+ default:
1046
+ return `image/${format}`;
1047
+ }
1048
+ }
1049
+
800
1050
  async function generateMetaDetails(
801
1051
  ctx: TemplateJobContext,
802
1052
  input: {
803
1053
  provider: ImageProvider;
804
1054
  textModel: string;
1055
+ configuredProvider: ImageProvider;
1056
+ configuredTextModel: string | null;
805
1057
  slides: SlideOutput[];
806
1058
  metaDetailsPrompt?: string;
807
1059
  },
808
1060
  ): Promise<MetaDetails> {
809
1061
  const prompt = buildMetaDetailsPrompt(input.slides, input.metaDetailsPrompt);
810
- const response = await ctx.providers.generateText({
811
- provider: input.provider,
812
- model: input.textModel,
813
- prompt,
814
- temperature: 0.7,
815
- });
816
- return parseMetaDetailsResponse(response.text, input.slides);
1062
+ let lastError: Error | null = null;
1063
+ for (const provider of providerFailoverOrder(input.provider)) {
1064
+ const textModel = resolveTextModelForAttempt(
1065
+ provider,
1066
+ input.configuredProvider,
1067
+ input.configuredTextModel,
1068
+ );
1069
+ try {
1070
+ const response = await ctx.providers.generateText({
1071
+ provider,
1072
+ model: textModel,
1073
+ prompt,
1074
+ temperature: 0.7,
1075
+ });
1076
+ return parseMetaDetailsResponse(response.text, input.slides);
1077
+ } catch (error) {
1078
+ lastError = error instanceof Error ? error : new Error(String(error));
1079
+ ctx.logger.warn("Metadata provider failed, trying next provider", {
1080
+ provider,
1081
+ model: textModel,
1082
+ message: lastError.message,
1083
+ });
1084
+ }
1085
+ }
1086
+ throw lastError ?? new Error("No metadata provider succeeded.");
817
1087
  }
818
1088
 
819
1089
  function buildMetaDetailsPrompt(
@@ -1224,7 +1494,12 @@ async function portraitScore(input: Uint8Array) {
1224
1494
  thumbnailWidth,
1225
1495
  thumbnailHeight,
1226
1496
  );
1227
- return Math.abs(aspect - 9 / 16) + edgeContrast;
1497
+ const edgeFlatnessPenalty = edgeBandFlatnessPenalty(
1498
+ sample,
1499
+ thumbnailWidth,
1500
+ thumbnailHeight,
1501
+ );
1502
+ return Math.abs(aspect - 9 / 16) + edgeContrast + edgeFlatnessPenalty;
1228
1503
  }
1229
1504
 
1230
1505
  async function chooseLayoutFromImage(
@@ -1541,6 +1816,26 @@ function edgeBandContrast(sample: Buffer, width: number, height: number) {
1541
1816
  return (top + bottom) / 220;
1542
1817
  }
1543
1818
 
1819
+ function edgeBandFlatnessPenalty(sample: Buffer, width: number, height: number) {
1820
+ const bandHeight = Math.max(8, Math.floor(height * 0.16));
1821
+ const centerStart = Math.max(0, Math.floor(height * 0.34));
1822
+ const centerEnd = Math.min(height, Math.ceil(height * 0.66));
1823
+ const top = bandActivity(sample, width, height, 0, bandHeight);
1824
+ const bottom = bandActivity(sample, width, height, height - bandHeight, height);
1825
+ const center = bandActivity(sample, width, height, centerStart, centerEnd);
1826
+ const edgeAverage = (top + bottom) / 2;
1827
+ if (center <= 0) {
1828
+ return 0;
1829
+ }
1830
+
1831
+ const ratio = edgeAverage / center;
1832
+ if (ratio >= 0.72) {
1833
+ return 0;
1834
+ }
1835
+
1836
+ return (0.72 - ratio) * 3.4;
1837
+ }
1838
+
1544
1839
  function bandActivity(
1545
1840
  sample: Buffer,
1546
1841
  width: number,
@@ -2,21 +2,16 @@
2
2
  "template_id": "4c7a7e1a-7f35-4f30-9f86-9c8a63c7f2db",
3
3
  "slug_id": "template_0000",
4
4
  "project_name": "vidfarm_template_0000",
5
- "github_repo": "mevdragon/vidfarm_template_0000",
6
- "source_branch": "production",
5
+ "github_repo": "your-org/your-template-repo",
6
+ "source_branch": "main",
7
7
  "skill_path": "SKILL.md",
8
- "template_module_path": "src/template.js",
9
- "release_controls": {
10
- "shared_remotion_publish": "admin_only",
11
- "platform_activation": "admin_only",
12
- "production_docker_promotion": "admin_only"
13
- },
8
+ "template_module_path": "src/template.ts",
14
9
  "remotion": {
15
- "region": "us-east-1",
16
- "function_name": "remotion-render-4-0-355-mem2048mb-disk2048mb-180sec",
17
- "bucket_name": "remotionlambda-useast1-ujg7c0h43q",
10
+ "region": "local",
11
+ "function_name": "",
12
+ "bucket_name": "",
18
13
  "site_name": "vidfarm-template-0000",
19
- "serve_url": "https://remotionlambda-useast1-ujg7c0h43q.s3.us-east-1.amazonaws.com/sites/vidfarm-template-0000/index.html",
14
+ "serve_url": "",
20
15
  "composition_id": "template-0000",
21
16
  "entry_point": "src/remotion/index.tsx",
22
17
  "props_file": "composition.json",