@mevdragon/vidfarm-devcli 0.5.2 → 0.6.0

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 (113) hide show
  1. package/README.md +3 -3
  2. package/demo/README.md +28 -0
  3. package/demo/dist/app.css +1 -0
  4. package/demo/dist/app.js +1184 -0
  5. package/demo/dist/chunks/chunk-DXB73IDG.js +1 -0
  6. package/demo/dist/chunks/chunk-S7OWAJDS.js +36 -0
  7. package/demo/dist/chunks/chunk-VTIBZ6AN.js +1 -0
  8. package/demo/dist/chunks/dist-ADSJKBVE.js +332 -0
  9. package/demo/dist/chunks/domEditingLayers-VZMLL4AP-SGHWPND4.js +1 -0
  10. package/demo/dist/chunks/hyperframes-player-XB65TCD6.js +425 -0
  11. package/demo/dist/chunks/lib-XAQ37YOE.js +1 -0
  12. package/demo/dist/chunks/src-TJ2QYA4U.js +207 -0
  13. package/demo/dist/favicon.ico +0 -0
  14. package/demo/dist/icons/timeline/audio.svg +7 -0
  15. package/demo/dist/icons/timeline/captions.svg +5 -0
  16. package/demo/dist/icons/timeline/composition.svg +12 -0
  17. package/demo/dist/icons/timeline/image.svg +18 -0
  18. package/demo/dist/icons/timeline/music.svg +10 -0
  19. package/demo/dist/icons/timeline/text.svg +3 -0
  20. package/demo/dist/index.html +15 -0
  21. package/dist/src/account-pages-legacy.js +9396 -0
  22. package/dist/src/account-pages.js +61 -0
  23. package/dist/src/app.js +14378 -0
  24. package/dist/src/cli.js +1 -1
  25. package/dist/src/composition-runtime.js +613 -0
  26. package/dist/src/config.js +166 -0
  27. package/dist/src/context.js +447 -0
  28. package/dist/src/dev-app-legacy.js +739 -0
  29. package/dist/src/dev-app.js +6 -0
  30. package/dist/src/domain.js +2 -0
  31. package/dist/src/editor-chat-history.js +82 -0
  32. package/dist/src/editor-chat.js +449 -0
  33. package/dist/src/editor-dark-theme.js +1128 -0
  34. package/dist/src/frontend/debug.js +71 -0
  35. package/dist/src/frontend/flockposter-cache-store.js +124 -0
  36. package/dist/src/frontend/homepage-client.js +182 -0
  37. package/dist/src/frontend/homepage-shared.js +4 -0
  38. package/dist/src/frontend/homepage-store.js +28 -0
  39. package/dist/src/frontend/homepage-view.js +547 -0
  40. package/dist/src/frontend/page-runtime-client.js +132 -0
  41. package/dist/src/frontend/page-runtime-store.js +9 -0
  42. package/dist/src/frontend/sentry.js +42 -0
  43. package/dist/src/frontend/template-editor-chat.js +3960 -0
  44. package/dist/src/help-page.js +346 -0
  45. package/dist/src/homepage.js +1235 -0
  46. package/dist/src/hyperframes/composition.js +180 -0
  47. package/dist/src/index.js +16 -0
  48. package/dist/src/instrument.js +30 -0
  49. package/dist/src/lib/crypto.js +45 -0
  50. package/dist/src/lib/dev-log.js +54 -0
  51. package/dist/src/lib/display-name.js +11 -0
  52. package/dist/src/lib/ids.js +24 -0
  53. package/dist/src/lib/images.js +19 -0
  54. package/dist/src/lib/json.js +15 -0
  55. package/dist/src/lib/package-root.js +47 -0
  56. package/dist/src/lib/template-paths.js +28 -0
  57. package/dist/src/lib/time.js +7 -0
  58. package/dist/src/lib/url-clean.js +85 -0
  59. package/dist/src/page-runtime.js +2 -0
  60. package/dist/src/page-shell.js +1381 -0
  61. package/dist/src/primitive-context.js +357 -0
  62. package/dist/src/primitive-registry.js +2436 -0
  63. package/dist/src/primitive-sdk.js +4 -0
  64. package/dist/src/primitives/hyperframes-media.js +108 -0
  65. package/dist/src/react-page-shell.js +35 -0
  66. package/dist/src/ready-post-schedule-component.js +1540 -0
  67. package/dist/src/registry.js +296 -0
  68. package/dist/src/runtime.js +35 -0
  69. package/dist/src/services/api-call-history.js +249 -0
  70. package/dist/src/services/auth.js +152 -0
  71. package/dist/src/services/billing-pricing.js +39 -0
  72. package/dist/src/services/billing.js +228 -0
  73. package/dist/src/services/cast.js +127 -0
  74. package/dist/src/services/chat-threads.js +92 -0
  75. package/dist/src/services/composition-sanitize.js +124 -0
  76. package/dist/src/services/composition-watch.js +79 -0
  77. package/dist/src/services/fork-access.js +93 -0
  78. package/dist/src/services/fork-manifest.js +42 -0
  79. package/dist/src/services/ghostcut.js +179 -0
  80. package/dist/src/services/hyperframes.js +2307 -0
  81. package/dist/src/services/job-capacity.js +14 -0
  82. package/dist/src/services/job-logs.js +197 -0
  83. package/dist/src/services/jobs.js +136 -0
  84. package/dist/src/services/local-dynamo.js +0 -0
  85. package/dist/src/services/media-processing.js +766 -0
  86. package/dist/src/services/primitive-media-lambda.js +280 -0
  87. package/dist/src/services/providers.js +2926 -0
  88. package/dist/src/services/rate-limits.js +262 -0
  89. package/dist/src/services/serverless-auth.js +382 -0
  90. package/dist/src/services/serverless-jobs.js +1082 -0
  91. package/dist/src/services/serverless-provider-keys.js +409 -0
  92. package/dist/src/services/serverless-records.js +1385 -0
  93. package/dist/src/services/serverless-template-configs.js +75 -0
  94. package/dist/src/services/storage.js +383 -0
  95. package/dist/src/services/template-certification.js +413 -0
  96. package/dist/src/services/template-loader.js +99 -0
  97. package/dist/src/services/template-runtime-bundles.js +217 -0
  98. package/dist/src/services/template-sources.js +1017 -0
  99. package/dist/src/services/video-normalization.js +2 -0
  100. package/dist/src/services/webhooks.js +62 -0
  101. package/dist/src/template-editor-pages.js +2576 -0
  102. package/dist/src/template-editor-shell.js +2840 -0
  103. package/dist/src/template-sdk.js +4 -0
  104. package/dist/src/worker.js +17 -0
  105. package/package.json +6 -4
  106. package/public/assets/homepage-app.js +54 -0
  107. package/public/assets/homepage-client-app.js +80 -0
  108. package/public/assets/page-runtime-client-app.js +94 -0
  109. package/src/assets/SELLING_AWARENESS_STAGES.md +579 -0
  110. package/src/assets/SELLING_WITH_HOOKS.md +377 -0
  111. package/src/assets/SELLING_WITH_VSLS.md +606 -0
  112. package/src/assets/favicon.ico +0 -0
  113. package/src/assets/logo-vidfarm.png +0 -0
