@mevdragon/vidfarm-devcli 0.2.6 → 0.2.8

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 (56) hide show
  1. package/GETTING_STARTED.developers.md +25 -0
  2. package/README.md +44 -3
  3. package/SKILL.developer.md +147 -11
  4. package/dist/src/account-pages.js +1 -1
  5. package/dist/src/app.js +380 -13
  6. package/dist/src/cli.js +246 -59
  7. package/dist/src/config.js +4 -5
  8. package/dist/src/context.js +16 -0
  9. package/dist/src/db.js +67 -13
  10. package/dist/src/homepage.js +33 -1
  11. package/dist/src/lib/template-paths.js +10 -4
  12. package/dist/src/registry.js +9 -2
  13. package/dist/src/services/auth.js +2 -6
  14. package/dist/src/services/job-logs.js +4 -4
  15. package/dist/src/services/providers.js +470 -0
  16. package/dist/src/services/storage.js +13 -0
  17. package/dist/src/services/template-certification.js +16 -2
  18. package/dist/src/services/template-loader.js +17 -1
  19. package/dist/src/services/template-sources.js +338 -17
  20. package/{templates/template_0000 → dist/templates/vidfarm_template_0000}/src/remotion/Root.js +1 -0
  21. package/dist/templates/{template_0000 → vidfarm_template_0000}/src/template.js +9 -3
  22. package/package.json +4 -3
  23. package/templates/{template_0000 → vidfarm_template_0000}/README.md +23 -0
  24. package/templates/{template_0000 → vidfarm_template_0000}/package.json +0 -1
  25. package/templates/{template_0000 → vidfarm_template_0000}/src/sdk.ts +18 -0
  26. package/templates/{template_0000 → vidfarm_template_0000}/src/template.js +9 -3
  27. package/templates/{template_0000 → vidfarm_template_0000}/src/template.ts +10 -3
  28. package/templates/vidfarm_template_0000/tmp/solobacterium-moorei-slideshow.request.json +31 -0
  29. /package/dist/templates/{template_0000 → vidfarm_template_0000}/src/lib/images.js +0 -0
  30. /package/dist/templates/{template_0000 → vidfarm_template_0000}/src/remotion/index.js +0 -0
  31. /package/dist/templates/{template_0000 → vidfarm_template_0000}/src/sdk.js +0 -0
  32. /package/dist/templates/{template_0000 → vidfarm_template_0000}/src/style-options.js +0 -0
  33. /package/dist/templates/{template_0000 → vidfarm_template_0000}/src/template-dna.js +0 -0
  34. /package/templates/{template_0000 → vidfarm_template_0000}/SKILL.md +0 -0
  35. /package/templates/{template_0000 → vidfarm_template_0000}/assets/Abel-Regular.ttf +0 -0
  36. /package/templates/{template_0000 → vidfarm_template_0000}/assets/DMSerifDisplay-Regular.ttf +0 -0
  37. /package/templates/{template_0000 → vidfarm_template_0000}/assets/Montserrat[wght].ttf +0 -0
  38. /package/templates/{template_0000 → vidfarm_template_0000}/assets/SourceCodePro[wght].ttf +0 -0
  39. /package/templates/{template_0000 → vidfarm_template_0000}/assets/TikTokSans-SemiBold.ttf +0 -0
  40. /package/templates/{template_0000 → vidfarm_template_0000}/assets/Yesteryear-Regular.ttf +0 -0
  41. /package/templates/{template_0000 → vidfarm_template_0000}/composition.json +0 -0
  42. /package/templates/{template_0000 → vidfarm_template_0000}/research/preview/.gitkeep +0 -0
  43. /package/templates/{template_0000 → vidfarm_template_0000}/research/source_notes.md +0 -0
  44. /package/templates/{template_0000 → vidfarm_template_0000}/src/lib/images.js +0 -0
  45. /package/templates/{template_0000 → vidfarm_template_0000}/src/lib/images.ts +0 -0
  46. /package/{dist/templates/template_0000 → templates/vidfarm_template_0000}/src/remotion/Root.js +0 -0
  47. /package/templates/{template_0000 → vidfarm_template_0000}/src/remotion/Root.tsx +0 -0
  48. /package/templates/{template_0000 → vidfarm_template_0000}/src/remotion/index.js +0 -0
  49. /package/templates/{template_0000 → vidfarm_template_0000}/src/remotion/index.tsx +0 -0
  50. /package/templates/{template_0000 → vidfarm_template_0000}/src/sdk.js +0 -0
  51. /package/templates/{template_0000 → vidfarm_template_0000}/src/style-options.js +0 -0
  52. /package/templates/{template_0000 → vidfarm_template_0000}/src/style-options.ts +0 -0
  53. /package/templates/{template_0000 → vidfarm_template_0000}/src/template-dna.js +0 -0
  54. /package/templates/{template_0000 → vidfarm_template_0000}/src/template-dna.ts +0 -0
  55. /package/templates/{template_0000 → vidfarm_template_0000}/template.config.json +0 -0
  56. /package/templates/{template_0000 → vidfarm_template_0000}/tsconfig.json +0 -0
