@mevdragon/vidfarm-devcli 0.2.1 → 0.2.3

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 (40) hide show
  1. package/.env.example +6 -39
  2. package/GETTING_STARTED.developers.md +87 -0
  3. package/README.md +94 -238
  4. package/SKILL.developer.md +430 -104
  5. package/dist/src/account-pages.js +1 -1
  6. package/dist/src/app.js +93 -5
  7. package/dist/src/cli.js +456 -8
  8. package/dist/src/config.js +3 -2
  9. package/dist/src/context.js +30 -11
  10. package/dist/src/db.js +2 -57
  11. package/dist/src/dev-app.js +0 -1
  12. package/dist/src/index.js +4 -2
  13. package/dist/src/lib/template-paths.js +21 -0
  14. package/dist/src/runtime.js +3 -1
  15. package/dist/src/services/auth.js +4 -4
  16. package/dist/src/services/job-logs.js +186 -0
  17. package/dist/src/services/jobs.js +3 -2
  18. package/dist/src/services/providers.js +14 -6
  19. package/dist/src/services/storage.js +85 -2
  20. package/dist/src/services/template-sources.js +29 -3
  21. package/dist/templates/template_0000/src/lib/images.js +46 -86
  22. package/dist/templates/template_0000/src/template.js +277 -53
  23. package/package.json +5 -6
  24. package/templates/template_0000/README.md +8 -52
  25. package/templates/template_0000/SKILL.md +35 -3
  26. package/templates/template_0000/package.json +3 -6
  27. package/templates/template_0000/src/lib/images.js +46 -86
  28. package/templates/template_0000/src/lib/images.ts +55 -98
  29. package/templates/template_0000/src/template-dna.js +9 -0
  30. package/templates/template_0000/src/template.js +523 -199
  31. package/templates/template_0000/src/template.ts +356 -61
  32. package/templates/template_0000/template.config.json +7 -12
  33. package/AWS_REMOTION_HANDOFF.md +0 -311
  34. package/PLATFORM_SPEC.md +0 -1039
  35. package/SKILL.director.md +0 -599
  36. package/dist/infra/cdk/bin/vidfarm-prod.js +0 -59
  37. package/dist/infra/cdk/lib/vidfarm-prod-stack.js +0 -212
  38. package/templates/template_0000/package-lock.json +0 -5505
  39. package/templates/template_0000/scripts/create-site.mjs +0 -27
  40. package/templates/template_0000/scripts/render-cloud.mjs +0 -72
package/dist/src/app.js CHANGED
@@ -60,6 +60,11 @@ const settingsProfileFormSchema = z.object({
60
60
  groupchat_url: z.union([z.literal(""), z.string().url()]).optional(),
61
61
  flockposter_api_key: z.string().max(1000).optional()
62
62
  });
