@mevdragon/vidfarm-devcli 0.20.6 → 0.20.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 (157) hide show
  1. package/.agents/skills/editor-capabilities/SKILL.md +15 -1
  2. package/.agents/skills/vidfarm-director/SKILL.md +106 -0
  3. package/.agents/skills/vidfarm-director/recipes/find-and-fork-template.md +16 -0
  4. package/.agents/skills/vidfarm-director/recipes/local-edit-render-approve.md +13 -0
  5. package/.agents/skills/vidfarm-director/recipes/onboard-a-new-director.md +13 -0
  6. package/.agents/skills/vidfarm-director/recipes/retheme-template.md +17 -0
  7. package/.agents/skills/vidfarm-director/references/assets-and-sourcing.md +117 -0
  8. package/.agents/skills/vidfarm-director/references/automation-and-local-dev.md +245 -0
  9. package/.agents/skills/vidfarm-director/references/core-workflows.md +273 -0
  10. package/.agents/skills/vidfarm-director/references/editor-workflows.md +365 -0
  11. package/.agents/skills/vidfarm-director/references/onboarding.md +28 -0
  12. package/.agents/skills/vidfarm-director/references/primitives.md +308 -0
  13. package/SKILL.director.md +517 -250
  14. package/dist/src/cli.js +563 -99
  15. package/dist/src/devcli/clips.js +29 -12
  16. package/dist/src/devcli/doctor.js +2 -2
  17. package/dist/src/devcli/local-backend.js +46 -2
  18. package/dist/src/services/clip-curation/cost.js +5 -1
  19. package/dist/src/services/clip-curation/gemini.js +14 -2
  20. package/dist/src/services/clip-curation/hunt.js +56 -0
  21. package/dist/src/services/clip-curation/index.js +1 -1
  22. package/dist/src/services/clip-curation/scan.js +5 -3
  23. package/package.json +30 -10
  24. package/SKILL.platform.md +0 -432
  25. package/demo/README.md +0 -28
  26. package/demo/dist/app.css +0 -1
  27. package/demo/dist/app.js +0 -1850
  28. package/demo/dist/chunks/chunk-DXB73IDG.js +0 -1
  29. package/demo/dist/chunks/chunk-S7OWAJDS.js +0 -36
  30. package/demo/dist/chunks/chunk-VTIBZ6AN.js +0 -1
  31. package/demo/dist/chunks/dist-ADSJKBVE.js +0 -332
  32. package/demo/dist/chunks/domEditingLayers-VZMLL4AP-SGHWPND4.js +0 -1
  33. package/demo/dist/chunks/hyperframes-player-XB65TCD6.js +0 -425
  34. package/demo/dist/chunks/lib-XAQ37YOE.js +0 -1
  35. package/demo/dist/chunks/src-TJ2QYA4U.js +0 -207
  36. package/demo/dist/favicon.ico +0 -0
  37. package/demo/dist/icons/timeline/audio.svg +0 -7
  38. package/demo/dist/icons/timeline/captions.svg +0 -5
  39. package/demo/dist/icons/timeline/composition.svg +0 -12
  40. package/demo/dist/icons/timeline/image.svg +0 -18
  41. package/demo/dist/icons/timeline/music.svg +0 -10
  42. package/demo/dist/icons/timeline/text.svg +0 -3
  43. package/demo/dist/index.html +0 -15
  44. package/dist/src/account-pages-legacy.js +0 -9396
  45. package/dist/src/account-pages.js +0 -61
  46. package/dist/src/app.js +0 -21603
  47. package/dist/src/composition-runtime.js +0 -1053
  48. package/dist/src/config.js +0 -217
  49. package/dist/src/context.js +0 -447
  50. package/dist/src/dev-app-legacy.js +0 -739
  51. package/dist/src/dev-app.js +0 -6
  52. package/dist/src/devcli/migrate-local.js +0 -140
  53. package/dist/src/devcli/sync.js +0 -368
  54. package/dist/src/domain.js +0 -5
  55. package/dist/src/editor-chat-history.js +0 -82
  56. package/dist/src/editor-chat.js +0 -828
  57. package/dist/src/editor-dark-theme.js +0 -1128
  58. package/dist/src/frontend/debug.js +0 -71
  59. package/dist/src/frontend/discover-client.js +0 -130
  60. package/dist/src/frontend/discover-store.js +0 -23
  61. package/dist/src/frontend/file-directory.js +0 -1018
  62. package/dist/src/frontend/flockposter-cache-store.js +0 -124
  63. package/dist/src/frontend/homepage-client.js +0 -446
  64. package/dist/src/frontend/homepage-shared.js +0 -201
  65. package/dist/src/frontend/homepage-store.js +0 -66
  66. package/dist/src/frontend/homepage-view.js +0 -705
  67. package/dist/src/frontend/page-runtime-client.js +0 -132
  68. package/dist/src/frontend/page-runtime-store.js +0 -9
  69. package/dist/src/frontend/sentry.js +0 -42
  70. package/dist/src/frontend/template-editor-chat.js +0 -4181
  71. package/dist/src/help-page.js +0 -346
  72. package/dist/src/homepage.js +0 -1458
  73. package/dist/src/index.js +0 -16
  74. package/dist/src/instrument.js +0 -30
  75. package/dist/src/landing-page.js +0 -384
  76. package/dist/src/page-runtime.js +0 -2
  77. package/dist/src/page-shell.js +0 -1452
  78. package/dist/src/primitive-context.js +0 -416
  79. package/dist/src/primitive-registry.js +0 -3940
  80. package/dist/src/primitive-sdk.js +0 -4
  81. package/dist/src/primitives/hyperframes-media.js +0 -108
  82. package/dist/src/react-page-shell.js +0 -35
  83. package/dist/src/ready-post-schedule-component.js +0 -1540
  84. package/dist/src/registry.js +0 -296
  85. package/dist/src/reskin/agency-page.js +0 -299
  86. package/dist/src/reskin/calendar-page.js +0 -568
  87. package/dist/src/reskin/chat-page.js +0 -942
  88. package/dist/src/reskin/discover-page.js +0 -1788
  89. package/dist/src/reskin/document.js +0 -1587
  90. package/dist/src/reskin/help-page.js +0 -357
  91. package/dist/src/reskin/index-page.js +0 -62
  92. package/dist/src/reskin/inpaint-clipper-page.js +0 -890
  93. package/dist/src/reskin/inpaint-page.js +0 -2554
  94. package/dist/src/reskin/inpaint-video-page.js +0 -1339
  95. package/dist/src/reskin/job-runs-page.js +0 -477
  96. package/dist/src/reskin/library-page.js +0 -1634
  97. package/dist/src/reskin/login-page.js +0 -262
  98. package/dist/src/reskin/portfolio-page.js +0 -687
  99. package/dist/src/reskin/pricing-page.js +0 -390
  100. package/dist/src/reskin/settings-page.js +0 -732
  101. package/dist/src/reskin/theme.js +0 -711
  102. package/dist/src/runtime.js +0 -35
  103. package/dist/src/services/api-call-history.js +0 -249
  104. package/dist/src/services/auth.js +0 -152
  105. package/dist/src/services/billing-pricing.js +0 -39
  106. package/dist/src/services/billing.js +0 -241
  107. package/dist/src/services/cast.js +0 -127
  108. package/dist/src/services/chat-threads.js +0 -92
  109. package/dist/src/services/clip-records.js +0 -250
  110. package/dist/src/services/clip-search.js +0 -77
  111. package/dist/src/services/clip-vectors.js +0 -125
  112. package/dist/src/services/composition-sanitize.js +0 -124
  113. package/dist/src/services/composition-watch.js +0 -79
  114. package/dist/src/services/elevenlabs.js +0 -222
  115. package/dist/src/services/file-directory.js +0 -117
  116. package/dist/src/services/fork-access.js +0 -93
  117. package/dist/src/services/fork-manifest.js +0 -42
  118. package/dist/src/services/ghostcut.js +0 -179
  119. package/dist/src/services/hyperframes.js +0 -3654
  120. package/dist/src/services/job-capacity.js +0 -14
  121. package/dist/src/services/job-logs.js +0 -197
  122. package/dist/src/services/jobs.js +0 -136
  123. package/dist/src/services/local-dynamo.js +0 -0
  124. package/dist/src/services/media-processing.js +0 -766
  125. package/dist/src/services/primitive-media-lambda.js +0 -280
  126. package/dist/src/services/providers.js +0 -2748
  127. package/dist/src/services/rate-limits.js +0 -262
  128. package/dist/src/services/scene-annotations.js +0 -32
  129. package/dist/src/services/serverless-auth.js +0 -382
  130. package/dist/src/services/serverless-jobs.js +0 -1084
  131. package/dist/src/services/serverless-provider-keys.js +0 -409
  132. package/dist/src/services/serverless-records.js +0 -1515
  133. package/dist/src/services/serverless-template-configs.js +0 -75
  134. package/dist/src/services/storage.js +0 -461
  135. package/dist/src/services/swipe-customize.js +0 -437
  136. package/dist/src/services/template-certification.js +0 -413
  137. package/dist/src/services/template-loader.js +0 -99
  138. package/dist/src/services/template-runtime-bundles.js +0 -217
  139. package/dist/src/services/template-sources.js +0 -1017
  140. package/dist/src/services/upstream.js +0 -248
  141. package/dist/src/services/video-normalization.js +0 -2
  142. package/dist/src/services/webhooks.js +0 -62
  143. package/dist/src/template-editor-pages.js +0 -2576
  144. package/dist/src/template-editor-shell.js +0 -2893
  145. package/dist/src/template-sdk.js +0 -4
  146. package/dist/src/worker.js +0 -17
  147. package/public/assets/discover-client-app.js +0 -1
  148. package/public/assets/file-directory-app.js +0 -3
  149. package/public/assets/homepage-app.js +0 -54
  150. package/public/assets/homepage-client-app.js +0 -80
  151. package/public/assets/page-runtime-client-app.js +0 -94
  152. package/public/assets/placeholders/scene-placeholder.png +0 -0
  153. package/src/assets/SELLING_AWARENESS_STAGES.md +0 -579
  154. package/src/assets/SELLING_WITH_HOOKS.md +0 -377
  155. package/src/assets/SELLING_WITH_VSLS.md +0 -606
  156. package/src/assets/favicon.ico +0 -0
  157. package/src/assets/logo-vidfarm.png +0 -0