package/dist/src/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
3
3
  import { execFile } from "node:child_process";
4
- import { randomUUID } from "node:crypto";
4
+ import { createHash, randomUUID } from "node:crypto";
5
5
  import os from "node:os";
6
6
  import path from "node:path";
7
7
  import { parseArgs } from "node:util";
@@ -37,6 +37,18 @@ async function main() {
37
37
  await runImportSourceProdCommand(process.argv.slice(3));
38
38
  return;
39
39
  }
40
+ if (command === "register-source-hosted") {
41
+ await runRegisterSourceHostedCommand(process.argv.slice(3));
42
+ return;
43
+ }
44
+ if (command === "publish-update-hosted") {
45
+ await runPublishUpdateHostedCommand(process.argv.slice(3));
46
+ return;
47
+ }
48
+ if (command === "register-source-prod") {
49
+ await runRegisterSourceProdCommand(process.argv.slice(3));
50
+ return;
51
+ }
40
52
  if (command === "deploy-template-cycle") {
41
53
  await runDeployTemplateCycleCommand(process.argv.slice(3));
42
54
  return;
@@ -249,14 +261,17 @@ async function runImportSourceCommand(argv) {
249
261
  sourceId: registration.source.id,
250
262
  commitSha: parsed.values["commit-sha"] ?? null
251
263
  });
264
+ let sourceRecord = registration.source;
252
265
  let activated = null;
253
266
  if (parsed.values.activate) {
267
+ sourceRecord = sources.approveSource({ sourceId: registration.source.id });
268
+ sources.approveRelease({ releaseId: release.id });
254
269
  const result = await sources.activateRelease({ releaseId: release.id });
255
270
  templateRegistry.registerRuntimeTemplate(result.template);
256
271
  activated = result.release;
257
272
  }
