@mevdragon/vidfarm-devcli 0.20.5 → 0.20.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) 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 +111 -0
  8. package/.agents/skills/vidfarm-director/references/automation-and-local-dev.md +243 -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 +265 -0
  13. package/SKILL.director.md +521 -261
  14. package/dist/src/cli.js +404 -96
  15. package/dist/src/devcli/clips.js +15 -10
  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 +10 -0
  20. package/package.json +30 -10
  21. package/SKILL.platform.md +0 -432
  22. package/demo/README.md +0 -28
  23. package/demo/dist/app.css +0 -1
  24. package/demo/dist/app.js +0 -1850
  25. package/demo/dist/chunks/chunk-DXB73IDG.js +0 -1
  26. package/demo/dist/chunks/chunk-S7OWAJDS.js +0 -36
  27. package/demo/dist/chunks/chunk-VTIBZ6AN.js +0 -1
  28. package/demo/dist/chunks/dist-ADSJKBVE.js +0 -332
  29. package/demo/dist/chunks/domEditingLayers-VZMLL4AP-SGHWPND4.js +0 -1
  30. package/demo/dist/chunks/hyperframes-player-XB65TCD6.js +0 -425
  31. package/demo/dist/chunks/lib-XAQ37YOE.js +0 -1
  32. package/demo/dist/chunks/src-TJ2QYA4U.js +0 -207
  33. package/demo/dist/favicon.ico +0 -0
  34. package/demo/dist/icons/timeline/audio.svg +0 -7
  35. package/demo/dist/icons/timeline/captions.svg +0 -5
  36. package/demo/dist/icons/timeline/composition.svg +0 -12
  37. package/demo/dist/icons/timeline/image.svg +0 -18
  38. package/demo/dist/icons/timeline/music.svg +0 -10
  39. package/demo/dist/icons/timeline/text.svg +0 -3
  40. package/demo/dist/index.html +0 -15
  41. package/dist/src/account-pages-legacy.js +0 -9396
  42. package/dist/src/account-pages.js +0 -61
  43. package/dist/src/app.js +0 -21602
  44. package/dist/src/composition-runtime.js +0 -1053
  45. package/dist/src/config.js +0 -217
  46. package/dist/src/context.js +0 -447
  47. package/dist/src/dev-app-legacy.js +0 -739
  48. package/dist/src/dev-app.js +0 -6
  49. package/dist/src/devcli/migrate-local.js +0 -140
  50. package/dist/src/devcli/sync.js +0 -368
  51. package/dist/src/domain.js +0 -5
  52. package/dist/src/editor-chat-history.js +0 -82
  53. package/dist/src/editor-chat.js +0 -756
  54. package/dist/src/editor-dark-theme.js +0 -1128
  55. package/dist/src/frontend/debug.js +0 -71
  56. package/dist/src/frontend/discover-client.js +0 -130
  57. package/dist/src/frontend/discover-store.js +0 -23
  58. package/dist/src/frontend/file-directory.js +0 -1018
  59. package/dist/src/frontend/flockposter-cache-store.js +0 -124
  60. package/dist/src/frontend/homepage-client.js +0 -446
  61. package/dist/src/frontend/homepage-shared.js +0 -201
  62. package/dist/src/frontend/homepage-store.js +0 -66
  63. package/dist/src/frontend/homepage-view.js +0 -705
  64. package/dist/src/frontend/page-runtime-client.js +0 -132
  65. package/dist/src/frontend/page-runtime-store.js +0 -9
  66. package/dist/src/frontend/sentry.js +0 -42
  67. package/dist/src/frontend/template-editor-chat.js +0 -4181
  68. package/dist/src/help-page.js +0 -346
  69. package/dist/src/homepage.js +0 -1458
  70. package/dist/src/index.js +0 -16
  71. package/dist/src/instrument.js +0 -30
  72. package/dist/src/landing-page.js +0 -384
  73. package/dist/src/page-runtime.js +0 -2
  74. package/dist/src/page-shell.js +0 -1452
  75. package/dist/src/primitive-context.js +0 -416
  76. package/dist/src/primitive-registry.js +0 -3940
  77. package/dist/src/primitive-sdk.js +0 -4
  78. package/dist/src/primitives/hyperframes-media.js +0 -108
  79. package/dist/src/react-page-shell.js +0 -35
  80. package/dist/src/ready-post-schedule-component.js +0 -1540
  81. package/dist/src/registry.js +0 -296
  82. package/dist/src/reskin/agency-page.js +0 -299
  83. package/dist/src/reskin/calendar-page.js +0 -568
  84. package/dist/src/reskin/chat-page.js +0 -942
  85. package/dist/src/reskin/discover-page.js +0 -1788
  86. package/dist/src/reskin/document.js +0 -1587
  87. package/dist/src/reskin/help-page.js +0 -357
  88. package/dist/src/reskin/index-page.js +0 -62
  89. package/dist/src/reskin/inpaint-clipper-page.js +0 -890
  90. package/dist/src/reskin/inpaint-page.js +0 -2554
  91. package/dist/src/reskin/inpaint-video-page.js +0 -1339
  92. package/dist/src/reskin/job-runs-page.js +0 -477
  93. package/dist/src/reskin/library-page.js +0 -1634
  94. package/dist/src/reskin/login-page.js +0 -262
  95. package/dist/src/reskin/portfolio-page.js +0 -687
  96. package/dist/src/reskin/pricing-page.js +0 -390
  97. package/dist/src/reskin/settings-page.js +0 -732
  98. package/dist/src/reskin/theme.js +0 -711
  99. package/dist/src/runtime.js +0 -35
  100. package/dist/src/services/api-call-history.js +0 -249
  101. package/dist/src/services/auth.js +0 -152
  102. package/dist/src/services/billing-pricing.js +0 -39
  103. package/dist/src/services/billing.js +0 -241
  104. package/dist/src/services/cast.js +0 -127
  105. package/dist/src/services/chat-threads.js +0 -92
  106. package/dist/src/services/clip-records.js +0 -250
  107. package/dist/src/services/clip-search.js +0 -77
  108. package/dist/src/services/clip-vectors.js +0 -125
  109. package/dist/src/services/composition-sanitize.js +0 -124
  110. package/dist/src/services/composition-watch.js +0 -79
  111. package/dist/src/services/elevenlabs.js +0 -222
  112. package/dist/src/services/file-directory.js +0 -117
  113. package/dist/src/services/fork-access.js +0 -93
  114. package/dist/src/services/fork-manifest.js +0 -42
  115. package/dist/src/services/ghostcut.js +0 -179
  116. package/dist/src/services/hyperframes.js +0 -3517
  117. package/dist/src/services/job-capacity.js +0 -14
  118. package/dist/src/services/job-logs.js +0 -197
  119. package/dist/src/services/jobs.js +0 -136
  120. package/dist/src/services/local-dynamo.js +0 -0
  121. package/dist/src/services/media-processing.js +0 -766
  122. package/dist/src/services/primitive-media-lambda.js +0 -280
  123. package/dist/src/services/providers.js +0 -2748
  124. package/dist/src/services/rate-limits.js +0 -262
  125. package/dist/src/services/scene-annotations.js +0 -32
  126. package/dist/src/services/serverless-auth.js +0 -382
  127. package/dist/src/services/serverless-jobs.js +0 -1084
  128. package/dist/src/services/serverless-provider-keys.js +0 -409
  129. package/dist/src/services/serverless-records.js +0 -1515
  130. package/dist/src/services/serverless-template-configs.js +0 -75
  131. package/dist/src/services/storage.js +0 -461
  132. package/dist/src/services/swipe-customize.js +0 -437
  133. package/dist/src/services/template-certification.js +0 -413
  134. package/dist/src/services/template-loader.js +0 -99
  135. package/dist/src/services/template-runtime-bundles.js +0 -217
  136. package/dist/src/services/template-sources.js +0 -1017
  137. package/dist/src/services/upstream.js +0 -248
  138. package/dist/src/services/video-normalization.js +0 -2
  139. package/dist/src/services/webhooks.js +0 -62
  140. package/dist/src/template-editor-pages.js +0 -2576
  141. package/dist/src/template-editor-shell.js +0 -2893
  142. package/dist/src/template-sdk.js +0 -4
  143. package/dist/src/worker.js +0 -17
  144. package/public/assets/discover-client-app.js +0 -1
  145. package/public/assets/file-directory-app.js +0 -3
  146. package/public/assets/homepage-app.js +0 -54
  147. package/public/assets/homepage-client-app.js +0 -80
  148. package/public/assets/page-runtime-client-app.js +0 -94
  149. package/public/assets/placeholders/scene-placeholder.png +0 -0
  150. package/src/assets/SELLING_AWARENESS_STAGES.md +0 -579
  151. package/src/assets/SELLING_WITH_HOOKS.md +0 -377
  152. package/src/assets/SELLING_WITH_VSLS.md +0 -606
  153. package/src/assets/favicon.ico +0 -0
  154. 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