@@ -1,413 +0,0 @@
1
- import { existsSync, readFileSync, readdirSync } from "node:fs";
2
- import path from "node:path";
3
- const APPROVED_TEMPLATE_MODEL_IDS = new Set([
4
- "gpt-5.4",
5
- "gemini-3.1-flash-lite",
6
- "gemini-3.1-flash-lite-preview",
7
- "gemini-2.5-flash-lite",
8
- "qwen/qwen3.6-plus",
9
- "sonar",
10
- "gpt-image-1",
11
- "gpt-image-2",
12
- "gemini-3.1-flash-image-preview",
13
- "gemini-2.5-flash-image",
14
- "gemini-3-pro-image-preview",
15
- "google/gemini-3.1-flash-image-preview",
16
- "google/gemini-2.5-flash-image-preview",
17
- "bytedance/seedance-2.0",
18
- "bytedance-seed/seedream-4.5",
19
- "google/gemini-3.1-flash-tts-preview",
20
- "gemini-3.1-flash-tts-preview",
21
- "gemini-2.5-flash-preview-tts",
22
- "gpt-4o-mini-tts-2025-12-15",
23
- "openai/gpt-4o-mini-transcribe",
24
- "gpt-4o-mini-transcribe-2025-12-15",
25
- "sora-2",
26
- "veo-3.0-generate-001"
27
- ]);
28
- const TEMPLATE_DIRECT_PROVIDER_ACCESS_PATTERNS = [
29
- /process\.env\.(OPENAI_API_KEY|GEMINI_API_KEY|OPENROUTER_API_KEY|PERPLEXITY_API_KEY)/,
30
- /https?:\/\/api\.openai\.com/,
31
- /https?:\/\/generativelanguage\.googleapis\.com/,
32
- /https?:\/\/openrouter\.ai/,
33
- /https?:\/\/api\.perplexity\.ai/,
34
- /\bAuthorization\s*:/,
35
- /\bapiKey\s*:/,
36
- /\bapi_key\s*:/
37
- ];
38
- const TEMPLATE_OPERATION_PROVIDER_DEFAULT_PATTERN = /(?:^|[\s,{])(?:provider|text_provider|image_provider|tts_provider|speech_provider|transcription_provider)\s*:\s*z\.enum\([^;\n]+\.default\(/;
39
- const TEMPLATE_GEMINI_PROVIDER_FALLBACK_PATTERN = /(?:default(?:Text|Image)?Provider|textProvider|imageProvider|ttsProvider|voiceProvider|transcriptionProvider)\s*:\s*z\.enum\([^;\n]+\.default\(["']gemini["']\)|return\s+["']gemini["'];/g;
40
- const TEMPLATE_MODEL_LITERAL_PATTERN = /["'`](dall-e-3|dall-e|gpt-[^"'`]+|gemini-[^"'`]+|google\/gemini-[^"'`]+|openai\/gpt-[^"'`]+|qwen\/qwen[^"'`]+|bytedance\/[^"'`]+|bytedance-seed\/[^"'`]+|sora-[^"'`]+|veo-[^"'`]+|sonar(?:-[^"'`]+)?)["'`]/g;
41
- const TEMPLATE_UNSAFE_MODEL_PASSTHROUGH_PATTERN = /String\(ctx\.templateConfig\.[A-Za-z_]*Model|ctx\.templateConfig\.[A-Za-z_]*Model\s*\?\?|payload\.[A-Za-z_]*model\s*\?\?|sanitizeConfiguredModel\([^;\n]*(?:textModel|imageModel|layoutModel|ttsModel|transcriptionModel|speechModel|payload\.model|payload\.tts_model|payload\.transcription_model|payload\.speech_model)/g;
42
- export class TemplateCertificationService {
43
- async certify(input) {
44
- const checks = [];
45
- const { template, skillPath } = input;
46
- checks.push(this.check("template.metadata", Boolean(template.id &&
47
- template.slugId &&
48
- template.version &&
49
- template.about &&
50
- typeof template.about.title === "string" &&
51
- typeof template.about.description === "string" &&
52
- ["easy", "medium", "hard", "expert"].includes(template.about.difficulty) &&
53
- typeof template.about.viral_dna === "string" &&
54
- typeof template.about.visual_dna === "string" &&
55
- Array.isArray(template.about.preview_media) &&
56
- template.about.preview_media.every((entry) => typeof entry === "string") &&
57
- typeof template.about.link_to_original === "string" &&
58
- Array.isArray(template.about.links_to_inspiration) &&
59
- template.about.links_to_inspiration.every((entry) => typeof entry === "string") &&
60
- template.about.sound &&
61
- typeof template.about.sound.sound_title === "string" &&
62
- typeof template.about.sound.sound_url === "string" &&
63
- typeof template.about.proposal_generator === "string" &&
64
- typeof template.about.skeleton_prompt === "string" &&
65
- template.about.skeleton_prompt.length > 0 &&
66
- Array.isArray(template.about.sample_prompts) &&
67
- template.about.sample_prompts.every((entry) => typeof entry === "string") &&
68
- template.about.sample_prompts.length > 0 &&
69
- Array.isArray(template.examples) &&
70
- template.examples.every((example) => Array.isArray(example) && example.every((entry) => (entry &&
71
- typeof entry.media_url === "string" &&
72
- typeof entry.note === "string")))), "Template must define id, slugId, version, examples, and complete about metadata, including difficulty, links_to_inspiration, sound, proposal_generator, skeleton_prompt, and sample prompts.", {
73
- templateId: template.id,
74
- slugId: template.slugId,
75
- version: template.version,
76
- difficulty: template.about?.difficulty ?? null,
77
- inspirationLinkCount: template.about?.links_to_inspiration?.length ?? 0,
78
- hasSound: Boolean(template.about?.sound),
79
- exampleCount: template.examples?.length ?? 0,
80
- previewMediaCount: template.about?.preview_media?.length ?? 0,
81
- hasProposalGenerator: typeof template.about?.proposal_generator === "string" && template.about.proposal_generator.length > 0,
82
- hasSkeletonPrompt: typeof template.about?.skeleton_prompt === "string" && template.about.skeleton_prompt.length > 0,
83
- samplePromptCount: template.about?.sample_prompts?.length ?? 0
84
- }));
85
- checks.push(this.check("template.operations", Object.keys(template.operations).length > 0, "Template must define at least one operation."));
86
- checks.push(this.checkSkill(skillPath));
87
- checks.push(...this.checkProviderPolicy({ modulePath: input.modulePath }));
88
- for (const [operationName, operation] of Object.entries(template.operations)) {
89
- checks.push(this.check(`operation.${operationName}.workflow`, typeof template.jobs[operation.workflow] === "function", `Operation ${operationName} must reference an existing workflow.`, { workflow: operation.workflow }));
90
- checks.push(this.check(`operation.${operationName}.smoke_payload`, Boolean(operation.smokeTestPayload), `Operation ${operationName} must define smokeTestPayload for certification.`));
91
- if (!operation.smokeTestPayload) {
92
- continue;
93
- }
94
- try {
95
- const payload = operation.inputSchema.parse(operation.smokeTestPayload);
96
- const workflow = template.jobs[operation.workflow];
97
- const smoke = createSmokeContext(template.id);
98
- const result = await workflow(smoke.ctx, payload);
99
- checks.push(this.check(`operation.${operationName}.smoke_run`, Boolean(result && (result.output || result.progress !== undefined)), `Operation ${operationName} smoke run completed.`, {
100
- logCount: smoke.logCount,
101
- storageWrites: smoke.storageWrites,
102
- billingCalls: smoke.billingCalls,
103
- providerCalls: smoke.providerCalls,
104
- remotionCalls: smoke.remotionCalls
105
- }));
106
- checks.push(this.check(`operation.${operationName}.logs`, smoke.logCount > 0, `Operation ${operationName} must emit at least one log/progress event during smoke run.`));
107
- }
108
- catch (error) {
109
- checks.push(this.check(`operation.${operationName}.smoke_run`, false, error instanceof Error ? error.message : "Smoke run failed."));
110
- }
111
- }
112
- return {
113
- passed: checks.every((check) => check.ok),
114
- checks
115
- };
116
- }
117
- check(code, ok, message, details) {
118
- return { code, ok, message, details };
119
- }
120
- checkSkill(skillPath) {
121
- try {
122
- const contents = readFileSync(skillPath, "utf8").trim();
123
- return this.check("template.skill", contents.length >= 40, "Template must include a non-trivial SKILL.md for customer agents.", { skillPath });
124
- }
125
- catch (error) {
126
- return this.check("template.skill", false, error instanceof Error ? error.message : "Unable to read SKILL.md.", { skillPath });
127
- }
128
- }
129
- checkProviderPolicy(input) {
130
- if (!input.modulePath) {
131
- return [
132
- this.check("template.provider_policy.source", true, "Template module path was not provided; skipped provider policy source validation.")
133
- ];
134
- }
135
- const moduleContents = readFileSync(input.modulePath, "utf8");
136
- const directProviderAccess = TEMPLATE_DIRECT_PROVIDER_ACCESS_PATTERNS
137
- .flatMap((pattern) => collectPatternMatches(moduleContents, pattern))
138
- .map((match) => ({
139
- line: lineNumberForOffset(moduleContents, match.index),
140
- text: match.text.slice(0, 140)
141
- }));
142
- const providerDefaults = collectPatternMatches(moduleContents, TEMPLATE_OPERATION_PROVIDER_DEFAULT_PATTERN)
143
- .map((match) => ({
144
- line: lineNumberForOffset(moduleContents, match.index),
145
- text: match.text.slice(0, 140)
146
- }));
147
- const geminiFallbacks = collectPatternMatches(moduleContents, TEMPLATE_GEMINI_PROVIDER_FALLBACK_PATTERN)
148
- .map((match) => ({
149
- line: lineNumberForOffset(moduleContents, match.index),
150
- text: match.text.slice(0, 140)
151
- }));
152
- const unsupportedModels = collectUnsupportedModelLiterals(moduleContents);
153
- const unsafeModelPassthrough = collectPatternMatches(moduleContents, TEMPLATE_UNSAFE_MODEL_PASSTHROUGH_PATTERN)
154
- .filter((match) => !isInsideSafeProviderModelResolver(moduleContents, match.index))
155
- .map((match) => ({
156
- line: lineNumberForOffset(moduleContents, match.index),
157
- text: match.text.slice(0, 140)
158
- }));
159
- return [
160
- this.check("template.provider_policy.keys", directProviderAccess.length === 0, "Templates must use ctx.providers so AI calls lease the customer's saved provider keys; direct provider env/API usage is not allowed.", directProviderAccess.length > 0 ? { modulePath: input.modulePath, matches: directProviderAccess.slice(0, 10) } : { modulePath: input.modulePath }),
161
- this.check("template.provider_policy.operation_defaults", providerDefaults.length === 0, "Operation payload provider fields must be optional explicit overrides; provider defaults belong in saved template config.", providerDefaults.length > 0 ? { modulePath: input.modulePath, matches: providerDefaults.slice(0, 10) } : { modulePath: input.modulePath }),
162
- this.check("template.provider_policy.provider_fallbacks", geminiFallbacks.length === 0, "Templates must not hide Gemini provider defaults or parser fallbacks. AI calls must use ctx.providers so platform resolution can choose from the customer's saved active keys first.", geminiFallbacks.length > 0 ? { modulePath: input.modulePath, matches: geminiFallbacks.slice(0, 10) } : { modulePath: input.modulePath }),
163
- this.check("template.provider_policy.models", unsupportedModels.length === 0, "Templates may only reference model IDs from the approved Vidfarm provider allowlist.", unsupportedModels.length > 0 ? { modulePath: input.modulePath, models: unsupportedModels.slice(0, 20) } : { modulePath: input.modulePath }),
164
- this.check("template.provider_policy.model_resolution", unsafeModelPassthrough.length === 0, "Template model config and payload overrides must be validated against the selected provider/model allowlist before calling ctx.providers.", unsafeModelPassthrough.length > 0 ? { modulePath: input.modulePath, matches: unsafeModelPassthrough.slice(0, 10) } : { modulePath: input.modulePath })
165
- ];
166
- }
167
- }
168
- const MOCK_PNG_BYTES = Uint8Array.from(Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAADUlEQVR4nGP4////fwAJ+wP9KobjigAAAABJRU5ErkJggg==", "base64"));
169
- function createSmokeContext(templateId) {
170
- let logCount = 0;
171
- let storageWrites = 0;
172
- let billingCalls = 0;
173
- let providerCalls = 0;
174
- let remotionCalls = 0;
175
- const prefix = `templates/${templateId}/users/certification-user/jobs/certification-job`;
176
- const ctx = {
177
- env: "development",
178
- customer: {
179
- id: "certification-user",
180
- email: "certification@vidfarm.local",
181
- name: "Certification",
182
- defaultWebhookUrl: null,
183
- isDeveloper: true,
184
- isAgency: false,
185
- isPaidPlan: true,
186
- planTier: "standalone",
187
- about: null,
188
- groupchatUrl: null,
189
- flockposterApiKey: null,
190
- createdAt: new Date(0).toISOString(),
191
- updatedAt: new Date(0).toISOString()
192
- },
193
- template: {
194
- id: templateId,
195
- slugId: "certification-template",
196
- previewMediaUrls: [],
197
- previewMediaPolicy: "auto"
198
- },
199
- templateConfig: {},
200
- logger: {
201
- debug() { logCount += 1; },
202
- info() { logCount += 1; },
203
- warn() { logCount += 1; },
204
- error() { logCount += 1; },
205
- progress() { logCount += 1; }
206
- },
207
- jobs: {
208
- async enqueueChild() {
209
- return { jobId: "child-certification-job" };
210
- }
211
- },
212
- storage: {
213
- async putJson(key) {
214
- storageWrites += 1;
215
- return { key: `${prefix}/${key}`, url: `https://certification.local/${encodeURIComponent(key)}` };
216
- },
217
- async putText(key) {
218
- storageWrites += 1;
219
- return { key: `${prefix}/${key}`, url: `https://certification.local/${encodeURIComponent(key)}` };
220
- },
221
- async putBuffer(key) {
222
- storageWrites += 1;
223
- return { key: `${prefix}/${key}`, url: `https://certification.local/${encodeURIComponent(key)}` };
224
- },
225
- getPublicUrl(key) {
226
- return `https://certification.local/${encodeURIComponent(`${prefix}/${key}`)}`;
227
- }
228
- },
229
- billing: {
230
- async record() {
231
- billingCalls += 1;
232
- }
233
- },
234
- providers: {
235
- listAvailableProviders() {
236
- return ["openai", "gemini", "openrouter", "perplexity"];
237
- },
238
- async generateText() {
239
- providerCalls += 1;
240
- return { text: "certification text", usage: { inputTokens: 1, outputTokens: 1, costUsd: 0 } };
241
- },
242
- async generateSpeech() {
243
- providerCalls += 1;
244
- return { bytes: Uint8Array.from([0, 1, 2, 3]), contentType: "audio/mpeg", usage: { inputTokens: 1, outputTokens: 1, costUsd: 0 } };
245
- },
246
- async generateImage() {
247
- providerCalls += 1;
248
- return { bytes: MOCK_PNG_BYTES, contentType: "image/png", revisedPrompt: null };
249
- },
250
- async analyzeImageLayout() {
251
- providerCalls += 1;
252
- return {
253
- zone: "bottom",
254
- align: "center",
255
- maxWidthPercent: 80,
256
- justification: "Certification mock layout."
257
- };
258
- },
259
- async transcribeSpeech() {
260
- providerCalls += 1;
261
- return { text: "certification transcript", usage: { inputTokens: 1, outputTokens: 1, costUsd: 0 } };
262
- }
263
- },
264
- hyperframes: {
265
- async render() {
266
- remotionCalls += 1;
267
- return {
268
- renderId: "hyperframes-render-certification",
269
- outputUrl: "https://certification.local/hyperframes-render.mp4",
270
- outputS3Uri: null,
271
- metadata: { mode: "certification-mock", architecture: "timeline_layers" }
272
- };
273
- }
274
- },
275
- media: {
276
- async normalizeVideo(input) {
277
- storageWrites += 1;
278
- return {
279
- key: `${prefix}/${input.outputKey}`,
280
- url: `https://certification.local/${encodeURIComponent(input.outputKey)}`,
281
- contentType: "video/mp4",
282
- normalization: {
283
- source: {
284
- durationSeconds: 5,
285
- width: 1080,
286
- height: 1920,
287
- videoCodec: "h264",
288
- audioCodec: "aac",
289
- videoBitrateKbps: 2000,
290
- audioBitrateKbps: 128,
291
- formatBitrateKbps: 2200,
292
- hasAudio: true,
293
- hasVideo: true,
294
- sampleRateHz: 48_000,
295
- channels: 2,
296
- frameRate: 30,
297
- formatName: "mp4",
298
- bytes: 1_000_000,
299
- url: input.sourceUrl
300
- },
301
- normalized: {
302
- durationSeconds: 5,
303
- width: 1080,
304
- height: 1920,
305
- videoCodec: "h264",
306
- audioCodec: "aac",
307
- videoBitrateKbps: 1500,
308
- audioBitrateKbps: 128,
309
- formatBitrateKbps: 1650,
310
- hasAudio: true,
311
- hasVideo: true,
312
- sampleRateHz: 48_000,
313
- channels: 2,
314
- frameRate: 30,
315
- formatName: "mp4",
316
- bytes: 750_000
317
- },
318
- compressionRatio: 0.75
319
- }
320
- };
321
- }
322
- }
323
- };
324
- return {
325
- ctx,
326
- get logCount() { return logCount; },
327
- get storageWrites() { return storageWrites; },
328
- get billingCalls() { return billingCalls; },
329
- get providerCalls() { return providerCalls; },
330
- get remotionCalls() { return remotionCalls; }
331
- };
332
- }
333
- function findTemplateRoot(modulePath) {
334
- let current = path.dirname(modulePath);
335
- for (let index = 0; index < 10; index += 1) {
336
- if (existsSync(path.join(current, "template.config.json"))) {
337
- return current;
338
- }
339
- const parent = path.dirname(current);
340
- if (parent === current) {
341
- break;
342
- }
343
- current = parent;
344
- }
345
- return null;
346
- }
347
- function nonBlankString(value) {
348
- if (typeof value !== "string") {
349
- return null;
350
- }
351
- const trimmed = value.trim();
352
- return trimmed ? trimmed : null;
353
- }
354
- function collectTemplateSourceFiles(templateRoot) {
355
- const files = [];
356
- const stack = [templateRoot];
357
- while (stack.length > 0) {
358
- const current = stack.pop();
359
- if (!current) {
360
- continue;
361
- }
362
- for (const entry of readdirSync(current, { withFileTypes: true })) {
363
- if (entry.name === "node_modules" || entry.name === "build" || entry.name === "dist" || entry.name.startsWith(".")) {
364
- continue;
365
- }
366
- const fullPath = path.join(current, entry.name);
367
- if (entry.isDirectory()) {
368
- stack.push(fullPath);
369
- continue;
370
- }
371
- if (/\.(?:[cm]?[jt]sx?)$/i.test(entry.name)) {
372
- files.push(fullPath);
373
- }
374
- }
375
- }
376
- return files;
377
- }
378
- function collectPatternMatches(contents, pattern) {
379
- const source = pattern.global ? pattern : new RegExp(pattern.source, pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`);
380
- const matches = [];
381
- for (const match of contents.matchAll(source)) {
382
- matches.push({ index: match.index ?? 0, text: match[0] });
383
- }
384
- return matches;
385
- }
386
- function collectUnsupportedModelLiterals(contents) {
387
- const seen = new Map();
388
- for (const match of contents.matchAll(TEMPLATE_MODEL_LITERAL_PATTERN)) {
389
- const model = match[1]?.trim();
390
- if (!model || APPROVED_TEMPLATE_MODEL_IDS.has(model) || seen.has(model)) {
391
- continue;
392
- }
393
- seen.set(model, {
394
- model,
395
- line: lineNumberForOffset(contents, match.index ?? 0)
396
- });
397
- }
398
- return [...seen.values()];
399
- }
400
- function lineNumberForOffset(contents, offset) {
401
- let line = 1;
402
- for (let index = 0; index < offset; index += 1) {
403
- if (contents.charCodeAt(index) === 10) {
404
- line += 1;
405
- }
406
- }
407
- return line;
408
- }
409
- function isInsideSafeProviderModelResolver(contents, offset) {
410
- const prefix = contents.slice(Math.max(0, offset - 80), offset);
411
- return prefix.includes("sanitizeProviderModel(");
412
- }
413
- //# sourceMappingURL=template-certification.js.map
@@ -1,99 +0,0 @@
1
- import { pathToFileURL } from "node:url";
2
- export async function loadTemplateFromModule(modulePath) {
3
- const loaded = await import(pathToFileURL(modulePath).href);
4
- const candidates = Object.values(loaded);
5
- for (const candidate of candidates) {
6
- if (isTemplateDefinition(candidate)) {
7
- return normalizeTemplateDefinition(candidate);
8
- }
9
- }
10
- throw new Error(`No template definition export found in ${modulePath}`);
11
- }
12
- function normalizeTemplateDefinition(template) {
13
- return {
14
- ...template,
15
- about: {
16
- ...template.about,
17
- // Older runtime template releases may predate prompt metadata fields.
18
- difficulty: template.about.difficulty ?? "easy",
19
- links_to_inspiration: normalizeTemplateInspirationLinks(template.about.links_to_inspiration, template.about.link_to_original),
20
- sound: normalizeTemplateSound(template.about.sound),
21
- proposal_generator: template.about.proposal_generator ?? "",
22
- skeleton_prompt: template.about.skeleton_prompt ?? "",
23
- sample_prompts: template.about.sample_prompts ?? []
24
- },
25
- examples: template.examples ?? []
26
- };
27
- }
28
- function isTemplateDefinition(value) {
29
- if (!value || typeof value !== "object") {
30
- return false;
31
- }
32
- const candidate = value;
33
- const about = typeof candidate.about === "object" && candidate.about !== null
34
- ? candidate.about
35
- : null;
36
- const previewMedia = about && Array.isArray(about.preview_media)
37
- ? about.preview_media
38
- : null;
39
- const samplePrompts = about && Array.isArray(about.sample_prompts)
40
- ? about.sample_prompts
41
- : null;
42
- const sound = about && typeof about.sound === "object" && about.sound !== null
43
- ? about.sound
44
- : null;
45
- const linksToInspiration = about && Array.isArray(about.links_to_inspiration)
46
- ? about.links_to_inspiration
47
- : null;
48
- const examples = Array.isArray(candidate.examples)
49
- ? candidate.examples
50
- : null;
51
- return (typeof candidate.id === "string" &&
52
- typeof candidate.slugId === "string" &&
53
- typeof candidate.version === "string" &&
54
- about !== null &&
55
- typeof about.title === "string" &&
56
- typeof about.description === "string" &&
57
- (["easy", "medium", "hard", "expert"].includes(String(about.difficulty)) || typeof about.difficulty === "undefined") &&
58
- typeof about.viral_dna === "string" &&
59
- typeof about.visual_dna === "string" &&
60
- Array.isArray(previewMedia) &&
61
- previewMedia.every((entry) => typeof entry === "string") &&
62
- typeof about.link_to_original === "string" &&
63
- (linksToInspiration === null || linksToInspiration.every((entry) => typeof entry === "string")) &&
64
- (sound === null || (typeof sound.sound_title === "string" &&
65
- typeof sound.sound_url === "string")) &&
66
- (typeof about.proposal_generator === "string" || typeof about.proposal_generator === "undefined") &&
67
- (typeof about.skeleton_prompt === "string" || typeof about.skeleton_prompt === "undefined") &&
68
- (samplePrompts === null || samplePrompts.every((entry) => typeof entry === "string")) &&
69
- (examples === null || examples.every((example) => Array.isArray(example) && example.every((entry) => {
70
- if (!entry || typeof entry !== "object") {
71
- return false;
72
- }
73
- const media = entry;
74
- return typeof media.media_url === "string" && typeof media.note === "string";
75
- }))) &&
76
- typeof candidate.configSchema === "object" &&
77
- typeof candidate.operations === "object" &&
78
- typeof candidate.jobs === "object");
79
- }
80
- function normalizeTemplateSound(value) {
81
- if (!value || typeof value !== "object") {
82
- return {
83
- sound_title: "",
84
- sound_url: ""
85
- };
86
- }
87
- const sound = value;
88
- return {
89
- sound_title: typeof sound.sound_title === "string" ? sound.sound_title : "",
90
- sound_url: typeof sound.sound_url === "string" ? sound.sound_url : ""
91
- };
92
- }
93
- function normalizeTemplateInspirationLinks(value, linkToOriginal) {
94
- if (Array.isArray(value)) {
95
- return value.filter((entry) => typeof entry === "string");
96
- }
97
- return linkToOriginal ? [linkToOriginal] : [];
98
- }
99
- //# sourceMappingURL=template-loader.js.map