@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,409 @@
1
+ import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
2
+ import { DeleteCommand, DynamoDBDocumentClient, PutCommand, QueryCommand, UpdateCommand } from "@aws-sdk/lib-dynamodb";
3
+ import { config } from "../config.js";
4
+ import { devLog } from "../lib/dev-log.js";
5
+ import { nowIso } from "../lib/time.js";
6
+ import { createLocalDynamoClient } from "./local-dynamo.js";
7
+ // `vidfarm serve` (RECORDS_DRIVER=local) swaps in the disk-backed shim so
8
+ // provider keys live on disk; deployed environments keep the real client.
9
+ const dynamodb = config.RECORDS_DRIVER === "local"
10
+ ? createLocalDynamoClient()
11
+ : DynamoDBDocumentClient.from(new DynamoDBClient({}), {
12
+ marshallOptions: {
13
+ removeUndefinedValues: true
14
+ }
15
+ });
16
+ export class ServerlessProviderKeyService {
17
+ async listProviderKeys(customerId) {
18
+ const items = await this.queryCustomerKeys(customerId);
19
+ devLog("provider_keys.list", {
20
+ customer_id: customerId,
21
+ count: items.length
22
+ });
23
+ return items.map(itemToPublicProviderKey);
24
+ }
25
+ async listProviderKeysWithSecrets(customerId) {
26
+ const items = await this.queryCustomerKeys(customerId);
27
+ return items.map(itemToSecretProviderKey);
28
+ }
29
+ async createProviderKey(record) {
30
+ const timestamp = nowIso();
31
+ await dynamodb.send(new PutCommand({
32
+ TableName: requireMainTableName(),
33
+ Item: {
34
+ pk: providerKeyPk(record.id),
35
+ sk: "META",
36
+ entity_type: "ProviderKey",
37
+ gsi1pk: customerProviderKeysPk(record.customerId),
38
+ gsi1sk: providerKeySortKey(record.provider, timestamp, record.id),
39
+ key_id: record.id,
40
+ customer_id: record.customerId,
41
+ provider: record.provider,
42
+ label: record.label,
43
+ encrypted_secret: record.encryptedSecret,
44
+ status: "active",
45
+ last_used_at: null,
46
+ cooldown_until: null,
47
+ disabled_reason: null,
48
+ created_at: timestamp,
49
+ updated_at: timestamp
50
+ },
51
+ ConditionExpression: "attribute_not_exists(pk)"
52
+ }));
53
+ devLog("provider_keys.created", {
54
+ key_id: record.id,
55
+ customer_id: record.customerId,
56
+ provider: record.provider,
57
+ label: record.label
58
+ });
59
+ }
60
+ async deleteProviderKey(customerId, keyId) {
61
+ await dynamodb.send(new DeleteCommand({
62
+ TableName: requireMainTableName(),
63
+ Key: providerKeyKey(keyId),
64
+ ConditionExpression: "customer_id = :customerId",
65
+ ExpressionAttributeValues: {
66
+ ":customerId": customerId
67
+ }
68
+ })).catch((error) => {
69
+ if (isConditionalCheckFailed(error)) {
70
+ return;
71
+ }
72
+ throw error;
73
+ });
74
+ devLog("provider_keys.deleted", {
75
+ key_id: keyId,
76
+ customer_id: customerId
77
+ }, "warn");
78
+ }
79
+ async listAvailableProviderKeyCounts(customerId) {
80
+ const now = nowIso();
81
+ const counts = new Map();
82
+ for (const key of await this.listProviderKeysWithSecrets(customerId)) {
83
+ if (key.status !== "active" || !key.secret.trim()) {
84
+ continue;
85
+ }
86
+ if (key.cooldown_until && key.cooldown_until > now) {
87
+ continue;
88
+ }
89
+ const item = await this.getProviderKeyItem(customerId, key.id);
90
+ if (!item) {
91
+ continue;
92
+ }
93
+ const leaseExpiresAt = typeof item.active_lease_expires_at === "string" ? item.active_lease_expires_at : null;
94
+ if (leaseExpiresAt && leaseExpiresAt > now) {
95
+ continue;
96
+ }
97
+ counts.set(key.provider, (counts.get(key.provider) ?? 0) + 1);
98
+ }
99
+ return [...counts.entries()].map(([provider, availableCount]) => ({
100
+ customerId,
101
+ provider,
102
+ availableCount
103
+ })).map((entry) => {
104
+ devLog("provider_keys.available_count", {
105
+ customer_id: entry.customerId,
106
+ provider: entry.provider,
107
+ available_count: entry.availableCount
108
+ });
109
+ return entry;
110
+ });
111
+ }
112
+ async acquireProviderKeyLease(input) {
113
+ const now = nowIso();
114
+ const keys = (await this.queryCustomerKeys(input.customerId))
115
+ .map(itemToSecretProviderKeyWithLease)
116
+ .filter((key) => key.provider === input.provider)
117
+ .filter((key) => key.status === "active")
118
+ .filter((key) => !key.cooldown_until || key.cooldown_until <= now)
119
+ .filter((key) => key.secret.trim().length > 0)
120
+ .sort((a, b) => {
121
+ const aLast = a.last_used_at ?? "";
122
+ const bLast = b.last_used_at ?? "";
123
+ if (!aLast && bLast)
124
+ return -1;
125
+ if (aLast && !bLast)
126
+ return 1;
127
+ return aLast.localeCompare(bLast) || a.created_at.localeCompare(b.created_at);
128
+ });
129
+ for (const key of keys) {
130
+ try {
131
+ await dynamodb.send(new UpdateCommand({
132
+ TableName: requireMainTableName(),
133
+ Key: providerKeyKey(key.id),
134
+ ConditionExpression: [
135
+ "customer_id = :customerId",
136
+ "#status = :active",
137
+ "(attribute_not_exists(cooldown_until) OR attribute_type(cooldown_until, :nullType) OR cooldown_until = :empty OR cooldown_until <= :now)",
138
+ "(attribute_not_exists(active_lease_expires_at) OR active_lease_expires_at <= :now OR active_lease_token = :leaseToken)"
139
+ ].join(" AND "),
140
+ UpdateExpression: [
141
+ "set active_lease_token = :leaseToken",
142
+ "active_lease_worker_id = :workerId",
143
+ "active_lease_job_id = :jobId",
144
+ "active_lease_expires_at = :expiresAt",
145
+ "updated_at = :now"
146
+ ].join(", "),
147
+ ExpressionAttributeNames: {
148
+ "#status": "status"
149
+ },
150
+ ExpressionAttributeValues: {
151
+ ":customerId": input.customerId,
152
+ ":active": "active",
153
+ ":nullType": "NULL",
154
+ ":empty": "",
155
+ ":now": now,
156
+ ":leaseToken": input.leaseToken,
157
+ ":workerId": input.workerId,
158
+ ":jobId": input.jobId,
159
+ ":expiresAt": input.expiresAt
160
+ }
161
+ }));
162
+ await this.putLeaseEvent({
163
+ keyId: key.id,
164
+ leaseToken: input.leaseToken,
165
+ workerId: input.workerId,
166
+ jobId: input.jobId,
167
+ expiresAt: input.expiresAt
168
+ });
169
+ devLog("provider_keys.lease.acquired", {
170
+ key_id: key.id,
171
+ customer_id: input.customerId,
172
+ provider: input.provider,
173
+ job_id: input.jobId,
174
+ worker_id: input.workerId,
175
+ expires_at: input.expiresAt
176
+ });
177
+ return {
178
+ keyId: key.id,
179
+ encryptedSecret: key.secret
180
+ };
181
+ }
182
+ catch (error) {
183
+ if (isConditionalCheckFailed(error)) {
184
+ continue;
185
+ }
186
+ throw error;
187
+ }
188
+ }
189
+ devLog("provider_keys.lease.unavailable", {
190
+ customer_id: input.customerId,
191
+ provider: input.provider,
192
+ job_id: input.jobId,
193
+ worker_id: input.workerId
194
+ }, "warn");
195
+ return null;
196
+ }
197
+ async releaseProviderKeyLease(input) {
198
+ const now = nowIso();
199
+ await dynamodb.send(new UpdateCommand({
200
+ TableName: requireMainTableName(),
201
+ Key: providerKeyKey(input.keyId),
202
+ ConditionExpression: "active_lease_token = :leaseToken",
203
+ UpdateExpression: "SET updated_at = :now REMOVE active_lease_token, active_lease_worker_id, active_lease_job_id, active_lease_expires_at",
204
+ ExpressionAttributeValues: {
205
+ ":leaseToken": input.leaseToken,
206
+ ":now": now
207
+ }
208
+ })).catch((error) => {
209
+ if (isConditionalCheckFailed(error)) {
210
+ return;
211
+ }
212
+ throw error;
213
+ });
214
+ await dynamodb.send(new DeleteCommand({
215
+ TableName: requireMainTableName(),
216
+ Key: {
217
+ pk: providerKeyPk(input.keyId),
218
+ sk: `LEASE#${input.leaseToken}`
219
+ }
220
+ }));
221
+ devLog("provider_keys.lease.released", {
222
+ key_id: input.keyId
223
+ });
224
+ }
225
+ async extendProviderKeyLease(input) {
226
+ await dynamodb.send(new UpdateCommand({
227
+ TableName: requireMainTableName(),
228
+ Key: providerKeyKey(input.keyId),
229
+ ConditionExpression: "active_lease_token = :leaseToken",
230
+ UpdateExpression: "set active_lease_expires_at = :expiresAt, updated_at = :now",
231
+ ExpressionAttributeValues: {
232
+ ":leaseToken": input.leaseToken,
233
+ ":expiresAt": input.expiresAt,
234
+ ":now": nowIso()
235
+ }
236
+ })).catch((error) => {
237
+ if (isConditionalCheckFailed(error)) {
238
+ return;
239
+ }
240
+ throw error;
241
+ });
242
+ devLog("provider_keys.lease.extended", {
243
+ key_id: input.keyId,
244
+ expires_at: input.expiresAt
245
+ });
246
+ }
247
+ async recordProviderKeyUsage(input) {
248
+ const timestamp = nowIso();
249
+ await dynamodb.send(new PutCommand({
250
+ TableName: requireMainTableName(),
251
+ Item: {
252
+ pk: providerKeyPk(input.keyId),
253
+ sk: `USAGE#${timestamp}#${input.id}`,
254
+ entity_type: "ProviderKeyUsage",
255
+ gsi1pk: `JOB#${input.jobId}#PROVIDER_USAGE`,
256
+ gsi1sk: `USAGE#${timestamp}#${input.id}`,
257
+ usage_id: input.id,
258
+ key_id: input.keyId,
259
+ job_id: input.jobId,
260
+ provider: input.provider,
261
+ model: input.model ?? null,
262
+ event_type: input.eventType,
263
+ input_tokens: input.inputTokens ?? null,
264
+ output_tokens: input.outputTokens ?? null,
265
+ cost_usd: input.costUsd ?? null,
266
+ metadata: input.metadata ?? {},
267
+ created_at: timestamp
268
+ }
269
+ }));
270
+ devLog("provider_keys.usage.recorded", {
271
+ usage_id: input.id,
272
+ key_id: input.keyId,
273
+ job_id: input.jobId,
274
+ provider: input.provider,
275
+ model: input.model ?? null,
276
+ event_type: input.eventType,
277
+ cost_usd: input.costUsd ?? null
278
+ });
279
+ }
280
+ async touchProviderKey(keyId) {
281
+ const timestamp = nowIso();
282
+ await dynamodb.send(new UpdateCommand({
283
+ TableName: requireMainTableName(),
284
+ Key: providerKeyKey(keyId),
285
+ UpdateExpression: "set last_used_at = :now, updated_at = :now",
286
+ ExpressionAttributeValues: {
287
+ ":now": timestamp
288
+ }
289
+ }));
290
+ }
291
+ async setProviderKeyCooldown(keyId, cooldownUntil, status = "active", disabledReason = null) {
292
+ const timestamp = nowIso();
293
+ await dynamodb.send(new UpdateCommand({
294
+ TableName: requireMainTableName(),
295
+ Key: providerKeyKey(keyId),
296
+ UpdateExpression: "set cooldown_until = :cooldownUntil, #status = :status, disabled_reason = :disabledReason, updated_at = :now",
297
+ ExpressionAttributeNames: {
298
+ "#status": "status"
299
+ },
300
+ ExpressionAttributeValues: {
301
+ ":cooldownUntil": cooldownUntil instanceof Date ? cooldownUntil.toISOString() : cooldownUntil,
302
+ ":status": status,
303
+ ":disabledReason": disabledReason,
304
+ ":now": timestamp
305
+ }
306
+ }));
307
+ }
308
+ async queryCustomerKeys(customerId) {
309
+ const response = await dynamodb.send(new QueryCommand({
310
+ TableName: requireMainTableName(),
311
+ IndexName: "gsi1",
312
+ KeyConditionExpression: "gsi1pk = :pk",
313
+ ExpressionAttributeValues: {
314
+ ":pk": customerProviderKeysPk(customerId)
315
+ }
316
+ }));
317
+ return response.Items ?? [];
318
+ }
319
+ async getProviderKeyItem(customerId, keyId) {
320
+ const response = await dynamodb.send(new QueryCommand({
321
+ TableName: requireMainTableName(),
322
+ KeyConditionExpression: "pk = :pk and sk = :sk",
323
+ FilterExpression: "customer_id = :customerId",
324
+ ExpressionAttributeValues: {
325
+ ":pk": providerKeyPk(keyId),
326
+ ":sk": "META",
327
+ ":customerId": customerId
328
+ },
329
+ Limit: 1
330
+ }));
331
+ return response.Items?.[0] ?? null;
332
+ }
333
+ async putLeaseEvent(input) {
334
+ await dynamodb.send(new PutCommand({
335
+ TableName: requireMainTableName(),
336
+ Item: {
337
+ pk: providerKeyPk(input.keyId),
338
+ sk: `LEASE#${input.leaseToken}`,
339
+ entity_type: "ProviderKeyLease",
340
+ key_id: input.keyId,
341
+ lease_token: input.leaseToken,
342
+ worker_id: input.workerId,
343
+ job_id: input.jobId,
344
+ leased_at: nowIso(),
345
+ expires_at: input.expiresAt,
346
+ expires_at_epoch_seconds: Math.ceil(Date.parse(input.expiresAt) / 1000)
347
+ }
348
+ }));
349
+ }
350
+ }
351
+ function requireMainTableName() {
352
+ if (config.RECORDS_DRIVER === "local") {
353
+ return config.VIDFARM_SERVERLESS_MAIN_TABLE_NAME || "vidfarm-local-main";
354
+ }
355
+ if (!config.VIDFARM_SERVERLESS_MAIN_TABLE_NAME) {
356
+ throw new Error("Serverless provider key store requires VIDFARM_SERVERLESS_MAIN_TABLE_NAME.");
357
+ }
358
+ return config.VIDFARM_SERVERLESS_MAIN_TABLE_NAME;
359
+ }
360
+ function providerKeyPk(keyId) {
361
+ return `PROVIDER_KEY#${keyId}`;
362
+ }
363
+ function providerKeyKey(keyId) {
364
+ return {
365
+ pk: providerKeyPk(keyId),
366
+ sk: "META"
367
+ };
368
+ }
369
+ function customerProviderKeysPk(customerId) {
370
+ return `CUSTOMER#${customerId}#PROVIDER_KEYS`;
371
+ }
372
+ function providerKeySortKey(provider, createdAt, keyId) {
373
+ return `PROVIDER#${provider}#${createdAt}#${keyId}`;
374
+ }
375
+ function itemToPublicProviderKey(item) {
376
+ return {
377
+ id: String(item.key_id),
378
+ provider: normalizeProvider(item.provider),
379
+ label: typeof item.label === "string" ? item.label : null,
380
+ status: typeof item.status === "string" ? item.status : "active",
381
+ last_used_at: typeof item.last_used_at === "string" ? item.last_used_at : null,
382
+ cooldown_until: typeof item.cooldown_until === "string" ? item.cooldown_until : null,
383
+ disabled_reason: typeof item.disabled_reason === "string" ? item.disabled_reason : null,
384
+ created_at: String(item.created_at),
385
+ updated_at: String(item.updated_at)
386
+ };
387
+ }
388
+ function itemToSecretProviderKey(item) {
389
+ return {
390
+ ...itemToPublicProviderKey(item),
391
+ secret: typeof item.encrypted_secret === "string" ? item.encrypted_secret : ""
392
+ };
393
+ }
394
+ function itemToSecretProviderKeyWithLease(item) {
395
+ return {
396
+ ...itemToSecretProviderKey(item),
397
+ active_lease_expires_at: typeof item.active_lease_expires_at === "string" ? item.active_lease_expires_at : null
398
+ };
399
+ }
400
+ function normalizeProvider(value) {
401
+ if (value === "openai" || value === "gemini" || value === "openrouter" || value === "perplexity") {
402
+ return value;
403
+ }
404
+ return "openai";
405
+ }
406
+ function isConditionalCheckFailed(error) {
407
+ return Boolean(error && typeof error === "object" && "name" in error && error.name === "ConditionalCheckFailedException");
408
+ }
409
+ //# sourceMappingURL=serverless-provider-keys.js.map