258
273
  console.log(JSON.stringify({
259
- source: registration.source,
274
+ source: sourceRecord,
260
275
  registration_action: registration.action,
261
276
  sync: registration.sync,
262
277
  release,
@@ -357,20 +372,121 @@ async function runGenerateTemplateCommand(argv) {
357
372
  site_name: siteName,
358
373
  research: stagedInputs,
359
374
  template_dna_module: moduleSync,
360
- dna_analysis_runs: dnaRuns
375
+ dna_analysis_runs: dnaRuns,
376
+ required_template_contract: {
377
+ template_fields: [
378
+ "id",
379
+ "slugId",
380
+ "version",
381
+ "about.title",
382
+ "about.description",
383
+ "about.viral_dna",
384
+ "about.visual_dna",
385
+ "about.preview_media",
386
+ "about.link_to_original",
387
+ "about.proposal_generator",
388
+ "about.sample_prompts",
389
+ "configSchema",
390
+ ],
391
+ operation_fields: [
392
+ "operations.<name>.workflow",
393
+ "operations.<name>.smokeTestPayload",
394
+ "jobs.<workflow>",
395
+ ],
396
+ files: [
397
+ "src/template.ts",
398
+ "SKILL.md",
399
+ ],
400
+ },
401
+ next_steps: [
402
+ "Review src/template.ts and replace the starter proposal_generator with template-specific proposal instructions.",
403
+ "Review src/template.ts and replace the starter sample_prompts with prompts that fit the new format.",
404
+ "If DNA analysis was skipped or the source research changed, run analyze-viral-dna and analyze-visual-dna.",
405
+ "Update SKILL.md so the usage examples match the template's real operations and payloads.",
406
+ `Run validate-template against ${slugId} and launch at least one real local job before treating the template as ready.`,
407
+ ],
408
+ }, null, 2));
409
+ }
410
+ async function runRegisterSourceProdCommand(argv) {
411
+ const input = parseProdTemplateCommandArgs(argv, {
412
+ envFile: ".env.production",
413
+ baseUrl: "https://vidfarm.cloud.zoomgtm.com",
414
+ activate: false
415
+ });
416
+ const registration = await registerSourceViaApi(input);
417
+ console.log(JSON.stringify({
418
+ mode: "prod-register-source",
419
+ base_url: input.baseUrl,
420
+ source: registration.source,
421
+ registration_action: registration.registration_action,
422
+ sync: registration.sync ?? null
423
+ }, null, 2));
424
+ }
425
+ async function runRegisterSourceHostedCommand(argv) {
426
+ const input = parseProdTemplateCommandArgs(argv, {
427
+ envFile: ".env",
428
+ baseUrl: process.env.VIDFARM_BASE_URL ?? "https://vidfarm.cloud.zoomgtm.com",
429
+ activate: false
430
+ });
431
+ const registration = await registerSourceViaApi(input);
432
+ console.log(JSON.stringify({
433
+ mode: "hosted-register-source",
434
+ base_url: input.baseUrl,
435
+ source: registration.source,
436
+ registration_action: registration.registration_action,
437
+ sync: registration.sync ?? null,
438
+ next_step: "Platform admin reviews the source, imports a release, approves it, and activates it."
439
+ }, null, 2));
440
+ }
441
+ async function runPublishUpdateHostedCommand(argv) {
442
+ const input = parseProdTemplateCommandArgs(argv, {
443
+ envFile: ".env",
444
+ baseUrl: process.env.VIDFARM_BASE_URL ?? "https://vidfarm.cloud.zoomgtm.com",
445
+ activate: false
446
+ });
447
+ if (!input.commitSha) {
448
+ throw new Error("publish-update-hosted requires --commit-sha.");
449
+ }
450
+ const registration = await registerSourceViaApi(input);
451
+ const artifact = await packageTemplateArtifact(input.repoDir, input.commitSha);
452
+ const presign = await postProdTemplateApi(input, `/api/v1/templates/sources/${registration.source.id}/artifacts/presign`, {
453
+ method: "POST",
454
+ body: JSON.stringify({
455
+ commit_sha: input.commitSha
456
+ })
457
+ });
458
+ await uploadArtifactArchive(presign, artifact.buffer);
459
+ const payload = await postProdTemplateApi(input, `/api/v1/templates/sources/${registration.source.id}/publish`, {
460
+ method: "POST",
461
+ body: JSON.stringify({
462
+ commit_sha: input.commitSha,
463
+ artifact_sha256: artifact.sha256
464
+ })
465
+ });
466
+ console.log(JSON.stringify({
467
+ mode: "hosted-publish-update",
468
+ base_url: input.baseUrl,
469
+ source: payload.source,
470
+ release: payload.release,
471
+ artifact: {
472
+ artifact_key: presign.artifact_key,
473
+ artifact_manifest_key: presign.artifact_manifest_key,
474
+ sha256: artifact.sha256
475
+ },
476
+ publish_action: payload.publish_action,
477
+ developer_handoff: payload.developer_handoff ?? null
361
478
  }, null, 2));
362
479
  }
