@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,217 +0,0 @@
1
- import { mkdirSync } from "node:fs";
2
- import path from "node:path";
3
- import dotenv from "dotenv";
4
- import { z } from "zod";
5
- dotenv.config();
6
- function normalizeEnvValue(value) {
7
- if (typeof value !== "string") {
8
- return value;
9
- }
10
- const trimmed = value.trim();
11
- const unquoted = ((trimmed.startsWith("\"") && trimmed.endsWith("\""))
12
- || (trimmed.startsWith("'") && trimmed.endsWith("'")))
13
- ? trimmed.slice(1, -1).trim()
14
- : trimmed;
15
- return unquoted === "" ? undefined : unquoted;
16
- }
17
- const optionalSampleRate = z.preprocess((value) => value === "" || value == null ? undefined : value, z.coerce.number().min(0).max(1).optional());
18
- const schema = z.object({
19
- NODE_ENV: z.enum(["development", "test", "production"]).default("development"),
20
- VIDFARM_DEPLOYMENT_TARGET: z.string().optional(),
21
- VIDFARM_RUNTIME_BACKEND: z.literal("serverless").default("serverless"),
22
- VIDFARM_DEBUG_LOGS: z.string().default("false"),
23
- VIDFARM_LOAD_LOCAL_TEMPLATES: z.enum(["auto", "true", "false"]).default("auto"),
24
- PORT: z.coerce.number().default(3000),
25
- VIDFARM_DATA_DIR: z.string().default("./data"),
26
- ENCRYPTION_SECRET: z.string().default("development-encryption-secret-change-me"),
27
- API_KEY_SALT: z.string().default("development-api-key-salt"),
28
- WEBHOOK_SECRET: z.string().default("development-webhook-secret"),
29
- DEFAULT_JOB_DELAY_SECONDS: z.coerce.number().default(20),
30
- MAX_JOB_WALL_CLOCK_SECONDS: z.coerce.number().default(6 * 60 * 60),
31
- STALE_RUNNING_JOB_SECONDS: z.coerce.number().default(60 * 60),
32
- PROVIDER_LEASE_WAIT_TIMEOUT_SECONDS: z.coerce.number().default(5 * 60),
33
- PROVIDER_LEASE_INLINE_WAIT_SECONDS: z.coerce.number().default(5),
34
- PROVIDER_LEASE_STEP_WAIT_SECONDS: z.coerce.number().int().min(1).default(60),
35
- RENDER_POLL_WAIT_SECONDS: z.coerce.number().int().min(1).default(20),
36
- PROVIDER_LEASE_MAX_STEP_WAIT_SECONDS: z.coerce.number().int().min(1).default(12 * 60 * 60),
37
- MAX_ACTIVE_JOBS_PER_CUSTOMER: z.coerce.number().int().min(1).default(30),
38
- STORAGE_DRIVER: z.enum(["local", "s3"]).default("local"),
39
- // Records/auth backend. Defaults to `dynamo` so deployed environments are
40
- // unaffected; `vidfarm serve` sets this to `local` to run fully on disk.
41
- RECORDS_DRIVER: z.enum(["local", "dynamo"]).default("dynamo"),
42
- AWS_REGION: z.string().default("us-east-1"),
43
- AWS_S3_BUCKET: z.string().default(""),
44
- AWS_S3_ENDPOINT: z.string().optional(),
45
- AWS_ACCESS_KEY_ID: z.string().optional(),
46
- AWS_SECRET_ACCESS_KEY: z.string().optional(),
47
- PUBLIC_BASE_URL: z.string().optional(),
48
- PUBLIC_EDITOR_CHAT_URL: z.string().url().optional(),
49
- VIDFARM_USER_ID: z.string().optional(),
50
- VIDFARM_API_KEY: z.string().optional(),
51
- VIDFARM_STAGING_BOOTSTRAP_USER_ID: z.string().optional(),
52
- VIDFARM_STAGING_BOOTSTRAP_EMAIL: z.string().optional(),
53
- VIDFARM_STAGING_BOOTSTRAP_API_KEY: z.string().optional(),
54
- EDITOR_CHAT_URL: z.string().url().optional(),
55
- RESEND_API_KEY: z.string().optional(),
56
- RESEND_FROM_EMAIL: z.string().default("vidfarm@fwd.zoomgtm.com"),
57
- FLOCKPOSTER_API_URL: z.string().url().optional(),
58
- VIDFARM_ZIP_LAMBDA_URL: z.string().url().optional(),
59
- VIDFARM_ZIP_LAMBDA_SECRET: z.string().optional(),
60
- VIDFARM_PRIMITIVE_MEDIA_LAMBDA_URL: z.string().url().optional(),
61
- VIDFARM_PRIMITIVE_MEDIA_LAMBDA_SECRET: z.string().optional(),
62
- VIDFARM_BILLING_URL: z.string().url().optional(),
63
- VIDFARM_BILLING_SECRET: z.string().optional(),
64
- VIDFARM_MIN_RENDER_WALLET_BALANCE_USD: z.coerce.number().default(1),
65
- OPENAI_API_KEY: z.string().optional(),
66
- OPENROUTER_API_KEY: z.string().optional(),
67
- GEMINI_API_KEY: z.string().optional(),
68
- PERPLEXITY_API_KEY: z.string().optional(),
69
- // Platform ElevenLabs key — the fallback used for music / TTS / STT primitives
70
- // when the caller opts into `use_wallet_credits` (the default) or has no usable
71
- // BYOK audio key. Usage on this key is billed to the customer's vidfarm wallet.
72
- ELEVENLABS_API_KEY: z.string().optional(),
73
- // Base provider costs for ElevenLabs primitives billed to the wallet (the
74
- // billing lambda applies the standard markup on top). Defaults track published
75
- // ElevenLabs list pricing: TTS multilingual v2/v3 $0.10 / 1K chars (flash/turbo
76
- // is $0.05), Music $0.15 / minute, Scribe STT $0.22 / hour ≈ $0.003667 / min.
77
- // Override per-environment as pricing shifts.
78
- ELEVENLABS_MUSIC_USD_PER_MIN: z.coerce.number().min(0).default(0.15),
79
- ELEVENLABS_TTS_USD_PER_1K_CHARS: z.coerce.number().min(0).default(0.10),
80
- ELEVENLABS_STT_USD_PER_MIN: z.coerce.number().min(0).default(0.003667),
81
- GHOSTCUT_KEY: z.string().optional(),
82
- GHOSTCUT_SECRET: z.string().optional(),
83
- GHOSTCUT_USD_PER_30S: z.coerce.number().min(0).default(0.10),
84
- // GhostCut caption removal is for SHORT clips, never long-form sources —
85
- // clip hunting must select text-free scenes instead of laundering a whole
86
- // long video through GhostCut. 15 min default ceiling (per-30s pricing makes
87
- // long-form runs absurd anyway).
88
- GHOSTCUT_MAX_DURATION_SEC: z.coerce.number().min(0).default(900),
89
- // Auto-decompose kicks off GhostCut subtitle removal on the source video, but
90
- // GhostCut prices per-30s chunk and is meant for short clips. Skip it entirely
91
- // when the decompose source is longer than this — the composition just keeps
92
- // the original burned-in captions rather than paying to launder a multi-minute
93
- // video. 0 = never skip on length. Distinct from GHOSTCUT_MAX_DURATION_SEC
94
- // (the standalone remove-captions primitive's hard ceiling).
95
- GHOSTCUT_DECOMPOSE_MAX_DURATION_SEC: z.coerce.number().min(0).default(180),
96
- // Inspiration ingest + auto-decompose used to hard-cap sources at 120s (a
97
- // short-form-only assumption). Longer inspiration is now allowed. 0 = unlimited;
98
- // set a positive ceiling to re-impose a limit. Kept separate from
99
- // MAX_PROBE_DURATION_SECONDS (the per-primitive probe cost-guard) so raising the
100
- // inspiration ceiling never loosens the generic probe default.
101
- INSPIRATION_MAX_DURATION_SEC: z.coerce.number().min(0).default(0),
102
- // Cloud passthrough for `vidfarm serve`: the local box keeps editing/render
103
- // fully local but lists the upstream (prod) catalog in /discover and
104
- // /library, seeds forks on demand, and can hand a render off to the cloud.
105
- // Set by the serve command from --host + the ambient cloud VIDFARM_API_KEY;
106
- // never set on deployed environments.
107
- VIDFARM_UPSTREAM_HOST: z.string().optional(),
108
- VIDFARM_UPSTREAM_API_KEY: z.string().optional(),
109
- RAPIDAPI_KEY: z.string().optional(),
110
- RAPIDAPI_VIDEO_DOWNLOAD_URL: z.string().url().default("https://snap-video3.p.rapidapi.com/download"),
111
- RAPIDAPI_VIDEO_DOWNLOAD_HOST: z.string().default("snap-video3.p.rapidapi.com"),
112
- RAPIDAPI_VIDEO_DOWNLOAD_BASE_COST_USD: z.coerce.number().min(0).default(0.005),
113
- RAPIDAPI_VIDEO_DOWNLOAD_LAMBDA_COST_USD: z.coerce.number().min(0).default(0.0002),
114
- RAPIDAPI_REMOVE_BACKGROUND_URL: z.string().url().default("https://remove-background18.p.rapidapi.com/public/remove-background/url"),
115
- RAPIDAPI_REMOVE_BACKGROUND_HOST: z.string().default("remove-background18.p.rapidapi.com"),
116
- RAPIDAPI_REMOVE_BACKGROUND_COST_USD: z.coerce.number().min(0).default(0.005),
117
- // Local sharp chroma-key background removal (no third-party API) — a small
118
- // compute/convenience wallet fee, far cheaper than the RapidAPI matting above.
119
- GREENSCREEN_CHROMA_KEY_COST_USD: z.coerce.number().min(0).default(0.002),
120
- // Two-step "create media overlay": BYOK AI image gen (provider cost rides the
121
- // caller's own key) + local chroma key. This fee bills only the platform
122
- // convenience/compute leg of the combined primitive.
123
- MEDIA_OVERLAY_COST_USD: z.coerce.number().min(0).default(0.008),
124
- SUPERAGENCY_KEY: z.string().optional(),
125
- ARTIFACT_SIGNING_SECRET: z.string().optional(),
126
- HYPERFRAMES_LAMBDA_STACK_NAME: z.string().default("hyperframes-vidfarm-prod"),
127
- HYPERFRAMES_RENDER_REGION: z.string().optional(),
128
- HYPERFRAMES_RENDER_BUCKET_NAME: z.string().optional(),
129
- HYPERFRAMES_LAMBDA_BUCKET_NAME: z.string().optional(),
130
- HYPERFRAMES_RENDER_STATE_MACHINE_ARN: z.string().optional(),
131
- HYPERFRAMES_STATE_MACHINE_ARN: z.string().optional(),
132
- HYPERFRAMES_RENDER_MEMORY_MB: z.coerce.number().int().min(512).default(3072),
133
- // Force the in-process (single-machine, headless-Chromium) HyperFrames
134
- // renderer even when a cloud render stack is configured. When unset, the
135
- // renderer auto-selects local whenever no cloud render stack is wired (the
136
- // `vidfarm serve` case). Accepts "1"/"true"/"yes".
137
- VIDFARM_LOCAL_RENDER: z
138
- .string()
139
- .optional()
140
- .transform((value) => /^(1|true|yes)$/i.test(String(value ?? ""))),
141
- MOCK_PROVIDER_RESPONSES: z.string().optional(),
142
- VIDFARM_ADMIN_EMAILS: z.string().default(""),
143
- VIDFARM_GITHUB_TOKEN: z.string().optional(),
144
- GITHUB_TOKEN: z.string().optional(),
145
- TEMPLATE_SOURCE_ROOT: z.string().default("./data/template-sources"),
146
- VIDFARM_RATE_LIMITS_TABLE_NAME: z.string().optional(),
147
- VIDFARM_SERVERLESS_MAIN_TABLE_NAME: z.string().optional(),
148
- VIDFARM_JOB_STATE_MACHINE_ARN: z.string().optional(),
149
- // Clip-curation scan pipeline (Step Functions). Optional: when unset, the
150
- // /clips/scan route records the scan request but doesn't dispatch the machine.
151
- VIDFARM_CLIP_SCAN_STATE_MACHINE_ARN: z.string().optional(),
152
- VIDFARM_CLIP_VECTOR_BACKEND: z.enum(["dynamo", "s3vectors"]).default("dynamo"),
153
- VIDFARM_CLIP_VECTORS_BUCKET: z.string().optional(),
154
- VIDFARM_CLIP_VECTORS_INDEX: z.string().optional(),
155
- SENTRY_DSN: z.string().optional(),
156
- SENTRY_ENVIRONMENT: z.string().optional(),
157
- SENTRY_RELEASE: z.string().optional(),
158
- SENTRY_TRACES_SAMPLE_RATE: optionalSampleRate,
159
- SENTRY_ENABLE_LOGS: z.string().default("true"),
160
- SENTRY_SEND_DEFAULT_PII: z.string().default("false"),
161
- SENTRY_DEBUG_ROUTE: z.string().default("false"),
162
- PUBLIC_SENTRY_DSN: z.string().optional(),
163
- PUBLIC_SENTRY_TRACES_SAMPLE_RATE: optionalSampleRate,
164
- PUBLIC_SENTRY_REPLAYS_SESSION_SAMPLE_RATE: optionalSampleRate,
165
- PUBLIC_SENTRY_REPLAYS_ON_ERROR_SAMPLE_RATE: optionalSampleRate,
166
- PUBLIC_SENTRY_ENABLE_LOGS: z.string().default("true"),
167
- PUBLIC_SENTRY_SEND_DEFAULT_PII: z.string().default("true"),
168
- PUBLIC_SENTRY_TRACE_PROPAGATION_TARGETS: z.string().default("")
169
- }).superRefine((data, ctx) => {
170
- if (data.NODE_ENV === "production" && data.VIDFARM_RUNTIME_BACKEND === "serverless") {
171
- if (!data.VIDFARM_SERVERLESS_MAIN_TABLE_NAME) {
172
- ctx.addIssue({
173
- code: z.ZodIssueCode.custom,
174
- path: ["VIDFARM_SERVERLESS_MAIN_TABLE_NAME"],
175
- message: "VIDFARM_SERVERLESS_MAIN_TABLE_NAME is required when VIDFARM_RUNTIME_BACKEND=serverless in production"
176
- });
177
- }
178
- if (!data.VIDFARM_RATE_LIMITS_TABLE_NAME) {
179
- ctx.addIssue({
180
- code: z.ZodIssueCode.custom,
181
- path: ["VIDFARM_RATE_LIMITS_TABLE_NAME"],
182
- message: "VIDFARM_RATE_LIMITS_TABLE_NAME is required when VIDFARM_RUNTIME_BACKEND=serverless in production"
183
- });
184
- }
185
- if (!data.VIDFARM_JOB_STATE_MACHINE_ARN) {
186
- ctx.addIssue({
187
- code: z.ZodIssueCode.custom,
188
- path: ["VIDFARM_JOB_STATE_MACHINE_ARN"],
189
- message: "VIDFARM_JOB_STATE_MACHINE_ARN is required when VIDFARM_RUNTIME_BACKEND=serverless in production"
190
- });
191
- }
192
- }
193
- });
194
- const normalizedEnv = Object.fromEntries(Object.entries(process.env).map(([key, value]) => [key, normalizeEnvValue(value)]));
195
- const parsed = schema.parse(normalizedEnv);
196
- const dataDir = path.resolve(parsed.VIDFARM_DATA_DIR);
197
- mkdirSync(dataDir, { recursive: true });
198
- const templateSourceRoot = path.resolve(parsed.TEMPLATE_SOURCE_ROOT);
199
- mkdirSync(templateSourceRoot, { recursive: true });
200
- const publicBaseUrl = parsed.PUBLIC_BASE_URL || `http://localhost:${parsed.PORT}`;
201
- export const config = {
202
- ...parsed,
203
- VIDFARM_DATA_DIR: dataDir,
204
- TEMPLATE_SOURCE_ROOT: templateSourceRoot,
205
- PUBLIC_BASE_URL: publicBaseUrl,
206
- ARTIFACT_SIGNING_SECRET: parsed.ARTIFACT_SIGNING_SECRET || parsed.SUPERAGENCY_KEY || "",
207
- isProduction: parsed.NODE_ENV === "production",
208
- mockProviders: parsed.MOCK_PROVIDER_RESPONSES === "true" ||
209
- (parsed.MOCK_PROVIDER_RESPONSES == null && parsed.NODE_ENV !== "production"),
210
- minRenderWalletBalanceUsd: parsed.VIDFARM_MIN_RENDER_WALLET_BALANCE_USD,
211
- debugLogs: parsed.VIDFARM_DEBUG_LOGS === "true" || parsed.VIDFARM_DEPLOYMENT_TARGET === "serverless-staging",
212
- adminEmails: parsed.VIDFARM_ADMIN_EMAILS
213
- .split(",")
214
- .map((value) => value.trim().toLowerCase())
215
- .filter(Boolean),
216
- };
217
- //# sourceMappingURL=config.js.map
@@ -1,447 +0,0 @@
1
- import { existsSync, statSync } from "node:fs";
2
- import path from "node:path";
3
- import * as Sentry from "@sentry/node";
4
- import { config } from "./config.js";
5
- import { devErrorFields, devLog } from "./lib/dev-log.js";
6
- import { createId } from "./lib/ids.js";
7
- import { estimateNormalizationCostUsd, normalizeVideoAsset } from "./services/video-normalization.js";
8
- export function createTemplateJobContext(input) {
9
- const templateConfig = {};
10
- const prefix = input.storage.templateJobPrefix(input.template.id, input.customer.id, input.job.id);
11
- const buildArtifactUrl = (storageKey, fallbackUrl) => fallbackUrl ?? input.storage.getPublicUrl(storageKey) ?? buildTemplateMediaUrl(storageKey);
12
- const templatePreviewMediaUrls = resolveTemplatePreviewMediaUrls(input.template, input.storage);
13
- const jobPreviewMediaPolicy = parseTemplatePreviewMediaPolicy(input.job.payload._templatePreviewMediaPolicy);
14
- // Billing records are written after the billable work (render, normalize,
15
- // etc.) has already succeeded. A transient billing failure must never fail
16
- // the job — the customer would retry and pay for the expensive work twice.
17
- const recordBillingNonFatal = async (record) => {
18
- try {
19
- await input.billing.record(record);
20
- }
21
- catch (error) {
22
- Sentry.captureException(error);
23
- devLog("billing.record_failed_nonfatal", {
24
- job_id: input.job.id,
25
- customer_id: input.customer.id,
26
- template_id: input.template.id,
27
- cost_center_slug: record.costCenterSlug ?? null,
28
- idempotency_key: record.idempotencyKey ?? null,
29
- ...devErrorFields(error)
30
- }, "warn");
31
- }
32
- };
33
- return {
34
- env: config.isProduction ? "production" : "development",
35
- customer: input.customer,
36
- template: {
37
- id: input.template.id,
38
- slugId: input.template.slugId,
39
- previewMediaUrls: templatePreviewMediaUrls,
40
- previewMediaPolicy: jobPreviewMediaPolicy
41
- },
42
- templateConfig,
43
- logger: {
44
- debug(message, metadata = {}) {
45
- void input.jobs.addLogEvent({
46
- id: createId("evt"),
47
- jobId: input.job.id,
48
- level: "debug",
49
- message,
50
- metadata,
51
- progress: null,
52
- artifactKey: null,
53
- createdAt: new Date().toISOString()
54
- });
55
- },
56
- info(message, metadata = {}) {
57
- void input.jobs.addLogEvent({
58
- id: createId("evt"),
59
- jobId: input.job.id,
60
- level: "info",
61
- message,
62
- metadata,
63
- progress: null,
64
- artifactKey: null,
65
- createdAt: new Date().toISOString()
66
- });
67
- },
68
- warn(message, metadata = {}) {
69
- void input.jobs.addLogEvent({
70
- id: createId("evt"),
71
- jobId: input.job.id,
72
- level: "warn",
73
- message,
74
- metadata,
75
- progress: null,
76
- artifactKey: null,
77
- createdAt: new Date().toISOString()
78
- });
79
- },
80
- error(message, metadata = {}) {
81
- void input.jobs.addLogEvent({
82
- id: createId("evt"),
83
- jobId: input.job.id,
84
- level: "error",
85
- message,
86
- metadata,
87
- progress: null,
88
- artifactKey: null,
89
- createdAt: new Date().toISOString()
90
- });
91
- },
92
- progress(progress, message, metadata = {}) {
93
- void input.jobs.addLogEvent({
94
- id: createId("evt"),
95
- jobId: input.job.id,
96
- level: "info",
97
- message,
98
- metadata,
99
- progress,
100
- artifactKey: typeof metadata.artifactKey === "string" ? metadata.artifactKey : null,
101
- createdAt: new Date().toISOString()
102
- });
103
- void input.jobs.updateJobStatus({
104
- id: input.job.id,
105
- status: "running",
106
- progress
107
- });
108
- }
109
- },
110
- jobs: {
111
- async enqueueChild({ operationName, workflowName, payload, providerHint }) {
112
- const child = await input.jobs.enqueueChildJob({
113
- templateId: input.template.id,
114
- operationName,
115
- workflowName,
116
- tracer: input.job.tracer,
117
- payload,
118
- customerId: input.customer.id,
119
- parentJobId: input.job.id,
120
- providerHint
121
- });
122
- return { jobId: child.id };
123
- }
124
- },
125
- storage: {
126
- async putJson(key, value) {
127
- const stored = await input.storage.putJson(`${prefix}/${key}`, value);
128
- const artifactUrl = buildArtifactUrl(stored.key, stored.url);
129
- await insertArtifact("json", stored.key, artifactUrl, {});
130
- return {
131
- ...stored,
132
- url: artifactUrl
133
- };
134
- },
135
- async putText(key, value, contentType = "text/plain; charset=utf-8") {
136
- const stored = await input.storage.putText(`${prefix}/${key}`, value, contentType);
137
- const artifactUrl = buildArtifactUrl(stored.key, stored.url);
138
- await insertArtifact("text", stored.key, artifactUrl, { contentType });
139
- return {
140
- ...stored,
141
- url: artifactUrl
142
- };
143
- },
144
- async putBuffer(key, value, options = {}) {
145
- const stored = await input.storage.putBuffer(`${prefix}/${key}`, value, options.contentType);
146
- const artifactUrl = buildArtifactUrl(stored.key, stored.url);
147
- await insertArtifact(options.kind ?? "binary", stored.key, artifactUrl, {
148
- ...(options.metadata ?? {}),
149
- contentType: options.contentType ?? null
150
- });
151
- return {
152
- ...stored,
153
- url: artifactUrl
154
- };
155
- },
156
- getPublicUrl(key) {
157
- const storageKey = `${prefix}/${key}`;
158
- return input.storage.getPublicUrl(storageKey) ?? buildTemplateMediaUrl(storageKey);
159
- }
160
- },
161
- billing: {
162
- async record(record) {
163
- await recordBillingNonFatal({
164
- customerId: input.customer.id,
165
- jobId: input.job.id,
166
- tracer: input.job.tracer,
167
- templateId: input.template.id,
168
- type: record.type,
169
- costUsd: record.costUsd,
170
- chargeUsd: record.chargeUsd ?? Number((record.costUsd * 1.2).toFixed(4)),
171
- costCenterSlug: record.costCenterSlug,
172
- idempotencyKey: record.idempotencyKey,
173
- occurredAtMs: record.occurredAtMs,
174
- metadata: record.metadata
175
- });
176
- }
177
- },
178
- providers: {
179
- listAvailableProviders() {
180
- return input.providers.listAvailableProviders(input.customer.id);
181
- },
182
- async generateText(request) {
183
- return input.providers.generateText({
184
- customerId: input.customer.id,
185
- jobId: input.job.id,
186
- workerId: input.workerId,
187
- ...request
188
- });
189
- },
190
- async generateSpeech(request) {
191
- return input.providers.generateSpeech({
192
- customerId: input.customer.id,
193
- jobId: input.job.id,
194
- workerId: input.workerId,
195
- ...request
196
- });
197
- },
198
- async generateImage(request) {
199
- return input.providers.generateImage({
200
- customerId: input.customer.id,
201
- jobId: input.job.id,
202
- workerId: input.workerId,
203
- ...request
204
- });
205
- },
206
- async analyzeImageLayout(request) {
207
- return input.providers.analyzeImageLayout({
208
- customerId: input.customer.id,
209
- jobId: input.job.id,
210
- workerId: input.workerId,
211
- ...request
212
- });
213
- },
214
- async transcribeSpeech(request) {
215
- return input.providers.transcribeSpeech({
216
- customerId: input.customer.id,
217
- jobId: input.job.id,
218
- workerId: input.workerId,
219
- ...request
220
- });
221
- }
222
- },
223
- hyperframes: {
224
- async render(request) {
225
- const hyperframesOutputKey = `renders/vidfarm/templates/${input.template.id}/${input.customer.id}/${input.job.id}/output.mp4`;
226
- const render = await input.hyperframes.render({
227
- ...request,
228
- outputKey: hyperframesOutputKey,
229
- wait: true
230
- });
231
- let outputUrl = render.outputUrl;
232
- let platformOutputKey = null;
233
- if (render.outputS3Uri) {
234
- const output = await input.hyperframes.readRenderOutput(render.outputS3Uri);
235
- platformOutputKey = `${prefix}/${request.outputKey || "renders/output.mp4"}`;
236
- const stored = await input.storage.putBuffer(platformOutputKey, output.bytes, output.contentType);
237
- outputUrl = buildArtifactUrl(stored.key, stored.url);
238
- await insertArtifact("video", stored.key, outputUrl, {
239
- operation: "hyperframes-render",
240
- contentType: output.contentType,
241
- renderId: render.renderId,
242
- outputS3Uri: render.outputS3Uri
243
- });
244
- }
245
- const estimatedCostUsd = typeof render.metadata.estimatedCostUsd === "number" ? render.metadata.estimatedCostUsd : null;
246
- const chargeUsd = typeof render.metadata.chargeUsd === "number" ? render.metadata.chargeUsd : null;
247
- if (estimatedCostUsd !== null && estimatedCostUsd > 0) {
248
- await recordBillingNonFatal({
249
- customerId: input.customer.id,
250
- jobId: input.job.id,
251
- tracer: input.job.tracer,
252
- templateId: input.template.id,
253
- type: "render",
254
- costUsd: estimatedCostUsd,
255
- chargeUsd: typeof chargeUsd === "number" ? chargeUsd : undefined,
256
- costCenterSlug: "hyperframes_lambda",
257
- idempotencyKey: `hyperframes_lambda:${render.renderId}`,
258
- occurredAtMs: Date.now(),
259
- metadata: {
260
- ...render.metadata,
261
- render_id: render.renderId,
262
- output_url: outputUrl,
263
- signed_output_url: render.outputUrl,
264
- output_s3_uri: render.outputS3Uri,
265
- platform_output_key: platformOutputKey
266
- }
267
- });
268
- }
269
- return {
270
- renderId: render.renderId,
271
- outputUrl,
272
- outputS3Uri: render.outputS3Uri,
273
- metadata: {
274
- ...render.metadata,
275
- outputUrl,
276
- signedOutputUrl: render.outputUrl,
277
- platformOutputKey
278
- }
279
- };
280
- }
281
- },
282
- media: {
283
- async normalizeVideo(request) {
284
- const normalized = await normalizeVideoAsset({
285
- sourceUrl: request.sourceUrl,
286
- maxWidth: request.maxWidth,
287
- maxHeight: request.maxHeight,
288
- crf: request.crf,
289
- preset: request.preset,
290
- maxVideoBitrateKbps: request.maxVideoBitrateKbps,
291
- audioBitrateKbps: request.audioBitrateKbps
292
- });
293
- const stored = await input.storage.putBuffer(`${prefix}/${request.outputKey}`, normalized.bytes, normalized.contentType);
294
- const artifactUrl = buildArtifactUrl(stored.key, stored.url);
295
- await insertArtifact("video", stored.key, artifactUrl, {
296
- ...(request.metadata ?? {}),
297
- operation: "normalize-video",
298
- contentType: normalized.contentType,
299
- normalization: normalized.metadata
300
- });
301
- await recordBillingNonFatal({
302
- customerId: input.customer.id,
303
- jobId: input.job.id,
304
- tracer: input.job.tracer,
305
- templateId: input.template.id,
306
- type: "cpu_estimate",
307
- costUsd: estimateNormalizationCostUsd(normalized.metadata),
308
- costCenterSlug: "video_normalization",
309
- idempotencyKey: `video_normalization:${input.job.id}:${request.outputKey}`,
310
- occurredAtMs: Date.now(),
311
- metadata: {
312
- ...(request.metadata ?? {}),
313
- operation: "normalize-video",
314
- source_url: request.sourceUrl,
315
- output_key: `${prefix}/${request.outputKey}`,
316
- normalization: normalized.metadata
317
- }
318
- });
319
- return {
320
- ...stored,
321
- url: artifactUrl,
322
- contentType: normalized.contentType,
323
- normalization: normalized.metadata
324
- };
325
- }
326
- }
327
- };
328
- async function insertArtifact(kind, storageKey, publicUrl, metadata) {
329
- await input.jobs.insertArtifact({
330
- id: createId("art"),
331
- jobId: input.job.id,
332
- customerId: input.customer.id,
333
- templateId: input.template.id,
334
- kind,
335
- storageKey,
336
- publicUrl,
337
- metadata
338
- });
339
- }
340
- }
341
- function parseTemplatePreviewMediaPolicy(value) {
342
- if (value === "include" || value === "exclude" || value === "auto") {
343
- return value;
344
- }
345
- if (value === true) {
346
- return "include";
347
- }
348
- if (value === false) {
349
- return "exclude";
350
- }
351
- return "auto";
352
- }
353
- function resolveTemplatePreviewMediaUrls(template, storage) {
354
- return uniqueStrings(template.about.preview_media
355
- .filter(isImagePreviewReference)
356
- .map((entry) => resolveTemplatePreviewMediaUrl(template, storage, entry))
357
- .filter((entry) => Boolean(entry)));
358
- }
359
- function resolveTemplatePreviewMediaUrl(template, storage, entry) {
360
- if (/^https?:\/\//i.test(entry)) {
361
- return entry;
362
- }
363
- const normalizedEntry = entry.replace(/^\/+/, "");
364
- if (normalizedEntry.startsWith("templates/")) {
365
- return resolveBundledTemplateAssetFromStorageKey(template, normalizedEntry)
366
- ? buildTemplateMediaUrl(normalizedEntry)
367
- : storage.getPublicUrl(normalizedEntry) ?? buildTemplateMediaUrl(normalizedEntry);
368
- }
369
- const templateAboutPrefix = storage.templateAboutKey(template.id, "");
370
- const storageAssetPath = normalizedEntry.startsWith(templateAboutPrefix)
371
- ? normalizedEntry.slice(templateAboutPrefix.length)
372
- : normalizedEntry.startsWith("about/")
373
- ? normalizedEntry.slice("about/".length)
374
- : normalizedEntry;
375
- const storageKey = storage.templateAboutKey(template.id, storageAssetPath);
376
- return resolveBundledTemplateAsset(template, storageAssetPath)
377
- ? buildTemplateMediaUrl(storageKey)
378
- : storage.getPublicUrl(storageKey) ?? buildTemplateMediaUrl(storageKey);
379
- }
380
- function isImagePreviewReference(entry) {
381
- return /\.(png|jpe?g|webp)(?:$|[?#])/i.test(entry);
382
- }
383
- function uniqueStrings(values) {
384
- return Array.from(new Set(values.filter((value) => value.trim().length > 0)));
385
- }
386
- function buildTemplateMediaUrl(storageKey) {
387
- return `${config.PUBLIC_BASE_URL.replace(/\/$/, "")}/template-media?key=${encodeURIComponent(storageKey)}`;
388
- }
389
- function resolveBundledTemplateAssetFromStorageKey(template, storageKey) {
390
- const match = /^templates\/([^/]+)\/about\/(.+)$/.exec(storageKey.replace(/^\/+/, ""));
391
- if (!match || match[1] !== template.id) {
392
- return null;
393
- }
394
- return resolveBundledTemplateAsset(template, match[2]);
395
- }
396
- function resolveBundledTemplateAsset(template, assetPath) {
397
- if (!template.skillPath || !assetPath) {
398
- return null;
399
- }
400
- const normalizedAssetPath = assetPath.replace(/^\/+/, "");
401
- if (!normalizedAssetPath || normalizedAssetPath.includes("\\") || normalizedAssetPath.split("/").some((segment) => !segment || segment === "." || segment === "..")) {
402
- return null;
403
- }
404
- let templateRoot = path.resolve(template.skillPath);
405
- try {
406
- if (existsSync(templateRoot) && !statSync(templateRoot).isDirectory()) {
407
- templateRoot = path.dirname(templateRoot);
408
- }
409
- }
410
- catch {
411
- return null;
412
- }
413
- const candidates = normalizedAssetPath.startsWith("about/")
414
- ? [normalizedAssetPath]
415
- : [`about/${normalizedAssetPath}`, normalizedAssetPath];
416
- for (const candidate of candidates) {
417
- const resolved = path.resolve(templateRoot, candidate);
418
- if ((resolved === templateRoot || resolved.startsWith(`${templateRoot}${path.sep}`)) && existsSync(resolved)) {
419
- return resolved;
420
- }
421
- }
422
- return null;
423
- }
424
- function findTemplateRoot(modulePath) {
425
- let current = path.dirname(modulePath);
426
- for (let index = 0; index < 10; index += 1) {
427
- if (existsSync(path.join(current, "template.config.json"))) {
428
- return current;
429
- }
430
- const parent = path.dirname(current);
431
- if (parent === current) {
432
- break;
433
- }
434
- current = parent;
435
- }
436
- return null;
437
- }
438
- function nonBlank(value) {
439
- const trimmed = value?.trim();
440
- return trimmed ? trimmed : undefined;
441
- }
442
- function normalizePositiveInteger(value) {
443
- return typeof value === "number" && Number.isFinite(value) && value > 0
444
- ? Math.floor(value)
445
- : undefined;
446
- }
447
- //# sourceMappingURL=context.js.map