@mevdragon/vidfarm-devcli 0.2.7 → 0.2.9

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 (98) hide show
  1. package/.env.example +9 -0
  2. package/GETTING_STARTED.developers.md +1 -30
  3. package/README.md +22 -5
  4. package/SKILL.developer.md +464 -12
  5. package/auto-create-templates/AUTO_CREATE_TEMPLATES.md +475 -0
  6. package/auto-create-templates/extractor-system-prompt.md +113 -0
  7. package/auto-create-templates/production-graph.schema.json +276 -0
  8. package/auto-create-templates/runbook.md +140 -0
  9. package/auto-create-templates/template-plan.schema.json +230 -0
  10. package/auto-create-templates/template-planner-prompt.md +75 -0
  11. package/dist/src/account-pages-legacy.js +9064 -0
  12. package/dist/src/account-pages.js +41 -620
  13. package/dist/src/app.js +7173 -494
  14. package/dist/src/cli.js +525 -29
  15. package/dist/src/config.js +14 -7
  16. package/dist/src/context.js +51 -35
  17. package/dist/src/db.js +1049 -55
  18. package/dist/src/dev-app-legacy.js +693 -0
  19. package/dist/src/dev-app.js +4 -904
  20. package/dist/src/domain.js +1 -1
  21. package/dist/src/editor-chat-history.js +72 -0
  22. package/dist/src/editor-chat.js +202 -0
  23. package/dist/src/frontend/flockposter-cache-store.js +116 -0
  24. package/dist/src/frontend/homepage-client.js +114 -0
  25. package/dist/src/frontend/homepage-shared.js +3 -0
  26. package/dist/src/frontend/homepage-store.js +27 -0
  27. package/dist/src/frontend/homepage-view.js +147 -0
  28. package/dist/src/frontend/page-runtime-client.js +100 -0
  29. package/dist/src/frontend/page-runtime-store.js +8 -0
  30. package/dist/src/frontend/template-editor-chat.js +2261 -0
  31. package/dist/src/homepage.js +695 -339
  32. package/dist/src/lib/template-paths.js +10 -4
  33. package/dist/src/lib/template-style-options.js +95 -15
  34. package/dist/src/page-runtime.js +1 -0
  35. package/dist/src/page-shell.js +941 -0
  36. package/dist/src/primitive-context.js +163 -0
  37. package/dist/src/primitive-registry.js +340 -0
  38. package/dist/src/primitive-sdk.js +3 -0
  39. package/dist/src/primitives/remotion/html-image.js +28 -0
  40. package/dist/src/react-page-shell.js +34 -0
  41. package/dist/src/ready-post-schedule-component.js +1514 -0
  42. package/dist/src/registry.js +44 -18
  43. package/dist/src/runtime.js +6 -1
  44. package/dist/src/services/api-call-history.js +245 -0
  45. package/dist/src/services/auth.js +27 -9
  46. package/dist/src/services/billing.js +248 -9
  47. package/dist/src/services/chat-threads.js +88 -0
  48. package/dist/src/services/job-logs.js +10 -3
  49. package/dist/src/services/jobs.js +13 -2
  50. package/dist/src/services/providers.js +944 -55
  51. package/dist/src/services/rate-limits.js +236 -0
  52. package/dist/src/services/remotion.js +143 -16
  53. package/dist/src/services/storage.js +23 -8
  54. package/dist/src/services/template-certification.js +26 -3
  55. package/dist/src/services/template-loader.js +19 -1
  56. package/dist/src/services/template-sources.js +316 -7
  57. package/dist/src/template-editor-pages.js +2309 -0
  58. package/dist/src/template-editor-shell.js +1507 -0
  59. package/dist/src/worker.js +120 -22
  60. package/package.json +30 -6
  61. package/public/assets/homepage-app.js +54 -0
  62. package/public/assets/homepage-client-app.js +54 -0
  63. package/public/assets/page-runtime-client-app.js +68 -0
  64. package/dist/templates/template_0000/src/lib/images.js +0 -202
  65. package/dist/templates/template_0000/src/remotion/Root.js +0 -33
  66. package/dist/templates/template_0000/src/remotion/index.js +0 -3
  67. package/dist/templates/template_0000/src/sdk.js +0 -3
  68. package/dist/templates/template_0000/src/style-options.js +0 -51
  69. package/dist/templates/template_0000/src/template-dna.js +0 -9
  70. package/dist/templates/template_0000/src/template.js +0 -1441
  71. package/templates/template_0000/README.md +0 -77
  72. package/templates/template_0000/SKILL.md +0 -225
  73. package/templates/template_0000/assets/Abel-Regular.ttf +0 -0
  74. package/templates/template_0000/assets/DMSerifDisplay-Regular.ttf +0 -0
  75. package/templates/template_0000/assets/Montserrat[wght].ttf +0 -0
  76. package/templates/template_0000/assets/SourceCodePro[wght].ttf +0 -0
  77. package/templates/template_0000/assets/TikTokSans-SemiBold.ttf +0 -0
  78. package/templates/template_0000/assets/Yesteryear-Regular.ttf +0 -0
  79. package/templates/template_0000/composition.json +0 -11
  80. package/templates/template_0000/package.json +0 -28
  81. package/templates/template_0000/research/preview/.gitkeep +0 -1
  82. package/templates/template_0000/research/source_notes.md +0 -7
  83. package/templates/template_0000/src/lib/images.js +0 -202
  84. package/templates/template_0000/src/lib/images.ts +0 -241
  85. package/templates/template_0000/src/remotion/Root.js +0 -33
  86. package/templates/template_0000/src/remotion/Root.tsx +0 -75
  87. package/templates/template_0000/src/remotion/index.js +0 -3
  88. package/templates/template_0000/src/remotion/index.tsx +0 -4
  89. package/templates/template_0000/src/sdk.js +0 -3
  90. package/templates/template_0000/src/sdk.ts +0 -122
  91. package/templates/template_0000/src/style-options.js +0 -51
  92. package/templates/template_0000/src/style-options.ts +0 -60
  93. package/templates/template_0000/src/template-dna.js +0 -9
  94. package/templates/template_0000/src/template-dna.ts +0 -15
  95. package/templates/template_0000/src/template.js +0 -1441
  96. package/templates/template_0000/src/template.ts +0 -2042
  97. package/templates/template_0000/template.config.json +0 -21
  98. package/templates/template_0000/tsconfig.json +0 -19
