@mevdragon/vidfarm-devcli 0.2.8 → 0.2.10
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.
- package/.env.example +9 -0
- package/GETTING_STARTED.developers.md +1 -26
- package/README.md +3 -0
- package/SKILL.developer.md +419 -8
- package/auto-create-templates/AUTO_CREATE_TEMPLATES.md +475 -0
- package/auto-create-templates/extractor-system-prompt.md +113 -0
- package/auto-create-templates/production-graph.schema.json +276 -0
- package/auto-create-templates/runbook.md +140 -0
- package/auto-create-templates/template-plan.schema.json +230 -0
- package/auto-create-templates/template-planner-prompt.md +75 -0
- package/dist/src/account-pages-legacy.js +9064 -0
- package/dist/src/account-pages.js +41 -620
- package/dist/src/app.js +6627 -244
- package/dist/src/cli.js +386 -26
- package/dist/src/config.js +12 -2
- package/dist/src/context.js +35 -35
- package/dist/src/db.js +1016 -53
- package/dist/src/dev-app-legacy.js +693 -0
- package/dist/src/dev-app.js +4 -904
- package/dist/src/domain.js +1 -1
- package/dist/src/editor-chat-history.js +72 -0
- package/dist/src/editor-chat.js +202 -0
- package/dist/src/frontend/flockposter-cache-store.js +116 -0
- package/dist/src/frontend/homepage-client.js +114 -0
- package/dist/src/frontend/homepage-shared.js +3 -0
- package/dist/src/frontend/homepage-store.js +27 -0
- package/dist/src/frontend/homepage-view.js +147 -0
- package/dist/src/frontend/page-runtime-client.js +100 -0
- package/dist/src/frontend/page-runtime-store.js +8 -0
- package/dist/src/frontend/template-editor-chat.js +2261 -0
- package/dist/src/homepage.js +695 -371
- package/dist/src/lib/template-style-options.js +95 -15
- package/dist/src/page-runtime.js +1 -0
- package/dist/src/page-shell.js +941 -0
- package/dist/src/primitive-context.js +163 -0
- package/dist/src/primitive-registry.js +340 -0
- package/dist/src/primitive-sdk.js +3 -0
- package/dist/src/primitives/remotion/html-image.js +28 -0
- package/dist/src/react-page-shell.js +34 -0
- package/dist/src/ready-post-schedule-component.js +1514 -0
- package/dist/src/registry.js +36 -17
- package/dist/src/runtime.js +6 -1
- package/dist/src/services/api-call-history.js +245 -0
- package/dist/src/services/auth.js +25 -3
- package/dist/src/services/billing.js +248 -9
- package/dist/src/services/chat-threads.js +88 -0
- package/dist/src/services/job-logs.js +14 -7
- package/dist/src/services/jobs.js +13 -2
- package/dist/src/services/providers.js +552 -133
- package/dist/src/services/rate-limits.js +236 -0
- package/dist/src/services/remotion.js +143 -16
- package/dist/src/services/storage.js +10 -8
- package/dist/src/services/template-certification.js +11 -2
- package/dist/src/services/template-loader.js +3 -1
- package/dist/src/services/template-sources.js +68 -3
- package/dist/src/template-editor-pages.js +2309 -0
- package/dist/src/template-editor-shell.js +1507 -0
- package/dist/src/worker.js +120 -22
- package/package.json +27 -4
- package/public/assets/homepage-app.js +54 -0
- package/public/assets/homepage-client-app.js +54 -0
- package/public/assets/page-runtime-client-app.js +68 -0
- package/dist/templates/vidfarm_template_0000/src/lib/images.js +0 -202
- package/dist/templates/vidfarm_template_0000/src/remotion/Root.js +0 -34
- package/dist/templates/vidfarm_template_0000/src/remotion/index.js +0 -3
- package/dist/templates/vidfarm_template_0000/src/sdk.js +0 -3
- package/dist/templates/vidfarm_template_0000/src/style-options.js +0 -51
- package/dist/templates/vidfarm_template_0000/src/template-dna.js +0 -9
- package/dist/templates/vidfarm_template_0000/src/template.js +0 -1447
- package/templates/vidfarm_template_0000/README.md +0 -100
- package/templates/vidfarm_template_0000/SKILL.md +0 -225
- package/templates/vidfarm_template_0000/assets/Abel-Regular.ttf +0 -0
- package/templates/vidfarm_template_0000/assets/DMSerifDisplay-Regular.ttf +0 -0
- package/templates/vidfarm_template_0000/assets/Montserrat[wght].ttf +0 -0
- package/templates/vidfarm_template_0000/assets/SourceCodePro[wght].ttf +0 -0
- package/templates/vidfarm_template_0000/assets/TikTokSans-SemiBold.ttf +0 -0
- package/templates/vidfarm_template_0000/assets/Yesteryear-Regular.ttf +0 -0
- package/templates/vidfarm_template_0000/composition.json +0 -11
- package/templates/vidfarm_template_0000/package.json +0 -27
- package/templates/vidfarm_template_0000/research/preview/.gitkeep +0 -1
- package/templates/vidfarm_template_0000/research/source_notes.md +0 -7
- package/templates/vidfarm_template_0000/src/lib/images.js +0 -202
- package/templates/vidfarm_template_0000/src/lib/images.ts +0 -241
- package/templates/vidfarm_template_0000/src/remotion/Root.js +0 -33
- package/templates/vidfarm_template_0000/src/remotion/Root.tsx +0 -75
- package/templates/vidfarm_template_0000/src/remotion/index.js +0 -3
- package/templates/vidfarm_template_0000/src/remotion/index.tsx +0 -4
- package/templates/vidfarm_template_0000/src/sdk.js +0 -3
- package/templates/vidfarm_template_0000/src/sdk.ts +0 -140
- package/templates/vidfarm_template_0000/src/style-options.js +0 -51
- package/templates/vidfarm_template_0000/src/style-options.ts +0 -60
- package/templates/vidfarm_template_0000/src/template-dna.js +0 -9
- package/templates/vidfarm_template_0000/src/template-dna.ts +0 -15
- package/templates/vidfarm_template_0000/src/template.js +0 -1447
- package/templates/vidfarm_template_0000/src/template.ts +0 -2049
- package/templates/vidfarm_template_0000/template.config.json +0 -21
- package/templates/vidfarm_template_0000/tmp/solobacterium-moorei-slideshow.request.json +0 -31
- package/templates/vidfarm_template_0000/tsconfig.json +0 -19
package/dist/src/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ import path from "node:path";
|
|
|
7
7
|
import { parseArgs } from "node:util";
|
|
8
8
|
import { promisify } from "node:util";
|
|
9
9
|
import dotenv from "dotenv";
|
|
10
|
-
import { STARTER_TEMPLATE_FONT_OPTIONS, STARTER_TEMPLATE_TEXT_BACKGROUND_COLOR_OPTIONS } from "./lib/template-style-options.js";
|
|
10
|
+
import { CAPTION_STANDARD_VERSION, STARTER_TEMPLATE_FONT_OPTIONS, STARTER_TEMPLATE_TEXT_BACKGROUND_COLOR_OPTIONS, TIKTOK_CAPTION_STYLE_PRESETS } from "./lib/template-style-options.js";
|
|
11
11
|
import { analyzeTemplateDna, hasTemplatePreviewMedia, stageTemplateDnaInputs, syncTemplateDnaModule } from "./lib/template-dna.js";
|
|
12
12
|
import { assertTemplateFolderNameHasPrefix, defaultSkillPathForTemplateModule, deriveTemplateRootDirFromModulePath } from "./lib/template-paths.js";
|
|
13
13
|
const execFileAsync = promisify(execFile);
|
|
@@ -57,6 +57,10 @@ async function main() {
|
|
|
57
57
|
await runGenerateTemplateCommand(process.argv.slice(3));
|
|
58
58
|
return;
|
|
59
59
|
}
|
|
60
|
+
if (command === "autocreate-template" || command === "autocreate") {
|
|
61
|
+
await runAutocreateTemplateCommand(process.argv.slice(3));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
60
64
|
if (command === "copy-reference-template") {
|
|
61
65
|
await runCopyReferenceTemplateCommand(process.argv.slice(3));
|
|
62
66
|
return;
|
|
@@ -128,7 +132,8 @@ async function runDevCommand(argv) {
|
|
|
128
132
|
email: parsed.values.email,
|
|
129
133
|
name: parsed.values.name,
|
|
130
134
|
isDeveloper: true,
|
|
131
|
-
isPaidPlan: true
|
|
135
|
+
isPaidPlan: true,
|
|
136
|
+
planTier: "standard"
|
|
132
137
|
});
|
|
133
138
|
const rawApiKey = parsed.values["api-key"] ?? `vf_local_${Math.random().toString(36).slice(2, 12)}`;
|
|
134
139
|
database.insertApiKey({
|
|
@@ -298,9 +303,28 @@ async function runGenerateTemplateCommand(argv) {
|
|
|
298
303
|
force: { type: "boolean", default: false }
|
|
299
304
|
}
|
|
300
305
|
});
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
306
|
+
const result = await generateTemplateFromStarter({
|
|
307
|
+
templateId: parsed.values["template-id"],
|
|
308
|
+
slugId: parsed.values["slug-id"],
|
|
309
|
+
templateDir: parsed.values["template-dir"],
|
|
310
|
+
projectName: parsed.values["project-name"],
|
|
311
|
+
githubRepo: parsed.values["github-repo"],
|
|
312
|
+
siteName: parsed.values["site-name"],
|
|
313
|
+
linkToOriginal: parsed.values["link-to-original"],
|
|
314
|
+
sourceNotesPath: parsed.values["source-notes-path"],
|
|
315
|
+
sourcePreviewDir: parsed.values["source-preview-dir"],
|
|
316
|
+
dnaEnvFile: parsed.values["dna-env-file"],
|
|
317
|
+
dnaModel: parsed.values["dna-model"],
|
|
318
|
+
skipDnaAnalysis: parsed.values["skip-dna-analysis"],
|
|
319
|
+
branch: parsed.values.branch,
|
|
320
|
+
force: parsed.values.force
|
|
321
|
+
});
|
|
322
|
+
console.log(JSON.stringify(result, null, 2));
|
|
323
|
+
}
|
|
324
|
+
async function generateTemplateFromStarter(input) {
|
|
325
|
+
const templateId = input.templateId ?? randomUUID();
|
|
326
|
+
const slugId = input.slugId;
|
|
327
|
+
const templateDir = input.templateDir;
|
|
304
328
|
if (!slugId || !templateDir) {
|
|
305
329
|
throw new Error("generate-template requires --slug-id and --template-dir. --template-id is optional and defaults to a new UUIDv4.");
|
|
306
330
|
}
|
|
@@ -313,7 +337,7 @@ async function runGenerateTemplateCommand(argv) {
|
|
|
313
337
|
throw new Error(`Starter template not found: ${starterDir}`);
|
|
314
338
|
}
|
|
315
339
|
if (existsSync(destinationDir)) {
|
|
316
|
-
if (!
|
|
340
|
+
if (!input.force) {
|
|
317
341
|
throw new Error(`Destination already exists: ${destinationDir}`);
|
|
318
342
|
}
|
|
319
343
|
rmSync(destinationDir, { recursive: true, force: true });
|
|
@@ -325,9 +349,9 @@ async function runGenerateTemplateCommand(argv) {
|
|
|
325
349
|
return ![".git", "node_modules", "dist", "package-lock.json"].includes(name);
|
|
326
350
|
}
|
|
327
351
|
});
|
|
328
|
-
const projectName =
|
|
329
|
-
const siteName =
|
|
330
|
-
const githubRepo =
|
|
352
|
+
const projectName = input.projectName ?? toProjectName(destinationFolderName);
|
|
353
|
+
const siteName = input.siteName ?? projectName.replace(/_/g, "-");
|
|
354
|
+
const githubRepo = input.githubRepo ?? `mevdragon/${projectName}`;
|
|
331
355
|
rewriteTemplateStarter(destinationDir, [
|
|
332
356
|
["mevdragon/vidfarm_template_0000", githubRepo],
|
|
333
357
|
["vidfarm_template_0000", projectName],
|
|
@@ -335,35 +359,35 @@ async function runGenerateTemplateCommand(argv) {
|
|
|
335
359
|
["template_0000", slugId],
|
|
336
360
|
["template-0000", slugId.replace(/_/g, "-")],
|
|
337
361
|
["4c7a7e1a-7f35-4f30-9f86-9c8a63c7f2db", templateId],
|
|
338
|
-
['"production"', JSON.stringify(
|
|
362
|
+
['"production"', JSON.stringify(input.branch ?? "production")]
|
|
339
363
|
]);
|
|
340
364
|
const stagedInputs = stageTemplateDnaInputs({
|
|
341
365
|
templateDir: destinationDir,
|
|
342
|
-
sourceNotesPath:
|
|
343
|
-
sourcePreviewDir:
|
|
366
|
+
sourceNotesPath: input.sourceNotesPath,
|
|
367
|
+
sourcePreviewDir: input.sourcePreviewDir
|
|
344
368
|
});
|
|
345
369
|
const moduleSync = syncTemplateDnaModule({
|
|
346
370
|
templateDir: destinationDir,
|
|
347
|
-
linkToOriginal:
|
|
371
|
+
linkToOriginal: input.linkToOriginal
|
|
348
372
|
});
|
|
349
373
|
const dnaRuns = [];
|
|
350
|
-
if (!
|
|
374
|
+
if (!input.skipDnaAnalysis && hasTemplatePreviewMedia(destinationDir)) {
|
|
351
375
|
dnaRuns.push(await analyzeTemplateDna({
|
|
352
376
|
mode: "viral",
|
|
353
377
|
templateDir: destinationDir,
|
|
354
|
-
envFile:
|
|
355
|
-
model:
|
|
356
|
-
linkToOriginal:
|
|
378
|
+
envFile: input.dnaEnvFile ?? ".env",
|
|
379
|
+
model: input.dnaModel,
|
|
380
|
+
linkToOriginal: input.linkToOriginal
|
|
357
381
|
}));
|
|
358
382
|
dnaRuns.push(await analyzeTemplateDna({
|
|
359
383
|
mode: "visual",
|
|
360
384
|
templateDir: destinationDir,
|
|
361
|
-
envFile:
|
|
362
|
-
model:
|
|
363
|
-
linkToOriginal:
|
|
385
|
+
envFile: input.dnaEnvFile ?? ".env",
|
|
386
|
+
model: input.dnaModel,
|
|
387
|
+
linkToOriginal: input.linkToOriginal
|
|
364
388
|
}));
|
|
365
389
|
}
|
|
366
|
-
|
|
390
|
+
return {
|
|
367
391
|
template_id: templateId,
|
|
368
392
|
slug_id: slugId,
|
|
369
393
|
template_dir: destinationDir,
|
|
@@ -385,6 +409,7 @@ async function runGenerateTemplateCommand(argv) {
|
|
|
385
409
|
"about.preview_media",
|
|
386
410
|
"about.link_to_original",
|
|
387
411
|
"about.proposal_generator",
|
|
412
|
+
"about.skeleton_prompt",
|
|
388
413
|
"about.sample_prompts",
|
|
389
414
|
"configSchema",
|
|
390
415
|
],
|
|
@@ -400,11 +425,123 @@ async function runGenerateTemplateCommand(argv) {
|
|
|
400
425
|
},
|
|
401
426
|
next_steps: [
|
|
402
427
|
"Review src/template.ts and replace the starter proposal_generator with template-specific proposal instructions.",
|
|
428
|
+
"Review src/template.ts and replace the starter skeleton_prompt with a fill-in-the-blanks prompt that fits the real format.",
|
|
403
429
|
"Review src/template.ts and replace the starter sample_prompts with prompts that fit the new format.",
|
|
404
430
|
"If DNA analysis was skipped or the source research changed, run analyze-viral-dna and analyze-visual-dna.",
|
|
405
431
|
"Update SKILL.md so the usage examples match the template's real operations and payloads.",
|
|
406
432
|
`Run validate-template against ${slugId} and launch at least one real local job before treating the template as ready.`,
|
|
407
433
|
],
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
async function runAutocreateTemplateCommand(argv) {
|
|
437
|
+
const parsed = parseArgs({
|
|
438
|
+
args: argv,
|
|
439
|
+
allowPositionals: true,
|
|
440
|
+
options: {
|
|
441
|
+
"input-path": { type: "string" },
|
|
442
|
+
"output-path": { type: "string" },
|
|
443
|
+
"run-id": { type: "string" },
|
|
444
|
+
"runs-dir": { type: "string", default: "auto-create-templates/runs" },
|
|
445
|
+
"harness-dir": { type: "string" },
|
|
446
|
+
"slug-id": { type: "string" },
|
|
447
|
+
"template-id": { type: "string" },
|
|
448
|
+
"project-name": { type: "string" },
|
|
449
|
+
"github-repo": { type: "string" },
|
|
450
|
+
"site-name": { type: "string" },
|
|
451
|
+
"link-to-original": { type: "string" },
|
|
452
|
+
"dna-env-file": { type: "string", default: ".env" },
|
|
453
|
+
"dna-model": { type: "string" },
|
|
454
|
+
"skip-dna-analysis": { type: "boolean", default: false },
|
|
455
|
+
"skip-scaffold": { type: "boolean", default: false },
|
|
456
|
+
"skip-media-probes": { type: "boolean", default: false },
|
|
457
|
+
"skip-harness-snapshot": { type: "boolean", default: false },
|
|
458
|
+
branch: { type: "string", default: "production" },
|
|
459
|
+
force: { type: "boolean", default: false }
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
const positionalInput = parsed.positionals[0];
|
|
463
|
+
const positionalOutput = parsed.positionals[1];
|
|
464
|
+
const inputPathRaw = parsed.values["input-path"] ?? positionalInput;
|
|
465
|
+
const outputPathRaw = parsed.values["output-path"] ?? positionalOutput;
|
|
466
|
+
if (!inputPathRaw || !outputPathRaw) {
|
|
467
|
+
throw new Error("autocreate-template requires --input-path and --output-path, or positional args: autocreate-template <input_path> <output_path>.");
|
|
468
|
+
}
|
|
469
|
+
const root = process.cwd();
|
|
470
|
+
const inputPath = path.resolve(root, inputPathRaw);
|
|
471
|
+
const outputPath = path.resolve(root, outputPathRaw);
|
|
472
|
+
if (!existsSync(inputPath)) {
|
|
473
|
+
throw new Error(`Input path not found: ${inputPath}`);
|
|
474
|
+
}
|
|
475
|
+
const outputFolderName = path.basename(outputPath);
|
|
476
|
+
assertTemplateFolderNameHasPrefix(outputFolderName);
|
|
477
|
+
const slugId = parsed.values["slug-id"] ?? toSlugId(outputFolderName);
|
|
478
|
+
const runId = parsed.values["run-id"] ?? `${toProjectName(slugId)}-${formatUtcTimestamp(new Date())}`;
|
|
479
|
+
const runsDir = path.resolve(root, parsed.values["runs-dir"]);
|
|
480
|
+
const runDir = path.join(runsDir, runId);
|
|
481
|
+
const sourceDir = path.join(runDir, "source");
|
|
482
|
+
const previewDir = path.join(sourceDir, "preview");
|
|
483
|
+
const harnessDir = parsed.values["harness-dir"]
|
|
484
|
+
? path.resolve(root, parsed.values["harness-dir"])
|
|
485
|
+
: resolveAutoCreateHarnessDir();
|
|
486
|
+
createAutoCreateRunFolders(runDir);
|
|
487
|
+
stageAutocreateSource({
|
|
488
|
+
inputPath,
|
|
489
|
+
sourceDir,
|
|
490
|
+
previewDir,
|
|
491
|
+
linkToOriginal: parsed.values["link-to-original"]
|
|
492
|
+
});
|
|
493
|
+
const mediaProbe = parsed.values["skip-media-probes"]
|
|
494
|
+
? { skipped: true, reason: "skip-media-probes was set" }
|
|
495
|
+
: await prepareAutocreateMediaArtifacts({ runDir, previewDir });
|
|
496
|
+
const harnessSnapshot = parsed.values["skip-harness-snapshot"]
|
|
497
|
+
? null
|
|
498
|
+
: snapshotAutoCreateHarness({ harnessDir, runDir });
|
|
499
|
+
const sourceNotesPath = path.join(sourceDir, "source.md");
|
|
500
|
+
const scaffold = parsed.values["skip-scaffold"]
|
|
501
|
+
? null
|
|
502
|
+
: await generateTemplateFromStarter({
|
|
503
|
+
templateId: parsed.values["template-id"],
|
|
504
|
+
slugId,
|
|
505
|
+
templateDir: path.relative(root, outputPath),
|
|
506
|
+
projectName: parsed.values["project-name"],
|
|
507
|
+
githubRepo: parsed.values["github-repo"],
|
|
508
|
+
siteName: parsed.values["site-name"],
|
|
509
|
+
linkToOriginal: parsed.values["link-to-original"],
|
|
510
|
+
sourceNotesPath,
|
|
511
|
+
sourcePreviewDir: previewDir,
|
|
512
|
+
dnaEnvFile: parsed.values["dna-env-file"],
|
|
513
|
+
dnaModel: parsed.values["dna-model"],
|
|
514
|
+
skipDnaAnalysis: parsed.values["skip-dna-analysis"],
|
|
515
|
+
branch: parsed.values.branch,
|
|
516
|
+
force: parsed.values.force
|
|
517
|
+
});
|
|
518
|
+
const agentPromptPath = path.join(runDir, "agent_prompt.md");
|
|
519
|
+
const manifest = {
|
|
520
|
+
mode: "autocreate-template",
|
|
521
|
+
harness_entry: path.join(harnessDir, "AUTO_CREATE_TEMPLATES.md"),
|
|
522
|
+
harness_snapshot: harnessSnapshot,
|
|
523
|
+
input_path: inputPath,
|
|
524
|
+
output_path: outputPath,
|
|
525
|
+
run_id: runId,
|
|
526
|
+
run_dir: runDir,
|
|
527
|
+
source_dir: sourceDir,
|
|
528
|
+
source_notes_path: sourceNotesPath,
|
|
529
|
+
source_preview_dir: previewDir,
|
|
530
|
+
slug_id: slugId,
|
|
531
|
+
scaffold,
|
|
532
|
+
media_probe: mediaProbe,
|
|
533
|
+
required_artifacts: {
|
|
534
|
+
viral_steering_and_visual_preservation: path.join(runDir, "dna", "preservation_decision.json"),
|
|
535
|
+
production_graph: path.join(runDir, "extraction", "production_graph.json"),
|
|
536
|
+
template_plan: path.join(runDir, "plan", "template_plan.json")
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
writeFileSync(path.join(runDir, "manifest.json"), JSON.stringify(manifest, null, 2) + "\n", "utf8");
|
|
540
|
+
writeFileSync(agentPromptPath, renderAutocreateAgentPrompt(manifest), "utf8");
|
|
541
|
+
console.log(JSON.stringify({
|
|
542
|
+
...manifest,
|
|
543
|
+
agent_prompt_path: agentPromptPath,
|
|
544
|
+
next_command_for_agent: `Use ${manifest.harness_entry} as the harness. Analyze ${sourceDir} as the source format, use run artifacts in ${runDir}, generate the production graph and template plan, then scaffold and implement a runnable Vidfarm template to ${outputPath}.`
|
|
408
545
|
}, null, 2));
|
|
409
546
|
}
|
|
410
547
|
async function runRegisterSourceProdCommand(argv) {
|
|
@@ -574,6 +711,7 @@ async function runCopyReferenceTemplateCommand(argv) {
|
|
|
574
711
|
"about.preview_media",
|
|
575
712
|
"about.link_to_original",
|
|
576
713
|
"about.proposal_generator",
|
|
714
|
+
"about.skeleton_prompt",
|
|
577
715
|
"about.sample_prompts",
|
|
578
716
|
"configSchema",
|
|
579
717
|
],
|
|
@@ -589,7 +727,7 @@ async function runCopyReferenceTemplateCommand(argv) {
|
|
|
589
727
|
},
|
|
590
728
|
next_steps: [
|
|
591
729
|
"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.",
|
|
730
|
+
"Use the starter proposal_generator, skeleton_prompt, and sample_prompts as placeholders only; rewrite them for the real format you are building.",
|
|
593
731
|
"Keep the template-local SKILL.md aligned with the operations and payloads in code.",
|
|
594
732
|
],
|
|
595
733
|
}, null, 2));
|
|
@@ -716,9 +854,18 @@ async function runSessionCommand(argv) {
|
|
|
716
854
|
base_url: session.baseUrl,
|
|
717
855
|
email: session.email,
|
|
718
856
|
starter_style_options: {
|
|
857
|
+
caption_standard_version: CAPTION_STANDARD_VERSION,
|
|
719
858
|
fonts: STARTER_TEMPLATE_FONT_OPTIONS,
|
|
720
859
|
text_background_colors: STARTER_TEMPLATE_TEXT_BACKGROUND_COLOR_OPTIONS,
|
|
721
|
-
|
|
860
|
+
presets: TIKTOK_CAPTION_STYLE_PRESETS,
|
|
861
|
+
package_dependencies: [
|
|
862
|
+
"@fontsource/tiktok-sans",
|
|
863
|
+
"@fontsource/montserrat",
|
|
864
|
+
"@fontsource/source-code-pro",
|
|
865
|
+
"@fontsource/libre-baskerville"
|
|
866
|
+
],
|
|
867
|
+
standard_file: "src/lib/template-style-options.ts",
|
|
868
|
+
starter_template_file: "demos/vidfarm_template_0000/src/style-options.ts"
|
|
722
869
|
},
|
|
723
870
|
headers: {
|
|
724
871
|
"vidfarm-api-key": session.apiKey,
|
|
@@ -754,8 +901,7 @@ function seedEnvProviderKeys(input) {
|
|
|
754
901
|
customerId: input.customerId,
|
|
755
902
|
provider: entry.provider,
|
|
756
903
|
label: "Local CLI env key",
|
|
757
|
-
encryptedSecret: input.encryptString(secret, input.encryptionSecret)
|
|
758
|
-
weight: 1
|
|
904
|
+
encryptedSecret: input.encryptString(secret, input.encryptionSecret)
|
|
759
905
|
});
|
|
760
906
|
}
|
|
761
907
|
}
|
|
@@ -806,7 +952,9 @@ function toProjectName(folderName) {
|
|
|
806
952
|
function resolveStarterTemplateDir() {
|
|
807
953
|
const candidates = [
|
|
808
954
|
path.resolve(import.meta.dirname, "..", "templates", "vidfarm_template_0000"),
|
|
809
|
-
path.resolve(import.meta.dirname, "..", "..", "templates", "vidfarm_template_0000")
|
|
955
|
+
path.resolve(import.meta.dirname, "..", "..", "templates", "vidfarm_template_0000"),
|
|
956
|
+
path.resolve(import.meta.dirname, "..", "demos", "vidfarm_template_0000"),
|
|
957
|
+
path.resolve(import.meta.dirname, "..", "..", "demos", "vidfarm_template_0000")
|
|
810
958
|
];
|
|
811
959
|
for (const candidate of candidates) {
|
|
812
960
|
if (existsSync(candidate)) {
|
|
@@ -815,6 +963,218 @@ function resolveStarterTemplateDir() {
|
|
|
815
963
|
}
|
|
816
964
|
return candidates[0];
|
|
817
965
|
}
|
|
966
|
+
function resolveAutoCreateHarnessDir() {
|
|
967
|
+
const candidates = [
|
|
968
|
+
path.resolve(process.cwd(), "auto-create-templates"),
|
|
969
|
+
path.resolve(import.meta.dirname, "..", "auto-create-templates"),
|
|
970
|
+
path.resolve(import.meta.dirname, "..", "..", "auto-create-templates")
|
|
971
|
+
];
|
|
972
|
+
for (const candidate of candidates) {
|
|
973
|
+
if (existsSync(path.join(candidate, "AUTO_CREATE_TEMPLATES.md"))) {
|
|
974
|
+
return candidate;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
return candidates[0];
|
|
978
|
+
}
|
|
979
|
+
function createAutoCreateRunFolders(runDir) {
|
|
980
|
+
for (const name of ["source", "frames", "audio", "transcripts", "ocr", "motion", "dna", "extraction", "plan"]) {
|
|
981
|
+
mkdirSync(path.join(runDir, name), { recursive: true });
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
function stageAutocreateSource(input) {
|
|
985
|
+
mkdirSync(input.sourceDir, { recursive: true });
|
|
986
|
+
mkdirSync(input.previewDir, { recursive: true });
|
|
987
|
+
const stats = statSync(input.inputPath);
|
|
988
|
+
if (stats.isDirectory()) {
|
|
989
|
+
const inputHadPreviewDir = existsSync(path.join(input.inputPath, "preview"));
|
|
990
|
+
copyDirectoryContentsShallow(input.inputPath, input.sourceDir);
|
|
991
|
+
const existingPreviewDir = path.join(input.sourceDir, "preview");
|
|
992
|
+
if (!inputHadPreviewDir) {
|
|
993
|
+
mkdirSync(existingPreviewDir, { recursive: true });
|
|
994
|
+
for (const mediaFile of listMediaFiles(input.sourceDir)) {
|
|
995
|
+
cpSync(mediaFile, path.join(existingPreviewDir, path.basename(mediaFile)));
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
else {
|
|
1000
|
+
cpSync(input.inputPath, path.join(input.previewDir, path.basename(input.inputPath)));
|
|
1001
|
+
}
|
|
1002
|
+
const notesPath = path.join(input.sourceDir, "source.md");
|
|
1003
|
+
if (!existsSync(notesPath)) {
|
|
1004
|
+
writeFileSync(notesPath, [
|
|
1005
|
+
"# Source Notes",
|
|
1006
|
+
"",
|
|
1007
|
+
`- input path: ${input.inputPath}`,
|
|
1008
|
+
`- original format URL: ${input.linkToOriginal ?? ""}`,
|
|
1009
|
+
"- creator/account:",
|
|
1010
|
+
"- product/category being adapted:",
|
|
1011
|
+
"- what appears repeatable:",
|
|
1012
|
+
"- constraints:",
|
|
1013
|
+
""
|
|
1014
|
+
].join("\n"), "utf8");
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
async function prepareAutocreateMediaArtifacts(input) {
|
|
1018
|
+
const videoFile = listMediaFiles(input.previewDir).find((filePath) => isVideoFile(filePath));
|
|
1019
|
+
if (!videoFile) {
|
|
1020
|
+
return {
|
|
1021
|
+
skipped: true,
|
|
1022
|
+
reason: "No video file found in source preview media.",
|
|
1023
|
+
preview_files: listMediaFiles(input.previewDir)
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
const ffprobePath = path.join(input.runDir, "source", "ffprobe.json");
|
|
1027
|
+
const contactSheetPath = path.join(input.runDir, "frames", "contact-sheet.jpg");
|
|
1028
|
+
const audioPath = path.join(input.runDir, "audio", "source.wav");
|
|
1029
|
+
const steps = [];
|
|
1030
|
+
steps.push(await runOptionalLocalCommand("ffprobe", [
|
|
1031
|
+
"-v", "error",
|
|
1032
|
+
"-show_entries", "format=duration:stream=codec_type,width,height,avg_frame_rate",
|
|
1033
|
+
"-of", "json",
|
|
1034
|
+
videoFile
|
|
1035
|
+
], ffprobePath));
|
|
1036
|
+
steps.push(await runOptionalLocalCommand("ffmpeg", [
|
|
1037
|
+
"-y",
|
|
1038
|
+
"-i", videoFile,
|
|
1039
|
+
"-vf", "fps=1/2,scale=240:-1,tile=3x4",
|
|
1040
|
+
"-frames:v", "1",
|
|
1041
|
+
contactSheetPath
|
|
1042
|
+
]));
|
|
1043
|
+
steps.push(await runOptionalLocalCommand("ffmpeg", [
|
|
1044
|
+
"-y",
|
|
1045
|
+
"-i", videoFile,
|
|
1046
|
+
"-vn",
|
|
1047
|
+
"-ac", "1",
|
|
1048
|
+
"-ar", "16000",
|
|
1049
|
+
audioPath
|
|
1050
|
+
]));
|
|
1051
|
+
const result = {
|
|
1052
|
+
skipped: false,
|
|
1053
|
+
video_file: videoFile,
|
|
1054
|
+
ffprobe_path: existsSync(ffprobePath) ? ffprobePath : null,
|
|
1055
|
+
contact_sheet_path: existsSync(contactSheetPath) ? contactSheetPath : null,
|
|
1056
|
+
audio_path: existsSync(audioPath) ? audioPath : null,
|
|
1057
|
+
steps
|
|
1058
|
+
};
|
|
1059
|
+
writeFileSync(path.join(input.runDir, "source", "media_artifacts.json"), JSON.stringify(result, null, 2) + "\n", "utf8");
|
|
1060
|
+
return result;
|
|
1061
|
+
}
|
|
1062
|
+
function snapshotAutoCreateHarness(input) {
|
|
1063
|
+
if (!existsSync(path.join(input.harnessDir, "AUTO_CREATE_TEMPLATES.md"))) {
|
|
1064
|
+
throw new Error(`Auto-create harness not found: ${input.harnessDir}`);
|
|
1065
|
+
}
|
|
1066
|
+
const destinationDir = path.join(input.runDir, "harness");
|
|
1067
|
+
mkdirSync(destinationDir, { recursive: true });
|
|
1068
|
+
for (const fileName of [
|
|
1069
|
+
"AUTO_CREATE_TEMPLATES.md",
|
|
1070
|
+
"runbook.md",
|
|
1071
|
+
"extractor-system-prompt.md",
|
|
1072
|
+
"production-graph.schema.json",
|
|
1073
|
+
"template-planner-prompt.md",
|
|
1074
|
+
"template-plan.schema.json"
|
|
1075
|
+
]) {
|
|
1076
|
+
const source = path.join(input.harnessDir, fileName);
|
|
1077
|
+
if (existsSync(source)) {
|
|
1078
|
+
cpSync(source, path.join(destinationDir, fileName));
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
return {
|
|
1082
|
+
harness_dir: destinationDir,
|
|
1083
|
+
entry: path.join(destinationDir, "AUTO_CREATE_TEMPLATES.md")
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
function renderAutocreateAgentPrompt(manifest) {
|
|
1087
|
+
return [
|
|
1088
|
+
"# Vidfarm Autocreate Template Handoff",
|
|
1089
|
+
"",
|
|
1090
|
+
`Use ${manifest.harness_entry} as the harness.`,
|
|
1091
|
+
`Analyze ${manifest.source_dir} as the source format.`,
|
|
1092
|
+
`Use the run artifacts in ${manifest.run_dir}.`,
|
|
1093
|
+
`Output the runnable Vidfarm template to ${manifest.output_path}.`,
|
|
1094
|
+
"",
|
|
1095
|
+
"Required work:",
|
|
1096
|
+
"",
|
|
1097
|
+
"1. Inspect source notes and preview media.",
|
|
1098
|
+
"2. Use or create analyzer artifacts in the run folder.",
|
|
1099
|
+
"3. Run or consume viral and visual DNA analysis.",
|
|
1100
|
+
"4. Create `dna/preservation_decision.json`.",
|
|
1101
|
+
"5. Create `extraction/production_graph.json` matching the production graph schema.",
|
|
1102
|
+
"6. Create `plan/template_plan.json` matching the template plan schema.",
|
|
1103
|
+
"7. Implement the template operations, jobs, metadata, prompts, storage outputs, and template-local `SKILL.md`.",
|
|
1104
|
+
"8. Run `vidfarm-devcli validate-template` and at least one real local job before calling it done.",
|
|
1105
|
+
"",
|
|
1106
|
+
"Manifest:",
|
|
1107
|
+
"",
|
|
1108
|
+
"```json",
|
|
1109
|
+
JSON.stringify(manifest, null, 2),
|
|
1110
|
+
"```",
|
|
1111
|
+
""
|
|
1112
|
+
].join("\n");
|
|
1113
|
+
}
|
|
1114
|
+
async function runOptionalLocalCommand(command, args, outputPath) {
|
|
1115
|
+
try {
|
|
1116
|
+
const result = await execFileAsync(command, args, {
|
|
1117
|
+
maxBuffer: 20 * 1024 * 1024
|
|
1118
|
+
});
|
|
1119
|
+
if (outputPath) {
|
|
1120
|
+
writeFileSync(outputPath, result.stdout, "utf8");
|
|
1121
|
+
}
|
|
1122
|
+
return {
|
|
1123
|
+
command,
|
|
1124
|
+
ok: true,
|
|
1125
|
+
output_path: outputPath ?? null,
|
|
1126
|
+
stderr: result.stderr.trim() || null
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
catch (error) {
|
|
1130
|
+
return {
|
|
1131
|
+
command,
|
|
1132
|
+
ok: false,
|
|
1133
|
+
output_path: outputPath ?? null,
|
|
1134
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
function copyDirectoryContentsShallow(sourceDir, destinationDir) {
|
|
1139
|
+
mkdirSync(destinationDir, { recursive: true });
|
|
1140
|
+
for (const entry of readdirSync(sourceDir, { withFileTypes: true })) {
|
|
1141
|
+
if (!entry.name.startsWith(".")) {
|
|
1142
|
+
cpSync(path.join(sourceDir, entry.name), path.join(destinationDir, entry.name), { recursive: true });
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
function listMediaFiles(dir) {
|
|
1147
|
+
if (!existsSync(dir)) {
|
|
1148
|
+
return [];
|
|
1149
|
+
}
|
|
1150
|
+
const files = [];
|
|
1151
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
1152
|
+
const filePath = path.join(dir, entry.name);
|
|
1153
|
+
if (entry.name.startsWith(".")) {
|
|
1154
|
+
continue;
|
|
1155
|
+
}
|
|
1156
|
+
if (entry.isDirectory()) {
|
|
1157
|
+
files.push(...listMediaFiles(filePath));
|
|
1158
|
+
continue;
|
|
1159
|
+
}
|
|
1160
|
+
if (entry.isFile() && isMediaFile(filePath)) {
|
|
1161
|
+
files.push(filePath);
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
return files.sort((a, b) => a.localeCompare(b));
|
|
1165
|
+
}
|
|
1166
|
+
function isMediaFile(filePath) {
|
|
1167
|
+
return [".png", ".jpg", ".jpeg", ".webp", ".mp4", ".mov", ".webm", ".m4v"].includes(path.extname(filePath).toLowerCase());
|
|
1168
|
+
}
|
|
1169
|
+
function isVideoFile(filePath) {
|
|
1170
|
+
return [".mp4", ".mov", ".webm", ".m4v"].includes(path.extname(filePath).toLowerCase());
|
|
1171
|
+
}
|
|
1172
|
+
function toSlugId(folderName) {
|
|
1173
|
+
return toProjectName(folderName).replace(/^vidfarm_template_/, "template_");
|
|
1174
|
+
}
|
|
1175
|
+
function formatUtcTimestamp(date) {
|
|
1176
|
+
return date.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
|
|
1177
|
+
}
|
|
818
1178
|
function parseProdTemplateCommandArgs(argv, defaults) {
|
|
819
1179
|
const parsed = parseArgs({
|
|
820
1180
|
args: argv,
|
package/dist/src/config.js
CHANGED
|
@@ -12,7 +12,7 @@ const schema = z.object({
|
|
|
12
12
|
API_KEY_SALT: z.string().default("development-api-key-salt"),
|
|
13
13
|
WORKER_POLL_MS: z.coerce.number().default(1500),
|
|
14
14
|
WORKER_BATCH_SIZE: z.coerce.number().default(2),
|
|
15
|
-
WORKER_MAX_CONCURRENT_JOBS: z.coerce.number().default(
|
|
15
|
+
WORKER_MAX_CONCURRENT_JOBS: z.coerce.number().default(2),
|
|
16
16
|
WEBHOOK_SECRET: z.string().default("development-webhook-secret"),
|
|
17
17
|
DEFAULT_JOB_DELAY_SECONDS: z.coerce.number().default(20),
|
|
18
18
|
MAX_PENDING_JOBS_GLOBAL: z.coerce.number().default(0),
|
|
@@ -25,8 +25,16 @@ const schema = z.object({
|
|
|
25
25
|
AWS_ACCESS_KEY_ID: z.string().optional(),
|
|
26
26
|
AWS_SECRET_ACCESS_KEY: z.string().optional(),
|
|
27
27
|
PUBLIC_BASE_URL: z.string().optional(),
|
|
28
|
+
PUBLIC_EDITOR_CHAT_URL: z.string().url().optional(),
|
|
29
|
+
EDITOR_CHAT_URL: z.string().url().optional(),
|
|
28
30
|
RESEND_API_KEY: z.string().optional(),
|
|
29
31
|
RESEND_FROM_EMAIL: z.string().default("vidfarm@fwd.zoomgtm.com"),
|
|
32
|
+
FLOCKPOSTER_API_URL: z.string().url().optional(),
|
|
33
|
+
VIDFARM_ZIP_LAMBDA_URL: z.string().url().optional(),
|
|
34
|
+
VIDFARM_ZIP_LAMBDA_SECRET: z.string().optional(),
|
|
35
|
+
VIDFARM_BILLING_URL: z.string().url().optional(),
|
|
36
|
+
VIDFARM_BILLING_SECRET: z.string().optional(),
|
|
37
|
+
VIDFARM_MIN_RENDER_WALLET_BALANCE_USD: z.coerce.number().default(1),
|
|
30
38
|
OPENAI_API_KEY: z.string().optional(),
|
|
31
39
|
OPENROUTER_API_KEY: z.string().optional(),
|
|
32
40
|
GEMINI_API_KEY: z.string().optional(),
|
|
@@ -46,7 +54,8 @@ const schema = z.object({
|
|
|
46
54
|
VIDFARM_ADMIN_EMAILS: z.string().default(""),
|
|
47
55
|
VIDFARM_GITHUB_TOKEN: z.string().optional(),
|
|
48
56
|
GITHUB_TOKEN: z.string().optional(),
|
|
49
|
-
TEMPLATE_SOURCE_ROOT: z.string().default("./data/template-sources")
|
|
57
|
+
TEMPLATE_SOURCE_ROOT: z.string().default("./data/template-sources"),
|
|
58
|
+
VIDFARM_RATE_LIMITS_TABLE_NAME: z.string().optional()
|
|
50
59
|
});
|
|
51
60
|
const parsed = schema.parse(process.env);
|
|
52
61
|
const dataDir = path.resolve(parsed.VIDFARM_DATA_DIR);
|
|
@@ -65,6 +74,7 @@ export const config = {
|
|
|
65
74
|
mockProviders: parsed.MOCK_PROVIDER_RESPONSES === "true" ||
|
|
66
75
|
(parsed.MOCK_PROVIDER_RESPONSES == null && parsed.NODE_ENV !== "production"),
|
|
67
76
|
s3PublicRead: parsed.AWS_S3_PUBLIC_READ === "true",
|
|
77
|
+
minRenderWalletBalanceUsd: parsed.VIDFARM_MIN_RENDER_WALLET_BALANCE_USD,
|
|
68
78
|
adminEmails: parsed.VIDFARM_ADMIN_EMAILS
|
|
69
79
|
.split(",")
|
|
70
80
|
.map((value) => value.trim().toLowerCase())
|