hyperframes 0.1.9 → 0.1.10

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 (22) hide show
  1. package/dist/cli.js +591 -249
  2. package/dist/skills/{captions → hyperframes-captions}/SKILL.md +1 -1
  3. package/dist/skills/{compose-video → hyperframes-compose}/SKILL.md +1 -1
  4. package/dist/templates/_shared/CLAUDE.md +21 -9
  5. package/dist/templates/play-mode/index.html +1 -1
  6. package/dist/templates/swiss-grid/compositions/graphics.html +1 -1
  7. package/dist/templates/swiss-grid/index.html +1 -1
  8. package/dist/templates/vignelli/index.html +1 -1
  9. package/dist/templates/warm-grain/index.html +1 -1
  10. package/package.json +2 -2
  11. /package/dist/skills/{compose-video → hyperframes-compose}/data-in-motion.md +0 -0
  12. /package/dist/skills/{compose-video → hyperframes-compose}/house-style.md +0 -0
  13. /package/dist/skills/{compose-video → hyperframes-compose}/palettes/bold-energetic.md +0 -0
  14. /package/dist/skills/{compose-video → hyperframes-compose}/palettes/clean-corporate.md +0 -0
  15. /package/dist/skills/{compose-video → hyperframes-compose}/palettes/dark-premium.md +0 -0
  16. /package/dist/skills/{compose-video → hyperframes-compose}/palettes/jewel-rich.md +0 -0
  17. /package/dist/skills/{compose-video → hyperframes-compose}/palettes/monochrome.md +0 -0
  18. /package/dist/skills/{compose-video → hyperframes-compose}/palettes/nature-earth.md +0 -0
  19. /package/dist/skills/{compose-video → hyperframes-compose}/palettes/neon-electric.md +0 -0
  20. /package/dist/skills/{compose-video → hyperframes-compose}/palettes/pastel-soft.md +0 -0
  21. /package/dist/skills/{compose-video → hyperframes-compose}/palettes/warm-editorial.md +0 -0
  22. /package/dist/skills/{compose-video → hyperframes-compose}/patterns.md +0 -0
package/dist/cli.js CHANGED
@@ -422,7 +422,7 @@ var VERSION;
422
422
  var init_version = __esm({
423
423
  "src/version.ts"() {
424
424
  "use strict";
425
- VERSION = true ? "0.1.9" : "0.0.0-dev";
425
+ VERSION = true ? "0.1.10" : "0.0.0-dev";
426
426
  }
427
427
  });
428
428
 
@@ -599,6 +599,97 @@ var init_env = __esm({
599
599
  }
600
600
  });
601
601
 
602
+ // src/telemetry/system.ts
603
+ var system_exports = {};
604
+ __export(system_exports, {
605
+ bytesToMb: () => bytesToMb,
606
+ getFreeDiskMb: () => getFreeDiskMb,
607
+ getShmSizeMb: () => getShmSizeMb,
608
+ getSystemMeta: () => getSystemMeta
609
+ });
610
+ import { cpus, totalmem, platform, release } from "os";
611
+ import { existsSync as existsSync2, readFileSync as readFileSync2, statfsSync } from "fs";
612
+ function bytesToMb(bytes) {
613
+ return Math.trunc(bytes / (1024 * 1024));
614
+ }
615
+ function getSystemMeta() {
616
+ if (cached) return cached;
617
+ const cpuInfo = cpus();
618
+ const firstCpu = cpuInfo[0] ?? null;
619
+ cached = {
620
+ os_release: release(),
621
+ cpu_count: cpuInfo.length,
622
+ cpu_model: firstCpu?.model?.trim() ?? null,
623
+ cpu_speed: firstCpu?.speed ?? null,
624
+ memory_total_mb: bytesToMb(totalmem()),
625
+ is_docker: detectDocker(),
626
+ is_ci: detectCI(),
627
+ ci_name: getCIName(),
628
+ is_wsl: detectWSL(),
629
+ is_tty: Boolean(process.stdout?.isTTY)
630
+ };
631
+ return cached;
632
+ }
633
+ function detectDocker() {
634
+ try {
635
+ if (existsSync2("/.dockerenv")) return true;
636
+ if (platform() === "linux") {
637
+ const cgroup = readFileSync2("/proc/1/cgroup", "utf-8");
638
+ if (cgroup.includes("docker") || cgroup.includes("containerd")) return true;
639
+ }
640
+ } catch {
641
+ }
642
+ return false;
643
+ }
644
+ function detectCI() {
645
+ return process.env["CI"] === "true" || process.env["CI"] === "1" || process.env["CONTINUOUS_INTEGRATION"] === "true" || process.env["GITHUB_ACTIONS"] === "true" || process.env["GITLAB_CI"] === "true" || process.env["CIRCLECI"] === "true" || process.env["JENKINS_URL"] != null || process.env["BUILDKITE"] === "true" || process.env["TRAVIS"] === "true" || false;
646
+ }
647
+ function getCIName() {
648
+ if (process.env["GITHUB_ACTIONS"] === "true") return "github_actions";
649
+ if (process.env["GITLAB_CI"] === "true") return "gitlab_ci";
650
+ if (process.env["CIRCLECI"] === "true") return "circleci";
651
+ if (process.env["JENKINS_URL"] != null) return "jenkins";
652
+ if (process.env["BUILDKITE"] === "true") return "buildkite";
653
+ if (process.env["TRAVIS"] === "true") return "travis";
654
+ if (detectCI()) return "unknown";
655
+ return null;
656
+ }
657
+ function detectWSL() {
658
+ if (platform() !== "linux") return false;
659
+ try {
660
+ const osRelease = release().toLowerCase();
661
+ if (osRelease.includes("microsoft") || osRelease.includes("wsl")) return true;
662
+ const procVersion = readFileSync2("/proc/version", "utf-8").toLowerCase();
663
+ return procVersion.includes("microsoft") || procVersion.includes("wsl");
664
+ } catch {
665
+ return false;
666
+ }
667
+ }
668
+ function getShmSizeMb() {
669
+ if (platform() !== "linux") return null;
670
+ try {
671
+ const stats = statfsSync("/dev/shm");
672
+ return bytesToMb(stats.bsize * stats.blocks);
673
+ } catch {
674
+ return null;
675
+ }
676
+ }
677
+ function getFreeDiskMb(path = ".") {
678
+ try {
679
+ const stats = statfsSync(path);
680
+ return bytesToMb(stats.bsize * stats.bavail);
681
+ } catch {
682
+ return null;
683
+ }
684
+ }
685
+ var cached;
686
+ var init_system = __esm({
687
+ "src/telemetry/system.ts"() {
688
+ "use strict";
689
+ cached = null;
690
+ }
691
+ });
692
+
602
693
  // src/telemetry/client.ts