63
+ const developerPreviewPresignSchema = z.object({
64
+ file_name: z.string().min(1).max(255),
65
+ content_type: z.string().trim().min(1).max(255).optional(),
66
+ directory: z.string().trim().max(500).optional()
67
+ });
63
68
  const listJobsQuerySchema = z.object({
64
69
  tracer: z.string().min(1).optional(),
65
70
  start_time: z.string().min(1).optional(),
@@ -195,6 +200,56 @@ function isAllowedAttachment(fileName, contentType) {
195
200
  || contentType === "text/plain"
196
201
  || contentType === "text/markdown";
197
202
  }
203
+ function sanitizeStorageSubpath(value) {
204
+ if (!value) {
205
+ return "";
206
+ }
207
+ return value
208
+ .split("/")
209
+ .map((segment) => segment.trim().replace(/[^\w.-]+/g, "_"))
210
+ .filter((segment) => segment && segment !== "." && segment !== "..")
211
+ .join("/");
212
+ }
213
+ function inferAttachmentContentType(fileName) {
214
+ switch (path.extname(fileName).toLowerCase()) {
215
+ case ".png":
216
+ return "image/png";
217
+ case ".gif":
218
+ return "image/gif";
219
+ case ".jpg":
220
+ case ".jpeg":
221
+ return "image/jpeg";
222
+ case ".webp":
223
+ return "image/webp";
224
+ case ".svg":
225
+ return "image/svg+xml";
226
+ case ".mp4":
227
+ case ".m4v":
228
+ return "video/mp4";
229
+ case ".mov":
230
+ return "video/quicktime";
231
+ case ".webm":
232
+ return "video/webm";
233
+ case ".mp3":
234
+ return "audio/mpeg";
235
+ case ".wav":
236
+ return "audio/wav";
237
+ case ".m4a":
238
+ return "audio/mp4";
239
+ case ".aac":
240
+ return "audio/aac";
241
+ case ".ogg":
242
+ return "audio/ogg";
243
+ case ".pdf":
244
+ return "application/pdf";
245
+ case ".md":
246
+ return "text/markdown; charset=utf-8";
247
+ case ".txt":
248
+ return "text/plain; charset=utf-8";
249
+ default:
250
+ return "application/octet-stream";
251
+ }
252
+ }
198
253
  function isFormFile(value) {
199
254
  return Boolean(value
200
255
  && typeof value === "object"
@@ -563,7 +618,7 @@ function requireCustomer(c) {
563
618
  }
564
619
  const requireAuth = async (c, next) => {
565
620
  try {
566
- const customer = auth.authenticate(c.req.header("vidfarm-user-id"), c.req.header("vidfarm-api-key"));
621
+ const customer = auth.authenticate(c.req.header("vidfarm-api-key"));
567
622
  c.set("customer", customer);
568
623
  await next();
569
624
  }
@@ -877,7 +932,7 @@ app.get(`${TEMPLATES_PREFIX}/:templateId/jobs/:jobId`, (c) => {
877
932
  }
878
933
  return c.json(serializeJob(job));
879
934
  });
880
- app.get(`${TEMPLATES_PREFIX}/:templateId/jobs/:jobId/logs`, (c) => {
935
+ app.get(`${TEMPLATES_PREFIX}/:templateId/jobs/:jobId/logs`, async (c) => {
881
936
  const customer = requireCustomer(c);
882
937
  const template = templateRegistry.get(c.req.param("templateId"));
883
938
  if (!template) {
@@ -892,7 +947,7 @@ app.get(`${TEMPLATES_PREFIX}/:templateId/jobs/:jobId/logs`, (c) => {
892
947
  end_time: c.req.query("end_time"),
893
948
  limit: c.req.query("limit")
894
949
  });
895
- const logs = jobs.listLogs({
950
+ const logs = await jobs.listLogs({
896
951
  jobId: job.id,
897
952
  startTime: query.start_time,
898
953
  endTime: query.end_time,
@@ -914,6 +969,39 @@ app.post(`${TEMPLATES_PREFIX}/:templateId/jobs/:jobId/cancel`, (c) => {
914
969
  return c.json({ ok: true, job_id: job.id, status: "cancelled" });
915
970
  });
916
971
  app.get(`${USER_PREFIX}/me`, (c) => c.json({ customer: requireCustomer(c) }));
972
+ app.post(`${USER_PREFIX}/me/developer/preview-media/presign`, async (c) => {
973
+ try {
974
+ const customer = requireCustomer(c);
975
+ const body = developerPreviewPresignSchema.parse(await c.req.json());
976
+ const fileName = sanitizeFileName(body.file_name);
977
+ const contentType = body.content_type?.trim() || inferAttachmentContentType(fileName);
978
+ if (!isAllowedAttachment(fileName, contentType)) {
979
+ return c.json({ error: `Unsupported preview media type: ${fileName}` }, 400);
980
+ }
981
+ const directory = sanitizeStorageSubpath(body.directory);
982
+ const storageKey = storage.developerPreviewMediaKey(customer.id, directory, createId("preview"), fileName);
983
+ const upload = await storage.createWriteUrl(storageKey, {
984
+ contentType,
985
+ publicRead: false,
986
+ expiresIn: 3600
987
+ });
988
+ return c.json({
989
+ file_name: fileName,
990
+ content_type: contentType,
991
+ storage_key: storageKey,
992
+ preview_media_url: buildAbsoluteUrl(c, `/template-media?key=${encodeURIComponent(storageKey)}`),
993
+ upload: {
994
+ method: upload.method,
995
+ url: upload.url,
996
+ headers: upload.headers,
997
+ expires_in_seconds: upload.expiresIn
998
+ }
999
+ });
1000
+ }
1001
+ catch (error) {
1002
+ return c.json({ error: error instanceof Error ? error.message : "Unable to create preview upload URL." }, 400);
1003
+ }
1004
+ });
917
1005
  app.get(`${USER_PREFIX}/me/jobs`, (c) => {
918
1006
  const query = listJobsQuerySchema.parse({
919
1007
  template_id: c.req.query("template_id"),
@@ -969,8 +1057,8 @@ app.post(`${TEMPLATES_PREFIX}/sources`, async (c) => {
969
1057
  slug_id: z.string().min(3).regex(/^[a-z0-9_]+$/i, "slug_id must contain only letters, numbers, and underscores."),
970
1058
  repo_url: z.string().url(),
971
1059
  branch: z.string().min(1).default("production"),
972
- template_module_path: z.string().min(1),
973
- skill_path: z.string().min(1).default("SKILL.md"),
1060
+ template_module_path: z.string().regex(/(^|\/)vidfarm_template_[^/]+\/src\/template\.ts$/i, "template_module_path must point at a template entrypoint inside a folder that starts with vidfarm_template_, for example templates/vidfarm_template_example/src/template.ts."),
1061
+ skill_path: z.string().min(1).optional(),
974
1062
  install_command: z.string().min(1).default("npm install"),
975
1063
  build_command: z.string().min(1).default("npm run build")
976
1064
  }).parse(await c.req.json());