@mevdragon/vidfarm-devcli 0.2.7 → 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 +14 -18
  2. package/README.md +19 -5
  3. package/SKILL.developer.md +140 -11
  4. package/dist/src/account-pages.js +1 -1
  5. package/dist/src/app.js +305 -9
  6. package/dist/src/cli.js +142 -6
  7. package/dist/src/config.js +2 -5
  8. package/dist/src/context.js +16 -0
  9. package/dist/src/db.js +33 -2
  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 +249 -5
  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/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,
@@ -245,6 +248,13 @@ db.exec(`
245
248
  set status = 'pending_approval'
246
249
  where status = 'certified';
247
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
+ }
248
258
  db.exec(`drop index if exists idx_job_events_job_time;`);
249
259
  db.exec(`drop table if exists job_events;`);
250
260
  function mapJob(row) {
@@ -298,6 +308,8 @@ function mapTemplateRelease(row) {
298
308
  checkoutPath: String(row.checkout_path),
299
309
  skillPath: String(row.skill_path),
300
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,
301
313
  status: row.status,
302
314
  certificationReport: parseJson(row.certification_report_json, null),
303
315
  activatedAt: row.activated_at ? String(row.activated_at) : null,
@@ -400,6 +412,21 @@ export const database = {
400
412
  where id = ?
401
413
  `).run(isPaidPlan ? 1 : 0, nowIso(), customerId);