@@ -0,0 +1,357 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import * as Sentry from "@sentry/node";
3
+ import { config } from "./config.js";
4
+ import { devErrorFields, devLog } from "./lib/dev-log.js";
5
+ import { createId } from "./lib/ids.js";
6
+ import { jobLogStore } from "./services/job-logs.js";
7
+ import { estimateNormalizationCostUsd, normalizeVideoAsset } from "./services/video-normalization.js";
8
+ export function createPrimitiveJobContext(input) {
9
+ const prefix = input.storage.primitiveJobPrefix(input.primitive.id, input.customer.id, input.job.id);
10
+ const buildArtifactUrl = (storageKey, fallbackUrl) => fallbackUrl ?? input.storage.getPublicUrl(storageKey) ?? buildPlatformMediaUrl(storageKey);
11
+ // Billing records are written after the billable work (render, normalize,
12
+ // etc.) has already succeeded. A transient billing failure must never fail
13
+ // the job — the customer would retry and pay for the expensive work twice.
14
+ const recordBillingNonFatal = async (record) => {
15
+ try {
16
+ await input.billing.record(record);
17
+ }
18
+ catch (error) {
19
+ Sentry.captureException(error);
20
+ devLog("billing.record_failed_nonfatal", {
21
+ job_id: input.job.id,
22
+ customer_id: input.customer.id,
23
+ template_id: input.primitive.id,
24
+ cost_center_slug: record.costCenterSlug ?? null,
25
+ idempotency_key: record.idempotencyKey ?? null,
26
+ ...devErrorFields(error)
27
+ }, "warn");
28
+ }
29
+ };
30
+ return {
31
+ env: config.isProduction ? "production" : "development",
32
+ customer: input.customer,
33
+ logger: {
34
+ debug(message, metadata = {}) {
35
+ jobLogStore.addEvent({
36
+ id: createId("evt"),
37
+ jobId: input.job.id,
38
+ level: "debug",
39
+ message,
40
+ metadata,
41
+ progress: null,
42
+ artifactKey: null
43
+ });
44
+ },
45
+ info(message, metadata = {}) {
46
+ jobLogStore.addEvent({
47
+ id: createId("evt"),
48
+ jobId: input.job.id,
49
+ level: "info",
50
+ message,
51
+ metadata,
52
+ progress: null,
53
+ artifactKey: null
54
+ });
55
+ },
56
+ warn(message, metadata = {}) {
57
+ jobLogStore.addEvent({
58
+ id: createId("evt"),
59
+ jobId: input.job.id,
60
+ level: "warn",
61
+ message,
62
+ metadata,
63
+ progress: null,
64
+ artifactKey: null
65
+ });
66
+ },
67
+ error(message, metadata = {}) {
68
+ jobLogStore.addEvent({
69
+ id: createId("evt"),
70
+ jobId: input.job.id,
71
+ level: "error",
72
+ message,
73
+ metadata,
74
+ progress: null,
75
+ artifactKey: null
76
+ });
77
+ },
78
+ progress(progress, message, metadata = {}) {
79
+ jobLogStore.addEvent({
80
+ id: createId("evt"),
81
+ jobId: input.job.id,
82
+ level: "info",
83
+ message,
84
+ metadata,
85
+ progress,
86
+ artifactKey: typeof metadata.artifactKey === "string" ? metadata.artifactKey : null
87
+ });
88
+ void input.jobs.updateJobStatus({
89
+ id: input.job.id,
90
+ status: "running",
91
+ progress
92
+ });
93
+ }
94
+ },
95
+ storage: {
96
+ async putJson(key, value) {
97
+ const stored = await input.storage.putJson(`${prefix}/${key}`, value);
98
+ const artifactUrl = buildArtifactUrl(stored.key, stored.url);
99
+ await insertArtifact("json", stored.key, artifactUrl, {});
100
+ return { ...stored, url: artifactUrl };
101
+ },
102
+ async putText(key, value, contentType = "text/plain; charset=utf-8") {
103
+ const stored = await input.storage.putText(`${prefix}/${key}`, value, contentType);
104
+ const artifactUrl = buildArtifactUrl(stored.key, stored.url);
105
+ await insertArtifact("text", stored.key, artifactUrl, { contentType });
106
+ return { ...stored, url: artifactUrl };
107
+ },
108
+ async putBuffer(key, value, options = {}) {
109
+ const stored = await input.storage.putBuffer(`${prefix}/${key}`, value, options.contentType);
110
+ const artifactUrl = buildArtifactUrl(stored.key, stored.url);
111
+ await insertArtifact(options.kind ?? "binary", stored.key, artifactUrl, {
112
+ ...(options.metadata ?? {}),
113
+ contentType: options.contentType ?? null
114
+ });
115
+ return { ...stored, url: artifactUrl };
116
+ },
117
+ getPublicUrl(key) {
118
+ return input.storage.getPublicUrl(`${prefix}/${key}`);
119
+ },
120
+ resolveKey(key) {
121
+ return `${prefix}/${key}`;
122
+ }
123
+ },
124
+ billing: {
125
+ async record(record) {
126
+ await recordBillingNonFatal({
127
+ customerId: input.customer.id,
128
+ jobId: input.job.id,
129
+ tracer: input.job.tracer,
130
+ templateId: input.primitive.id,
131
+ type: record.type,
132
+ costUsd: record.costUsd,
133
+ chargeUsd: record.chargeUsd ?? Number((record.costUsd * 1.2).toFixed(4)),
134
+ costCenterSlug: record.costCenterSlug,
135
+ idempotencyKey: record.idempotencyKey,
136
+ occurredAtMs: record.occurredAtMs,
137
+ metadata: record.metadata
138
+ });
139
+ }
140
+ },
141
+ providers: {
142
+ listAvailableProviders() {
143
+ return input.providers.listAvailableProviders(input.customer.id);
144
+ },
145
+ async generateText(request) {
146
+ return input.providers.generateText({
147
+ customerId: input.customer.id,
148
+ jobId: input.job.id,
149
+ workerId: input.workerId,
150
+ ...request
151
+ });
152
+ },
153
+ async generateImage(request) {
154
+ return input.providers.generateImage({
155
+ customerId: input.customer.id,
156
+ jobId: input.job.id,
157
+ workerId: input.workerId,
158
+ ...request
159
+ });
160
+ },
161
+ async editImage(request) {
162
+ return input.providers.editImage({
163
+ customerId: input.customer.id,
164
+ jobId: input.job.id,
165
+ workerId: input.workerId,
166
+ ...request
167
+ });
168
+ },
169
+ async inpaintImage(request) {
170
+ return input.providers.inpaintImage({
171
+ customerId: input.customer.id,
172
+ jobId: input.job.id,
173
+ workerId: input.workerId,
174
+ ...request
175
+ });
176
+ },
177
+ async generateVideo(request) {
178
+ return input.providers.generateVideo({
179
+ customerId: input.customer.id,
180
+ jobId: input.job.id,
181
+ workerId: input.workerId,
182
+ ...request
183
+ });
184
+ }
185
+ },
186
+ hyperframes: {
187
+ async render(request) {
188
+ const hyperframesOutputKey = `renders/vidfarm/primitives/${input.primitive.id}/${input.customer.id}/${input.job.id}/output.mp4`;
189
+ const render = await input.hyperframes.render({
190
+ ...request,
191
+ outputKey: hyperframesOutputKey,
192
+ wait: true
193
+ });
194
+ let outputUrl = render.outputUrl;
195
+ let platformOutputKey = null;
196
+ const localOutputLocation = typeof render.metadata.outputLocation === "string"
197
+ ? render.metadata.outputLocation
198
+ : null;
199
+ if (render.outputS3Uri) {
200
+ const output = await input.hyperframes.readRenderOutput(render.outputS3Uri);
201
+ platformOutputKey = `${prefix}/${request.outputKey || "output.mp4"}`;
202
+ const stored = await input.storage.putBuffer(platformOutputKey, output.bytes, output.contentType);
203
+ outputUrl = buildArtifactUrl(stored.key, stored.url);
204
+ await insertArtifact("video", stored.key, outputUrl, {
205
+ operation: "hyperframes-render",
206
+ contentType: output.contentType,
207
+ renderId: render.renderId,
208
+ outputS3Uri: render.outputS3Uri
209
+ });
210
+ }
211
+ else if (localOutputLocation) {
212
+ // In-process (local) render writes the MP4 to ephemeral local disk and
213
+ // returns no S3 URI. On serverless boxes that disk is per-invocation
214
+ // and the `renders/` prefix is not publicly served, so the raw
215
+ // outputUrl 404s ("Artifact not found"). Persist the bytes into the
216
+ // public job-scoped prefix — S3 on cloud, local disk on serve boxes —
217
+ // exactly like the cloud branch above.
218
+ const bytes = await readFile(localOutputLocation);
219
+ platformOutputKey = `${prefix}/${request.outputKey || "output.mp4"}`;
220
+ const stored = await input.storage.putBuffer(platformOutputKey, bytes, "video/mp4");
221
+ outputUrl = buildArtifactUrl(stored.key, stored.url);
222
+ await insertArtifact("video", stored.key, outputUrl, {
223
+ operation: "hyperframes-render-local",
224
+ contentType: "video/mp4",
225
+ renderId: render.renderId,
226
+ outputLocation: localOutputLocation
227
+ });
228
+ }
229
+ const estimatedCostUsd = typeof render.metadata.estimatedCostUsd === "number" ? render.metadata.estimatedCostUsd : null;
230
+ const chargeUsd = typeof render.metadata.chargeUsd === "number" ? render.metadata.chargeUsd : null;
231
+ if (estimatedCostUsd !== null && estimatedCostUsd > 0) {
232
+ await recordBillingNonFatal({
233
+ customerId: input.customer.id,
234
+ jobId: input.job.id,
235
+ tracer: input.job.tracer,
236
+ templateId: input.primitive.id,
237
+ type: "render",
238
+ costUsd: estimatedCostUsd,
239
+ chargeUsd: typeof chargeUsd === "number" ? chargeUsd : undefined,
240
+ costCenterSlug: "hyperframes_lambda",
241
+ idempotencyKey: `hyperframes_lambda:${render.renderId}`,
242
+ occurredAtMs: Date.now(),
243
+ metadata: {
244
+ ...render.metadata,
245
+ render_id: render.renderId,
246
+ output_url: outputUrl,
247
+ signed_output_url: render.outputUrl,
248
+ output_s3_uri: render.outputS3Uri,
249
+ platform_output_key: platformOutputKey
250
+ }
251
+ });
252
+ }
253
+ return {
254
+ renderId: render.renderId,
255
+ outputUrl,
256
+ outputS3Uri: render.outputS3Uri,
257
+ metadata: {
258
+ ...render.metadata,
259
+ outputUrl,
260
+ signedOutputUrl: render.outputUrl,
261
+ platformOutputKey
262
+ }
263
+ };
264
+ },
265
+ async renderStill(request) {
266
+ const extension = (request.imageFormat ?? "png") === "jpeg" ? "jpg" : (request.imageFormat ?? "png");
267
+ const localOutputKey = `renders/vidfarm/primitives/${input.primitive.id}/${input.customer.id}/${input.job.id}/${request.outputKey || `output.${extension}`}`;
268
+ const rendered = await input.hyperframes.renderStill({
269
+ compositionHtml: request.compositionHtml,
270
+ compositionId: request.compositionId,
271
+ imageFormat: request.imageFormat,
272
+ outputKey: localOutputKey
273
+ });
274
+ // renderStill writes to ephemeral local disk under the non-public
275
+ // `renders/` prefix. Persist it into the public job-scoped prefix (S3 on
276
+ // cloud, local disk on serve boxes) so the artifact URL resolves — same
277
+ // fix as render() above.
278
+ const platformKey = `${prefix}/${request.outputKey || `output.${extension}`}`;
279
+ const contentType = extension === "jpg" ? "image/jpeg" : extension === "webp" ? "image/webp" : "image/png";
280
+ const bytes = await readFile(rendered.outputLocation);
281
+ const stored = await input.storage.putBuffer(platformKey, bytes, contentType);
282
+ const outputUrl = buildArtifactUrl(stored.key, stored.url);
283
+ await insertArtifact("image", stored.key, outputUrl, {
284
+ operation: "hyperframes-render-still",
285
+ renderId: rendered.renderId,
286
+ imageFormat: request.imageFormat ?? "png"
287
+ });
288
+ return {
289
+ renderId: rendered.renderId,
290
+ outputUrl,
291
+ metadata: rendered.metadata
292
+ };
293
+ }
294
+ },
295
+ media: {
296
+ async normalizeVideo(request) {
297
+ const normalized = await normalizeVideoAsset({
298
+ sourceUrl: request.sourceUrl,
299
+ maxWidth: request.maxWidth,
300
+ maxHeight: request.maxHeight,
301
+ crf: request.crf,
302
+ preset: request.preset,
303
+ maxVideoBitrateKbps: request.maxVideoBitrateKbps,
304
+ audioBitrateKbps: request.audioBitrateKbps
305
+ });
306
+ const stored = await input.storage.putBuffer(`${prefix}/${request.outputKey}`, normalized.bytes, normalized.contentType);
307
+ const artifactUrl = buildArtifactUrl(stored.key, stored.url);
308
+ await insertArtifact("video", stored.key, artifactUrl, {
309
+ ...(request.metadata ?? {}),
310
+ operation: "normalize-video",
311
+ contentType: normalized.contentType,
312
+ normalization: normalized.metadata
313
+ });
314
+ await recordBillingNonFatal({
315
+ customerId: input.customer.id,
316
+ jobId: input.job.id,
317
+ tracer: input.job.tracer,
318
+ templateId: input.primitive.id,
319
+ type: "cpu_estimate",
320
+ costUsd: estimateNormalizationCostUsd(normalized.metadata),
321
+ costCenterSlug: "video_normalization",
322
+ idempotencyKey: `video_normalization:${input.job.id}:${request.outputKey}`,
323
+ occurredAtMs: Date.now(),
324
+ metadata: {
325
+ ...(request.metadata ?? {}),
326
+ operation: "normalize-video",
327
+ source_url: request.sourceUrl,
328
+ output_key: `${prefix}/${request.outputKey}`,
329
+ normalization: normalized.metadata
330
+ }
331
+ });
332
+ return {
333
+ ...stored,
334
+ url: artifactUrl,
335
+ contentType: normalized.contentType,
336
+ normalization: normalized.metadata
337
+ };
338
+ }
339
+ }
340
+ };
341
+ async function insertArtifact(kind, storageKey, publicUrl, metadata) {
342
+ await input.jobs.insertArtifact({
343
+ id: createId("art"),
344
+ jobId: input.job.id,
345
+ customerId: input.customer.id,
346
+ templateId: input.primitive.id,
347
+ kind,
348
+ storageKey,
349
+ publicUrl,
350
+ metadata
351
+ });
352
+ }
353
+ }
354
+ function buildPlatformMediaUrl(storageKey) {
355
+ return `${config.PUBLIC_BASE_URL.replace(/\/$/, "")}/template-media?key=${encodeURIComponent(storageKey)}`;
356
+ }
357
+ //# sourceMappingURL=primitive-context.js.map