MCPcopy Create free account
hub / github.com/heygen-com/hyperframes / run

Function run

packages/cli/src/commands/render.ts:368–950  ·  view source on GitHub ↗
({ args })

Source from the content-addressed store, hash-verified

366 // Full decomposition is tracked separately and out of scope for #1199.
367 // fallow-ignore-next-line complexity
368 async run({ args }) {
369 // ── Resolve project ────────────────────────────────────────────────────
370 const project = resolveProject(args.dir);
371
372 // ── Validate fps ───────────────────────────────────────────────────────
373 // Accept either integer (`30`) or ffmpeg-style rational (`30000/1001`).
374 // The whitelist-based validator was replaced with a sane numeric range so
375 // legitimate framerates (NTSC trio, PAL, 120/240 slow-mo) work without
376 // CLI gymnastics. The exact rational survives end-to-end into FFmpeg's
377 // `-r` / `-framerate` flags via `fpsToFfmpegArg`.
378 const fpsParse = parseFps(args.fps ?? "30");
379 if (!fpsParse.ok) {
380 errorBox("Invalid fps", formatFpsParseError(args.fps ?? "30", fpsParse.reason));
381 process.exit(1);
382 }
383 let fps: Fps = fpsParse.value;
384
385 // ── Validate quality ───────────────────────────────────────────────────
386 const qualityRaw = args.quality ?? "standard";
387 if (!VALID_QUALITY.has(qualityRaw)) {
388 errorBox("Invalid quality", `Got "${qualityRaw}". Must be draft, standard, or high.`);
389 process.exit(1);
390 }
391 const quality = qualityRaw as "draft" | "standard" | "high";
392
393 // ── Authoring skill (telemetry attribution) ────────────────────────────
394 // Optional slug naming the workflow skill that drove this render (e.g.
395 // "product-launch-video"), tagged onto render telemetry for per-skill usage
396 // breakdowns. Slug-gated (shared with the `events` command) so a caller
397 // can't push high-cardinality or PII strings into the anonymous event
398 // stream; a missing/invalid value is omitted.
399 const authoringSkill = normalizeSkillSlug(args.skill);
400 if (typeof args.skill === "string" && args.skill.trim() !== "" && !authoringSkill) {
401 // Surface a typo (e.g. camelCase) instead of silently losing attribution.
402 // Warning only — never fails the render.
403 process.stderr.write(
404 `hyperframes: ignoring --skill="${args.skill}" — not a valid slug ` +
405 "(lowercase letters/digits/hyphens, max 64); this render will be unattributed.\n",
406 );
407 }
408
409 // ── Validate format ─────────────────────────────────────────────────
410 const formatRaw = args.format ?? "mp4";
411 const format = parseRenderFormat(formatRaw);
412 if (!format) {
413 errorBox("Invalid format", `Got "${formatRaw}". Must be ${RENDER_FORMAT_LABEL}.`);
414 process.exit(1);
415 }
416
417 let gifFpsCapped = false;
418 if (format === "gif" && fpsToNumber(fps) > 30) {
419 fps = { num: 30, den: 1 };
420 gifFpsCapped = true;
421 }
422
423 const gifLoopParse = parseGifLoopArg(args["gif-loop"]);
424 if (!gifLoopParse.ok) {
425 errorBox("Invalid gif-loop", gifLoopParse.message);

Callers

nothing calls this directly

Calls 15

parseFpsFunction · 0.90
fpsToNumberFunction · 0.90
isVideoFrameFormatFunction · 0.90
fpsToFfmpegArgFunction · 0.90
errorBoxFunction · 0.85
formatFpsParseErrorFunction · 0.85
normalizeSkillSlugFunction · 0.85
parseRenderFormatFunction · 0.85
parseGifLoopArgFunction · 0.85
normalizeResolutionFlagFunction · 0.85
resolveFunction · 0.85
resolveBrowserGpuForCliFunction · 0.85

Tested by

no test coverage detected