@@ -1,4 +1,6 @@
1
- import { existsSync, mkdirSync } from "node:fs";
1
+ import { createHash, createHmac } from "node:crypto";
2
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
+ import os from "node:os";
2
4
  import path from "node:path";
3
5
  import { execFile } from "node:child_process";
4
6
  import { promisify } from "node:util";
@@ -9,7 +11,9 @@ import { defaultSkillPathForTemplateModule, deriveTemplateRootDirFromModulePath
9
11
  import { nowIso } from "../lib/time.js";
10
12
  import { loadTemplateFromModule } from "./template-loader.js";
11
13
  import { TemplateCertificationService } from "./template-certification.js";
14
+ import { StorageService } from "./storage.js";
12
15
  const execFileAsync = promisify(execFile);
16
+ const storage = new StorageService();
13
17
  export class TemplateSourceAlreadyRegisteredError extends Error {
14
18
  existingSource;
15
19
  conflictField;
@@ -22,8 +26,8 @@ export class TemplateSourceAlreadyRegisteredError extends Error {
22
26
  }
23
27
  export class TemplateSourceService {
24
28
  certification = new TemplateCertificationService();
25
- listSources(status) {
26
- return database.listTemplateSources(status);
29
+ listSources(input) {
30
+ return database.listTemplateSources(input);
27
31
  }
28
32
  listReleases(input) {
29
33
  return database.listTemplateReleases(input);
@@ -127,14 +131,26 @@ export class TemplateSourceService {
127
131
  if (!source) {
128
132
  throw new Error("Template source not found.");
129
133
  }
130
- const commitSha = input.commitSha ?? await this.resolveBranchHead(source.repoUrl, source.branch);
134
+ const artifactManifest = input.artifactManifestKey
135
+ ? await this.loadAndVerifyArtifactManifest(input.artifactManifestKey, source)
136
+ : null;
137
+ const commitSha = artifactManifest?.commit_sha ?? input.commitSha ?? await this.resolveBranchHead(source.repoUrl, source.branch);
131
138
  const checkoutPath = path.join(config.TEMPLATE_SOURCE_ROOT, source.templateId, commitSha);
132
- const skillPath = path.join(checkoutPath, source.skillPath);
139
+ const skillPath = path.join(checkoutPath, artifactManifest?.skill_path ?? source.skillPath);
133
140
  const authRepoUrl = this.resolveGitRemoteUrl(source.repoUrl);
134
141
  if (!existsSync(checkoutPath)) {
135
142
  mkdirSync(path.dirname(checkoutPath), { recursive: true });
136
- await this.runShell(["git", "clone", "--branch", source.branch, authRepoUrl, checkoutPath], process.cwd());
137
- await this.runShell(["git", "checkout", commitSha], checkoutPath);
143
+ if (artifactManifest) {
144
+ await this.extractArtifactToCheckout({
145
+ checkoutPath,
146
+ artifactKey: artifactManifest.artifact_key,
147
+ expectedSha256: artifactManifest.artifact_sha256
148
+ });
149
+ }
150
+ else {
151
+ await this.runShell(["git", "clone", "--branch", source.branch, authRepoUrl, checkoutPath], process.cwd());
152
+ await this.runShell(["git", "checkout", commitSha], checkoutPath);
153
+ }
138
154
  if (source.installCommand.trim()) {
139
155
  await this.runCommandString(source.installCommand, checkoutPath);
140
156
  }
@@ -161,11 +177,112 @@ export class TemplateSourceService {
161
177
  checkoutPath,
162
178
  skillPath,
163
179
  modulePath,
180
+ artifactManifestKey: input.artifactManifestKey ?? null,
181
+ artifactKey: artifactManifest?.artifact_key ?? null,
164
182
  status,
165
183
  certificationReport,
166
184
  activatedAt: null
167
185
  });
168
186
  }
187
+ async publishRelease(input) {
188
+ const source = database.getTemplateSource(input.sourceId);
189
+ if (!source) {
190
+ throw new Error("Template source not found.");
191
+ }
192
+ if (source.status !== "approved") {
193
+ throw new Error("Template source must be approved before a developer can publish an update.");
194
+ }
195
+ const trimmedCommitSha = input.commitSha.trim();
196
+ if (!trimmedCommitSha) {
197
+ throw new Error("commit_sha is required.");
198
+ }
199
+ const existingRelease = database.getTemplateReleaseBySourceAndCommit(source.id, trimmedCommitSha);
200
+ if (existingRelease &&
201
+ (existingRelease.status === "pending_approval"
202
+ || existingRelease.status === "approved"
203
+ || existingRelease.status === "active")) {
204
+ return {
205
+ source,
206
+ release: existingRelease,
207
+ publishAction: "updated"
208
+ };
209
+ }
210
+ const release = await this.importRelease({
211
+ sourceId: source.id,
212
+ commitSha: trimmedCommitSha
213
+ });
214
+ return {
215
+ source,
216
+ release,
217
+ publishAction: existingRelease ? "updated" : "created"
218
+ };
219
+ }
220
+ async publishArtifactRelease(input) {
221
+ const source = database.getTemplateSource(input.sourceId);
222
+ if (!source) {
223
+ throw new Error("Template source not found.");
224
+ }
225
+ if (source.status !== "approved") {
226
+ throw new Error("Template source must be approved before a developer can publish an update.");
227
+ }
228
+ const trimmedCommitSha = input.commitSha.trim();
229
+ const trimmedArtifactSha256 = input.artifactSha256.trim().toLowerCase();
230
+ if (!trimmedCommitSha) {
231
+ throw new Error("commit_sha is required.");
232
+ }
233
+ if (!/^[0-9a-f]{64}$/i.test(trimmedArtifactSha256)) {
234
+ throw new Error("artifact_sha256 must be a 64-character hexadecimal SHA-256 hash.");
235
+ }
236
+ const existingRelease = database.getTemplateReleaseBySourceAndCommit(source.id, trimmedCommitSha);
237
+ if (existingRelease &&
238
+ (existingRelease.status === "pending_approval"
239
+ || existingRelease.status === "approved"
240
+ || existingRelease.status === "active")) {
241
+ return {
242
+ source,
243
+ release: existingRelease,
244
+ publishAction: "updated"
245
+ };
246
+ }
247
+ const { manifestKey } = await this.createSignedArtifactManifest({
248
+ source,
249
+ commitSha: trimmedCommitSha,
250
+ artifactSha256: trimmedArtifactSha256,
251
+ signedBy: input.signedBy
252
+ });
253
+ const release = await this.importRelease({
254
+ sourceId: source.id,
255
+ artifactManifestKey: manifestKey
256
+ });
257
+ return {
258
+ source,
259
+ release,
260
+ publishAction: existingRelease ? "updated" : "created"
261
+ };
262
+ }
263
+ async createDeveloperArtifactUpload(input) {
264
+ const source = database.getTemplateSource(input.sourceId);
265
+ if (!source) {
266
+ throw new Error("Template source not found.");
267
+ }
268
+ if (source.status !== "approved") {
269
+ throw new Error("Template source must be approved before a developer can publish an update.");
270
+ }
271
+ const trimmedCommitSha = input.commitSha.trim();
272
+ if (!trimmedCommitSha) {
273
+ throw new Error("commit_sha is required.");
274
+ }
275
+ const artifact = this.buildArtifactKeys(source, trimmedCommitSha);
276
+ const upload = await storage.createWriteUrl(artifact.artifactKey, {
277
+ contentType: "application/gzip",
278
+ publicRead: false,
279
+ expiresIn: 3600
280
+ });
281
+ return {
282
+ ...artifact,
283
+ upload
284
+ };
285
+ }
169
286
  approveSource(input) {
170
287
  const source = database.getTemplateSource(input.sourceId);
171
288
  if (!source) {
@@ -251,6 +368,7 @@ export class TemplateSourceService {
251
368
  });
252
369
  throw new Error("Template release failed certification and cannot be activated.");
253
370
  }
371
+ await this.syncTemplateAboutAssets({ release, template });
254
372
  database.clearActiveTemplateReleases(release.templateId);
255
373
  database.updateTemplateReleaseStatus({
256
374
  id: release.id,
@@ -315,6 +433,174 @@ export class TemplateSourceService {
315
433
  }
316
434
  });
317
435
  }
436
+ async syncTemplateAboutAssets(input) {
437
+ const entries = Array.from(new Set(input.template.about.preview_media
438
+ .filter((entry) => !/^https?:\/\//i.test(entry))
439
+ .filter((entry) => !entry.replace(/^\/+/, "").startsWith("templates/"))));
440
+ for (const entry of entries) {
441
+ const asset = this.resolveTemplateAboutAsset(input.release, entry);
442
+ if (!asset) {
443
+ throw new Error(`Template about asset not found in release checkout: ${entry}`);
444
+ }
445
+ await storage.putBuffer(storage.templateAboutKey(input.template.id, asset.storageAssetPath), readFileSync(asset.sourcePath), inferContentType(asset.sourcePath));
446
+ }
447
+ }
448
+ resolveTemplateAboutAsset(release, entry) {
449
+ const normalizedEntry = entry.replace(/^\/+/, "");
450
+ const sourceRelativePath = normalizedEntry.startsWith("about/")
451
+ ? normalizedEntry
452
+ : path.posix.join("about", normalizedEntry);
453
+ const storageAssetPath = normalizedEntry.startsWith("about/")
454
+ ? normalizedEntry.slice("about/".length)
455
+ : normalizedEntry;
456
+ const checkoutRoot = path.resolve(release.checkoutPath);
457
+ let currentDir = path.dirname(release.modulePath);
458
+ while (true) {
459
+ const candidatePath = path.join(currentDir, sourceRelativePath);
460
+ if (existsSync(candidatePath)) {
461
+ return {
462
+ sourcePath: candidatePath,
463
+ storageAssetPath
464
+ };
465
+ }
466
+ if (path.resolve(currentDir) === checkoutRoot) {
467
+ break;
468
+ }
469
+ const parentDir = path.dirname(currentDir);
470
+ if (parentDir === currentDir) {
471
+ break;
472
+ }
473
+ currentDir = parentDir;
474
+ }
475
+ return null;
476
+ }
477
+ async loadAndVerifyArtifactManifest(manifestKey, source) {
478
+ const raw = await storage.readText(manifestKey);
479
+ if (!raw) {
480
+ throw new Error(`Artifact manifest not found: ${manifestKey}`);
481
+ }
482
+ const manifest = JSON.parse(raw);
483
+ if (manifest.version !== 1 || manifest.algorithm !== "hmac-sha256") {
484
+ throw new Error("Unsupported template artifact manifest version or algorithm.");
485
+ }
486
+ if (!config.ARTIFACT_SIGNING_SECRET) {
487
+ throw new Error("ARTIFACT_SIGNING_SECRET or SUPERAGENCY_KEY is required to verify artifact manifests.");
488
+ }
489
+ const expectedSignature = createHmac("sha256", config.ARTIFACT_SIGNING_SECRET)
490
+ .update(this.serializeArtifactManifestPayload({
491
+ version: 1,
492
+ algorithm: "hmac-sha256",
493
+ template_id: String(manifest.template_id ?? ""),
494
+ slug_id: String(manifest.slug_id ?? ""),
495
+ repo_url: String(manifest.repo_url ?? ""),
496
+ branch: String(manifest.branch ?? ""),
497
+ commit_sha: String(manifest.commit_sha ?? ""),
498
+ template_module_path: String(manifest.template_module_path ?? ""),
499
+ skill_path: String(manifest.skill_path ?? ""),
500
+ install_command: String(manifest.install_command ?? ""),
501
+ build_command: String(manifest.build_command ?? ""),
502
+ artifact_key: String(manifest.artifact_key ?? ""),
503
+ artifact_sha256: String(manifest.artifact_sha256 ?? ""),
504
+ created_at: String(manifest.created_at ?? ""),
505
+ signed_by: String(manifest.signed_by ?? "")
506
+ }))
507
+ .digest("hex");
508
+ if (manifest.signature !== expectedSignature) {
509
+ throw new Error("Template artifact manifest signature verification failed.");
510
+ }
511
+ if (manifest.template_id !== source.templateId || manifest.slug_id !== source.slugId) {
512
+ throw new Error("Template artifact manifest does not match the registered template identity.");
513
+ }
514
+ if (manifest.repo_url !== source.repoUrl || manifest.branch !== source.branch) {
515
+ throw new Error("Template artifact manifest does not match the registered repo URL or branch.");
516
+ }
517
+ if (manifest.template_module_path !== source.templateModulePath) {
518
+ throw new Error("Template artifact manifest does not match the registered template module path.");
519
+ }
520
+ if (manifest.skill_path !== source.skillPath) {
521
+ throw new Error("Template artifact manifest does not match the registered skill path.");
522
+ }
523
+ if (manifest.install_command !== source.installCommand || manifest.build_command !== source.buildCommand) {
524
+ throw new Error("Template artifact manifest does not match the registered install or build command.");
525
+ }
526
+ return manifest;
527
+ }
528
+ async extractArtifactToCheckout(input) {
529
+ const archive = await storage.readBuffer(input.artifactKey);
530
+ if (!archive) {
531
+ throw new Error(`Template artifact not found: ${input.artifactKey}`);
532
+ }
533
+ const actualSha256 = createHash("sha256").update(archive).digest("hex");
534
+ if (actualSha256 !== input.expectedSha256) {
535
+ throw new Error(`Template artifact hash mismatch for ${input.artifactKey}.`);
536
+ }
537
+ mkdirSync(input.checkoutPath, { recursive: true });
538
+ const tempArchivePath = path.join(os.tmpdir(), `vidfarm-template-${createId("tmp")}.tar.gz`);
539
+ writeFileSync(tempArchivePath, archive);
540
+ try {
541
+ await execFileAsync("tar", ["-xzf", tempArchivePath, "-C", input.checkoutPath], { cwd: process.cwd() });
542
+ }
543
+ finally {
544
+ rmSync(tempArchivePath, { force: true });
545
+ }
546
+ }
547
+ serializeArtifactManifestPayload(input) {
548
+ return JSON.stringify({
549
+ version: input.version,
550
+ algorithm: input.algorithm,
551
+ template_id: input.template_id,
552
+ slug_id: input.slug_id,
553
+ repo_url: input.repo_url,
554
+ branch: input.branch,
555
+ commit_sha: input.commit_sha,
556
+ template_module_path: input.template_module_path,
557
+ skill_path: input.skill_path,
558
+ install_command: input.install_command,
559
+ build_command: input.build_command,
560
+ artifact_key: input.artifact_key,
561
+ artifact_sha256: input.artifact_sha256,
562
+ created_at: input.created_at,
563
+ signed_by: input.signed_by
564
+ });
565
+ }
566
+ buildArtifactKeys(source, commitSha) {
567
+ return {
568
+ artifactKey: `template-artifacts/${source.templateId}/${commitSha}/source.tar.gz`,
569
+ manifestKey: `template-artifacts/${source.templateId}/${commitSha}/manifest.json`
570
+ };
571
+ }
572
+ async createSignedArtifactManifest(input) {
573
+ const artifact = this.buildArtifactKeys(input.source, input.commitSha);
574
+ const createdAt = nowIso();
575
+ const payload = {
576
+ version: 1,
577
+ algorithm: "hmac-sha256",
578
+ template_id: input.source.templateId,
579
+ slug_id: input.source.slugId,
580
+ repo_url: input.source.repoUrl,
581
+ branch: input.source.branch,
582
+ commit_sha: input.commitSha,
583
+ template_module_path: input.source.templateModulePath,
584
+ skill_path: input.source.skillPath,
585
+ install_command: input.source.installCommand,
586
+ build_command: input.source.buildCommand,
587
+ artifact_key: artifact.artifactKey,
588
+ artifact_sha256: input.artifactSha256,
589
+ created_at: createdAt,
590
+ signed_by: input.signedBy
591
+ };
592
+ const signature = createHmac("sha256", config.ARTIFACT_SIGNING_SECRET)
593
+ .update(this.serializeArtifactManifestPayload(payload))
594
+ .digest("hex");
595
+ await storage.putText(artifact.manifestKey, JSON.stringify({
596
+ ...payload,
597
+ signature
598
+ }, null, 2), "application/json");
599
+ return {
600
+ ...artifact,
601
+ signature
602
+ };
603
+ }
318
604
  resolveGitRemoteUrl(repoUrl) {
319
605
  const githubToken = config.VIDFARM_GITHUB_TOKEN || config.GITHUB_TOKEN;
320
606
  if (!githubToken) {
@@ -341,3 +627,26 @@ export class TemplateSourceService {
341
627
  throw new Error(`Template module path ${declaredModulePath} was not found in the imported release.`);
342
628
  }
343
629
  }
630
+ function inferContentType(filePath) {
631
+ switch (path.extname(filePath).toLowerCase()) {
632
+ case ".png":
633
+ return "image/png";
634
+ case ".gif":
635
+ return "image/gif";
636
+ case ".jpg":
637
+ case ".jpeg":
638
+ return "image/jpeg";
639
+ case ".webp":
640
+ return "image/webp";
641
+ case ".svg":
642
+ return "image/svg+xml";
643
+ case ".mp4":
644
+ return "video/mp4";
645
+ case ".webm":
646
+ return "video/webm";
647
+ case ".mov":
648
+ return "video/quicktime";
649
+ default:
650
+ return "application/octet-stream";
651
+ }
652
+ }