363
480
  async function runImportSourceProdCommand(argv) {
364
481
  const input = parseProdTemplateCommandArgs(argv, {
365
482
  envFile: ".env.production",
366
- stackName: "VidfarmProdStack",
483
+ baseUrl: "https://vidfarm.cloud.zoomgtm.com",
367
484
  activate: true
368
485
  });
369
486
  const result = await importSourceIntoProd(input);
370
487
  console.log(JSON.stringify({
371
488
  mode: "prod-import-source",
372
- stack_name: input.stackName,
373
- instance_id: result.instanceId,
489
+ base_url: input.baseUrl,
374
490
  source: result.payload.source,
375
491
  release: result.payload.release,
376
492
  activated_release: result.payload.activated_release ?? null
@@ -379,9 +495,13 @@ async function runImportSourceProdCommand(argv) {
379
495
  async function runDeployTemplateCycleCommand(argv) {
380
496
  const input = parseProdTemplateCommandArgs(argv, {
381
497
  envFile: ".env.production",
498
+ baseUrl: "https://vidfarm.cloud.zoomgtm.com",
382
499
  stackName: "VidfarmProdStack",
383
500
  activate: true
384
501
  });
502
+ if (!input.stackName) {
503
+ throw new Error("deploy-template-cycle requires --stack-name.");
504
+ }
385
505
  const importResult = await importSourceIntoProd(input);
386
506
  const currentImage = await getProdCurrentImage({
387
507
  stackName: input.stackName,
@@ -399,7 +519,6 @@ async function runDeployTemplateCycleCommand(argv) {
399
519
  console.log(JSON.stringify({
400
520
  mode: "deploy-template-cycle",
401
521
  stack_name: input.stackName,
402
- instance_id: importResult.instanceId,
403
522
  release: importResult.payload.activated_release ?? importResult.payload.release,
404
523
  restart_image: currentImage.image
405
524
  }, null, 2));
@@ -442,7 +561,37 @@ async function runCopyReferenceTemplateCommand(argv) {
442
561
  template_dir: destinationDir,
443
562
  template_id: "4c7a7e1a-7f35-4f30-9f86-9c8a63c7f2db",
444
563
  slug_id: "template_0000",
445
- rewritten: false
564
+ rewritten: false,
565
+ required_template_contract: {
566
+ template_fields: [
567
+ "id",
568
+ "slugId",
569
+ "version",
570
+ "about.title",
571
+ "about.description",
572
+ "about.viral_dna",
573
+ "about.visual_dna",
574
+ "about.preview_media",
575
+ "about.link_to_original",
576
+ "about.proposal_generator",
577
+ "about.sample_prompts",
578
+ "configSchema",
579
+ ],
580
+ operation_fields: [
581
+ "operations.<name>.workflow",
582
+ "operations.<name>.smokeTestPayload",
583
+ "jobs.<workflow>",
584
+ ],
585
+ files: [
586
+ "src/template.ts",
587
+ "SKILL.md",
588
+ ],
589
+ },
590
+ next_steps: [
591
+ "Treat src/template.ts as the canonical entrypoint and review the required about metadata before copying patterns into a new template.",
592
+ "Use the starter proposal_generator and sample_prompts as placeholders only; rewrite them for the real format you are building.",
593
+ "Keep the template-local SKILL.md aligned with the operations and payloads in code.",
594
+ ],
446
595
  }, null, 2));
447
596
  }
448
597
  async function runAnalyzeTemplateDnaCommand(mode, argv) {
@@ -569,7 +718,7 @@ async function runSessionCommand(argv) {
569
718
  starter_style_options: {
570
719
  fonts: STARTER_TEMPLATE_FONT_OPTIONS,
571
720
  text_background_colors: STARTER_TEMPLATE_TEXT_BACKGROUND_COLOR_OPTIONS,
572
- starter_template_file: "templates/template_0000/src/style-options.ts"
721
+ starter_template_file: "templates/vidfarm_template_0000/src/style-options.ts"
573
722
  },
574
723
  headers: {
575
724
  "vidfarm-api-key": session.apiKey,
@@ -656,8 +805,8 @@ function toProjectName(folderName) {
656
805
  }
657
806
  function resolveStarterTemplateDir() {
658
807
  const candidates = [
659
- path.resolve(import.meta.dirname, "..", "templates", "template_0000"),
660
- path.resolve(import.meta.dirname, "..", "..", "templates", "template_0000")
808
+ path.resolve(import.meta.dirname, "..", "templates", "vidfarm_template_0000"),
809
+ path.resolve(import.meta.dirname, "..", "..", "templates", "vidfarm_template_0000")
661
810
  ];
662
811
  for (const candidate of candidates) {
663
812
  if (existsSync(candidate)) {
@@ -679,7 +828,10 @@ function parseProdTemplateCommandArgs(argv, defaults) {
679
828
  "install-command": { type: "string", default: "npm install" },
680
829
  "build-command": { type: "string", default: "npm run build" },
681
830
  "commit-sha": { type: "string" },
831
+ "repo-dir": { type: "string", default: "." },
682
832
  "env-file": { type: "string", default: defaults.envFile },
833
+ "base-url": { type: "string", default: defaults.baseUrl },
834
+ "api-key": { type: "string" },
683
835
  "stack-name": { type: "string", default: defaults.stackName }
684
836
  }
685
837
  });
@@ -691,6 +843,11 @@ function parseProdTemplateCommandArgs(argv, defaults) {
691
843
  throw new Error("Command requires --template-id, --slug-id, --repo-url, and --template-module-path.");
692
844
  }
693
845
  deriveTemplateRootDirFromModulePath(templateModulePath);
846
+ loadEnvFile(parsed.values["env-file"]);
847
+ const apiKey = parsed.values["api-key"] ?? process.env.VIDFARM_API_KEY;
848
+ if (!apiKey) {
849
+ throw new Error("Missing Vidfarm API key. Pass --api-key or set VIDFARM_API_KEY in the selected env file.");
850
+ }
694
851
  return {
695
852
  templateId,
696
853
  slugId,
@@ -701,57 +858,93 @@ function parseProdTemplateCommandArgs(argv, defaults) {
701
858
  installCommand: parsed.values["install-command"],
702
859
  buildCommand: parsed.values["build-command"],
703
860
  commitSha: parsed.values["commit-sha"] ?? undefined,
861
+ repoDir: path.resolve(process.cwd(), parsed.values["repo-dir"]),
704
862
  envFile: parsed.values["env-file"],
705
- stackName: parsed.values["stack-name"],
863
+ baseUrl: normalizeBaseUrl(parsed.values["base-url"]),
864
+ apiKey,
865
+ stackName: parsed.values["stack-name"] || undefined,
706
866
  activate: defaults.activate
707
867
  };
708
868
  }
709
869
  async function importSourceIntoProd(input) {
710
- loadEnvFile(input.envFile);
711
- const instanceId = await resolveStackOutput(input.stackName, "VidfarmInstanceId");
712
- const containerScript = buildContainerImportCommand(input);
713
- const hostScript = [
714
- "set -euo pipefail",
715
- `sudo docker exec -e GITHUB_TOKEN=${shellQuote(resolveGithubToken())} vidfarm /bin/bash -lc ${shellQuote(containerScript)}`
716
- ].join("\n");
717
- const stdout = await runSsmScript({
718
- instanceId,
719
- comment: `Vidfarm import ${input.slugId}`,
720
- script: hostScript
870
+ const registration = await registerSourceViaApi(input);
871
+ const source = await postProdTemplateApi(input, `/api/v1/templates/sources/${registration.source.id}/approve`, { method: "POST" });
872
+ const releaseResponse = await postProdTemplateApi(input, `/api/v1/templates/sources/${registration.source.id}/import`, {
873
+ method: "POST",
874
+ body: JSON.stringify({
875
+ commit_sha: input.commitSha ?? undefined
876
+ })
721
877
  });
878
+ const approvedRelease = await postProdTemplateApi(input, `/api/v1/templates/releases/${releaseResponse.release.id}/approve`, { method: "POST" });
879
+ const activatedRelease = input.activate
880
+ ? await postProdTemplateApi(input, `/api/v1/templates/releases/${releaseResponse.release.id}/activate`, { method: "POST" })
881
+ : null;
722
882
  return {
723
- instanceId,
724
- payload: extractLastJson(stdout)
883
+ payload: {
884
+ source: source.source,
885
+ release: approvedRelease.release,
886
+ activated_release: activatedRelease?.release ?? null
887
+ }
725
888
  };
726
889
  }
727
- function buildContainerImportCommand(input) {
728
- const args = [
729
- "node",
730
- "/app/dist/src/cli.js",
731
- "import-source",
732
- "--template-id", input.templateId,
733
- "--slug-id", input.slugId,
734
- "--repo-url", input.repoUrl,
735
- "--branch", input.branch,
736
- "--template-module-path", input.templateModulePath,
737
- "--skill-path", input.skillPath,
738
- "--install-command", input.installCommand,
739
- "--build-command", input.buildCommand
740
- ];
741
- if (input.commitSha) {
742
- args.push("--commit-sha", input.commitSha);
743
- }
744
- if (!input.activate) {
745
- throw new Error("Non-activating prod import is not implemented.");
746
- }
747
- const lines = [
748
- "set -euo pipefail",
749
- "if [ -n \"${GITHUB_TOKEN:-}\" ]; then",
750
- " git config --global url.\"https://x-access-token:${GITHUB_TOKEN}@github.com/\".insteadOf \"https://github.com/\"",
751
- "fi",
752
- args.map(shellQuote).join(" ")
753
- ];
754
- return lines.join("\n");
890
+ async function registerSourceViaApi(input) {
891
+ return postProdTemplateApi(input, "/api/v1/templates/sources", {
892
+ method: "POST",
893
+ body: JSON.stringify({
894
+ template_id: input.templateId,
895
+ slug_id: input.slugId,
896
+ repo_url: input.repoUrl,
897
+ branch: input.branch,
898
+ template_module_path: input.templateModulePath,
899
+ skill_path: input.skillPath,
900
+ install_command: input.installCommand,
901
+ build_command: input.buildCommand
902
+ })
903
+ });
904
+ }
905
+ async function postProdTemplateApi(input, pathname, init) {
906
+ const response = await fetch(`${input.baseUrl}${pathname}`, {
907
+ ...init,
908
+ headers: {
909
+ "content-type": "application/json",
910
+ "vidfarm-api-key": input.apiKey,
911
+ ...(init.headers ?? {})
912
+ }
913
+ });
914
+ const payload = await response.json().catch(() => ({}));
915
+ if (!response.ok) {
916
+ throw new Error(typeof payload.error === "string" ? payload.error : `Request failed with ${response.status}.`);
917
+ }
918
+ return payload;
919
+ }
920
+ async function packageTemplateArtifact(repoDir, commitSha) {
921
+ const tempDir = mkdtempSync(path.join(os.tmpdir(), "vidfarm-template-artifact-"));
922
+ const repoRoot = (await execFileAsync("git", ["-C", repoDir, "rev-parse", "--show-toplevel"])).stdout.trim();
923
+ const artifactPath = path.join(tempDir, "template-source.tar.gz");
924
+ try {
925
+ await execFileAsync("git", ["-C", repoRoot, "archive", "--format=tar.gz", `--output=${artifactPath}`, commitSha], {
926
+ cwd: repoRoot
927
+ });
928
+ const buffer = readFileSync(artifactPath);
929
+ return {
930
+ buffer,
931
+ sha256: createHash("sha256").update(buffer).digest("hex")
932
+ };
933
+ }
934
+ finally {
935
+ rmSync(tempDir, { recursive: true, force: true });
936
+ }
937
+ }
938
+ async function uploadArtifactArchive(presign, buffer) {
939
+ const response = await fetch(presign.upload.url, {
940
+ method: presign.upload.method,
941
+ headers: presign.upload.headers,
942
+ body: new Uint8Array(buffer)
943
+ });
944
+ if (!response.ok) {
945
+ const body = await response.text().catch(() => "");
946
+ throw new Error(body || `Artifact upload failed with ${response.status}.`);
947
+ }
755
948
  }
756
949
  function loadEnvFile(envFile) {
757
950
  const resolved = path.resolve(process.cwd(), envFile);
@@ -823,12 +1016,6 @@ function inferUploadContentType(fileName) {
823
1016
  return "application/octet-stream";
824
1017
  }
825
1018
  }
826
- function resolveGithubToken() {
827
- return process.env.VIDFARM_GITHUB_TOKEN
828
- ?? process.env.GITHUB_TOKEN
829
- ?? process.env.GH_TOKEN
830
- ?? "";
831
- }
832
1019
  async function resolveStackOutput(stackName, outputKey) {
833
1020
  const { stdout } = await runLocalCommand("aws", [
834
1021
  "cloudformation",
@@ -32,6 +32,7 @@ const schema = z.object({
32
32
  GEMINI_API_KEY: z.string().optional(),
33
33
  PERPLEXITY_API_KEY: z.string().optional(),
34
34
  SUPERAGENCY_KEY: z.string().optional(),
35
+ ARTIFACT_SIGNING_SECRET: z.string().optional(),
35
36
  REMOTION_REGION: z.string().default("us-east-1"),
36
37
  REMOTION_BUCKET_NAME: z.string().optional(),
37
38
  REMOTION_SITE_NAME: z.string().optional(),
@@ -43,7 +44,8 @@ const schema = z.object({
43
44
  REMOTION_MODE: z.enum(["auto", "mock", "local", "lambda"]).default("auto"),
44
45
  MOCK_PROVIDER_RESPONSES: z.string().optional(),
45
46
  VIDFARM_ADMIN_EMAILS: z.string().default(""),
46
- VIDFARM_DEVELOPER_EMAILS: z.string().default(""),
47
+ VIDFARM_GITHUB_TOKEN: z.string().optional(),
48
+ GITHUB_TOKEN: z.string().optional(),
47
49
  TEMPLATE_SOURCE_ROOT: z.string().default("./data/template-sources")
48
50
  });
49
51
  const parsed = schema.parse(process.env);
@@ -58,6 +60,7 @@ export const config = {
58
60
  VIDFARM_DATA_DIR: dataDir,
59
61
  TEMPLATE_SOURCE_ROOT: templateSourceRoot,
60
62
  PUBLIC_BASE_URL: publicBaseUrl,
63
+ ARTIFACT_SIGNING_SECRET: parsed.ARTIFACT_SIGNING_SECRET || parsed.SUPERAGENCY_KEY || "",
61
64
  isProduction: parsed.NODE_ENV === "production",
62
65
  mockProviders: parsed.MOCK_PROVIDER_RESPONSES === "true" ||
63
66
  (parsed.MOCK_PROVIDER_RESPONSES == null && parsed.NODE_ENV !== "production"),
@@ -66,8 +69,4 @@ export const config = {
66
69
  .split(",")
67
70
  .map((value) => value.trim().toLowerCase())
68
71
  .filter(Boolean),
69
- developerEmails: parsed.VIDFARM_DEVELOPER_EMAILS
70
- .split(",")
71
- .map((value) => value.trim().toLowerCase())
72
- .filter(Boolean)
73
72
  };
@@ -176,6 +176,14 @@ export function createTemplateJobContext(input) {
176
176
  ...request
177
177
  });
178
178
  },
179
+ async generateSpeech(request) {
180
+ return input.providers.generateSpeech({
181
+ customerId: input.customer.id,
182
+ jobId: input.job.id,
183
+ workerId: input.workerId,
184
+ ...request
185
+ });
186
+ },
179
187
  async generateImage(request) {
180
188
  return input.providers.generateImage({
181
189
  customerId: input.customer.id,
@@ -191,6 +199,14 @@ export function createTemplateJobContext(input) {
191
199
  workerId: input.workerId,
192
200
  ...request
193
201
  });
202
+ },
203
+ async transcribeSpeech(request) {
204
+ return input.providers.transcribeSpeech({
205
+ customerId: input.customer.id,
206
+ jobId: input.job.id,
207
+ workerId: input.workerId,
208
+ ...request
209
+ });
194
210
  }
195
211
  },
196
212
  remotion: {
package/dist/src/db.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import Database from "better-sqlite3";
2
2
  import { config } from "./config.js";
3
+ import { createId } from "./lib/ids.js";
3
4
  import { parseJson, stringifyJson } from "./lib/json.js";
4
5
  import { nowIso } from "./lib/time.js";
5
6
  const db = new Database(config.VIDFARM_DB_PATH);
@@ -192,6 +193,8 @@ create table if not exists template_releases (
192
193
  checkout_path text not null,
193
194
  skill_path text not null,
194
195
  module_path text not null,
196
+ artifact_manifest_key text,
197
+ artifact_key text,
195
198
  status text not null,
196
199
  certification_report_json text,
197
200
  activated_at text,
@@ -235,6 +238,23 @@ db.exec(`
235
238
  where slug_id is null or trim(slug_id) = '';
236
239
  `);
237
240
  db.exec(`create unique index if not exists idx_template_sources_slug_id on template_sources(slug_id);`);
241
+ db.exec(`
242
+ update template_sources
243
+ set status = 'approved'
244
+ where status = 'active';
245
+ `);
246
+ db.exec(`
247
+ update template_releases
248
+ set status = 'pending_approval'
249
+ where status = 'certified';
250
+ `);
251
+ const templateReleaseColumns = db.prepare(`pragma table_info(template_releases)`).all();
252
+ if (!templateReleaseColumns.some((column) => column.name === "artifact_manifest_key")) {
253
+ db.exec(`alter table template_releases add column artifact_manifest_key text;`);
254
+ }
255
+ if (!templateReleaseColumns.some((column) => column.name === "artifact_key")) {
256
+ db.exec(`alter table template_releases add column artifact_key text;`);
257
+ }
238
258
  db.exec(`drop index if exists idx_job_events_job_time;`);
239
259
  db.exec(`drop table if exists job_events;`);
240
260
  function mapJob(row) {
@@ -288,6 +308,8 @@ function mapTemplateRelease(row) {
288
308
  checkoutPath: String(row.checkout_path),
289
309
  skillPath: String(row.skill_path),
290
310
  modulePath: String(row.module_path),
311
+ artifactManifestKey: row.artifact_manifest_key ? String(row.artifact_manifest_key) : null,
312
+ artifactKey: row.artifact_key ? String(row.artifact_key) : null,
291
313
  status: row.status,
292
314
  certificationReport: parseJson(row.certification_report_json, null),
293
315
  activatedAt: row.activated_at ? String(row.activated_at) : null,
@@ -390,6 +412,21 @@ export const database = {
390
412
  where id = ?
391
413
  `).run(isPaidPlan ? 1 : 0, nowIso(), customerId);
392
414
  },
415
+ setCustomerAccessByEmail(input) {
416
+ const normalizedEmail = input.email.trim().toLowerCase();
417
+ const existing = this.getCustomerByEmail(normalizedEmail);
418
+ return this.upsertCustomer({
419
+ id: existing?.id ?? createId("cus"),
420
+ email: normalizedEmail,
421
+ name: input.name === undefined ? (existing?.name ?? null) : input.name,
422
+ defaultWebhookUrl: existing?.defaultWebhookUrl ?? null,
423
+ isDeveloper: input.isDeveloper === undefined ? (existing?.isDeveloper ?? false) : input.isDeveloper,
424
+ isPaidPlan: input.isPaidPlan === undefined ? (existing?.isPaidPlan ?? false) : input.isPaidPlan,
425
+ about: existing?.about ?? null,
426
+ groupchatUrl: existing?.groupchatUrl ?? null,
427
+ flockposterApiKey: existing?.flockposterApiKey ?? null
428
+ });
429
+ },
393
430
  updateCustomerProfile(input) {
394
431
  db.prepare(`
395
432
  update customers
@@ -895,23 +932,28 @@ export const database = {
895
932
  `).get(repoUrl, branch, templateModulePath);
896
933
  return row ? mapTemplateSource(row) : null;
897
934
  },
898
- listTemplateSources() {
899
- return db.prepare(`select * from template_sources order by template_id asc`).all().map(mapTemplateSource);
935
+ listTemplateSources(status) {
936
+ const rows = status
937
+ ? db.prepare(`select * from template_sources where status = ? order by created_at asc`).all(status)
938
+ : db.prepare(`select * from template_sources order by created_at asc`).all();
939
+ return rows.map(mapTemplateSource);
900
940
  },
901
941
  createTemplateRelease(record) {
902
942
  const timestamp = nowIso();
903
943
  db.prepare(`
904
944
  insert into template_releases (
905
- id, source_id, template_id, branch, commit_sha, checkout_path, skill_path, module_path, status,
945
+ id, source_id, template_id, branch, commit_sha, checkout_path, skill_path, module_path, artifact_manifest_key, artifact_key, status,
906
946
  certification_report_json, activated_at, created_at, updated_at
907
947
  ) values (
908
- @id, @source_id, @template_id, @branch, @commit_sha, @checkout_path, @skill_path, @module_path, @status,
948
+ @id, @source_id, @template_id, @branch, @commit_sha, @checkout_path, @skill_path, @module_path, @artifact_manifest_key, @artifact_key, @status,
909
949
  @certification_report_json, @activated_at, @created_at, @updated_at
910
950
  )
911
951
  on conflict(source_id, commit_sha) do update set
912
952
  checkout_path = excluded.checkout_path,
913
953
  skill_path = excluded.skill_path,
914
954
  module_path = excluded.module_path,
955
+ artifact_manifest_key = excluded.artifact_manifest_key,
956
+ artifact_key = excluded.artifact_key,
915
957
  status = excluded.status,
916
958
  certification_report_json = excluded.certification_report_json,
917
959
  activated_at = excluded.activated_at,
@@ -925,6 +967,8 @@ export const database = {
925
967
  checkout_path: record.checkoutPath,
926
968
  skill_path: record.skillPath,
927
969
  module_path: record.modulePath,
970
+ artifact_manifest_key: record.artifactManifestKey ?? null,
971
+ artifact_key: record.artifactKey ?? null,
928
972
  status: record.status,
929
973
  certification_report_json: stringifyJson(record.certificationReport ?? null),
930
974
  activated_at: record.activatedAt ?? null,
@@ -951,10 +995,16 @@ export const database = {
951
995
  `).get(sourceId);
952
996
  return row ? mapTemplateRelease(row) : null;
953
997
  },
954
- listTemplateReleases(templateId) {
955
- const rows = templateId
956
- ? db.prepare(`select * from template_releases where template_id = ? order by created_at desc`).all(templateId)
957
- : db.prepare(`select * from template_releases order by created_at desc`).all();
998
+ listTemplateReleases(input) {
999
+ const templateId = input?.templateId;
1000
+ const status = input?.status;
1001
+ const rows = templateId && status
1002
+ ? db.prepare(`select * from template_releases where template_id = ? and status = ? order by created_at desc`).all(templateId, status)
1003
+ : templateId
1004
+ ? db.prepare(`select * from template_releases where template_id = ? order by created_at desc`).all(templateId)
1005
+ : status
1006
+ ? db.prepare(`select * from template_releases where status = ? order by created_at desc`).all(status)
1007
+ : db.prepare(`select * from template_releases order by created_at desc`).all();
958
1008
  return rows.map(mapTemplateRelease);
959
1009
  },
960
1010
  getActiveTemplateReleases() {
@@ -979,12 +1029,16 @@ export const database = {
979
1029
  updated_at: nowIso()
980
1030
  });
981
1031
  },
982
- clearActiveTemplateReleases(templateId) {
1032
+ clearActiveTemplateReleases(templateId, fallbackStatus = "approved") {
983
1033
  db.prepare(`
984
1034
  update template_releases
985
- set status = case when status = 'active' then 'certified' else status end,
986
- updated_at = ?
987
- where template_id = ?
988
- `).run(nowIso(), templateId);
1035
+ set status = case when status = 'active' then @fallback_status else status end,
1036
+ updated_at = @updated_at
1037
+ where template_id = @template_id
1038
+ `).run({
1039
+ fallback_status: fallbackStatus,
1040
+ updated_at: nowIso(),
1041
+ template_id: templateId
1042
+ });
989
1043
  }
990
1044
  };