402
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
+ },
403
430
  updateCustomerProfile(input) {
404
431
  db.prepare(`
405
432
  update customers
@@ -915,16 +942,18 @@ export const database = {
915
942
  const timestamp = nowIso();
916
943
  db.prepare(`
917
944
  insert into template_releases (
918
- 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,
919
946
  certification_report_json, activated_at, created_at, updated_at
920
947
  ) values (
921
- @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,
922
949
  @certification_report_json, @activated_at, @created_at, @updated_at
923
950
  )
924
951
  on conflict(source_id, commit_sha) do update set
925
952
  checkout_path = excluded.checkout_path,
926
953
  skill_path = excluded.skill_path,
927
954
  module_path = excluded.module_path,
955
+ artifact_manifest_key = excluded.artifact_manifest_key,
956
+ artifact_key = excluded.artifact_key,
928
957
  status = excluded.status,
929
958
  certification_report_json = excluded.certification_report_json,
930
959
  activated_at = excluded.activated_at,
@@ -938,6 +967,8 @@ export const database = {
938
967
  checkout_path: record.checkoutPath,
939
968
  skill_path: record.skillPath,
940
969
  module_path: record.modulePath,
970
+ artifact_manifest_key: record.artifactManifestKey ?? null,
971
+ artifact_key: record.artifactKey ?? null,
941
972
  status: record.status,
942
973
  certification_report_json: stringifyJson(record.certificationReport ?? null),
943
974
  activated_at: record.activatedAt ?? null,
@@ -35,7 +35,7 @@ export function renderHomepage(input) {
35
35
  const search = `${template.templateId} ${template.title} ${template.slugId}`.toLowerCase();
36
36
  const media = template.previewUrl
37
37
  ? isVideoPreview(template.previewUrl)
38
- ? `<video class="preview" src="${escapeHtml(template.previewUrl)}" preload="none" muted playsinline controls></video>`
38
+ ? `<video class="preview" data-lazy-video data-src="${escapeHtml(template.previewUrl)}" preload="metadata" muted playsinline controls></video>`
39
39
  : `<img class="preview" src="${escapeHtml(template.previewUrl)}" alt="${escapeHtml(template.title)} preview" loading="lazy" decoding="async" />`
40
40
  : `<div class="preview preview-empty">No preview</div>`;
41
41
  return `
@@ -393,6 +393,7 @@ export function renderHomepage(input) {
393
393
  const rows = Array.from(document.querySelectorAll(".row"));
394
394
  const count = document.getElementById("count");
395
395
  const emptyState = document.getElementById("empty-state");
396
+ const lazyVideos = Array.from(document.querySelectorAll("[data-lazy-video]"));
396
397
 
397
398
  const updateFilter = () => {
398
399
  const query = (searchInput.value || "").trim().toLowerCase();
@@ -411,6 +412,37 @@ export function renderHomepage(input) {
411
412
 
412
413
  searchInput.addEventListener("input", updateFilter);
413
414
 
415
+ const loadVideo = (video) => {
416
+ const src = video.getAttribute("data-src");
417
+ if (!src || video.getAttribute("src")) {
418
+ return;
419
+ }
420
+ video.setAttribute("src", src);
421
+ video.load();
422
+ };
423
+
424
+ if ("IntersectionObserver" in window) {
425
+ const observer = new IntersectionObserver((entries) => {
426
+ for (const entry of entries) {
427
+ if (!entry.isIntersecting) {
428
+ continue;
429
+ }
430
+ loadVideo(entry.target);
431
+ observer.unobserve(entry.target);
432
+ }
433
+ }, {
434
+ rootMargin: "240px 0px"
435
+ });
436
+
437
+ for (const video of lazyVideos) {
438
+ observer.observe(video);
439
+ }
440
+ } else {
441
+ for (const video of lazyVideos) {
442
+ loadVideo(video);
443
+ }
444
+ }
445
+
414
446
  for (const button of document.querySelectorAll("[data-copy-text]")) {
415
447
  button.addEventListener("click", async () => {
416
448
  const text = button.getAttribute("data-copy-text") || "";
@@ -1,21 +1,27 @@
1
1
  import path from "node:path";
2
2
  export const TEMPLATE_FOLDER_PREFIX = "vidfarm_template_";
3
+ export const LEGACY_TEMPLATE_FOLDER_PREFIX = "template_";
3
4
  export function assertTemplateFolderNameHasPrefix(folderName) {
4
- if (!folderName.startsWith(TEMPLATE_FOLDER_PREFIX)) {
5
- throw new Error(`Template folder name must start with ${TEMPLATE_FOLDER_PREFIX}. Received: ${folderName}`);
5
+ if (!folderName.startsWith(TEMPLATE_FOLDER_PREFIX) && !folderName.startsWith(LEGACY_TEMPLATE_FOLDER_PREFIX)) {
6
+ throw new Error(`Template folder name must start with ${TEMPLATE_FOLDER_PREFIX} or ${LEGACY_TEMPLATE_FOLDER_PREFIX}. Received: ${folderName}`);
6
7
  }
7
8
  }
8
9
  export function deriveTemplateRootDirFromModulePath(templateModulePath) {
9
10
  const normalizedModulePath = templateModulePath.replace(/\\/g, "/");
10
11
  const templateSourceDir = path.posix.dirname(normalizedModulePath);
11
12
  const templateRootDir = path.posix.dirname(templateSourceDir);
13
+ const sourceDirName = path.posix.basename(templateSourceDir);
14
+ if (sourceDirName !== "src") {
15
+ throw new Error("template_module_path must point at a TypeScript entrypoint inside a src/ folder, such as src/template.ts or templates/vidfarm_template_example/src/template.ts.");
16
+ }
12
17
  if (templateRootDir === "." || templateRootDir === "") {
13
- throw new Error("template_module_path must point to a template inside a folder such as templates/vidfarm_template_example/src/template.ts.");
18
+ return "";
14
19
  }
15
20
  const folderName = path.posix.basename(templateRootDir);
16
21
  assertTemplateFolderNameHasPrefix(folderName);
17
22
  return templateRootDir;
18
23
  }
19
24
  export function defaultSkillPathForTemplateModule(templateModulePath) {
20
- return path.posix.join(deriveTemplateRootDirFromModulePath(templateModulePath), "SKILL.md");
25
+ const templateRootDir = deriveTemplateRootDirFromModulePath(templateModulePath);
26
+ return templateRootDir ? path.posix.join(templateRootDir, "SKILL.md") : "SKILL.md";
21
27
  }
@@ -1,5 +1,5 @@
1
1
  import { database } from "./db.js";
2
- import { template0000Definition } from "../templates/template_0000/src/template.js";
2
+ import { template0000Definition } from "../templates/vidfarm_template_0000/src/template.js";
3
3
  import { loadTemplateFromModule } from "./services/template-loader.js";
4
4
  import { TemplateCertificationService } from "./services/template-certification.js";
5
5
  class TemplateRegistry {
@@ -42,7 +42,14 @@ class TemplateRegistry {
42
42
  return this.initPromise;
43
43
  }
44
44
  list() {
45
- return [...this.localTemplates.values(), ...this.runtimeTemplates.values()].sort((a, b) => a.id.localeCompare(b.id));
45
+ const merged = new Map();
46
+ for (const template of this.localTemplates.values()) {
47
+ merged.set(template.id, template);
48
+ }
49
+ for (const template of this.runtimeTemplates.values()) {
50
+ merged.set(template.id, template);
51
+ }
52
+ return [...merged.values()].sort((a, b) => a.id.localeCompare(b.id));
46
53
  }
47
54
  get(templateId) {
48
55
  return this.findTemplate(templateId, this.runtimeTemplates) ?? this.findTemplate(templateId, this.localTemplates);
@@ -54,9 +54,7 @@ export class AuthService {
54
54
  email,
55
55
  name: name ?? existing?.name ?? null,
56
56
  defaultWebhookUrl: existing?.defaultWebhookUrl ?? null,
57
- isDeveloper: existing?.isDeveloper
58
- || config.adminEmails.includes(normalizedEmail)
59
- || config.developerEmails.includes(normalizedEmail),
57
+ isDeveloper: existing?.isDeveloper || config.adminEmails.includes(normalizedEmail),
60
58
  isPaidPlan: existing?.isPaidPlan ?? false
61
59
  });
62
60
  const rawApiKey = `vf_${createId("key")}`;
@@ -134,9 +132,7 @@ export class AuthService {
134
132
  email: normalizedEmail,
135
133
  name: input.name ?? existing?.name ?? null,
136
134
  defaultWebhookUrl: existing?.defaultWebhookUrl ?? null,
137
- isDeveloper: existing?.isDeveloper
138
- || config.adminEmails.includes(normalizedEmail)
139
- || config.developerEmails.includes(normalizedEmail),
135
+ isDeveloper: existing?.isDeveloper || config.adminEmails.includes(normalizedEmail),
140
136
  isPaidPlan: true,
141
137
  passwordHash: hashPassword(input.password)
142
138
  });
@@ -1,8 +1,8 @@
1
1
  import { StorageService } from "./storage.js";
2
2
  const JOB_LOG_PREFIX = "job-logs";
3
- const JOB_LOG_CHUNK_INTERVAL_MS = 60_000;
3
+ const JOB_LOG_CHUNK_INTERVAL_MS = 60000;
4
4
  const JOB_LOG_CHUNK_MAX_BYTES = 1024 * 1024;
5
- const JOB_LOG_FLUSH_INTERVAL_MS = 5_000;
5
+ const JOB_LOG_FLUSH_INTERVAL_MS = 5000;
6
6
  export class JobLogStore {
7
7
  storage = new StorageService();
8
8
  activeChunks = new Map();
@@ -81,7 +81,7 @@ export class JobLogStore {
81
81
  const minMs = Math.min(startMs, endMs);
82
82
  const maxMs = Math.max(startMs, endMs);
83
83
  const minuteStarts = [];
84
- for (let cursor = minMs; cursor <= maxMs; cursor += 60_000) {
84
+ for (let cursor = minMs; cursor <= maxMs; cursor += 60000) {
85
85
  minuteStarts.push(cursor);
86
86
  }
87
87
  return minuteStarts;
@@ -155,7 +155,7 @@ function serializedEventSize(event) {
155
155
  return Buffer.byteLength(`${JSON.stringify(event)}\n`, "utf8");
156
156
  }
157
157
  function floorToMinute(timestampMs) {
158
- return Math.floor(timestampMs / 60_000) * 60_000;
158
+ return Math.floor(timestampMs / 60000) * 60000;
159
159
  }
160
160
  function parseChunk(content) {
161
161
  if (!content) {