603
694
  function shouldTrack() {
604
695
  if (telemetryEnabled !== null) return telemetryEnabled;
@@ -624,6 +715,7 @@ function shouldTrack() {
624
715
  }
625
716
  function trackEvent(event, properties = {}) {
626
717
  if (!shouldTrack()) return;
718
+ const sys = getSystemMeta();
627
719
  eventQueue.push({
628
720
  event,
629
721
  properties: {
@@ -631,7 +723,17 @@ function trackEvent(event, properties = {}) {
631
723
  cli_version: VERSION,
632
724
  os: process.platform,
633
725
  arch: process.arch,
634
- node_version: process.version
726
+ node_version: process.version,
727
+ os_release: sys.os_release,
728
+ cpu_count: sys.cpu_count,
729
+ cpu_model: sys.cpu_model ?? void 0,
730
+ cpu_speed: sys.cpu_speed ?? void 0,
731
+ memory_total_mb: sys.memory_total_mb,
732
+ is_docker: sys.is_docker,
733
+ is_ci: sys.is_ci,
734
+ ci_name: sys.ci_name ?? void 0,
735
+ is_wsl: sys.is_wsl,
736
+ is_tty: sys.is_tty
635
737
  },
636
738
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
637
739
  });
@@ -655,7 +757,7 @@ async function flush() {
655
757
  try {
656
758
  await fetch(`${POSTHOG_HOST}/batch/`, {
657
759
  method: "POST",
658
- headers: { "Content-Type": "application/json" },
760
+ headers: { "Content-Type": "application/json", Connection: "close" },
659
761
  body: JSON.stringify({ api_key: POSTHOG_API_KEY, batch }),
660
762
  signal: controller.signal
661
763
  });
@@ -713,6 +815,7 @@ var init_client = __esm({
713
815
  init_version();
714
816
  init_colors();
715
817
  init_env();
818
+ init_system();
716
819
  POSTHOG_API_KEY = "phc_zjjbX0PnWxERXrMHhkEJWj9A9BhGVLRReICgsfTMmpx";
717
820
  POSTHOG_HOST = "https://us.i.posthog.com";
718
821
  FLUSH_TIMEOUT_MS = 5e3;
@@ -722,6 +825,14 @@ var init_client = __esm({
722
825
  });
723
826
 
724
827
  // src/telemetry/events.ts
828
+ var events_exports = {};
829
+ __export(events_exports, {
830
+ trackBrowserInstall: () => trackBrowserInstall,
831
+ trackCommand: () => trackCommand,
832
+ trackInitTemplate: () => trackInitTemplate,
833
+ trackRenderComplete: () => trackRenderComplete,
834
+ trackRenderError: () => trackRenderError
835
+ });
725
836
  function trackCommand(command2) {
726
837
  trackEvent("cli_command", { command: command2 });
727
838
  }
@@ -732,14 +843,30 @@ function trackRenderComplete(props) {
732
843
  quality: props.quality,
733
844
  workers: props.workers,
734
845
  docker: props.docker,
735
- gpu: props.gpu
846
+ gpu: props.gpu,
847
+ composition_duration_ms: props.compositionDurationMs,
848
+ composition_width: props.compositionWidth,
849
+ composition_height: props.compositionHeight,
850
+ total_frames: props.totalFrames,
851
+ speed_ratio: props.speedRatio,
852
+ capture_avg_ms: props.captureAvgMs,
853
+ capture_peak_ms: props.capturePeakMs,
854
+ peak_memory_mb: props.peakMemoryMb,
855
+ memory_free_mb: props.memoryFreeMb
736
856
  });
737
857
  }
738
858
  function trackRenderError(props) {
739
859
  trackEvent("render_error", {
740
860
  fps: props.fps,
741
861
  quality: props.quality,
742
- docker: props.docker
862
+ docker: props.docker,
863
+ workers: props.workers,
864
+ gpu: props.gpu,
865
+ failed_stage: props.failedStage,
866
+ error_message: props.errorMessage,
867
+ elapsed_ms: props.elapsedMs,
868
+ peak_memory_mb: props.peakMemoryMb,
869
+ memory_free_mb: props.memoryFreeMb
743
870
  });
744
871
  }
745
872
  function trackInitTemplate(templateId) {
@@ -772,7 +899,10 @@ async function checkForUpdate(force) {
772
899
  try {
773
900
  const controller = new AbortController();
774
901
  const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
775
- const res = await fetch(NPM_REGISTRY_URL, { signal: controller.signal });
902
+ const res = await fetch(NPM_REGISTRY_URL, {
903
+ signal: controller.signal,
904
+ headers: { Connection: "close" }
905
+ });
776
906
  clearTimeout(timeout);
777
907
  if (!res.ok) return fallbackResult(config.latestVersion);
778
908
  const data = await res.json();
@@ -2536,7 +2666,15 @@ var init_generators = __esm({
2536
2666
  { id: "warm-grain", label: "Warm Grain", hint: "Cream aesthetic with grain texture" },
2537
2667
  { id: "play-mode", label: "Play Mode", hint: "Playful elastic animations" },
2538
2668
  { id: "swiss-grid", label: "Swiss Grid", hint: "Structured grid layout" },
2539
- { id: "vignelli", label: "Vignelli", hint: "Bold typography with red accents" }
2669
+ { id: "vignelli", label: "Vignelli", hint: "Bold typography with red accents" },
2670
+ { id: "decision-tree", label: "Decision Tree", hint: "Animated flowchart with branching paths" },
2671
+ { id: "kinetic-type", label: "Kinetic Type", hint: "Bold kinetic typography promo" },
2672
+ {
2673
+ id: "product-promo",
2674
+ label: "Product Promo",
2675
+ hint: "Multi-scene product showcase with SVG assets"
2676
+ },
2677
+ { id: "nyt-graph", label: "NYT Graph", hint: "Animated data chart in print editorial style" }
2540
2678
  ];
2541
2679
  }
2542
2680
  });
@@ -2553,8 +2691,8 @@ __export(manager_exports, {
2553
2691
  hasFFmpeg: () => hasFFmpeg
2554
2692
  });
2555
2693
  import { execFileSync } from "child_process";
2556
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, createWriteStream, rmSync } from "fs";
2557
- import { homedir as homedir2, platform } from "os";
2694
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, createWriteStream, rmSync } from "fs";
2695
+ import { homedir as homedir2, platform as platform2 } from "os";
2558
2696
  import { join as join2 } from "path";
2559
2697
  import { get as httpsGet } from "https";
2560
2698
  import { pipeline } from "stream/promises";
@@ -2597,7 +2735,7 @@ function whichBinary(name) {
2597
2735
  }
2598
2736
  function findFromEnv() {
2599
2737
  const envPath = process.env["HYPERFRAMES_WHISPER_PATH"];
2600
- if (envPath && existsSync2(envPath)) {
2738
+ if (envPath && existsSync3(envPath)) {
2601
2739
  return { executablePath: envPath, source: "env" };
2602
2740
  }
2603
2741
  return void 0;
@@ -2607,9 +2745,9 @@ function findFromSystem() {
2607
2745
  const path = whichBinary(name);
2608
2746
  if (path) return { executablePath: path, source: "system" };
2609
2747
  }
2610
- if (platform() === "darwin") {
2748
+ if (platform2() === "darwin") {
2611
2749
  for (const p of ["/opt/homebrew/bin/whisper-cli", "/usr/local/bin/whisper-cli"]) {
2612
- if (existsSync2(p)) return { executablePath: p, source: "system" };
2750
+ if (existsSync3(p)) return { executablePath: p, source: "system" };
2613
2751
  }
2614
2752
  }
2615
2753
  return void 0;
@@ -2619,15 +2757,15 @@ function findBuiltBinary() {
2619
2757
  join2(BUILD_DIR, "build", "bin", "whisper-cli"),
2620
2758
  join2(BUILD_DIR, "build", "whisper-cli")
2621
2759
  ]) {
2622
- if (existsSync2(p)) return { executablePath: p, source: "build" };
2760
+ if (existsSync3(p)) return { executablePath: p, source: "build" };
2623
2761
  }
2624
2762
  return void 0;
2625
2763
  }
2626
2764
  function buildFromSource(onProgress) {
2627
- if (existsSync2(BUILD_DIR) && !findBuiltBinary()) {
2765
+ if (existsSync3(BUILD_DIR) && !findBuiltBinary()) {
2628
2766
  rmSync(BUILD_DIR, { recursive: true, force: true });
2629
2767
  }
2630
- if (!existsSync2(BUILD_DIR)) {
2768
+ if (!existsSync3(BUILD_DIR)) {
2631
2769
  onProgress?.("Downloading whisper.cpp...");
2632
2770
  mkdirSync2(join2(homedir2(), ".cache", "hyperframes", "whisper"), {
2633
2771
  recursive: true
@@ -2670,7 +2808,7 @@ function findWhisper() {
2670
2808
  return findFromEnv() ?? findFromSystem() ?? findBuiltBinary();
2671
2809
  }
2672
2810
  function getInstallInstructions() {
2673
- if (platform() === "darwin") {
2811
+ if (platform2() === "darwin") {
2674
2812
  return "brew install whisper-cpp";
2675
2813
  }
2676
2814
  return "See https://github.com/ggml-org/whisper.cpp#building";
@@ -2687,7 +2825,7 @@ function hasCmake() {
2687
2825
  async function ensureWhisper(options) {
2688
2826
  const existing = findWhisper();
2689
2827
  if (existing) return existing;
2690
- if (platform() === "darwin" && hasBrew()) {
2828
+ if (platform2() === "darwin" && hasBrew()) {
2691
2829
  options?.onProgress?.("Installing whisper-cpp via Homebrew...");
2692
2830
  try {
2693
2831
  execFileSync("brew", ["install", "whisper-cpp"], {
@@ -2709,11 +2847,11 @@ async function ensureWhisper(options) {
2709
2847
  }
2710
2848
  async function ensureModel(model = DEFAULT_MODEL, options) {
2711
2849
  const modelPath = join2(MODELS_DIR, `ggml-${model}.bin`);
2712
- if (existsSync2(modelPath)) return modelPath;
2850
+ if (existsSync3(modelPath)) return modelPath;
2713
2851
  mkdirSync2(MODELS_DIR, { recursive: true });
2714
2852
  options?.onProgress?.(`Downloading model ${model}...`);
2715
2853
  await downloadFile(getModelUrl(model), modelPath);
2716
- if (!existsSync2(modelPath)) {
2854
+ if (!existsSync3(modelPath)) {
2717
2855
  throw new Error(`Model download failed: ${model}`);
2718
2856
  }
2719
2857
  return modelPath;
@@ -2744,7 +2882,7 @@ __export(install_skills_exports, {
2744
2882
  default: () => install_skills_default,
2745
2883
  installAllSkills: () => installAllSkills
2746
2884
  });
2747
- import { existsSync as existsSync3, mkdirSync as mkdirSync3, readdirSync, rmSync as rmSync2, cpSync } from "fs";
2885
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync, rmSync as rmSync2, cpSync } from "fs";
2748
2886
  import { join as join3, dirname } from "path";
2749
2887
  import { homedir as homedir3 } from "os";
2750
2888
  import { execFileSync as execFileSync2 } from "child_process";
@@ -2784,7 +2922,7 @@ function gitClone(repo, dest) {
2784
2922
  }
2785
2923
  function fetchRepo(source) {
2786
2924
  const gitUrl = `https://github.com/${source.repo}.git`;
2787
- if (existsSync3(source.cache)) {
2925
+ if (existsSync4(source.cache)) {
2788
2926
  try {
2789
2927
  execFileSync2("git", ["pull", "--ff-only"], {
2790
2928
  cwd: source.cache,
@@ -2794,7 +2932,7 @@ function fetchRepo(source) {
2794
2932
  });
2795
2933
  } catch {
2796
2934
  const skillsDir2 = join3(source.cache, source.skillsPath);
2797
- if (existsSync3(skillsDir2)) return skillsDir2;
2935
+ if (existsSync4(skillsDir2)) return skillsDir2;
2798
2936
  rmSync2(source.cache, { recursive: true, force: true });
2799
2937
  gitClone(gitUrl, source.cache);
2800
2938
  }
@@ -2803,18 +2941,18 @@ function fetchRepo(source) {
2803
2941
  gitClone(gitUrl, source.cache);
2804
2942
  }
2805
2943
  const skillsDir = join3(source.cache, source.skillsPath);
2806
- return existsSync3(skillsDir) ? skillsDir : void 0;
2944
+ return existsSync4(skillsDir) ? skillsDir : void 0;
2807
2945
  }
2808
2946
  function installSkillsFromDir(sourceDir, targetDir, sourceName) {
2809
2947
  const installed = [];
2810
- if (!existsSync3(sourceDir)) return installed;
2948
+ if (!existsSync4(sourceDir)) return installed;
2811
2949
  const entries2 = readdirSync(sourceDir, { withFileTypes: true });
2812
2950
  for (const entry of entries2) {
2813
2951
  if (!entry.isDirectory()) continue;
2814
2952
  const skillFile = join3(sourceDir, entry.name, "SKILL.md");
2815
- if (!existsSync3(skillFile)) continue;
2953
+ if (!existsSync4(skillFile)) continue;
2816
2954
  const destDir = join3(targetDir, entry.name);
2817
- if (existsSync3(destDir)) rmSync2(destDir, { recursive: true, force: true });
2955
+ if (existsSync4(destDir)) rmSync2(destDir, { recursive: true, force: true });
2818
2956
  mkdirSync3(destDir, { recursive: true });
2819
2957
  cpSync(join3(sourceDir, entry.name), destDir, { recursive: true });
2820
2958
  installed.push({ name: entry.name, source: sourceName });
@@ -3844,6 +3982,76 @@ ${right.raw}`)
3844
3982
  });
3845
3983
  }
3846
3984
  }
3985
+ for (const script of scripts) {
3986
+ const templateLiteralSelectorPattern = /(?:querySelector|querySelectorAll)\s*\(\s*`[^`]*\$\{[^}]+\}[^`]*`\s*\)/g;
3987
+ let tlMatch;
3988
+ while ((tlMatch = templateLiteralSelectorPattern.exec(script.content)) !== null) {
3989
+ pushFinding({
3990
+ code: "template_literal_selector",
3991
+ severity: "error",
3992
+ message: "querySelector uses a template literal variable (e.g. `${compId}`). The HTML bundler's CSS parser crashes on these. Use a hardcoded string instead.",
3993
+ file: filePath,
3994
+ fixHint: "Replace the template literal variable with a hardcoded string. The bundler's CSS parser cannot handle interpolated variables in script content.",
3995
+ snippet: truncateSnippet(tlMatch[0])
3996
+ });
3997
+ }
3998
+ }
3999
+ {
4000
+ const cssTranslateSelectors = /* @__PURE__ */ new Map();
4001
+ const cssScaleSelectors = /* @__PURE__ */ new Map();
4002
+ for (const style of styles) {
4003
+ for (const [, selector, body] of style.content.matchAll(
4004
+ /([#.][a-zA-Z0-9_-]+)\s*\{([^}]+)\}/g
4005
+ )) {
4006
+ const tMatch = body?.match(/transform\s*:\s*([^;]+)/);
4007
+ if (!tMatch || !tMatch[1]) continue;
4008
+ const transformVal = tMatch[1].trim();
4009
+ if (/translate/i.test(transformVal)) {
4010
+ cssTranslateSelectors.set((selector ?? "").trim(), transformVal);
4011
+ }
4012
+ if (/scale/i.test(transformVal)) {
4013
+ cssScaleSelectors.set((selector ?? "").trim(), transformVal);
4014
+ }
4015
+ }
4016
+ }
4017
+ if (cssTranslateSelectors.size > 0 || cssScaleSelectors.size > 0) {
4018
+ for (const script of scripts) {
4019
+ if (!/gsap\.timeline/.test(script.content)) continue;
4020
+ const windows = extractGsapWindows(script.content);
4021
+ const conflicts = /* @__PURE__ */ new Map();
4022
+ for (const win of windows) {
4023
+ if (win.method === "fromTo") continue;
4024
+ const sel = win.targetSelector;
4025
+ const cssKey = sel.startsWith("#") || sel.startsWith(".") ? sel : `#${sel}`;
4026
+ const translateProps = win.properties.filter(
4027
+ (p) => ["x", "y", "xPercent", "yPercent"].includes(p)
4028
+ );
4029
+ const scaleProps = win.properties.filter((p) => p === "scale");
4030
+ const cssFromTranslate = translateProps.length > 0 ? cssTranslateSelectors.get(cssKey) : void 0;
4031
+ const cssFromScale = scaleProps.length > 0 ? cssScaleSelectors.get(cssKey) : void 0;
4032
+ if (!cssFromTranslate && !cssFromScale) continue;
4033
+ const existing = conflicts.get(sel) ?? {
4034
+ cssTransform: [cssFromTranslate, cssFromScale].filter(Boolean).join(" "),
4035
+ props: /* @__PURE__ */ new Set(),
4036
+ raw: win.raw
4037
+ };
4038
+ for (const p of [...translateProps, ...scaleProps]) existing.props.add(p);
4039
+ conflicts.set(sel, existing);
4040
+ }
4041
+ for (const [sel, { cssTransform, props, raw }] of conflicts) {
4042
+ const propList = [...props].join("/");
4043
+ pushFinding({
4044
+ code: "gsap_css_transform_conflict",
4045
+ severity: "warning",
4046
+ message: `"${sel}" has CSS \`transform: ${cssTransform}\` and a GSAP tween animates ${propList}. GSAP will overwrite the full CSS transform, discarding any translateX(-50%) centering or CSS scale value.`,
4047
+ selector: sel,
4048
+ fixHint: `Remove the transform from CSS and use tl.fromTo('${sel}', { xPercent: -50, x: -1000 }, { xPercent: -50, x: 0 }) so GSAP owns the full transform state. tl.fromTo is exempt from this rule.`,
4049
+ snippet: truncateSnippet(raw)
4050
+ });
4051
+ }
4052
+ }
4053
+ }
4054
+ }
3847
4055
  const errorCount = findings.filter((finding) => finding.severity === "error").length;
3848
4056
  const warningCount = findings.length - errorCount;
3849
4057
  return {
@@ -3987,6 +4195,7 @@ function extractGsapWindows(script) {
3987
4195
  end: animation.position + meta.effectiveDuration,
3988
4196
  properties: meta.properties.length > 0 ? meta.properties : Object.keys(animation.properties),
3989
4197
  overwriteAuto: meta.overwriteAuto,
4198
+ method: match[1] ?? "to",
3990
4199
  raw
3991
4200
  });
3992
4201
  }
@@ -4172,7 +4381,7 @@ var init_staticGuard = __esm({
4172
4381
  });
4173
4382
 
4174
4383
  // ../core/src/compiler/htmlBundler.ts
4175
- import { readFileSync as readFileSync2, existsSync as existsSync4 } from "fs";
4384
+ import { readFileSync as readFileSync3, existsSync as existsSync5 } from "fs";
4176
4385
  import { join as join4, resolve as resolve2, isAbsolute, sep } from "path";
4177
4386
  import * as cheerio from "cheerio";
4178
4387
  import { transformSync } from "esbuild";
@@ -4235,17 +4444,17 @@ function isRelativeUrl(url) {
4235
4444
  return !url.startsWith("http://") && !url.startsWith("https://") && !url.startsWith("//") && !url.startsWith("data:") && !isAbsolute(url);
4236
4445
  }
4237
4446
  function safeReadFile(filePath) {
4238
- if (!existsSync4(filePath)) return null;
4447
+ if (!existsSync5(filePath)) return null;
4239
4448
  try {
4240
- return readFileSync2(filePath, "utf-8");
4449
+ return readFileSync3(filePath, "utf-8");
4241
4450
  } catch {
4242
4451
  return null;
4243
4452
  }
4244
4453
  }
4245
4454
  function safeReadFileBuffer(filePath) {
4246
- if (!existsSync4(filePath)) return null;
4455
+ if (!existsSync5(filePath)) return null;
4247
4456
  try {
4248
- return readFileSync2(filePath);
4457
+ return readFileSync3(filePath);
4249
4458
  } catch {
4250
4459
  return null;
4251
4460
  }
@@ -4433,8 +4642,8 @@ function stripJsCommentsParserSafe(source) {
4433
4642
  }
4434
4643
  async function bundleToSingleHtml(projectDir, options) {
4435
4644
  const indexPath = join4(projectDir, "index.html");
4436
- if (!existsSync4(indexPath)) throw new Error("index.html not found in project directory");
4437
- const rawHtml = readFileSync2(indexPath, "utf-8");
4645
+ if (!existsSync5(indexPath)) throw new Error("index.html not found in project directory");
4646
+ const rawHtml = readFileSync3(indexPath, "utf-8");
4438
4647
  const compiled = await compileHtml(rawHtml, projectDir, options?.probeMediaDuration);
4439
4648
  const staticGuard = validateHyperframeHtmlContract(compiled);
4440
4649
  if (!staticGuard.isValid) {
@@ -17298,7 +17507,7 @@ var init_config2 = __esm({
17298
17507
  });
17299
17508
 
17300
17509
  // ../engine/src/services/browserManager.ts
17301
- import { existsSync as existsSync5, readdirSync as readdirSync2 } from "fs";
17510
+ import { existsSync as existsSync6, readdirSync as readdirSync2 } from "fs";
17302
17511
  import { join as join5 } from "path";
17303
17512
  import { homedir as homedir4 } from "os";
17304
17513
  async function getPuppeteer() {
@@ -17321,7 +17530,7 @@ function resolveHeadlessShellPath(config) {
17321
17530
  return process.env.PRODUCER_HEADLESS_SHELL_PATH;
17322
17531
  }
17323
17532
  const baseDir = join5(homedir4(), ".cache", "puppeteer", "chrome-headless-shell");
17324
- if (!existsSync5(baseDir)) return void 0;
17533
+ if (!existsSync6(baseDir)) return void 0;
17325
17534
  try {
17326
17535
  const versions = readdirSync2(baseDir).sort().reverse();
17327
17536
  for (const version of versions) {
@@ -17332,7 +17541,7 @@ function resolveHeadlessShellPath(config) {
17332
17541
  join5(baseDir, version, "chrome-headless-shell-win64", "chrome-headless-shell.exe")
17333
17542
  ];
17334
17543
  for (const binary of candidates) {
17335
- if (existsSync5(binary)) return binary;
17544
+ if (existsSync6(binary)) return binary;
17336
17545
  }
17337
17546
  }
17338
17547
  } catch {
@@ -18025,9 +18234,9 @@ async function beginFrameCapture(page, options, frameTimeTicks, interval) {
18025
18234
  buffer = Buffer.from(result.screenshotData, "base64");
18026
18235
  lastFrameCache.set(page, buffer);
18027
18236
  } else {
18028
- const cached = lastFrameCache.get(page);
18029
- if (cached) {
18030
- buffer = cached;
18237
+ const cached2 = lastFrameCache.get(page);
18238
+ if (cached2) {
18239
+ buffer = cached2;
18031
18240
  } else {
18032
18241
  const retry = await client.send("HeadlessExperimental.beginFrame", {
18033
18242
  frameTimeTicks: frameTimeTicks + 1e-3,
@@ -18163,10 +18372,10 @@ var init_screenshotService = __esm({
18163
18372
  });
18164
18373
 
18165
18374
  // ../engine/src/services/frameCapture.ts
18166
- import { existsSync as existsSync6, mkdirSync as mkdirSync4, writeFileSync as writeFileSync2 } from "fs";
18375
+ import { existsSync as existsSync7, mkdirSync as mkdirSync4, writeFileSync as writeFileSync2 } from "fs";
18167
18376
  import { join as join6 } from "path";
18168
18377
  async function createCaptureSession(serverUrl, outputDir, options, onBeforeCapture = null, config) {
18169
- if (!existsSync6(outputDir)) mkdirSync4(outputDir, { recursive: true });
18378
+ if (!existsSync7(outputDir)) mkdirSync4(outputDir, { recursive: true });
18170
18379
  const headlessShell = resolveHeadlessShellPath(config);
18171
18380
  const isLinux = process.platform === "linux";
18172
18381
  const forceScreenshot = config?.forceScreenshot ?? DEFAULT_CONFIG2.forceScreenshot;
@@ -18326,7 +18535,7 @@ async function initializeSession(session) {
18326
18535
  async function captureFrameErrorDiagnostics(session, frameIndex, time, error) {
18327
18536
  try {
18328
18537
  const diagnosticsDir = join6(session.outputDir, "diagnostics");
18329
- if (!existsSync6(diagnosticsDir)) mkdirSync4(diagnosticsDir, { recursive: true });
18538
+ if (!existsSync7(diagnosticsDir)) mkdirSync4(diagnosticsDir, { recursive: true });
18330
18539
  const base = join6(diagnosticsDir, `frame-error-${frameIndex}`);
18331
18540
  await session.page.screenshot({ path: `${base}.png`, type: "png", fullPage: true });
18332
18541
  const html = await session.page.content();
@@ -18440,7 +18649,7 @@ async function closeCaptureSession(session) {
18440
18649
  session.isInitialized = false;
18441
18650
  }
18442
18651
  function prepareCaptureSessionForReuse(session, outputDir, onBeforeCapture) {
18443
- if (!existsSync6(outputDir)) {
18652
+ if (!existsSync7(outputDir)) {
18444
18653
  mkdirSync4(outputDir, { recursive: true });
18445
18654
  }
18446
18655
  session.outputDir = outputDir;
@@ -18595,7 +18804,7 @@ var init_runFfmpeg = __esm({
18595
18804
 
18596
18805
  // ../engine/src/services/chunkEncoder.ts
18597
18806
  import { spawn as spawn3 } from "child_process";
18598
- import { copyFileSync, existsSync as existsSync7, mkdirSync as mkdirSync5, readdirSync as readdirSync3, statSync, writeFileSync as writeFileSync3 } from "fs";
18807
+ import { copyFileSync, existsSync as existsSync8, mkdirSync as mkdirSync5, readdirSync as readdirSync3, statSync, writeFileSync as writeFileSync3 } from "fs";
18599
18808
  import { join as join7, dirname as dirname3 } from "path";
18600
18809
  function getEncoderPreset(quality, format = "mp4") {
18601
18810
  const base = ENCODER_PRESETS[quality];
@@ -18678,7 +18887,7 @@ function buildEncoderArgs(options, inputArgs, outputPath, gpuEncoder = null) {
18678
18887
  async function encodeFramesFromDir(framesDir, framePattern, outputPath, options, signal, config) {
18679
18888
  const startTime = Date.now();
18680
18889
  const outputDir = dirname3(outputPath);
18681
- if (!existsSync7(outputDir)) mkdirSync5(outputDir, { recursive: true });
18890
+ if (!existsSync8(outputDir)) mkdirSync5(outputDir, { recursive: true });
18682
18891
  const files = readdirSync3(framesDir).filter((f) => f.match(/\.(jpg|jpeg|png)$/i));
18683
18892
  const frameCount = files.length;
18684
18893
  if (frameCount === 0) {
@@ -18744,7 +18953,7 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
18744
18953
  });
18745
18954
  return;
18746
18955
  }
18747
- const fileSize = existsSync7(outputPath) ? statSync(outputPath).size : 0;
18956
+ const fileSize = existsSync8(outputPath) ? statSync(outputPath).size : 0;
18748
18957
  resolve17({ success: true, outputPath, durationMs, framesEncoded: frameCount, fileSize });
18749
18958
  });
18750
18959
  ffmpeg.on("error", (err) => {
@@ -18777,7 +18986,7 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
18777
18986
  const chunkSize = Math.max(30, Math.floor(chunkSizeFrames));
18778
18987
  const chunkCount = Math.ceil(files.length / chunkSize);
18779
18988
  const chunkDir = join7(dirname3(outputPath), "chunk-encode");
18780
- if (!existsSync7(chunkDir)) mkdirSync5(chunkDir, { recursive: true });
18989
+ if (!existsSync8(chunkDir)) mkdirSync5(chunkDir, { recursive: true });
18781
18990
  const chunkPaths = [];
18782
18991
  for (let i = 0; i < chunkCount; i++) {
18783
18992
  if (signal?.aborted) {
@@ -18873,7 +19082,7 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
18873
19082
  error: concatResult.error
18874
19083
  };
18875
19084
  }
18876
- const fileSize = existsSync7(outputPath) ? statSync(outputPath).size : 0;
19085
+ const fileSize = existsSync8(outputPath) ? statSync(outputPath).size : 0;
18877
19086
  return {
18878
19087
  success: true,
18879
19088
  outputPath,
@@ -18884,7 +19093,7 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
18884
19093
  }
18885
19094
  async function muxVideoWithAudio(videoPath, audioPath, outputPath, signal, config) {
18886
19095
  const outputDir = dirname3(outputPath);
18887
- if (!existsSync7(outputDir)) mkdirSync5(outputDir, { recursive: true });
19096
+ if (!existsSync8(outputDir)) mkdirSync5(outputDir, { recursive: true });
18888
19097
  const isWebm = outputPath.endsWith(".webm");
18889
19098
  const args = ["-i", videoPath, "-i", audioPath, "-c:v", "copy"];
18890
19099
  if (isWebm) {
@@ -18951,7 +19160,7 @@ var init_chunkEncoder = __esm({
18951
19160
 
18952
19161
  // ../engine/src/services/streamingEncoder.ts
18953
19162
  import { spawn as spawn4 } from "child_process";
18954
- import { existsSync as existsSync8, mkdirSync as mkdirSync6, statSync as statSync2 } from "fs";
19163
+ import { existsSync as existsSync9, mkdirSync as mkdirSync6, statSync as statSync2 } from "fs";
18955
19164
  import { dirname as dirname4 } from "path";
18956
19165
  function createFrameReorderBuffer(startFrame, endFrame) {
18957
19166
  let nextFrame = startFrame;
@@ -19060,7 +19269,7 @@ function buildStreamingArgs(options, outputPath, gpuEncoder = null) {
19060
19269
  }
19061
19270
  async function spawnStreamingEncoder(outputPath, options, signal, config) {
19062
19271
  const outputDir = dirname4(outputPath);
19063
- if (!existsSync8(outputDir)) mkdirSync6(outputDir, { recursive: true });
19272
+ if (!existsSync9(outputDir)) mkdirSync6(outputDir, { recursive: true });
19064
19273
  let gpuEncoder = null;
19065
19274
  if (options.useGpu) {
19066
19275
  gpuEncoder = await getCachedGpuEncoder();
@@ -19140,7 +19349,7 @@ Process error: ${err.message}`;
19140
19349
  error: `FFmpeg exited with code ${exitCode}`
19141
19350
  };
19142
19351
  }
19143
- const fileSize = existsSync8(outputPath) ? statSync2(outputPath).size : 0;
19352
+ const fileSize = existsSync9(outputPath) ? statSync2(outputPath).size : 0;
19144
19353
  return { success: true, durationMs, fileSize };
19145
19354
  },
19146
19355
  getExitStatus: () => exitStatus
@@ -19168,9 +19377,9 @@ function parseFrameRate(frameRateStr) {
19168
19377
  return parseFloat(frameRateStr) || 0;
19169
19378
  }
19170
19379
  async function extractVideoMetadata(filePath) {
19171
- const cached = videoMetadataCache.get(filePath);
19172
- if (cached) {
19173
- return cached;
19380
+ const cached2 = videoMetadataCache.get(filePath);
19381
+ if (cached2) {
19382
+ return cached2;
19174
19383
  }
19175
19384
  const probePromise = new Promise((resolve17, reject) => {
19176
19385
  const args = [
@@ -19240,9 +19449,9 @@ async function extractVideoMetadata(filePath) {
19240
19449
  return probePromise;
19241
19450
  }
19242
19451
  async function extractAudioMetadata(filePath) {
19243
- const cached = audioMetadataCache.get(filePath);
19244
- if (cached) {
19245
- return cached;
19452
+ const cached2 = audioMetadataCache.get(filePath);
19453
+ if (cached2) {
19454
+ return cached2;
19246
19455
  }
19247
19456
  const probePromise = new Promise((resolve17, reject) => {
19248
19457
  const args = [
@@ -19318,7 +19527,7 @@ var init_ffprobe = __esm({
19318
19527
  });
19319
19528
 
19320
19529
  // ../engine/src/utils/urlDownloader.ts
19321
- import { createWriteStream as createWriteStream2, existsSync as existsSync9, mkdirSync as mkdirSync7 } from "fs";
19530
+ import { createWriteStream as createWriteStream2, existsSync as existsSync10, mkdirSync as mkdirSync7 } from "fs";
19322
19531
  import { createHash } from "crypto";
19323
19532
  import { join as join8, extname } from "path";
19324
19533
  import { Readable } from "stream";
@@ -19331,19 +19540,19 @@ function getFilenameFromUrl(url) {
19331
19540
  }
19332
19541
  async function downloadToTemp(url, destDir, timeoutMs = 3e5) {
19333
19542
  const cachedPath = downloadPathCache.get(url);
19334
- if (cachedPath && existsSync9(cachedPath)) {
19543
+ if (cachedPath && existsSync10(cachedPath)) {
19335
19544
  return cachedPath;
19336
19545
  }
19337
19546
  const inFlight = inFlightDownloads.get(url);
19338
19547
  if (inFlight) {
19339
19548
  return inFlight;
19340
19549
  }
19341
- if (!existsSync9(destDir)) {
19550
+ if (!existsSync10(destDir)) {
19342
19551
  mkdirSync7(destDir, { recursive: true });
19343
19552
  }
19344
19553
  const filename = getFilenameFromUrl(url);
19345
19554
  const localPath = join8(destDir, filename);
19346
- if (existsSync9(localPath)) {
19555
+ if (existsSync10(localPath)) {
19347
19556
  downloadPathCache.set(url, localPath);
19348
19557
  return localPath;
19349
19558
  }
@@ -19391,7 +19600,7 @@ var init_urlDownloader = __esm({
19391
19600
 
19392
19601
  // ../engine/src/services/videoFrameExtractor.ts
19393
19602
  import { spawn as spawn6 } from "child_process";
19394
- import { existsSync as existsSync10, mkdirSync as mkdirSync8, readdirSync as readdirSync4, rmSync as rmSync3 } from "fs";
19603
+ import { existsSync as existsSync11, mkdirSync as mkdirSync8, readdirSync as readdirSync4, rmSync as rmSync3 } from "fs";
19395
19604
  import { join as join9 } from "path";
19396
19605
  function parseVideoElements(html) {
19397
19606
  const videos = [];
@@ -19420,7 +19629,7 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
19420
19629
  const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG2.ffmpegProcessTimeout;
19421
19630
  const { fps, outputDir, quality = 95, format = "jpg" } = options;
19422
19631
  const videoOutputDir = join9(outputDir, videoId);
19423
- if (!existsSync10(videoOutputDir)) mkdirSync8(videoOutputDir, { recursive: true });
19632
+ if (!existsSync11(videoOutputDir)) mkdirSync8(videoOutputDir, { recursive: true });
19424
19633
  const metadata = await extractVideoMetadata(videoPath);
19425
19634
  const framePattern = `frame_%05d.${format}`;
19426
19635
  const outputPattern = join9(videoOutputDir, framePattern);
@@ -19515,7 +19724,7 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config) {
19515
19724
  mkdirSync8(downloadDir, { recursive: true });
19516
19725
  videoPath = await downloadToTemp(videoPath, downloadDir);
19517
19726
  }
19518
- if (!existsSync10(videoPath)) {
19727
+ if (!existsSync11(videoPath)) {
19519
19728
  return { error: { videoId: video.id, error: `Video file not found: ${videoPath}` } };
19520
19729
  }
19521
19730
  let videoDuration = video.end - video.start;
@@ -19669,7 +19878,7 @@ var init_videoFrameExtractor = __esm({
19669
19878
  }
19670
19879
  cleanup() {
19671
19880
  for (const video of this.videos.values()) {
19672
- if (existsSync10(video.extracted.outputDir)) {
19881
+ if (existsSync11(video.extracted.outputDir)) {
19673
19882
  rmSync3(video.extracted.outputDir, { recursive: true, force: true });
19674
19883
  }
19675
19884
  }
@@ -19700,10 +19909,10 @@ function createFrameDataUriCache(cacheLimit) {
19700
19909
  return dataUri;
19701
19910
  }
19702
19911
  async function get(framePath) {
19703
- const cached = cache.get(framePath);
19704
- if (cached) {
19705
- remember(framePath, cached);
19706
- return cached;
19912
+ const cached2 = cache.get(framePath);
19913
+ if (cached2) {
19914
+ remember(framePath, cached2);
19915
+ return cached2;
19707
19916
  }
19708
19917
  const existing = inFlight.get(framePath);
19709
19918
  if (existing) {
@@ -19771,7 +19980,7 @@ var init_videoFrameInjector = __esm({
19771
19980
  });
19772
19981
 
19773
19982
  // ../engine/src/services/audioMixer.ts
19774
- import { existsSync as existsSync11, mkdirSync as mkdirSync9, rmSync as rmSync4 } from "fs";
19983
+ import { existsSync as existsSync12, mkdirSync as mkdirSync9, rmSync as rmSync4 } from "fs";
19775
19984
  import { join as join10, dirname as dirname5 } from "path";
19776
19985
  function parseAudioElements(html) {
19777
19986
  const elements = [];
@@ -19823,7 +20032,7 @@ function parseAudioElements(html) {
19823
20032
  async function extractAudioFromVideo(videoPath, outputPath, options, signal, config) {
19824
20033
  const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG2.ffmpegProcessTimeout;
19825
20034
  const outputDir = dirname5(outputPath);
19826
- if (!existsSync11(outputDir)) mkdirSync9(outputDir, { recursive: true });
20035
+ if (!existsSync12(outputDir)) mkdirSync9(outputDir, { recursive: true });
19827
20036
  const args = ["-i", videoPath];
19828
20037
  if (options?.startTime !== void 0) args.push("-ss", String(options.startTime));
19829
20038
  if (options?.duration !== void 0) args.push("-t", String(options.duration));
@@ -19850,7 +20059,7 @@ async function extractAudioFromVideo(videoPath, outputPath, options, signal, con
19850
20059
  async function prepareAudioTrack(srcPath, outputPath, mediaStart, duration, signal, config) {
19851
20060
  const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG2.ffmpegProcessTimeout;
19852
20061
  const outputDir = dirname5(outputPath);
19853
- if (!existsSync11(outputDir)) mkdirSync9(outputDir, { recursive: true });
20062
+ if (!existsSync12(outputDir)) mkdirSync9(outputDir, { recursive: true });
19854
20063
  const args = [
19855
20064
  "-ss",
19856
20065
  String(mediaStart),
@@ -19886,7 +20095,7 @@ async function prepareAudioTrack(srcPath, outputPath, mediaStart, duration, sign
19886
20095
  async function generateSilence(outputPath, duration, signal, config) {
19887
20096
  const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG2.ffmpegProcessTimeout;
19888
20097
  const outputDir = dirname5(outputPath);
19889
- if (!existsSync11(outputDir)) mkdirSync9(outputDir, { recursive: true });
20098
+ if (!existsSync12(outputDir)) mkdirSync9(outputDir, { recursive: true });
19890
20099
  const args = [
19891
20100
  "-f",
19892
20101
  "lavfi",
@@ -19929,7 +20138,7 @@ async function mixAudioTracks(tracks, outputPath, totalDuration, signal, config)
19929
20138
  };
19930
20139
  }
19931
20140
  const outputDir = dirname5(outputPath);
19932
- if (!existsSync11(outputDir)) mkdirSync9(outputDir, { recursive: true });
20141
+ if (!existsSync12(outputDir)) mkdirSync9(outputDir, { recursive: true });
19933
20142
  const inputs = [];
19934
20143
  const filterParts = [];
19935
20144
  tracks.forEach((track, i) => {
@@ -19990,7 +20199,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
19990
20199
  const startMs = Date.now();
19991
20200
  const tracks = [];
19992
20201
  const errors = [];
19993
- if (!existsSync11(workDir)) mkdirSync9(workDir, { recursive: true });
20202
+ if (!existsSync12(workDir)) mkdirSync9(workDir, { recursive: true });
19994
20203
  await Promise.all(
19995
20204
  elements.map(async (element) => {
19996
20205
  if (signal?.aborted) {
@@ -20012,7 +20221,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
20012
20221
  return;
20013
20222
  }
20014
20223
  }
20015
- if (!existsSync11(srcPath)) {
20224
+ if (!existsSync12(srcPath)) {
20016
20225
  errors.push(`Source not found: ${element.id}`);
20017
20226
  return;
20018
20227
  }
@@ -20092,8 +20301,8 @@ var init_audioMixer = __esm({
20092
20301
  });
20093
20302
 
20094
20303
  // ../engine/src/services/parallelCoordinator.ts
20095
- import { cpus, freemem, totalmem } from "os";
20096
- import { existsSync as existsSync12, mkdirSync as mkdirSync10, readdirSync as readdirSync5 } from "fs";
20304
+ import { cpus as cpus2, freemem, totalmem as totalmem2 } from "os";
20305
+ import { existsSync as existsSync13, mkdirSync as mkdirSync10, readdirSync as readdirSync5 } from "fs";
20097
20306
  import { copyFile, rename } from "fs/promises";
20098
20307
  import { join as join11 } from "path";
20099
20308
  function calculateOptimalWorkers(totalFrames, requested, config) {
@@ -20111,9 +20320,9 @@ function calculateOptimalWorkers(totalFrames, requested, config) {
20111
20320
  return Math.max(MIN_WORKERS, Math.min(effectiveMaxWorkers, requested));
20112
20321
  }
20113
20322
  if (totalFrames < MIN_FRAMES_PER_WORKER * 2) return 1;
20114
- const cpuCount = cpus().length;
20323
+ const cpuCount = cpus2().length;
20115
20324
  const cpuBasedWorkers = Math.max(1, cpuCount - 2);
20116
- const totalMemoryMB = Math.round(totalmem() / (1024 * 1024));
20325
+ const totalMemoryMB = Math.round(totalmem2() / (1024 * 1024));
20117
20326
  const memoryBasedWorkers = Math.max(1, Math.floor(totalMemoryMB * 0.5 / MEMORY_PER_WORKER_MB));
20118
20327
  const frameBasedWorkers = Math.floor(totalFrames / MIN_FRAMES_PER_WORKER);
20119
20328
  const optimal = Math.min(cpuBasedWorkers, memoryBasedWorkers, frameBasedWorkers);
@@ -20146,7 +20355,7 @@ function distributeFrames(totalFrames, workerCount, workDir) {
20146
20355
  async function executeWorkerTask(task, serverUrl, captureOptions, createBeforeCaptureHook, signal, onFrameCaptured, onFrameBuffer, config) {
20147
20356
  const startTime = Date.now();
20148
20357
  let framesCaptured = 0;
20149
- if (!existsSync12(task.outputDir)) mkdirSync10(task.outputDir, { recursive: true });
20358
+ if (!existsSync13(task.outputDir)) mkdirSync10(task.outputDir, { recursive: true });
20150
20359
  let session = null;
20151
20360
  let perf;
20152
20361
  try {
@@ -20236,11 +20445,11 @@ async function executeParallelCapture(serverUrl, workDir, tasks, captureOptions,
20236
20445
  return results;
20237
20446
  }
20238
20447
  async function mergeWorkerFrames(workDir, tasks, outputDir) {
20239
- if (!existsSync12(outputDir)) mkdirSync10(outputDir, { recursive: true });
20448
+ if (!existsSync13(outputDir)) mkdirSync10(outputDir, { recursive: true });
20240
20449
  let totalFrames = 0;
20241
20450
  const sortedTasks = [...tasks].sort((a, b) => a.startFrame - b.startFrame);
20242
20451
  for (const task of sortedTasks) {
20243
- if (!existsSync12(task.outputDir)) {
20452
+ if (!existsSync13(task.outputDir)) {
20244
20453
  continue;
20245
20454
  }
20246
20455
  const files = readdirSync5(task.outputDir).filter((f) => f.startsWith("frame_") && (f.endsWith(".jpg") || f.endsWith(".png"))).sort();
@@ -20275,7 +20484,7 @@ var init_parallelCoordinator = __esm({
20275
20484
  // ../engine/src/services/fileServer.ts
20276
20485
  import { Hono } from "hono";
20277
20486
  import { serve } from "@hono/node-server";
20278
- import { readFileSync as readFileSync3, existsSync as existsSync13, statSync as statSync3 } from "fs";
20487
+ import { readFileSync as readFileSync4, existsSync as existsSync14, statSync as statSync3 } from "fs";
20279
20488
  import { join as join12, extname as extname2 } from "path";
20280
20489
  var init_fileServer = __esm({
20281
20490
  "../engine/src/services/fileServer.ts"() {
@@ -20306,7 +20515,7 @@ var init_src2 = __esm({
20306
20515
 
20307
20516
  // ../producer/src/services/hyperframeRuntimeLoader.ts
20308
20517
  import { createHash as createHash2 } from "crypto";
20309
- import { existsSync as existsSync14, readFileSync as readFileSync4 } from "fs";
20518
+ import { existsSync as existsSync15, readFileSync as readFileSync5 } from "fs";
20310
20519
  import { dirname as dirname6, resolve as resolve4 } from "path";
20311
20520
  import { fileURLToPath as fileURLToPath2 } from "url";
20312
20521
  function resolveHyperframeManifestPath() {
@@ -20319,7 +20528,7 @@ function resolveHyperframeManifestPath() {
20319
20528
  MODULE_RELATIVE_MANIFEST_PATH
20320
20529
  ];
20321
20530
  for (const candidate of candidates) {
20322
- if (existsSync14(candidate)) {
20531
+ if (existsSync15(candidate)) {
20323
20532
  return candidate;
20324
20533
  }
20325
20534
  }
@@ -20330,12 +20539,12 @@ function getVerifiedHyperframeRuntimeSource() {
20330
20539
  }
20331
20540
  function resolveVerifiedHyperframeRuntime() {
20332
20541
  const manifestPath = resolveHyperframeManifestPath();
20333
- if (!existsSync14(manifestPath)) {
20542
+ if (!existsSync15(manifestPath)) {
20334
20543
  throw new Error(
20335
20544
  `[HyperframeRuntimeLoader] Missing manifest at ${manifestPath}. Build core runtime artifacts before rendering.`
20336
20545
  );
20337
20546
  }
20338
- const manifestRaw = readFileSync4(manifestPath, "utf8");
20547
+ const manifestRaw = readFileSync5(manifestPath, "utf8");
20339
20548
  const manifest = JSON.parse(manifestRaw);
20340
20549
  const runtimeFileName = manifest.artifacts?.iife;
20341
20550
  if (!runtimeFileName || !manifest.sha256) {
@@ -20344,10 +20553,10 @@ function resolveVerifiedHyperframeRuntime() {
20344
20553
  );
20345
20554
  }
20346
20555
  const runtimePath = resolve4(dirname6(manifestPath), runtimeFileName);
20347
- if (!existsSync14(runtimePath)) {
20556
+ if (!existsSync15(runtimePath)) {
20348
20557
  throw new Error(`[HyperframeRuntimeLoader] Missing runtime artifact at ${runtimePath}.`);
20349
20558
  }
20350
- const runtimeSource = readFileSync4(runtimePath, "utf8");
20559
+ const runtimeSource = readFileSync5(runtimePath, "utf8");
20351
20560
  const runtimeSha = createHash2("sha256").update(runtimeSource, "utf8").digest("hex");
20352
20561
  if (runtimeSha !== manifest.sha256) {
20353
20562
  throw new Error(
@@ -20386,7 +20595,7 @@ var init_hyperframeRuntimeLoader = __esm({
20386
20595
  // ../producer/src/services/fileServer.ts
20387
20596
  import { Hono as Hono2 } from "hono";
20388
20597
  import { serve as serve2 } from "@hono/node-server";
20389
- import { readFileSync as readFileSync5, existsSync as existsSync15, statSync as statSync4 } from "fs";
20598
+ import { readFileSync as readFileSync6, existsSync as existsSync16, statSync as statSync4 } from "fs";
20390
20599
  import { join as join13, extname as extname3 } from "path";
20391
20600
  function stripEmbeddedRuntimeScripts2(html) {
20392
20601
  if (!html) return html;
@@ -20458,21 +20667,21 @@ function createFileServer2(options) {
20458
20667
  const relativePath = requestPath.replace(/^\//, "");
20459
20668
  const compiledPath = compiledDir ? join13(compiledDir, relativePath) : null;
20460
20669
  const hasCompiledFile = Boolean(
20461
- compiledPath && existsSync15(compiledPath) && statSync4(compiledPath).isFile()
20670
+ compiledPath && existsSync16(compiledPath) && statSync4(compiledPath).isFile()
20462
20671
  );
20463
20672
  const filePath = hasCompiledFile ? compiledPath : join13(projectDir, relativePath);
20464
- if (!existsSync15(filePath) || !statSync4(filePath).isFile()) {
20673
+ if (!existsSync16(filePath) || !statSync4(filePath).isFile()) {
20465
20674
  return c2.text("Not found", 404);
20466
20675
  }
20467
20676
  const ext = extname3(filePath).toLowerCase();
20468
20677
  const contentType = MIME_TYPES[ext] || "application/octet-stream";
20469
20678
  if (ext === ".html") {
20470
- const rawHtml = readFileSync5(filePath, "utf-8");
20679
+ const rawHtml = readFileSync6(filePath, "utf-8");
20471
20680
  const isIndex = relativePath === "index.html";
20472
20681
  const html = isIndex ? injectScriptsIntoHtml(rawHtml, headScripts, bodyScripts, stripEmbeddedRuntime) : rawHtml;
20473
20682
  return c2.text(html, 200, { "Content-Type": contentType });
20474
20683
  }
20475
- const content = readFileSync5(filePath);
20684
+ const content = readFileSync6(filePath);
20476
20685
  return new Response(content, {
20477
20686
  status: 200,
20478
20687
  headers: { "Content-Type": contentType }
@@ -20924,7 +21133,7 @@ var init_deterministicFonts = __esm({
20924
21133
  });
20925
21134
 
20926
21135
  // ../producer/src/services/htmlCompiler.ts
20927
- import { readFileSync as readFileSync6, existsSync as existsSync16, mkdirSync as mkdirSync11 } from "fs";
21136
+ import { readFileSync as readFileSync7, existsSync as existsSync17, mkdirSync as mkdirSync11 } from "fs";
20928
21137
  import { join as join14, dirname as dirname7, resolve as resolve5 } from "path";
20929
21138
  function dedupeElementsById(elements) {
20930
21139
  const deduped = /* @__PURE__ */ new Map();
@@ -20936,7 +21145,7 @@ function dedupeElementsById(elements) {
20936
21145
  async function resolveMediaDuration(src, mediaStart, baseDir, downloadDir, tagName19) {
20937
21146
  let filePath = src;
20938
21147
  if (isHttpUrl(src)) {
20939
- if (!existsSync16(downloadDir)) mkdirSync11(downloadDir, { recursive: true });
21148
+ if (!existsSync17(downloadDir)) mkdirSync11(downloadDir, { recursive: true });
20940
21149
  try {
20941
21150
  filePath = await downloadToTemp(src, downloadDir);
20942
21151
  } catch {
@@ -20945,7 +21154,7 @@ async function resolveMediaDuration(src, mediaStart, baseDir, downloadDir, tagNa
20945
21154
  } else if (!filePath.startsWith("/")) {
20946
21155
  filePath = join14(baseDir, filePath);
20947
21156
  }
20948
- if (!existsSync16(filePath)) {
21157
+ if (!existsSync17(filePath)) {
20949
21158
  return { duration: 0, resolvedPath: filePath };
20950
21159
  }
20951
21160
  const metadata = tagName19 === "video" ? await extractVideoMetadata(filePath) : await extractAudioMetadata(filePath);
@@ -21012,10 +21221,10 @@ async function parseSubCompositions(html, projectDir, downloadDir, parentOffset
21012
21221
  if (visited.has(filePath)) {
21013
21222
  continue;
21014
21223
  }
21015
- if (!existsSync16(filePath)) {
21224
+ if (!existsSync17(filePath)) {
21016
21225
  continue;
21017
21226
  }
21018
- const rawSubHtml = readFileSync6(filePath, "utf-8");
21227
+ const rawSubHtml = readFileSync7(filePath, "utf-8");
21019
21228
  const nestedVisited = new Set(visited);
21020
21229
  nestedVisited.add(filePath);
21021
21230
  workItems.push({ srcPath, absoluteStart, absoluteEnd, filePath, rawSubHtml, nestedVisited });
@@ -21209,8 +21418,8 @@ function inlineSubCompositions(html, subCompositions, projectDir) {
21209
21418
  let compHtml = subCompositions.get(srcPath) || null;
21210
21419
  if (!compHtml) {
21211
21420
  const filePath = resolve5(projectDir, srcPath);
21212
- if (existsSync16(filePath)) {
21213
- compHtml = readFileSync6(filePath, "utf-8");
21421
+ if (existsSync17(filePath)) {
21422
+ compHtml = readFileSync7(filePath, "utf-8");
21214
21423
  }
21215
21424
  }
21216
21425
  if (!compHtml) {
@@ -21330,7 +21539,7 @@ ${html}
21330
21539
  </html>`;
21331
21540
  }
21332
21541
  async function compileForRender(projectDir, htmlPath, downloadDir) {
21333
- const rawHtml = readFileSync6(htmlPath, "utf-8");
21542
+ const rawHtml = readFileSync7(htmlPath, "utf-8");
21334
21543
  const { html: compiledHtml, unresolvedCompositions } = await compileHtmlFile(
21335
21544
  rawHtml,
21336
21545
  projectDir,
@@ -21518,10 +21727,10 @@ var init_logger = __esm({
21518
21727
 
21519
21728
  // ../producer/src/services/renderOrchestrator.ts
21520
21729
  import {
21521
- existsSync as existsSync17,
21730
+ existsSync as existsSync18,
21522
21731
  mkdirSync as mkdirSync12,
21523
21732
  rmSync as rmSync5,
21524
- readFileSync as readFileSync7,
21733
+ readFileSync as readFileSync8,
21525
21734
  writeFileSync as writeFileSync4,
21526
21735
  copyFileSync as copyFileSync2,
21527
21736
  appendFileSync
@@ -21683,28 +21892,28 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
21683
21892
  };
21684
21893
  job.startedAt = /* @__PURE__ */ new Date();
21685
21894
  assertNotAborted();
21686
- if (!existsSync17(workDir)) mkdirSync12(workDir, { recursive: true });
21895
+ if (!existsSync18(workDir)) mkdirSync12(workDir, { recursive: true });
21687
21896
  if (job.config.debug) {
21688
21897
  const logPath = join15(workDir, "render.log");
21689
21898
  restoreLogger = installDebugLogger(logPath, log);
21690
21899
  }
21691
21900
  const entryFile = job.config.entryFile || "index.html";
21692
21901
  let htmlPath = join15(projectDir, entryFile);
21693
- if (!existsSync17(htmlPath)) {
21902
+ if (!existsSync18(htmlPath)) {
21694
21903
  throw new Error(`Entry file not found: ${htmlPath}`);
21695
21904
  }
21696
21905
  assertNotAborted();
21697
- const rawEntry = readFileSync7(htmlPath, "utf-8");
21906
+ const rawEntry = readFileSync8(htmlPath, "utf-8");
21698
21907
  if (entryFile !== "index.html" && rawEntry.trimStart().startsWith("<template")) {
21699
21908
  const wrapperPath = join15(workDir, "standalone-entry.html");
21700
21909
  const projectIndexPath = join15(projectDir, "index.html");
21701
- if (!existsSync17(projectIndexPath)) {
21910
+ if (!existsSync18(projectIndexPath)) {
21702
21911
  throw new Error(
21703
21912
  `Template entry file "${entryFile}" requires a project index.html to extract its render shell.`
21704
21913
  );
21705
21914
  }
21706
21915
  const standaloneHtml = extractStandaloneEntryFromIndex(
21707
- readFileSync7(projectIndexPath, "utf-8"),
21916
+ readFileSync8(projectIndexPath, "utf-8"),
21708
21917
  entryFile
21709
21918
  );
21710
21919
  if (!standaloneHtml) {
@@ -21952,7 +22161,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
21952
22161
  assertNotAborted();
21953
22162
  }
21954
22163
  const framesDir = join15(workDir, "captured-frames");
21955
- if (!existsSync17(framesDir)) mkdirSync12(framesDir, { recursive: true });
22164
+ if (!existsSync18(framesDir)) mkdirSync12(framesDir, { recursive: true });
21956
22165
  const captureOptions = {
21957
22166
  width,
21958
22167
  height,
@@ -22240,7 +22449,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
22240
22449
  }
22241
22450
  }
22242
22451
  if (job.config.debug) {
22243
- if (existsSync17(outputPath)) {
22452
+ if (existsSync18(outputPath)) {
22244
22453
  const debugOutput = join15(workDir, isWebm ? "output.webm" : "output.mp4");
22245
22454
  copyFileSync2(outputPath, debugOutput);
22246
22455
  }
@@ -22317,7 +22526,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
22317
22526
  await safeCleanup(
22318
22527
  "remove workDir (error)",
22319
22528
  () => {
22320
- if (existsSync17(workDir)) rmSync5(workDir, { recursive: true, force: true });
22529
+ if (existsSync18(workDir)) rmSync5(workDir, { recursive: true, force: true });
22321
22530
  },
22322
22531
  log
22323
22532
  );
@@ -22379,7 +22588,7 @@ var init_lint = __esm({
22379
22588
  });
22380
22589
 
22381
22590
  // ../producer/src/services/hyperframeLint.ts
22382
- import { existsSync as existsSync18, readFileSync as readFileSync8, statSync as statSync5 } from "fs";
22591
+ import { existsSync as existsSync19, readFileSync as readFileSync9, statSync as statSync5 } from "fs";
22383
22592
  import { resolve as resolve7, join as join16 } from "path";
22384
22593
  function isStringRecord(value) {
22385
22594
  if (!value || typeof value !== "object" || Array.isArray(value)) {
@@ -22408,7 +22617,7 @@ function pickEntryFile(files, preferredEntryFile) {
22408
22617
  }
22409
22618
  function readProjectEntryFile(projectDir, preferredEntryFile) {
22410
22619
  const absProjectDir = resolve7(projectDir);
22411
- if (!existsSync18(absProjectDir) || !statSync5(absProjectDir).isDirectory()) {
22620
+ if (!existsSync19(absProjectDir) || !statSync5(absProjectDir).isDirectory()) {
22412
22621
  return { error: `Project directory not found: ${absProjectDir}` };
22413
22622
  }
22414
22623
  const entryCandidates = [preferredEntryFile, "index.html", "src/index.html"].filter(
@@ -22419,10 +22628,10 @@ function readProjectEntryFile(projectDir, preferredEntryFile) {
22419
22628
  if (!absoluteEntryPath.startsWith(absProjectDir)) {
22420
22629
  return { error: `Entry file must stay inside project directory: ${entryFile}` };
22421
22630
  }
22422
- if (existsSync18(absoluteEntryPath) && statSync5(absoluteEntryPath).isFile()) {
22631
+ if (existsSync19(absoluteEntryPath) && statSync5(absoluteEntryPath).isFile()) {
22423
22632
  return {
22424
22633
  entryFile,
22425
- html: readFileSync8(absoluteEntryPath, "utf-8"),
22634
+ html: readFileSync9(absoluteEntryPath, "utf-8"),
22426
22635
  source: "projectDir"
22427
22636
  };
22428
22637
  }
@@ -22493,7 +22702,7 @@ var init_paths = __esm({
22493
22702
 
22494
22703
  // ../producer/src/server.ts
22495
22704
  import {
22496
- existsSync as existsSync19,
22705
+ existsSync as existsSync20,
22497
22706
  mkdirSync as mkdirSync13,
22498
22707
  statSync as statSync6,
22499
22708
  mkdtempSync,
@@ -22523,11 +22732,11 @@ async function prepareRenderBody(body) {
22523
22732
  const projectDir = typeof body.projectDir === "string" ? body.projectDir : void 0;
22524
22733
  if (projectDir) {
22525
22734
  const absProjectDir = resolve9(projectDir);
22526
- if (!existsSync19(absProjectDir) || !statSync6(absProjectDir).isDirectory()) {
22735
+ if (!existsSync20(absProjectDir) || !statSync6(absProjectDir).isDirectory()) {
22527
22736
  return { error: `Project directory not found: ${absProjectDir}` };
22528
22737
  }
22529
22738
  const entry = options.entryFile || "index.html";
22530
- if (!existsSync19(resolve9(absProjectDir, entry))) {
22739
+ if (!existsSync20(resolve9(absProjectDir, entry))) {
22531
22740
  return { error: `Entry file "${entry}" not found in project directory: ${absProjectDir}` };
22532
22741
  }
22533
22742
  return { prepared: { input: { projectDir: absProjectDir, ...options } } };
@@ -22674,7 +22883,7 @@ function createRenderHandlers(options = {}) {
22674
22883
  log
22675
22884
  );
22676
22885
  const outputDir = dirname9(absoluteOutputPath);
22677
- if (!existsSync19(outputDir)) mkdirSync13(outputDir, { recursive: true });
22886
+ if (!existsSync20(outputDir)) mkdirSync13(outputDir, { recursive: true });
22678
22887
  log.info("render started", {
22679
22888
  requestId,
22680
22889
  projectDir: input.projectDir,
@@ -22699,7 +22908,7 @@ function createRenderHandlers(options = {}) {
22699
22908
  log.info(`render progress ${pct}%`, { requestId, stage: j2.currentStage, message });
22700
22909
  }
22701
22910
  });
22702
- const fileSize = existsSync19(absoluteOutputPath) ? statSync6(absoluteOutputPath).size : 0;
22911
+ const fileSize = existsSync20(absoluteOutputPath) ? statSync6(absoluteOutputPath).size : 0;
22703
22912
  const durationMs = Date.now() - t0;
22704
22913
  const outputToken = store.register(absoluteOutputPath);
22705
22914
  const outputUrl = `${outputUrlPrefix}/${outputToken}`;
@@ -22782,7 +22991,7 @@ function createRenderHandlers(options = {}) {
22782
22991
  log
22783
22992
  );
22784
22993
  const outputDir = dirname9(absoluteOutputPath);
22785
- if (!existsSync19(outputDir)) mkdirSync13(outputDir, { recursive: true });
22994
+ if (!existsSync20(outputDir)) mkdirSync13(outputDir, { recursive: true });
22786
22995
  log.info("render-stream started", { requestId, projectDir: input.projectDir });
22787
22996
  const job = createRenderJob({
22788
22997
  fps: input.fps,
@@ -22816,7 +23025,7 @@ function createRenderHandlers(options = {}) {
22816
23025
  },
22817
23026
  abortController.signal
22818
23027
  );
22819
- const fileSize = existsSync19(absoluteOutputPath) ? statSync6(absoluteOutputPath).size : 0;
23028
+ const fileSize = existsSync20(absoluteOutputPath) ? statSync6(absoluteOutputPath).size : 0;
22820
23029
  const outputToken = store.register(absoluteOutputPath);
22821
23030
  const outputUrl = `${outputUrlPrefix}/${outputToken}`;
22822
23031
  log.info("render-stream completed", { requestId, fileSize, perf: job.perfSummary ?? null });
@@ -22874,7 +23083,7 @@ function createRenderHandlers(options = {}) {
22874
23083
  if (!artifact) {
22875
23084
  return c2.json({ success: false, error: "Output artifact not found or expired" }, 404);
22876
23085
  }
22877
- if (!existsSync19(artifact.path)) {
23086
+ if (!existsSync20(artifact.path)) {
22878
23087
  store.delete(token);
22879
23088
  return c2.json({ success: false, error: "Output artifact file missing" }, 404);
22880
23089
  }
@@ -23008,7 +23217,7 @@ __export(manager_exports2, {
23008
23217
  setBrowserPath: () => setBrowserPath
23009
23218
  });
23010
23219
  import { execSync } from "child_process";
23011
- import { existsSync as existsSync20, rmSync as rmSync7 } from "fs";
23220
+ import { existsSync as existsSync21, rmSync as rmSync7 } from "fs";
23012
23221
  import { homedir as homedir5 } from "os";
23013
23222
  import { join as join19 } from "path";
23014
23223
  import { Browser, detectBrowserPlatform, getInstalledBrowsers, install } from "@puppeteer/browsers";
@@ -23028,17 +23237,17 @@ function whichBinary2(name) {
23028
23237
  }
23029
23238
  }
23030
23239
  function findFromEnv2() {
23031
- if (_browserPathOverride && existsSync20(_browserPathOverride)) {
23240
+ if (_browserPathOverride && existsSync21(_browserPathOverride)) {
23032
23241
  return { executablePath: _browserPathOverride, source: "env" };
23033
23242
  }
23034
23243
  const envPath = process.env["HYPERFRAMES_BROWSER_PATH"];
23035
- if (envPath && existsSync20(envPath)) {
23244
+ if (envPath && existsSync21(envPath)) {
23036
23245
  return { executablePath: envPath, source: "env" };
23037
23246
  }
23038
23247
  return void 0;
23039
23248
  }
23040
23249
  async function findFromCache() {
23041
- if (!existsSync20(CACHE_DIR)) {
23250
+ if (!existsSync21(CACHE_DIR)) {
23042
23251
  return void 0;
23043
23252
  }
23044
23253
  const installed = await getInstalledBrowsers({ cacheDir: CACHE_DIR });
@@ -23050,7 +23259,7 @@ async function findFromCache() {
23050
23259
  }
23051
23260
  function findFromSystem2() {
23052
23261
  for (const p of SYSTEM_CHROME_PATHS) {
23053
- if (existsSync20(p)) {
23262
+ if (existsSync21(p)) {
23054
23263
  return { executablePath: p, source: "system" };
23055
23264
  }
23056
23265
  }
@@ -23070,21 +23279,21 @@ async function findBrowser() {
23070
23279
  async function ensureBrowser(options) {
23071
23280
  const existing = await findBrowser();
23072
23281
  if (existing) return existing;
23073
- const platform2 = detectBrowserPlatform();
23074
- if (!platform2) {
23282
+ const platform4 = detectBrowserPlatform();
23283
+ if (!platform4) {
23075
23284
  throw new Error(`Unsupported platform: ${process.platform} ${process.arch}`);
23076
23285
  }
23077
23286
  const installed = await install({
23078
23287
  cacheDir: CACHE_DIR,
23079
23288
  browser: Browser.CHROMEHEADLESSSHELL,
23080
23289
  buildId: CHROME_VERSION,
23081
- platform: platform2,
23290
+ platform: platform4,
23082
23291
  downloadProgressCallback: options?.onProgress
23083
23292
  });
23084
23293
  return { executablePath: installed.executablePath, source: "download" };
23085
23294
  }
23086
23295
  function clearBrowser() {
23087
- if (!existsSync20(CACHE_DIR)) {
23296
+ if (!existsSync21(CACHE_DIR)) {
23088
23297
  return false;
23089
23298
  }
23090
23299
  rmSync7(CACHE_DIR, { recursive: true, force: true });
@@ -23112,18 +23321,18 @@ __export(studioServer_exports, {
23112
23321
  });
23113
23322
  import { Hono as Hono4 } from "hono";
23114
23323
  import { streamSSE as streamSSE2 } from "hono/streaming";
23115
- import { existsSync as existsSync21, readFileSync as readFileSync9, readdirSync as readdirSync6, statSync as statSync7, writeFileSync as writeFileSync6, mkdirSync as mkdirSync14 } from "fs";
23324
+ import { existsSync as existsSync22, readFileSync as readFileSync10, readdirSync as readdirSync6, statSync as statSync7, writeFileSync as writeFileSync6, mkdirSync as mkdirSync14 } from "fs";
23116
23325
  import { resolve as resolve10, join as join20, sep as sep2, basename as basename2, dirname as dirname10, extname as extname4 } from "path";
23117
23326
  function resolveDistDir() {
23118
23327
  const builtPath = resolve10(__dirname, "studio");
23119
- if (existsSync21(resolve10(builtPath, "index.html"))) return builtPath;
23328
+ if (existsSync22(resolve10(builtPath, "index.html"))) return builtPath;
23120
23329
  const devPath = resolve10(__dirname, "..", "..", "..", "studio", "dist");
23121
- if (existsSync21(resolve10(devPath, "index.html"))) return devPath;
23330
+ if (existsSync22(resolve10(devPath, "index.html"))) return devPath;
23122
23331
  return builtPath;
23123
23332
  }
23124
23333
  function resolveRuntimePath() {
23125
23334
  const builtPath = resolve10(__dirname, "hyperframe-runtime.js");
23126
- if (existsSync21(builtPath)) return builtPath;
23335
+ if (existsSync22(builtPath)) return builtPath;
23127
23336
  const devPath = resolve10(
23128
23337
  __dirname,
23129
23338
  "..",
@@ -23133,7 +23342,7 @@ function resolveRuntimePath() {
23133
23342
  "dist",
23134
23343
  "hyperframe.runtime.iife.js"
23135
23344
  );
23136
- if (existsSync21(devPath)) return devPath;
23345
+ if (existsSync22(devPath)) return devPath;
23137
23346
  return builtPath;
23138
23347
  }
23139
23348
  function isSafePath(base, resolved) {
@@ -23157,27 +23366,27 @@ function walkDir(dir, prefix = "") {
23157
23366
  return files;
23158
23367
  }
23159
23368
  function serveStaticFile(filePath) {
23160
- if (!existsSync21(filePath) || !statSync7(filePath).isFile()) return null;
23369
+ if (!existsSync22(filePath) || !statSync7(filePath).isFile()) return null;
23161
23370
  const mime = getMimeType(filePath);
23162
- const content = readFileSync9(filePath);
23371
+ const content = readFileSync10(filePath);
23163
23372
  return new Response(content, {
23164
23373
  headers: { "Content-Type": mime, "Cache-Control": "no-store" }
23165
23374
  });
23166
23375
  }
23167
23376
  function buildSubCompositionHtml(projectDir, compPath, runtimeUrl) {
23168
23377
  const compFile = resolve10(projectDir, compPath);
23169
- if (!isSafePath(projectDir, compFile) || !existsSync21(compFile) || !statSync7(compFile).isFile()) {
23378
+ if (!isSafePath(projectDir, compFile) || !existsSync22(compFile) || !statSync7(compFile).isFile()) {
23170
23379
  return null;
23171
23380
  }
23172
- let rawComp = readFileSync9(compFile, "utf-8");
23381
+ let rawComp = readFileSync10(compFile, "utf-8");
23173
23382
  const templateMatch = rawComp.match(/<template>([\s\S]*)<\/template>/i);
23174
23383
  let content = (templateMatch ? templateMatch[1] : rawComp) ?? rawComp;
23175
23384
  content = content.replace(
23176
23385
  /(<[^>]*?)(data-composition-src=["']([^"']+)["'])([^>]*>)/g,
23177
23386
  (_match, before2, srcAttr, src, after2) => {
23178
23387
  const nestedFile = join20(projectDir, src);
23179
- if (!existsSync21(nestedFile)) return before2 + srcAttr + after2;
23180
- const nestedRaw = readFileSync9(nestedFile, "utf-8");
23388
+ if (!existsSync22(nestedFile)) return before2 + srcAttr + after2;
23389
+ const nestedRaw = readFileSync10(nestedFile, "utf-8");
23181
23390
  const nestedTemplate = nestedRaw.match(/<template>([\s\S]*)<\/template>/i);
23182
23391
  const nestedContent = (nestedTemplate ? nestedTemplate[1] : nestedRaw) ?? nestedRaw;
23183
23392
  const styles = [];
@@ -23215,8 +23424,8 @@ function createStudioServer(options) {
23215
23424
  const watcher = createProjectWatcher(projectDir);
23216
23425
  const app = new Hono4();
23217
23426
  app.get("/api/runtime.js", (c2) => {
23218
- if (!existsSync21(runtimePath)) return c2.text("runtime not built", 404);
23219
- return c2.body(readFileSync9(runtimePath, "utf-8"), 200, {
23427
+ if (!existsSync22(runtimePath)) return c2.text("runtime not built", 404);
23428
+ return c2.body(readFileSync10(runtimePath, "utf-8"), 200, {
23220
23429
  "Content-Type": "text/javascript",
23221
23430
  "Cache-Control": "no-store"
23222
23431
  });
@@ -23251,8 +23460,8 @@ function createStudioServer(options) {
23251
23460
  bundled = await bundleToSingleHtml2(projectDir);
23252
23461
  } catch {
23253
23462
  const file = join20(projectDir, "index.html");
23254
- if (!existsSync21(file)) return c2.text("not found", 404);
23255
- bundled = readFileSync9(file, "utf-8");
23463
+ if (!existsSync22(file)) return c2.text("not found", 404);
23464
+ bundled = readFileSync10(file, "utf-8");
23256
23465
  }
23257
23466
  const baseTag = `<base href="/api/projects/${projectId}/preview/">`;
23258
23467
  if (bundled.includes("<head>")) {
@@ -23285,11 +23494,11 @@ function createStudioServer(options) {
23285
23494
  c2.req.path.replace(`/api/projects/${id}/preview/`, "").split("?")[0] ?? ""
23286
23495
  );
23287
23496
  const file = resolve10(projectDir, subPath);
23288
- if (!isSafePath(projectDir, file) || !existsSync21(file) || !statSync7(file).isFile()) {
23497
+ if (!isSafePath(projectDir, file) || !existsSync22(file) || !statSync7(file).isFile()) {
23289
23498
  return c2.text("not found", 404);
23290
23499
  }
23291
23500
  const mime = getMimeType(file);
23292
- const content = readFileSync9(file);
23501
+ const content = readFileSync10(file);
23293
23502
  return new Response(content, {
23294
23503
  headers: { "Content-Type": mime, "Cache-Control": "no-store" }
23295
23504
  });
@@ -23299,10 +23508,10 @@ function createStudioServer(options) {
23299
23508
  if (id !== projectId) return c2.json({ error: "not found" }, 404);
23300
23509
  const filePath = decodeURIComponent(c2.req.path.replace(`/api/projects/${id}/files/`, ""));
23301
23510
  const file = resolve10(projectDir, filePath);
23302
- if (!isSafePath(projectDir, file) || !existsSync21(file)) {
23511
+ if (!isSafePath(projectDir, file) || !existsSync22(file)) {
23303
23512
  return c2.text("not found", 404);
23304
23513
  }
23305
- const content = readFileSync9(file, "utf-8");
23514
+ const content = readFileSync10(file, "utf-8");
23306
23515
  return c2.json({ filename: filePath, content });
23307
23516
  });
23308
23517
  app.put("/api/projects/:id/files/*", async (c2) => {
@@ -23314,7 +23523,7 @@ function createStudioServer(options) {
23314
23523
  return c2.json({ error: "forbidden" }, 403);
23315
23524
  }
23316
23525
  const dir = dirname10(file);
23317
- if (!existsSync21(dir)) mkdirSync14(dir, { recursive: true });
23526
+ if (!existsSync22(dir)) mkdirSync14(dir, { recursive: true });
23318
23527
  const body = await c2.req.text();
23319
23528
  writeFileSync6(file, body, "utf-8");
23320
23529
  return c2.json({ ok: true });
@@ -23326,7 +23535,7 @@ function createStudioServer(options) {
23326
23535
  if (id !== projectId) return c2.json({ error: "not found" }, 404);
23327
23536
  const jobId = Math.random().toString(36).slice(2, 10);
23328
23537
  const outputDir = join20(projectDir, "renders");
23329
- if (!existsSync21(outputDir)) mkdirSync14(outputDir, { recursive: true });
23538
+ if (!existsSync22(outputDir)) mkdirSync14(outputDir, { recursive: true });
23330
23539
  const outputPath = join20(outputDir, `${projectId}.mp4`);
23331
23540
  renderJobs.set(jobId, { status: "rendering", progress: 0, outputPath });
23332
23541
  (async () => {
@@ -23340,7 +23549,11 @@ function createStudioServer(options) {
23340
23549
  }
23341
23550
  } catch {
23342
23551
  }
23552
+ const { trackRenderComplete: trackRenderComplete2 } = await Promise.resolve().then(() => (init_events(), events_exports));
23553
+ const { bytesToMb: bytesToMb2 } = await Promise.resolve().then(() => (init_system(), system_exports));
23554
+ const { freemem: freemem5 } = await import("os");
23343
23555
  const job = createRenderJob2({ fps: 30, quality: "standard" });
23556
+ const startTime = Date.now();
23344
23557
  const onProgress = (j2) => {
23345
23558
  const entry2 = renderJobs.get(jobId);
23346
23559
  if (entry2) entry2.progress = j2.progress;
@@ -23351,7 +23564,41 @@ function createStudioServer(options) {
23351
23564
  entry.status = "complete";
23352
23565
  entry.progress = 100;
23353
23566
  }
23567
+ const elapsed = Date.now() - startTime;
23568
+ const perf = job.perfSummary;
23569
+ const compositionDurationMs = perf ? Math.round(perf.compositionDurationSeconds * 1e3) : void 0;
23570
+ trackRenderComplete2({
23571
+ durationMs: elapsed,
23572
+ fps: 30,
23573
+ quality: "standard",
23574
+ workers: perf?.workers ?? 1,
23575
+ docker: false,
23576
+ gpu: false,
23577
+ compositionDurationMs,
23578
+ compositionWidth: perf?.resolution.width,
23579
+ compositionHeight: perf?.resolution.height,
23580
+ totalFrames: perf?.totalFrames,
23581
+ speedRatio: compositionDurationMs && compositionDurationMs > 0 && elapsed > 0 ? Math.round(compositionDurationMs / elapsed * 100) / 100 : void 0,
23582
+ captureAvgMs: perf?.captureAvgMs,
23583
+ capturePeakMs: perf?.capturePeakMs,
23584
+ peakMemoryMb: bytesToMb2(process.memoryUsage.rss()),
23585
+ memoryFreeMb: bytesToMb2(freemem5())
23586
+ });
23354
23587
  } catch (err) {
23588
+ try {
23589
+ const { trackRenderError: trackRenderError2 } = await Promise.resolve().then(() => (init_events(), events_exports));
23590
+ const { bytesToMb: bytesToMb2 } = await Promise.resolve().then(() => (init_system(), system_exports));
23591
+ const { freemem: freemem5 } = await import("os");
23592
+ trackRenderError2({
23593
+ fps: 30,
23594
+ quality: "standard",
23595
+ docker: false,
23596
+ errorMessage: err instanceof Error ? err.message : String(err),
23597
+ peakMemoryMb: bytesToMb2(process.memoryUsage.rss()),
23598
+ memoryFreeMb: bytesToMb2(freemem5())
23599
+ });
23600
+ } catch {
23601
+ }
23355
23602
  const entry = renderJobs.get(jobId);
23356
23603
  if (entry) {
23357
23604
  entry.status = "failed";
@@ -23385,10 +23632,10 @@ function createStudioServer(options) {
23385
23632
  app.get("/api/render/:jobId/download", (c2) => {
23386
23633
  const { jobId } = c2.req.param();
23387
23634
  const job = renderJobs.get(jobId);
23388
- if (!job?.outputPath || !existsSync21(job.outputPath)) {
23635
+ if (!job?.outputPath || !existsSync22(job.outputPath)) {
23389
23636
  return c2.json({ error: "not found" }, 404);
23390
23637
  }
23391
- const content = readFileSync9(job.outputPath);
23638
+ const content = readFileSync10(job.outputPath);
23392
23639
  return new Response(content, {
23393
23640
  headers: {
23394
23641
  "Content-Type": "video/mp4",
@@ -23408,10 +23655,10 @@ function createStudioServer(options) {
23408
23655
  });
23409
23656
  app.get("*", (c2) => {
23410
23657
  const indexPath = resolve10(studioDir, "index.html");
23411
- if (!existsSync21(indexPath)) {
23658
+ if (!existsSync22(indexPath)) {
23412
23659
  return c2.text("Studio not found. Rebuild with: pnpm run build", 500);
23413
23660
  }
23414
- return c2.html(readFileSync9(indexPath, "utf-8"));
23661
+ return c2.html(readFileSync10(indexPath, "utf-8"));
23415
23662
  });
23416
23663
  return { app, watcher };
23417
23664
  }
@@ -23450,7 +23697,7 @@ __export(dev_exports, {
23450
23697
  default: () => dev_default
23451
23698
  });
23452
23699
  import { spawn as spawn7 } from "child_process";
23453
- import { existsSync as existsSync22, lstatSync, symlinkSync, unlinkSync, readlinkSync, mkdirSync as mkdirSync15 } from "fs";
23700
+ import { existsSync as existsSync23, lstatSync, symlinkSync, unlinkSync, readlinkSync, mkdirSync as mkdirSync15 } from "fs";
23454
23701
  import { resolve as resolve11, dirname as dirname11, basename as basename3, join as join21 } from "path";
23455
23702
  import { fileURLToPath as fileURLToPath4 } from "url";
23456
23703
  import { createRequire } from "module";
@@ -23496,7 +23743,7 @@ async function runDevMode(dir) {
23496
23743
  mkdirSync15(projectsDir, { recursive: true });
23497
23744
  let createdSymlink = false;
23498
23745
  if (dir !== symlinkPath) {
23499
- if (existsSync22(symlinkPath)) {
23746
+ if (existsSync23(symlinkPath)) {
23500
23747
  try {
23501
23748
  const stat = lstatSync(symlinkPath);
23502
23749
  if (stat.isSymbolicLink()) {
@@ -23508,7 +23755,7 @@ async function runDevMode(dir) {
23508
23755
  } catch {
23509
23756
  }
23510
23757
  }
23511
- if (!existsSync22(symlinkPath)) {
23758
+ if (!existsSync23(symlinkPath)) {
23512
23759
  symlinkSync(dir, symlinkPath, "dir");
23513
23760
  createdSymlink = true;
23514
23761
  }
@@ -23550,7 +23797,7 @@ async function runDevMode(dir) {
23550
23797
  if (createdSymlink) {
23551
23798
  process.on("exit", () => {
23552
23799
  try {
23553
- if (existsSync22(symlinkPath)) unlinkSync(symlinkPath);
23800
+ if (existsSync23(symlinkPath)) unlinkSync(symlinkPath);
23554
23801
  } catch {
23555
23802
  }
23556
23803
  });
@@ -23577,12 +23824,12 @@ async function runLocalStudioMode(dir) {
23577
23824
  mkdirSync15(projectsDir, { recursive: true });
23578
23825
  let createdSymlink = false;
23579
23826
  if (dir !== symlinkPath) {
23580
- if (existsSync22(symlinkPath) && lstatSync(symlinkPath).isSymbolicLink()) {
23827
+ if (existsSync23(symlinkPath) && lstatSync(symlinkPath).isSymbolicLink()) {
23581
23828
  if (resolve11(readlinkSync(symlinkPath)) !== resolve11(dir)) {
23582
23829
  unlinkSync(symlinkPath);
23583
23830
  }
23584
23831
  }
23585
- if (!existsSync22(symlinkPath)) {
23832
+ if (!existsSync23(symlinkPath)) {
23586
23833
  symlinkSync(dir, symlinkPath, "dir");
23587
23834
  createdSymlink = true;
23588
23835
  }
@@ -23621,7 +23868,7 @@ async function runLocalStudioMode(dir) {
23621
23868
  if (createdSymlink) {
23622
23869
  process.on("exit", () => {
23623
23870
  try {
23624
- if (existsSync22(symlinkPath)) unlinkSync(symlinkPath);
23871
+ if (existsSync23(symlinkPath)) unlinkSync(symlinkPath);
23625
23872
  } catch {
23626
23873
  }
23627
23874
  });
@@ -23658,6 +23905,9 @@ async function runEmbeddedMode(dir, startPort) {
23658
23905
  console.log(` ${c.dim("Project")} ${c.accent(projectName)}`);
23659
23906
  console.log(` ${c.dim("Studio")} ${c.accent(url)}`);
23660
23907
  console.log();
23908
+ console.log(` ${c.dim("Edit with your AI agent \u2014 it has HyperFrames skills installed.")}`);
23909
+ console.log(` ${c.dim("Changes reload automatically in the studio.")}`);
23910
+ console.log();
23661
23911
  console.log(` ${c.dim("Press Ctrl+C to stop")}`);
23662
23912
  console.log();
23663
23913
  import("open").then((mod) => mod.default(`${url}#project/${projectName}`)).catch(() => {
@@ -23727,17 +23977,17 @@ var init_format = __esm({
23727
23977
  });
23728
23978
 
23729
23979
  // src/utils/project.ts
23730
- import { existsSync as existsSync23, statSync as statSync8 } from "fs";
23980
+ import { existsSync as existsSync24, statSync as statSync8 } from "fs";
23731
23981
  import { resolve as resolve12, basename as basename4 } from "path";
23732
23982
  function resolveProject(dirArg) {
23733
23983
  const dir = resolve12(dirArg ?? ".");
23734
23984
  const name = basename4(dir);
23735
23985
  const indexPath = resolve12(dir, "index.html");
23736
- if (!existsSync23(dir) || !statSync8(dir).isDirectory()) {
23986
+ if (!existsSync24(dir) || !statSync8(dir).isDirectory()) {
23737
23987
  errorBox("Not a directory: " + dir);
23738
23988
  process.exit(1);
23739
23989
  }
23740
- if (!existsSync23(indexPath)) {
23990
+ if (!existsSync24(indexPath)) {
23741
23991
  errorBox(
23742
23992
  "No composition found in " + dir,
23743
23993
  "No index.html file found.",
@@ -23826,8 +24076,8 @@ var render_exports = {};
23826
24076
  __export(render_exports, {
23827
24077
  default: () => render_default
23828
24078
  });
23829
- import { existsSync as existsSync24, mkdirSync as mkdirSync16, statSync as statSync9 } from "fs";
23830
- import { cpus as cpus2 } from "os";
24079
+ import { existsSync as existsSync25, mkdirSync as mkdirSync16, statSync as statSync9 } from "fs";
24080
+ import { cpus as cpus3, freemem as freemem3 } from "os";
23831
24081
  import { resolve as resolve13, dirname as dirname12, join as join22 } from "path";
23832
24082
  function defaultWorkerCount() {
23833
24083
  return Math.max(1, Math.min(Math.floor(CPU_CORE_COUNT / 2), 4));
@@ -23835,8 +24085,9 @@ function defaultWorkerCount() {
23835
24085
  async function renderDocker(projectDir, outputPath, options) {
23836
24086
  const producer = await loadProducer();
23837
24087
  const startTime = Date.now();
24088
+ let job;
23838
24089
  try {
23839
- const job = producer.createRenderJob({
24090
+ job = producer.createRenderJob({
23840
24091
  fps: options.fps,
23841
24092
  quality: options.quality,
23842
24093
  format: options.format,
@@ -23845,24 +24096,10 @@ async function renderDocker(projectDir, outputPath, options) {
23845
24096
  });
23846
24097
  await producer.executeRenderJob(job, projectDir, outputPath);
23847
24098
  } catch (error) {
23848
- trackRenderError({
23849
- fps: options.fps,
23850
- quality: options.quality,
23851
- docker: true
23852
- });
23853
- const message = error instanceof Error ? error.message : String(error);
23854
- errorBox("Render failed", message, "Check Docker is running: docker info");
23855
- process.exit(1);
24099
+ handleRenderError(error, options, startTime, true, "Check Docker is running: docker info");
23856
24100
  }
23857
24101
  const elapsed = Date.now() - startTime;
23858
- trackRenderComplete({
23859
- durationMs: elapsed,
23860
- fps: options.fps,
23861
- quality: options.quality,
23862
- workers: options.workers,
23863
- docker: true,
23864
- gpu: options.gpu
23865
- });
24102
+ trackRenderMetrics(job, elapsed, options, true);
23866
24103
  printRenderComplete(outputPath, elapsed, options.quiet);
23867
24104
  }
23868
24105
  async function renderLocal(projectDir, outputPath, options) {
@@ -23884,30 +24121,58 @@ async function renderLocal(projectDir, outputPath, options) {
23884
24121
  try {
23885
24122
  await producer.executeRenderJob(job, projectDir, outputPath, onProgress);
23886
24123
  } catch (error) {
23887
- trackRenderError({
23888
- fps: options.fps,
23889
- quality: options.quality,
23890
- docker: false
23891
- });
23892
- const message = error instanceof Error ? error.message : String(error);
23893
- errorBox("Render failed", message, "Try --docker for containerized rendering");
23894
- process.exit(1);
24124
+ handleRenderError(error, options, startTime, false, "Try --docker for containerized rendering");
23895
24125
  }
23896
24126
  const elapsed = Date.now() - startTime;
24127
+ trackRenderMetrics(job, elapsed, options, false);
24128
+ printRenderComplete(outputPath, elapsed, options.quiet);
24129
+ }
24130
+ function getMemorySnapshot() {
24131
+ return {
24132
+ peakMemoryMb: bytesToMb(process.memoryUsage.rss()),
24133
+ memoryFreeMb: bytesToMb(freemem3())
24134
+ };
24135
+ }
24136
+ function handleRenderError(error, options, startTime, docker, hint) {
24137
+ const message = error instanceof Error ? error.message : String(error);
24138
+ trackRenderError({
24139
+ fps: options.fps,
24140
+ quality: options.quality,
24141
+ docker,
24142
+ workers: options.workers,
24143
+ gpu: options.gpu,
24144
+ elapsedMs: Date.now() - startTime,
24145
+ errorMessage: message,
24146
+ ...getMemorySnapshot()
24147
+ });
24148
+ errorBox("Render failed", message, hint);
24149
+ process.exit(1);
24150
+ }
24151
+ function trackRenderMetrics(job, elapsedMs, options, docker) {
24152
+ const perf = job.perfSummary;
24153
+ const compositionDurationMs = perf ? Math.round(perf.compositionDurationSeconds * 1e3) : void 0;
24154
+ const speedRatio = compositionDurationMs && compositionDurationMs > 0 && elapsedMs > 0 ? Math.round(compositionDurationMs / elapsedMs * 100) / 100 : void 0;
23897
24155
  trackRenderComplete({
23898
- durationMs: elapsed,
24156
+ durationMs: elapsedMs,
23899
24157
  fps: options.fps,
23900
24158
  quality: options.quality,
23901
24159
  workers: options.workers,
23902
- docker: false,
23903
- gpu: options.gpu
24160
+ docker,
24161
+ gpu: options.gpu,
24162
+ compositionDurationMs,
24163
+ compositionWidth: perf?.resolution.width,
24164
+ compositionHeight: perf?.resolution.height,
24165
+ totalFrames: perf?.totalFrames,
24166
+ speedRatio,
24167
+ captureAvgMs: perf?.captureAvgMs,
24168
+ capturePeakMs: perf?.capturePeakMs,
24169
+ ...getMemorySnapshot()
23904
24170
  });
23905
- printRenderComplete(outputPath, elapsed, options.quiet);
23906
24171
  }
23907
24172
  function printRenderComplete(outputPath, elapsedMs, quiet) {
23908
24173
  if (quiet) return;
23909
24174
  let fileSize = "unknown";
23910
- if (existsSync24(outputPath)) {
24175
+ if (existsSync25(outputPath)) {
23911
24176
  const stat = statSync9(outputPath);
23912
24177
  fileSize = formatBytes(stat.size);
23913
24178
  }
@@ -23927,10 +24192,11 @@ var init_render = __esm({
23927
24192
  init_format();
23928
24193
  init_progress();
23929
24194
  init_events();
24195
+ init_system();
23930
24196
  VALID_FPS = /* @__PURE__ */ new Set([24, 30, 60]);
23931
24197
  VALID_QUALITY = /* @__PURE__ */ new Set(["draft", "standard", "high"]);
23932
24198
  VALID_FORMAT = /* @__PURE__ */ new Set(["mp4", "webm"]);
23933
- CPU_CORE_COUNT = cpus2().length;
24199
+ CPU_CORE_COUNT = cpus3().length;
23934
24200
  render_default = defineCommand({
23935
24201
  meta: {
23936
24202
  name: "render",
@@ -24101,7 +24367,7 @@ __export(transcribe_exports, {
24101
24367
  transcribe: () => transcribe
24102
24368
  });
24103
24369
  import { execFileSync as execFileSync3 } from "child_process";
24104
- import { existsSync as existsSync25, readFileSync as readFileSync10, mkdirSync as mkdirSync17, unlinkSync as unlinkSync2 } from "fs";
24370
+ import { existsSync as existsSync26, readFileSync as readFileSync11, mkdirSync as mkdirSync17, unlinkSync as unlinkSync2 } from "fs";
24105
24371
  import { join as join23, extname as extname5 } from "path";
24106
24372
  import { tmpdir as tmpdir2 } from "os";
24107
24373
  function isAudioFile(filePath) {
@@ -24188,10 +24454,10 @@ async function transcribe(inputPath, outputDir, options) {
24188
24454
  { stdio: "ignore", timeout: 3e5 }
24189
24455
  );
24190
24456
  const transcriptPath = `${outputBase}.json`;
24191
- if (!existsSync25(transcriptPath)) {
24457
+ if (!existsSync26(transcriptPath)) {
24192
24458
  throw new Error("Whisper did not produce output. Check the input file.");
24193
24459
  }
24194
- const transcript = JSON.parse(readFileSync10(transcriptPath, "utf-8"));
24460
+ const transcript = JSON.parse(readFileSync11(transcriptPath, "utf-8"));
24195
24461
  const segments = transcript.transcription ?? [];
24196
24462
  let wordCount = 0;
24197
24463
  let maxEnd = 0;
@@ -24230,12 +24496,12 @@ __export(init_exports, {
24230
24496
  default: () => init_default
24231
24497
  });
24232
24498
  import {
24233
- existsSync as existsSync26,
24499
+ existsSync as existsSync27,
24234
24500
  mkdirSync as mkdirSync18,
24235
24501
  copyFileSync as copyFileSync3,
24236
24502
  cpSync as cpSync2,
24237
24503
  writeFileSync as writeFileSync7,
24238
- readFileSync as readFileSync11,
24504
+ readFileSync as readFileSync12,
24239
24505
  readdirSync as readdirSync7
24240
24506
  } from "fs";
24241
24507
  import { resolve as resolve14, basename as basename5, join as join24, dirname as dirname13 } from "path";
@@ -24361,7 +24627,7 @@ function resolveAssetDir(devSegments, builtSegments) {
24361
24627
  const base = dirname13(fileURLToPath5(import.meta.url));
24362
24628
  const devPath = resolve14(base, ...devSegments);
24363
24629
  const builtPath = resolve14(base, ...builtSegments);
24364
- return existsSync26(devPath) ? devPath : builtPath;
24630
+ return existsSync27(devPath) ? devPath : builtPath;
24365
24631
  }
24366
24632
  function getStaticTemplateDir(templateId) {
24367
24633
  return resolveAssetDir(["..", "templates", templateId], ["templates", templateId]);
@@ -24375,7 +24641,7 @@ function getBundledSkillsDir() {
24375
24641
  function patchVideoSrc(dir, videoFilename, durationSeconds) {
24376
24642
  const htmlFiles = readdirSync7(dir, { withFileTypes: true, recursive: true }).filter((e) => e.isFile() && e.name.endsWith(".html")).map((e) => join24(e.parentPath ?? e.path, e.name));
24377
24643
  for (const file of htmlFiles) {
24378
- let content = readFileSync11(file, "utf-8");
24644
+ let content = readFileSync12(file, "utf-8");
24379
24645
  if (videoFilename) {
24380
24646
  content = content.replaceAll("__VIDEO_SRC__", videoFilename);
24381
24647
  } else {
@@ -24390,7 +24656,7 @@ function patchVideoSrc(dir, videoFilename, durationSeconds) {
24390
24656
  }
24391
24657
  }
24392
24658
  function patchTranscript(dir, transcriptPath) {
24393
- const raw = JSON.parse(readFileSync11(transcriptPath, "utf-8"));
24659
+ const raw = JSON.parse(readFileSync12(transcriptPath, "utf-8"));
24394
24660
  const words = [];
24395
24661
  for (const seg of raw.transcription ?? []) {
24396
24662
  for (const token of seg.tokens ?? []) {
@@ -24414,7 +24680,7 @@ function patchTranscript(dir, transcriptPath) {
24414
24680
  const wordsJson = JSON.stringify(words, null, 10).replace(/^\[/, "[").replace(/\n {10}/g, "\n ");
24415
24681
  const htmlFiles = readdirSync7(dir, { withFileTypes: true, recursive: true }).filter((e) => e.isFile() && e.name.endsWith(".html")).map((e) => join24(e.parentPath ?? e.path, e.name));
24416
24682
  for (const file of htmlFiles) {
24417
- let content = readFileSync11(file, "utf-8");
24683
+ let content = readFileSync12(file, "utf-8");
24418
24684
  const scriptBlocks = content.match(/<script>[\s\S]*?<\/script>/g) ?? [];
24419
24685
  let scriptMatch = null;
24420
24686
  let transcriptMatch = null;
@@ -24531,7 +24797,7 @@ function scaffoldProject(destDir, name, templateId, localVideoName, durationSeco
24531
24797
  "utf-8"
24532
24798
  );
24533
24799
  const sharedDir = getSharedTemplateDir();
24534
- if (existsSync26(sharedDir)) {
24800
+ if (existsSync27(sharedDir)) {
24535
24801
  for (const entry of readdirSync7(sharedDir, { withFileTypes: true })) {
24536
24802
  const src = join24(sharedDir, entry.name);
24537
24803
  const dest = resolve14(destDir, entry.name);
@@ -24541,11 +24807,11 @@ function scaffoldProject(destDir, name, templateId, localVideoName, durationSeco
24541
24807
  }
24542
24808
  }
24543
24809
  const skillsSrcDir = getBundledSkillsDir();
24544
- if (existsSync26(skillsSrcDir)) {
24545
- const projectSkills = ["compose-video", "captions"];
24810
+ if (existsSync27(skillsSrcDir)) {
24811
+ const projectSkills = ["hyperframes-compose", "hyperframes-captions"];
24546
24812
  for (const skill of projectSkills) {
24547
24813
  const src = join24(skillsSrcDir, skill);
24548
- if (existsSync26(src)) {
24814
+ if (existsSync27(src)) {
24549
24815
  const dest = resolve14(destDir, ".claude", "skills", skill);
24550
24816
  mkdirSync18(dest, { recursive: true });
24551
24817
  cpSync2(src, dest, { recursive: true });
@@ -24664,7 +24930,7 @@ Examples:
24664
24930
  const templateId2 = resolvedTemplate;
24665
24931
  const name2 = args.name ?? "my-video";
24666
24932
  const destDir2 = resolve14(name2);
24667
- if (existsSync26(destDir2) && readdirSync7(destDir2).length > 0) {
24933
+ if (existsSync27(destDir2) && readdirSync7(destDir2).length > 0) {
24668
24934
  console.error(c.error(`Directory already exists and is not empty: ${name2}`));
24669
24935
  process.exit(1);
24670
24936
  }
@@ -24674,7 +24940,7 @@ Examples:
24674
24940
  let sourceFilePath2;
24675
24941
  if (videoFlag) {
24676
24942
  const videoPath = resolve14(videoFlag);
24677
- if (!existsSync26(videoPath)) {
24943
+ if (!existsSync27(videoPath)) {
24678
24944
  console.error(c.error(`Video file not found: ${videoFlag}`));
24679
24945
  process.exit(1);
24680
24946
  }
@@ -24688,7 +24954,7 @@ Examples:
24688
24954
  }
24689
24955
  if (audioFlag) {
24690
24956
  const audioPath = resolve14(audioFlag);
24691
- if (!existsSync26(audioPath)) {
24957
+ if (!existsSync27(audioPath)) {
24692
24958
  console.error(c.error(`Audio file not found: ${audioFlag}`));
24693
24959
  process.exit(1);
24694
24960
  }
@@ -24715,7 +24981,7 @@ Examples:
24715
24981
  scaffoldProject(destDir2, basename5(destDir2), templateId2, localVideoName2, videoDuration2);
24716
24982
  trackInitTemplate(templateId2);
24717
24983
  const transcriptFile2 = resolve14(destDir2, "transcript.json");
24718
- if (existsSync26(transcriptFile2)) {
24984
+ if (existsSync27(transcriptFile2)) {
24719
24985
  patchTranscript(destDir2, transcriptFile2);
24720
24986
  }
24721
24987
  if (!skipSkills) {
@@ -24726,23 +24992,22 @@ Examples:
24726
24992
  console.log(` ${c.accent(f)}`);
24727
24993
  }
24728
24994
  console.log();
24729
- console.log("Next steps:");
24730
- console.log(
24731
- ` ${c.accent(`cd ${name2}`)} && ${c.accent("npx hyperframes dev")} ${c.dim("# preview in studio")}`
24732
- );
24733
- console.log(
24734
- ` ${c.accent(`cd ${name2}`)} && ${c.accent("npx hyperframes render")} ${c.dim("# render to MP4")}`
24735
- );
24995
+ console.log("Get started:");
24996
+ console.log();
24997
+ console.log(` ${c.accent("1.")} Open this project with your AI coding agent:`);
24736
24998
  console.log(
24737
- ` ${c.accent("npx hyperframes docs")} ${c.dim("<topic>")} ${c.dim("# learn composition syntax")}`
24999
+ ` ${c.accent(`cd ${name2}`)} then start ${c.accent("Claude Code")}, ${c.accent("Cursor")}, or your preferred agent`
24738
25000
  );
24739
25001
  console.log(
24740
- ` ${c.dim("topics: data-attributes, gsap, compositions, rendering, templates, troubleshooting")}`
25002
+ ` ${c.dim("AI skills are installed \u2014 your agent knows how to create and edit compositions.")}`
24741
25003
  );
24742
25004
  console.log();
24743
- console.log(
24744
- ` ${c.dim("AI skills installed \u2014 open this folder in your AI coding agent to get started.")}`
24745
- );
25005
+ console.log(` ${c.accent("2.")} Preview in the browser:`);
25006
+ console.log(` ${c.accent(`cd ${name2}`)} && ${c.accent("npx hyperframes dev")}`);
25007
+ console.log();
25008
+ console.log(` ${c.accent("3.")} Render to MP4 when ready:`);
25009
+ console.log(` ${c.accent(`cd ${name2}`)} && ${c.accent("npx hyperframes render")}`);
25010
+ console.log();
24746
25011
  console.log(` ${c.dim("Full docs: hyperframes.heygen.com")}`);
24747
25012
  return;
24748
25013
  }
@@ -24764,7 +25029,7 @@ Examples:
24764
25029
  name = nameResult;
24765
25030
  }
24766
25031
  const destDir = resolve14(name);
24767
- if (existsSync26(destDir) && readdirSync7(destDir).length > 0) {
25032
+ if (existsSync27(destDir) && readdirSync7(destDir).length > 0) {
24768
25033
  const overwrite = await Rt({
24769
25034
  message: `Directory ${c.accent(name)} already exists and is not empty. Overwrite?`,
24770
25035
  initialValue: false
@@ -24780,7 +25045,7 @@ Examples:
24780
25045
  let isAudioOnly = false;
24781
25046
  if (videoFlag) {
24782
25047
  const videoPath = resolve14(videoFlag);
24783
- if (!existsSync26(videoPath)) {
25048
+ if (!existsSync27(videoPath)) {
24784
25049
  R2.error(`File not found: ${videoFlag}`);
24785
25050
  Nt("Setup cancelled.");
24786
25051
  process.exit(1);
@@ -24815,7 +25080,7 @@ Examples:
24815
25080
  validate(val) {
24816
25081
  const trimmed = val?.trim();
24817
25082
  if (!trimmed) return "Please enter a file path";
24818
- if (!existsSync26(resolve14(trimmed))) return "File not found";
25083
+ if (!existsSync27(resolve14(trimmed))) return "File not found";
24819
25084
  return void 0;
24820
25085
  }
24821
25086
  });
@@ -24901,7 +25166,7 @@ Examples:
24901
25166
  scaffoldProject(destDir, name, templateId, localVideoName, videoDuration);
24902
25167
  trackInitTemplate(templateId);
24903
25168
  const transcriptFile = resolve14(destDir, "transcript.json");
24904
- if (existsSync26(transcriptFile)) {
25169
+ if (existsSync27(transcriptFile)) {
24905
25170
  patchTranscript(destDir, transcriptFile);
24906
25171
  }
24907
25172
  if (!skipSkills) {
@@ -24909,6 +25174,10 @@ Examples:
24909
25174
  }
24910
25175
  const files = readdirSync7(destDir);
24911
25176
  Vt2(files.map((f) => c.accent(f)).join("\n"), c.success(`Created ${name}/`));
25177
+ R2.message(
25178
+ `${c.dim("Tip:")} Open this project with ${c.accent("Claude Code")}, ${c.accent("Cursor")}, or your preferred AI agent.
25179
+ ${c.dim(" AI skills are installed \u2014 your agent knows how to create and edit compositions.")}`
25180
+ );
24912
25181
  await nextStepLoop(destDir);
24913
25182
  }
24914
25183
  });
@@ -24920,7 +25189,7 @@ var lint_exports = {};
24920
25189
  __export(lint_exports, {
24921
25190
  default: () => lint_default
24922
25191
  });
24923
- import { readFileSync as readFileSync12 } from "fs";
25192
+ import { readFileSync as readFileSync13 } from "fs";
24924
25193
  var lint_default;
24925
25194
  var init_lint2 = __esm({
24926
25195
  "src/commands/lint.ts"() {
@@ -24938,7 +25207,7 @@ var init_lint2 = __esm({
24938
25207
  },
24939
25208
  async run({ args }) {
24940
25209
  const project = resolveProject(args.dir);
24941
- const html = readFileSync12(project.indexPath, "utf-8");
25210
+ const html = readFileSync13(project.indexPath, "utf-8");
24942
25211
  const result = lintHyperframeHtml(html, { filePath: project.indexPath });
24943
25212
  if (args.json) {
24944
25213
  console.log(JSON.stringify(withMeta(result), null, 2));
@@ -24987,7 +25256,7 @@ var info_exports = {};
24987
25256
  __export(info_exports, {
24988
25257
  default: () => info_default
24989
25258
  });
24990
- import { readFileSync as readFileSync13, readdirSync as readdirSync8, statSync as statSync10 } from "fs";
25259
+ import { readFileSync as readFileSync14, readdirSync as readdirSync8, statSync as statSync10 } from "fs";
24991
25260
  import { join as join25 } from "path";
24992
25261
  function totalSize(dir) {
24993
25262
  let total = 0;
@@ -25020,7 +25289,7 @@ var init_info = __esm({
25020
25289
  },
25021
25290
  async run({ args }) {
25022
25291
  const project = resolveProject(args.dir);
25023
- const html = readFileSync13(project.indexPath, "utf-8");
25292
+ const html = readFileSync14(project.indexPath, "utf-8");
25024
25293
  ensureDOMParser();
25025
25294
  const parsed = parseHtml(html);
25026
25295
  const tracks = new Set(parsed.elements.map((el) => el.zIndex));
@@ -25071,7 +25340,7 @@ var compositions_exports = {};
25071
25340
  __export(compositions_exports, {
25072
25341
  default: () => compositions_default
25073
25342
  });
25074
- import { readFileSync as readFileSync14 } from "fs";
25343
+ import { readFileSync as readFileSync15 } from "fs";
25075
25344
  function parseCompositions(html) {
25076
25345
  const parser = new DOMParser();
25077
25346
  const doc = parser.parseFromString(html, "text/html");
@@ -25128,7 +25397,7 @@ var init_compositions = __esm({
25128
25397
  },
25129
25398
  async run({ args }) {
25130
25399
  const project = resolveProject(args.dir);
25131
- const html = readFileSync14(project.indexPath, "utf-8");
25400
+ const html = readFileSync15(project.indexPath, "utf-8");
25132
25401
  ensureDOMParser();
25133
25402
  const compositions = parseCompositions(html);
25134
25403
  if (compositions.length === 0) {
@@ -25164,7 +25433,7 @@ var benchmark_exports = {};
25164
25433
  __export(benchmark_exports, {
25165
25434
  default: () => benchmark_default
25166
25435
  });
25167
- import { existsSync as existsSync27, statSync as statSync11 } from "fs";
25436
+ import { existsSync as existsSync28, statSync as statSync11 } from "fs";
25168
25437
  import { resolve as resolve15, join as join26 } from "path";
25169
25438
  var DEFAULT_CONFIGS, benchmark_default;
25170
25439
  var init_benchmark = __esm({
@@ -25249,7 +25518,7 @@ var init_benchmark = __esm({
25249
25518
  await producer.executeRenderJob(job, project.dir, outputPath);
25250
25519
  const elapsedMs = Date.now() - startTime;
25251
25520
  let fileSize = null;
25252
- if (existsSync27(outputPath)) {
25521
+ if (existsSync28(outputPath)) {
25253
25522
  const stat = statSync11(outputPath);
25254
25523
  fileSize = stat.size;
25255
25524
  }
@@ -25458,7 +25727,7 @@ var docs_exports = {};
25458
25727
  __export(docs_exports, {
25459
25728
  default: () => docs_default
25460
25729
  });
25461
- import { readFileSync as readFileSync15, existsSync as existsSync28 } from "fs";
25730
+ import { readFileSync as readFileSync16, existsSync as existsSync29 } from "fs";
25462
25731
  import { resolve as resolve16, dirname as dirname14, join as join27 } from "path";
25463
25732
  import { fileURLToPath as fileURLToPath6 } from "url";
25464
25733
  function docsDir() {
@@ -25466,7 +25735,7 @@ function docsDir() {
25466
25735
  const dir = dirname14(thisFile);
25467
25736
  const devPath = resolve16(dir, "..", "docs");
25468
25737
  const builtPath = resolve16(dir, "docs");
25469
- return existsSync28(devPath) ? devPath : builtPath;
25738
+ return existsSync29(devPath) ? devPath : builtPath;
25470
25739
  }
25471
25740
  function formatInlineCode(line) {
25472
25741
  return line.replace(/`([^`]+)`/g, (_match, code) => c.accent(code));
@@ -25558,11 +25827,11 @@ var init_docs = __esm({
25558
25827
  process.exit(1);
25559
25828
  }
25560
25829
  const filePath = join27(docsDir(), entry.file);
25561
- if (!existsSync28(filePath)) {
25830
+ if (!existsSync29(filePath)) {
25562
25831
  console.error(c.error(`Doc file not found: ${filePath}`));
25563
25832
  process.exit(1);
25564
25833
  }
25565
- const content = readFileSync15(filePath, "utf-8");
25834
+ const content = readFileSync16(filePath, "utf-8");
25566
25835
  console.log();
25567
25836
  renderMarkdown(content);
25568
25837
  }
@@ -25576,6 +25845,7 @@ __export(doctor_exports, {
25576
25845
  default: () => doctor_default
25577
25846
  });
25578
25847
  import { execSync as execSync3 } from "child_process";
25848
+ import { freemem as freemem4, platform as platform3 } from "os";
25579
25849
  function checkFFmpeg() {
25580
25850
  const path = findFFmpeg();
25581
25851
  if (path) {
@@ -25653,6 +25923,67 @@ function checkVersion() {
25653
25923
  function checkNode() {
25654
25924
  return { ok: true, detail: `${process.version} (${process.platform} ${process.arch})` };
25655
25925
  }
25926
+ function checkCPU() {
25927
+ const sys = getSystemMeta();
25928
+ const model = sys.cpu_model ?? "Unknown";
25929
+ const speedStr = sys.cpu_speed ? ` @ ${sys.cpu_speed}MHz` : "";
25930
+ return { ok: true, detail: `${sys.cpu_count} cores \xB7 ${model}${speedStr}` };
25931
+ }
25932
+ function checkMemory() {
25933
+ const sys = getSystemMeta();
25934
+ const freeMb = bytesToMb(freemem4());
25935
+ const totalGb = (sys.memory_total_mb / 1024).toFixed(1);
25936
+ const freeGb = (freeMb / 1024).toFixed(1);
25937
+ if (freeMb < 2048) {
25938
+ return {
25939
+ ok: false,
25940
+ detail: `${totalGb} GB total \xB7 ${freeGb} GB free`,
25941
+ hint: "Low memory \u2014 renders may fail. Close other apps or increase RAM."
25942
+ };
25943
+ }
25944
+ return { ok: true, detail: `${totalGb} GB total \xB7 ${freeGb} GB free` };
25945
+ }
25946
+ function checkShm() {
25947
+ const shmMb = getShmSizeMb();
25948
+ if (shmMb === null) {
25949
+ return { ok: true, detail: "N/A (non-Linux)" };
25950
+ }
25951
+ if (shmMb < 256) {
25952
+ return {
25953
+ ok: false,
25954
+ detail: `${shmMb} MB`,
25955
+ hint: "Chrome needs \u2265256 MB. Use: docker run --shm-size=512m"
25956
+ };
25957
+ }
25958
+ return { ok: true, detail: `${shmMb} MB` };
25959
+ }
25960
+ function checkDisk() {
25961
+ const freeMb = getFreeDiskMb(".");
25962
+ if (freeMb === null) {
25963
+ return { ok: true, detail: "Unable to check" };
25964
+ }
25965
+ const freeGb = (freeMb / 1024).toFixed(1);
25966
+ if (freeMb < 1024) {
25967
+ return {
25968
+ ok: false,
25969
+ detail: `${freeGb} GB free`,
25970
+ hint: "Low disk space \u2014 renders produce large temp files."
25971
+ };
25972
+ }
25973
+ return { ok: true, detail: `${freeGb} GB free` };
25974
+ }
25975
+ function checkEnvironment() {
25976
+ const sys = getSystemMeta();
25977
+ const parts = [];
25978
+ if (sys.is_docker) parts.push("Docker");
25979
+ if (sys.is_wsl) parts.push("WSL");
25980
+ if (sys.is_ci) parts.push(`CI (${sys.ci_name ?? "detected"})`);
25981
+ if (!sys.is_tty) parts.push("non-TTY");
25982
+ if (parts.length === 0) {
25983
+ return { ok: true, detail: "Native terminal" };
25984
+ }
25985
+ return { ok: true, detail: parts.join(" \xB7 ") };
25986
+ }
25656
25987
  var doctor_default;
25657
25988
  var init_doctor = __esm({
25658
25989
  "src/commands/doctor.ts"() {
@@ -25663,6 +25994,7 @@ var init_doctor = __esm({
25663
25994
  init_ffmpeg();
25664
25995
  init_version();
25665
25996
  init_updateCheck();
25997
+ init_system();
25666
25998
  doctor_default = defineCommand({
25667
25999
  meta: { name: "doctor", description: "Check system dependencies and environment" },
25668
26000
  args: {},
@@ -25673,12 +26005,21 @@ var init_doctor = __esm({
25673
26005
  const checks = [
25674
26006
  { name: "Version", run: checkVersion },
25675
26007
  { name: "Node.js", run: checkNode },
26008
+ { name: "CPU", run: checkCPU },
26009
+ { name: "Memory", run: checkMemory },
26010
+ { name: "Disk", run: checkDisk }
26011
+ ];
26012
+ if (platform3() === "linux") {
26013
+ checks.push({ name: "/dev/shm", run: checkShm });
26014
+ }
26015
+ checks.push(
26016
+ { name: "Environment", run: checkEnvironment },
25676
26017
  { name: "FFmpeg", run: checkFFmpeg },
25677
26018
  { name: "FFprobe", run: checkFFprobe },
25678
26019
  { name: "Chrome", run: checkChrome },
25679
26020
  { name: "Docker", run: checkDocker },
25680
26021
  { name: "Docker running", run: checkDockerRunning }
25681
- ];
26022
+ );
25682
26023
  let allOk = true;
25683
26024
  for (const check of checks) {
25684
26025
  const result = await check.run();
@@ -25877,6 +26218,7 @@ init_version();
25877
26218
  init_config();
25878
26219
  init_client();
25879
26220
  init_events();
26221
+ init_system();
25880
26222
 
25881
26223
  // src/cli.ts
25882
26224
  init_updateCheck();