hyperframes 0.7.76 → 0.7.78

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.
package/dist/cli.js CHANGED
@@ -50,7 +50,7 @@ var VERSION;
50
50
  var init_version = __esm({
51
51
  "src/version.ts"() {
52
52
  "use strict";
53
- VERSION = true ? "0.7.76" : "0.0.0-dev";
53
+ VERSION = true ? "0.7.78" : "0.0.0-dev";
54
54
  }
55
55
  });
56
56
 
@@ -62886,6 +62886,15 @@ function memoryAdaptiveCacheBytesMb() {
62886
62886
  if (total <= LOW_MEMORY_TOTAL_MB_THRESHOLD) return 256;
62887
62887
  return DEFAULT_CONFIG2.frameDataUriCacheBytesLimitMb;
62888
62888
  }
62889
+ function isDrawElementPlatform(platform10) {
62890
+ return platform10 === "darwin" || platform10 === "win32";
62891
+ }
62892
+ function resolveDefaultDrawElement(args) {
62893
+ if (!args.useDrawElement) return false;
62894
+ if (args.explicitOptIn) return true;
62895
+ if (!isDrawElementPlatform(args.platform) || args.browserGpuMode === "software") return false;
62896
+ return args.workerEncode;
62897
+ }
62889
62898
  function resolveConfig(overrides) {
62890
62899
  const env = (key2) => process.env[key2];
62891
62900
  const envNum = (key2, fallback) => {
@@ -63014,12 +63023,13 @@ function resolveConfig(overrides) {
63014
63023
  ...overrides
63015
63024
  };
63016
63025
  const explicitDrawElementOptIn = env("PRODUCER_EXPERIMENTAL_FAST_CAPTURE") === "true" || overrides?.useDrawElement === true;
63017
- if (merged.useDrawElement && !explicitDrawElementOptIn && !(process.platform === "darwin" && merged.browserGpuMode !== "software")) {
63018
- merged.useDrawElement = false;
63019
- }
63020
- if (merged.useDrawElement && !explicitDrawElementOptIn && !merged.enableDrawElementWorkerEncode) {
63021
- merged.useDrawElement = false;
63022
- }
63026
+ merged.useDrawElement = resolveDefaultDrawElement({
63027
+ useDrawElement: merged.useDrawElement,
63028
+ explicitOptIn: explicitDrawElementOptIn,
63029
+ platform: process.platform,
63030
+ browserGpuMode: merged.browserGpuMode,
63031
+ workerEncode: merged.enableDrawElementWorkerEncode
63032
+ });
63023
63033
  const explicitForceScreenshotOptOut = env("PRODUCER_FORCE_SCREENSHOT") === "false" || overrides?.forceScreenshot === false;
63024
63034
  if (explicitForceScreenshotOptOut) {
63025
63035
  merged.forceScreenshotExplicitlyOptedOut = true;
@@ -64510,15 +64520,22 @@ function instrumentAcceleratedCanvases() {
64510
64520
  return ctx;
64511
64521
  };
64512
64522
  }
64513
- async function detectSwiftShader(page) {
64523
+ function classifyGpuRenderer(renderer) {
64524
+ if (!renderer) return void 0;
64525
+ const r2 = renderer.toLowerCase();
64526
+ const backend = r2.includes("swiftshader") ? "swiftshader" : r2.includes("metal") ? "metal" : r2.includes("direct3d11") || r2.includes("d3d11") ? "d3d11" : r2.includes("direct3d9") || r2.includes("d3d9") ? "d3d9" : r2.includes("vulkan") ? "vulkan" : r2.includes("opengl") || r2.includes("angle") ? "opengl" : "other";
64527
+ const vendor = r2.includes("apple") ? "apple" : r2.includes("nvidia") ? "nvidia" : r2.includes("amd") || r2.includes("radeon") ? "amd" : r2.includes("intel") ? "intel" : r2.includes("microsoft") ? "microsoft" : "other";
64528
+ return `${backend}/${vendor}`;
64529
+ }
64530
+ async function detectGpuBackend(page) {
64514
64531
  return page.evaluate(() => {
64515
64532
  const canvas = document.createElement("canvas");
64516
64533
  const gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
64517
- if (!gl) return false;
64534
+ if (!gl) return { isSwiftShader: false, renderer: null };
64518
64535
  const ext = gl.getExtension("WEBGL_debug_renderer_info");
64519
- if (!ext) return false;
64536
+ if (!ext) return { isSwiftShader: false, renderer: null };
64520
64537
  const renderer = gl.getParameter(ext.UNMASKED_RENDERER_WEBGL);
64521
- return renderer.toLowerCase().includes("swiftshader");
64538
+ return { isSwiftShader: renderer.toLowerCase().includes("swiftshader"), renderer };
64522
64539
  });
64523
64540
  }
64524
64541
  async function injectDrawElementCanvas(page, width, height) {
@@ -66248,7 +66265,9 @@ async function initDrawElementOrTransparentBackground(session, page, logInitPhas
66248
66265
  );
66249
66266
  }
66250
66267
  if (useDrawElement) {
66251
- session.isSwiftShader = await detectSwiftShader(page);
66268
+ const gpuBackend = await detectGpuBackend(page);
66269
+ session.isSwiftShader = gpuBackend.isSwiftShader;
66270
+ session.gpuRenderer = classifyGpuRenderer(gpuBackend.renderer);
66252
66271
  const transparent = session.options.format === "png";
66253
66272
  async function routeToFallback() {
66254
66273
  session.captureMode = session.launchCaptureMode;
@@ -67960,6 +67979,7 @@ function getCapturePerfSummary(session) {
67960
67979
  beginFrameNoDamage: session.beginFrameNoDamageCount,
67961
67980
  beginFrameHasDamage: session.beginFrameHasDamageCount,
67962
67981
  captureMode: session.captureMode,
67982
+ gpuRenderer: session.gpuRenderer,
67963
67983
  deGateReason: session.deGateReason,
67964
67984
  deFallbackTrigger: session.deFallbackTrigger,
67965
67985
  deWorkerEncode: session.workerEncodeEnabled ?? false,
@@ -88550,8 +88570,10 @@ __export(system_exports, {
88550
88570
  bytesToMb: () => bytesToMb,
88551
88571
  getAvailableMemoryMb: () => getAvailableMemoryMb,
88552
88572
  getFreeDiskMb: () => getFreeDiskMb,
88573
+ getPowerState: () => getPowerState,
88553
88574
  getShmSizeMb: () => getShmSizeMb,
88554
- getSystemMeta: () => getSystemMeta
88575
+ getSystemMeta: () => getSystemMeta,
88576
+ parsePmsetPowerSource: () => parsePmsetPowerSource
88555
88577
  });
88556
88578
  import { cpus as cpus2, freemem as freemem2, platform as platform3, release as release3 } from "os";
88557
88579
  import { existsSync as existsSync20, readFileSync as readFileSync10, statfsSync } from "fs";
@@ -88626,6 +88648,31 @@ function getFreeDiskMb(path2 = ".") {
88626
88648
  return null;
88627
88649
  }
88628
88650
  }
88651
+ function parsePmsetPowerSource(raw) {
88652
+ const m2 = raw.match(/Now drawing from '([^']+)'/);
88653
+ if (!m2) return null;
88654
+ return m2[1] === "Battery Power";
88655
+ }
88656
+ function getPowerState() {
88657
+ if (platform3() !== "darwin") {
88658
+ return { on_battery: null, low_power_mode: null };
88659
+ }
88660
+ let on_battery = null;
88661
+ let low_power_mode = null;
88662
+ try {
88663
+ on_battery = parsePmsetPowerSource(
88664
+ execSync2("pmset -g batt", { encoding: "utf-8", timeout: 2e3 })
88665
+ );
88666
+ } catch {
88667
+ }
88668
+ try {
88669
+ const raw = execSync2("pmset -g", { encoding: "utf-8", timeout: 2e3 });
88670
+ const m2 = raw.match(/lowpowermode\s+(\d)/);
88671
+ if (m2) low_power_mode = m2[1] === "1";
88672
+ } catch {
88673
+ }
88674
+ return { on_battery, low_power_mode };
88675
+ }
88629
88676
  function getAvailableMemoryMb() {
88630
88677
  const fallback = bytesToMb(freemem2());
88631
88678
  if (platform3() === "darwin") {
@@ -88675,57 +88722,15 @@ var init_system = __esm({
88675
88722
  }
88676
88723
  });
88677
88724
 
88678
- // src/telemetry/client.ts
88725
+ // src/telemetry/transport.ts
88679
88726
  import { spawn as spawn5 } from "child_process";
88680
88727
  import { randomUUID as randomUUID3 } from "crypto";
88681
- function shouldTrack() {
88682
- if (telemetryEnabled !== null) return telemetryEnabled;
88683
- if (process.env["HYPERFRAMES_NO_TELEMETRY"] === "1" || process.env["DO_NOT_TRACK"] === "1") {
88684
- telemetryEnabled = false;
88685
- return false;
88686
- }
88687
- if (isDevMode()) {
88688
- telemetryEnabled = false;
88689
- return false;
88690
- }
88691
- if (!POSTHOG_API_KEY.startsWith("phc_")) {
88692
- telemetryEnabled = false;
88693
- return false;
88694
- }
88695
- const config = readConfig();
88696
- telemetryEnabled = config.telemetryEnabled;
88697
- return telemetryEnabled;
88698
- }
88699
- function trackEvent(event, properties = {}, distinctId) {
88700
- if (!shouldTrack()) return;
88701
- const sys = getSystemMeta();
88728
+ function enqueue(event, properties, distinctId) {
88702
88729
  eventQueue.push({
88703
88730
  uuid: randomUUID3(),
88704
88731
  event,
88705
88732
  distinctId,
88706
- properties: {
88707
- ...properties,
88708
- cli_version: VERSION,
88709
- os: process.platform,
88710
- arch: process.arch,
88711
- node_version: process.version,
88712
- os_release: sys.os_release,
88713
- cpu_count: sys.cpu_count,
88714
- cpu_model: sys.cpu_model ?? void 0,
88715
- cpu_speed: sys.cpu_speed ?? void 0,
88716
- memory_total_mb: sys.memory_total_mb,
88717
- is_docker: sys.is_docker,
88718
- is_ci: sys.is_ci,
88719
- ci_name: sys.ci_name ?? void 0,
88720
- is_wsl: sys.is_wsl,
88721
- is_tty: sys.is_tty,
88722
- sandbox_runtime: sys.sandbox_runtime ?? void 0,
88723
- agent_runtime: sys.agent_runtime ?? void 0,
88724
- // New-agent discovery signals — populated only when agent_runtime is null.
88725
- agent_hint: sys.agent_hint ?? void 0,
88726
- term_program: sys.term_program ?? void 0,
88727
- agent_env_hints: sys.agent_env_hints ?? void 0
88728
- },
88733
+ properties,
88729
88734
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
88730
88735
  });
88731
88736
  }
@@ -88778,6 +88783,68 @@ function flushSync() {
88778
88783
  } catch {
88779
88784
  }
88780
88785
  }
88786
+ var POSTHOG_API_KEY, POSTHOG_HOST, FLUSH_TIMEOUT_MS, eventQueue;
88787
+ var init_transport = __esm({
88788
+ "src/telemetry/transport.ts"() {
88789
+ "use strict";
88790
+ init_config();
88791
+ POSTHOG_API_KEY = "phc_zjjbX0PnWxERXrMHhkEJWj9A9BhGVLRReICgsfTMmpx";
88792
+ POSTHOG_HOST = "https://us.i.posthog.com";
88793
+ FLUSH_TIMEOUT_MS = 5e3;
88794
+ eventQueue = [];
88795
+ }
88796
+ });
88797
+
88798
+ // src/telemetry/client.ts
88799
+ function shouldTrack() {
88800
+ if (telemetryEnabled !== null) return telemetryEnabled;
88801
+ if (process.env["HYPERFRAMES_NO_TELEMETRY"] === "1" || process.env["DO_NOT_TRACK"] === "1") {
88802
+ telemetryEnabled = false;
88803
+ return false;
88804
+ }
88805
+ if (isDevMode()) {
88806
+ telemetryEnabled = false;
88807
+ return false;
88808
+ }
88809
+ if (!POSTHOG_API_KEY.startsWith("phc_")) {
88810
+ telemetryEnabled = false;
88811
+ return false;
88812
+ }
88813
+ const config = readConfig();
88814
+ telemetryEnabled = config.telemetryEnabled;
88815
+ return telemetryEnabled;
88816
+ }
88817
+ function trackEvent(event, properties = {}, distinctId) {
88818
+ if (!shouldTrack()) return;
88819
+ const sys = getSystemMeta();
88820
+ enqueue(
88821
+ event,
88822
+ {
88823
+ ...properties,
88824
+ cli_version: VERSION,
88825
+ os: process.platform,
88826
+ arch: process.arch,
88827
+ node_version: process.version,
88828
+ os_release: sys.os_release,
88829
+ cpu_count: sys.cpu_count,
88830
+ cpu_model: sys.cpu_model ?? void 0,
88831
+ cpu_speed: sys.cpu_speed ?? void 0,
88832
+ memory_total_mb: sys.memory_total_mb,
88833
+ is_docker: sys.is_docker,
88834
+ is_ci: sys.is_ci,
88835
+ ci_name: sys.ci_name ?? void 0,
88836
+ is_wsl: sys.is_wsl,
88837
+ is_tty: sys.is_tty,
88838
+ sandbox_runtime: sys.sandbox_runtime ?? void 0,
88839
+ agent_runtime: sys.agent_runtime ?? void 0,
88840
+ // New-agent discovery signals — populated only when agent_runtime is null.
88841
+ agent_hint: sys.agent_hint ?? void 0,
88842
+ term_program: sys.term_program ?? void 0,
88843
+ agent_env_hints: sys.agent_env_hints ?? void 0
88844
+ },
88845
+ distinctId
88846
+ );
88847
+ }
88781
88848
  function showTelemetryNotice() {
88782
88849
  if (!shouldTrack()) return false;
88783
88850
  const config = readConfig();
@@ -88795,7 +88862,7 @@ function showTelemetryNotice() {
88795
88862
  diag.notice();
88796
88863
  return true;
88797
88864
  }
88798
- var POSTHOG_API_KEY, POSTHOG_HOST, FLUSH_TIMEOUT_MS, eventQueue, telemetryEnabled;
88865
+ var telemetryEnabled;
88799
88866
  var init_client = __esm({
88800
88867
  "src/telemetry/client.ts"() {
88801
88868
  "use strict";
@@ -88805,10 +88872,8 @@ var init_client = __esm({
88805
88872
  init_diagnostics2();
88806
88873
  init_env();
88807
88874
  init_system();
88808
- POSTHOG_API_KEY = "phc_zjjbX0PnWxERXrMHhkEJWj9A9BhGVLRReICgsfTMmpx";
88809
- POSTHOG_HOST = "https://us.i.posthog.com";
88810
- FLUSH_TIMEOUT_MS = 5e3;
88811
- eventQueue = [];
88875
+ init_transport();
88876
+ init_transport();
88812
88877
  telemetryEnabled = null;
88813
88878
  }
88814
88879
  });
@@ -88837,6 +88902,14 @@ __export(events_exports, {
88837
88902
  trackSkillsInstallSkipped: () => trackSkillsInstallSkipped,
88838
88903
  trackTranscribeUnavailable: () => trackTranscribeUnavailable
88839
88904
  });
88905
+ function powerStateFields() {
88906
+ if (!shouldTrack()) return {};
88907
+ const power = getPowerState();
88908
+ return {
88909
+ on_battery: power.on_battery ?? void 0,
88910
+ low_power_mode: power.low_power_mode ?? void 0
88911
+ };
88912
+ }
88840
88913
  function runIdField(runId3) {
88841
88914
  return runId3 !== void 0 ? { run_id: runId3 } : {};
88842
88915
  }
@@ -88874,6 +88947,7 @@ function renderObservabilityEventProperties(props) {
88874
88947
  de_worker_inversion: props.captureDeWorkerInversion,
88875
88948
  de_pre_inversion_workers: props.captureDePreInversionWorkers,
88876
88949
  de_parallel_router: props.captureDeParallelRouter,
88950
+ gpu_renderer: props.captureDeGpuRenderer,
88877
88951
  de_pre_router_workers: props.captureDePreRouterWorkers,
88878
88952
  de_self_verify_fallback: props.captureDeSelfVerifyFallback,
88879
88953
  de_fallback_reason: props.captureDeFallbackReason,
@@ -88942,6 +89016,7 @@ function trackRenderComplete(props) {
88942
89016
  de_parallel_router: props.deParallelRouter,
88943
89017
  de_pre_router_workers: props.dePreRouterWorkers,
88944
89018
  de_gate_reason: props.deGateReason,
89019
+ gpu_renderer: props.gpuRenderer,
88945
89020
  de_worker_encode: props.deWorkerEncode,
88946
89021
  de_verify_armed: props.deVerifyArmed,
88947
89022
  de_verify_checked: props.deVerifyChecked,
@@ -88957,6 +89032,7 @@ function trackRenderComplete(props) {
88957
89032
  de_blank_recaptures: props.deBlankRecaptures,
88958
89033
  de_boundary_frames: props.deBoundaryFrames,
88959
89034
  de_ncpr_fallbacks: props.deNcprFallbacks,
89035
+ ...powerStateFields(),
88960
89036
  source: props.source ?? "cli",
88961
89037
  composition_duration_ms: props.compositionDurationMs,
88962
89038
  composition_width: props.compositionWidth,
@@ -89009,6 +89085,11 @@ function trackRenderError(props) {
89009
89085
  elapsed_ms: props.elapsedMs,
89010
89086
  peak_memory_mb: props.peakMemoryMb,
89011
89087
  memory_free_mb: props.memoryFreeMb,
89088
+ ...powerStateFields(),
89089
+ // gpu_renderer arrives via renderObservabilityEventProperties below:
89090
+ // on the failure path perfSummary is never built, so live capture
89091
+ // observability is the only source. Backend attribution matters MOST
89092
+ // here — a win32 D3D11 crash is what the rollout is watching for.
89012
89093
  ...renderObservabilityEventProperties(props)
89013
89094
  },
89014
89095
  props.distinctId
@@ -89121,11 +89202,10 @@ function trackSkillsInstallSkipped(props) {
89121
89202
  trackEvent("cli skill install skipped", { reason: props.reason });
89122
89203
  }
89123
89204
  function trackRenderFeedback(props) {
89124
- trackEvent("survey sent", {
89125
- $survey_id: "render_satisfaction",
89126
- $survey_response: props.rating,
89205
+ trackEvent("cli_render_feedback", {
89206
+ rating: props.rating,
89127
89207
  rating_scale: FEEDBACK_RATING_SCALE,
89128
- ...props.comment ? { $survey_response_2: props.comment } : {},
89208
+ ...props.comment ? { comment: props.comment } : {},
89129
89209
  ...props.renderDurationMs !== void 0 ? { render_duration_ms: props.renderDurationMs } : {},
89130
89210
  ...props.doctorSummary ? { doctor_summary: props.doctorSummary } : {},
89131
89211
  ...props.feedbackId ? { feedback_id: props.feedbackId } : {},
@@ -89176,6 +89256,7 @@ var init_events = __esm({
89176
89256
  init_feedbackRating();
89177
89257
  init_client();
89178
89258
  init_config();
89259
+ init_system();
89179
89260
  }
89180
89261
  });
89181
89262
 
@@ -92120,7 +92201,11 @@ function downloadFile(url, dest, options = {}) {
92120
92201
  const timeoutMs = options.timeoutMs ?? DEFAULT_DOWNLOAD_TIMEOUT_MS;
92121
92202
  return new Promise((resolve77, reject) => {
92122
92203
  const follow = (u) => {
92204
+ let activeResponse;
92205
+ let responsePipelineStarted = false;
92206
+ let requestError;
92123
92207
  const request = httpsGet(u, (res) => {
92208
+ activeResponse = res;
92124
92209
  if (res.statusCode === 301 || res.statusCode === 302) {
92125
92210
  const location = res.headers.location;
92126
92211
  if (location) {
@@ -92136,18 +92221,24 @@ function downloadFile(url, dest, options = {}) {
92136
92221
  return;
92137
92222
  }
92138
92223
  const file = createWriteStream2(tmp);
92224
+ responsePipelineStarted = true;
92139
92225
  pipeline2(res, file).then(() => {
92140
92226
  renameSync5(tmp, dest);
92141
92227
  resolve77();
92142
92228
  }).catch((err) => {
92143
92229
  removePartialFile(tmp);
92144
- reject(err);
92230
+ reject(requestError ?? err);
92145
92231
  });
92146
92232
  });
92147
92233
  request.setTimeout(timeoutMs, () => {
92148
92234
  request.destroy(new Error(`Download timed out after ${timeoutMs}ms`));
92149
92235
  });
92150
92236
  request.on("error", (err) => {
92237
+ if (responsePipelineStarted) {
92238
+ requestError = err;
92239
+ activeResponse?.destroy(err);
92240
+ return;
92241
+ }
92151
92242
  removePartialFile(tmp);
92152
92243
  reject(err);
92153
92244
  });
@@ -93044,6 +93135,25 @@ var init_normalize = __esm({
93044
93135
  }
93045
93136
  });
93046
93137
 
93138
+ // src/telemetry/skill.ts
93139
+ var skill_exports = {};
93140
+ __export(skill_exports, {
93141
+ SKILL_SLUG: () => SKILL_SLUG,
93142
+ normalizeSkillSlug: () => normalizeSkillSlug
93143
+ });
93144
+ function normalizeSkillSlug(raw) {
93145
+ if (typeof raw !== "string") return void 0;
93146
+ const slug = raw.trim();
93147
+ return SKILL_SLUG.test(slug) ? slug : void 0;
93148
+ }
93149
+ var SKILL_SLUG;
93150
+ var init_skill = __esm({
93151
+ "src/telemetry/skill.ts"() {
93152
+ "use strict";
93153
+ SKILL_SLUG = /^[a-z0-9][a-z0-9-]{0,63}$/;
93154
+ }
93155
+ });
93156
+
93047
93157
  // src/utils/projectConfig.ts
93048
93158
  var projectConfig_exports = {};
93049
93159
  __export(projectConfig_exports, {
@@ -93054,6 +93164,7 @@ __export(projectConfig_exports, {
93054
93164
  projectConfigPath: () => projectConfigPath,
93055
93165
  readProjectConfig: () => readProjectConfig,
93056
93166
  resolveAutoProxy: () => resolveAutoProxy,
93167
+ seedProjectAuthoringSkill: () => seedProjectAuthoringSkill,
93057
93168
  writeProjectConfig: () => writeProjectConfig
93058
93169
  });
93059
93170
  import { readFileSync as readFileSync16, writeFileSync as writeFileSync10 } from "fs";
@@ -93081,7 +93192,10 @@ function normalizeConfig(partial) {
93081
93192
  },
93082
93193
  media: {
93083
93194
  autoProxy: typeof partial.media?.autoProxy === "boolean" ? partial.media.autoProxy : DEFAULT_PROJECT_CONFIG.media?.autoProxy
93084
- }
93195
+ },
93196
+ // Slug-gate on read so a hand-edited or corrupt value never reaches the
93197
+ // telemetry stream; an invalid slug simply drops the attribution.
93198
+ authoringSkill: normalizeSkillSlug(partial.authoringSkill)
93085
93199
  };
93086
93200
  }
93087
93201
  function writeProjectConfig(projectDir, config = DEFAULT_PROJECT_CONFIG) {
@@ -93097,11 +93211,44 @@ function resolveAutoProxy(projectDir, flagValue) {
93097
93211
  }
93098
93212
  return loadProjectConfig(projectDir).media?.autoProxy ?? true;
93099
93213
  }
93214
+ function isJsonObject(value) {
93215
+ return typeof value === "object" && value !== null && !Array.isArray(value);
93216
+ }
93217
+ function isFileNotFound(error) {
93218
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
93219
+ }
93220
+ function seedProjectAuthoringSkill(projectDir, rawSkill) {
93221
+ const skill = normalizeSkillSlug(rawSkill);
93222
+ if (!skill) return;
93223
+ const path2 = projectConfigPath(projectDir);
93224
+ let text2;
93225
+ try {
93226
+ text2 = readFileSync16(path2, "utf-8");
93227
+ } catch (error) {
93228
+ if (isFileNotFound(error)) {
93229
+ try {
93230
+ writeProjectConfig(projectDir, { ...DEFAULT_PROJECT_CONFIG, authoringSkill: skill });
93231
+ } catch {
93232
+ }
93233
+ }
93234
+ return;
93235
+ }
93236
+ try {
93237
+ const parsed = JSON.parse(text2);
93238
+ if (!isJsonObject(parsed)) return;
93239
+ if (normalizeSkillSlug(parsed.authoringSkill)) return;
93240
+ parsed.authoringSkill = skill;
93241
+ const indent = /\n([ \t]+)"/.exec(text2)?.[1] ?? " ";
93242
+ writeFileSync10(path2, JSON.stringify(parsed, null, indent) + "\n", "utf-8");
93243
+ } catch {
93244
+ }
93245
+ }
93100
93246
  var PROJECT_CONFIG_FILENAME, PROJECT_CONFIG_SCHEMA_URL, DEFAULT_PROJECT_CONFIG;
93101
93247
  var init_projectConfig = __esm({
93102
93248
  "src/utils/projectConfig.ts"() {
93103
93249
  "use strict";
93104
93250
  init_registry2();
93251
+ init_skill();
93105
93252
  PROJECT_CONFIG_FILENAME = "hyperframes.json";
93106
93253
  PROJECT_CONFIG_SCHEMA_URL = "https://hyperframes.heygen.com/schema/hyperframes.json";
93107
93254
  DEFAULT_PROJECT_CONFIG = {
@@ -95843,6 +95990,7 @@ function renderObservabilityTelemetryPayload(observability) {
95843
95990
  captureDeWorkerInversion: capture2.deWorkerInversion,
95844
95991
  captureDePreInversionWorkers: capture2.dePreInversionWorkers,
95845
95992
  captureDeParallelRouter: capture2.deParallelRouter,
95993
+ captureDeGpuRenderer: capture2.deGpuRenderer,
95846
95994
  captureDePreRouterWorkers: capture2.dePreRouterWorkers,
95847
95995
  captureDeSelfVerifyFallback: capture2.deSelfVerifyFallback,
95848
95996
  captureDeFallbackReason: capture2.deFallbackReason,
@@ -105263,6 +105411,14 @@ function bakeVisibilityOnDelete(document2, anim) {
105263
105411
  } catch {
105264
105412
  }
105265
105413
  }
105414
+ function resolveReplacementEaseEach(scriptText, request) {
105415
+ if (request.easeEach !== void 0) return request.easeEach;
105416
+ const original = parseGsapScriptAcorn2(scriptText).animations.find(
105417
+ (animation) => animation.id === request.animationId
105418
+ );
105419
+ if (!original?.arcPath?.enabled) return void 0;
105420
+ return original?.keyframes?.easeEach ?? original?.ease;
105421
+ }
105266
105422
  async function executeGsapMutation(body, block, respond2, writer) {
105267
105423
  if (writer === "recast") {
105268
105424
  return executeGsapMutationRecast(body, block, respond2);
@@ -105617,7 +105773,8 @@ function executeGsapMutationAcorn(body, block, respond2) {
105617
105773
  body.position,
105618
105774
  body.duration,
105619
105775
  body.keyframes,
105620
- body.ease
105776
+ body.ease,
105777
+ resolveReplacementEaseEach(block.scriptText, body)
105621
105778
  );
105622
105779
  return added.script;
105623
105780
  }
@@ -105947,7 +106104,8 @@ async function executeGsapMutationRecast(body, block, respond2) {
105947
106104
  body.position,
105948
106105
  body.duration,
105949
106106
  body.keyframes,
105950
- body.ease
106107
+ body.ease,
106108
+ resolveReplacementEaseEach(block.scriptText, body)
105951
106109
  );
105952
106110
  return added.script;
105953
106111
  }
@@ -110190,6 +110348,9 @@ function aggregateDrawElement(perfs, de2) {
110190
110348
  const gateReasons = [
110191
110349
  ...new Set(perfs.map((p2) => p2.deGateReason).filter((r2) => !!r2))
110192
110350
  ].sort();
110351
+ const gpuRenderers = [
110352
+ ...new Set(perfs.map((p2) => p2.gpuRenderer).filter((r2) => !!r2))
110353
+ ].sort();
110193
110354
  const drain = de2.drainStats;
110194
110355
  return {
110195
110356
  mode: modes.join("|") || "unknown",
@@ -110200,6 +110361,7 @@ function aggregateDrawElement(perfs, de2) {
110200
110361
  parallelRouter: de2.parallelRouter ?? "none",
110201
110362
  preRouterWorkers: de2.preRouterWorkers,
110202
110363
  gateReason: gateReasons.length > 0 ? gateReasons.join("|") : void 0,
110364
+ gpuRenderer: gpuRenderers.length > 0 ? gpuRenderers.join("|") : void 0,
110203
110365
  workerEncode: perfs.some((p2) => p2.deWorkerEncode),
110204
110366
  verifyArmed: perfs.reduce((sum, p2) => sum + (p2.deVerifyArmed ?? 0), 0),
110205
110367
  verifyChecked: drain?.verifyChecked ?? 0,
@@ -117386,6 +117548,10 @@ function resolveVideoExtractionPolicy(env = process.env) {
117386
117548
  const maxTransientRetries = failureMode !== "off" && env.HF_VIDEO_EXTRACTION_MAX_RETRIES?.trim() === "1" ? 1 : 0;
117387
117549
  return { failureMode, maxTransientRetries };
117388
117550
  }
117551
+ function assertVideoExtractionSucceeded(result) {
117552
+ const error = buildVideoExtractionStageError(result);
117553
+ if (error) throw error;
117554
+ }
117389
117555
  function buildVideoExtractionStageError(result) {
117390
117556
  if (result.success && result.errors.length === 0) return null;
117391
117557
  const counts = /* @__PURE__ */ new Map();
@@ -121298,7 +121464,7 @@ function resolveInversionRetryPlan(args) {
121298
121464
  };
121299
121465
  }
121300
121466
  function shouldPreferParallelDrawElement(args) {
121301
- return args.routerEnabled && args.workerCount > 1 && typeof args.requestedWorkers !== "number" && args.useDrawElement && !args.deCompileGate && !args.forceScreenshot && args.outputFormat === "mp4" && args.minFrames > 0 && args.totalFrames >= args.minFrames && !args.layeredOrEffectRoute && !args.supersampling && !args.probeDeGated && !args.experimentalParallelDeOptIn && // RAM floor: routed parallel DE runs 3 concurrent hardware-GPU Chrome
121467
+ return args.routerEnabled && args.parallelStreamingAvailable && args.workerCount > 1 && typeof args.requestedWorkers !== "number" && args.useDrawElement && !args.deCompileGate && !args.forceScreenshot && args.outputFormat === "mp4" && args.minFrames > 0 && args.totalFrames >= args.minFrames && !args.layeredOrEffectRoute && !args.supersampling && !args.probeDeGated && !args.experimentalParallelDeOptIn && // RAM floor: routed parallel DE runs 3 concurrent hardware-GPU Chrome
121302
121468
  // instances. On a 16 GB machine that produced vertical black slabs in the
121303
121469
  // final MP4 (wild report, CLI 0.7.52) — compositor tiles evicted under
121304
121470
  // GPU/memory pressure, and sampled self-verify can miss partial-frame
@@ -121892,8 +122058,8 @@ async function executeRenderPipeline(input2) {
121892
122058
  });
121893
122059
  const deParallelRouterEnabled = process.env.HF_DE_PARALLEL_ROUTER === "true";
121894
122060
  const deParallelMinFramesRaw = process.env.HF_DE_PARALLEL_MIN_FRAMES;
121895
- const deParallelMinFramesNum = deParallelMinFramesRaw === void 0 || deParallelMinFramesRaw.trim() === "" ? 2e3 : Number(deParallelMinFramesRaw);
121896
- const deParallelMinFrames = Number.isFinite(deParallelMinFramesNum) ? deParallelMinFramesNum : 2e3;
122061
+ const deParallelMinFramesNum = deParallelMinFramesRaw === void 0 || deParallelMinFramesRaw.trim() === "" ? 700 : Number(deParallelMinFramesRaw);
122062
+ const deParallelMinFrames = Number.isFinite(deParallelMinFramesNum) ? deParallelMinFramesNum : 700;
121897
122063
  const deParallelMinMemRaw = process.env.HF_DE_PARALLEL_MIN_MEM_MB;
121898
122064
  const deParallelMinMemNum = deParallelMinMemRaw === void 0 || deParallelMinMemRaw.trim() === "" ? 24576 : Number(deParallelMinMemRaw);
121899
122065
  const deParallelMinMemoryMb = Number.isFinite(deParallelMinMemNum) ? deParallelMinMemNum : 24576;
@@ -121911,6 +122077,15 @@ async function executeRenderPipeline(input2) {
121911
122077
  probeDeGated: probeSession !== null && probeSession.captureMode !== "drawelement" && !probeSession.deInitDeferred,
121912
122078
  experimentalParallelDeOptIn: process.env.PRODUCER_EXPERIMENTAL_FAST_CAPTURE === "true" || process.env.HF_DE_PARALLEL_STREAM === "true",
121913
122079
  routerEnabled: deParallelRouterEnabled,
122080
+ // Router pins 3 workers for the streaming path; don't pin when the
122081
+ // duration cap (or any other streaming gate) would turn that path off.
122082
+ parallelStreamingAvailable: shouldUseStreamingEncode(
122083
+ cfg,
122084
+ outputFormat,
122085
+ 3,
122086
+ job.duration,
122087
+ true
122088
+ ),
121914
122089
  totalMemoryMb: Math.round(totalmem2() / (1024 * 1024)),
121915
122090
  minMemoryMb: deParallelMinMemoryMb
121916
122091
  });
@@ -122034,7 +122209,12 @@ async function executeRenderPipeline(input2) {
122034
122209
  // to 3 workers regardless of calibration is the leading suspect for
122035
122210
  // any resource-pressure failure unique to this cohort.
122036
122211
  dePreInversionWorkers: deWorkerInversion ? preRoutingWorkerCount : void 0,
122037
- dePreRouterWorkers: deParallelRouter ? preRoutingWorkerCount : void 0
122212
+ dePreRouterWorkers: deParallelRouter ? preRoutingWorkerCount : void 0,
122213
+ // Same rationale as the counters above: carried on live capture
122214
+ // observability, not only the success-path perfSummary, so a crash /
122215
+ // OOM / timeout still reports which GPU backend it happened on. That
122216
+ // is the cohort the win32 D3D11 rollout most needs to attribute.
122217
+ deGpuRenderer: probeSession?.gpuRenderer
122038
122218
  });
122039
122219
  observability.checkpoint("worker_resolution", "resolved", {
122040
122220
  workerCount,
@@ -124157,6 +124337,7 @@ var init_server = __esm({
124157
124337
  init_renderRequest();
124158
124338
  DEFAULT_SERVER_FPS = { num: 30, den: 1 };
124159
124339
  SAFE_RENDER_ERROR_CODES = /* @__PURE__ */ new Set([
124340
+ "INVALID_VIDEO_METADATA",
124160
124341
  "VIDEO_SOURCE_UNRENDERABLE",
124161
124342
  "VIDEO_EXTRACTION_FAILED"
124162
124343
  ]);
@@ -124541,6 +124722,154 @@ import { dirname as dirname28, join as join63 } from "path";
124541
124722
  import { existsSync as existsSync56, readFileSync as readFileSync33 } from "fs";
124542
124723
  import { fileURLToPath as fileURLToPath7 } from "url";
124543
124724
  import { promisify as promisify4 } from "util";
124725
+ function metadataError(field, expectation) {
124726
+ throw new PlanVideosMetadataError(`${field} ${expectation}`);
124727
+ }
124728
+ function readRecord(value, field) {
124729
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
124730
+ metadataError(field, "must be an object");
124731
+ }
124732
+ return value;
124733
+ }
124734
+ function readNonEmptyString(value, field) {
124735
+ if (typeof value !== "string" || value.length === 0) {
124736
+ metadataError(field, "must be a non-empty string");
124737
+ }
124738
+ return value;
124739
+ }
124740
+ function readString(value, field) {
124741
+ if (typeof value !== "string") {
124742
+ metadataError(field, "must be a string");
124743
+ }
124744
+ return value;
124745
+ }
124746
+ function readFiniteNumber(value, field) {
124747
+ if (typeof value !== "number" || !Number.isFinite(value)) {
124748
+ metadataError(field, "must be a finite number");
124749
+ }
124750
+ return value;
124751
+ }
124752
+ function readBoolean(value, field) {
124753
+ if (typeof value !== "boolean") {
124754
+ metadataError(field, "must be boolean");
124755
+ }
124756
+ return value;
124757
+ }
124758
+ function readPositiveInteger(value, field) {
124759
+ if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
124760
+ metadataError(field, "must be a positive integer");
124761
+ }
124762
+ return value;
124763
+ }
124764
+ function readNonNegativeInteger(value, field) {
124765
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
124766
+ metadataError(field, "must be a non-negative integer");
124767
+ }
124768
+ return value;
124769
+ }
124770
+ function readVideoMetadata(value, field) {
124771
+ const record = readRecord(value, field);
124772
+ let colorSpace = null;
124773
+ if (record.colorSpace !== null) {
124774
+ const color = readRecord(record.colorSpace, `${field}.colorSpace`);
124775
+ colorSpace = {
124776
+ colorTransfer: readString(color.colorTransfer, `${field}.colorSpace.colorTransfer`),
124777
+ colorPrimaries: readString(color.colorPrimaries, `${field}.colorSpace.colorPrimaries`),
124778
+ colorSpace: readString(color.colorSpace, `${field}.colorSpace.colorSpace`)
124779
+ };
124780
+ }
124781
+ return {
124782
+ durationSeconds: readFiniteNumber(record.durationSeconds, `${field}.durationSeconds`),
124783
+ videoStreamDurationSeconds: readFiniteNumber(
124784
+ record.videoStreamDurationSeconds,
124785
+ `${field}.videoStreamDurationSeconds`
124786
+ ),
124787
+ width: readPositiveInteger(record.width, `${field}.width`),
124788
+ height: readPositiveInteger(record.height, `${field}.height`),
124789
+ fps: readFiniteNumber(record.fps, `${field}.fps`),
124790
+ videoCodec: readNonEmptyString(record.videoCodec, `${field}.videoCodec`),
124791
+ hasAudio: readBoolean(record.hasAudio, `${field}.hasAudio`),
124792
+ isVFR: readBoolean(record.isVFR, `${field}.isVFR`),
124793
+ hasAlpha: readBoolean(record.hasAlpha, `${field}.hasAlpha`),
124794
+ colorSpace
124795
+ };
124796
+ }
124797
+ function parsePlanVideosJson(value) {
124798
+ const record = readRecord(value, "meta/videos.json");
124799
+ if (!Array.isArray(record.videos) || !Array.isArray(record.extracted)) {
124800
+ metadataError("meta/videos.json", "must contain videos and extracted arrays");
124801
+ }
124802
+ const videos = record.videos.map((value2, index) => {
124803
+ const field = `meta/videos.json.videos[${index}]`;
124804
+ const video = readRecord(value2, field);
124805
+ return {
124806
+ id: readNonEmptyString(video.id, `${field}.id`),
124807
+ src: readNonEmptyString(video.src, `${field}.src`),
124808
+ start: readFiniteNumber(video.start, `${field}.start`),
124809
+ end: readFiniteNumber(video.end, `${field}.end`),
124810
+ mediaStart: readFiniteNumber(video.mediaStart, `${field}.mediaStart`),
124811
+ loop: readBoolean(video.loop, `${field}.loop`),
124812
+ hasAudio: readBoolean(video.hasAudio, `${field}.hasAudio`)
124813
+ };
124814
+ });
124815
+ const extracted = record.extracted.map((value2, index) => {
124816
+ const field = `meta/videos.json.extracted[${index}]`;
124817
+ const entry = readRecord(value2, field);
124818
+ return {
124819
+ videoId: readNonEmptyString(entry.videoId, `${field}.videoId`),
124820
+ srcPath: readNonEmptyString(entry.srcPath, `${field}.srcPath`),
124821
+ framePattern: readNonEmptyString(entry.framePattern, `${field}.framePattern`),
124822
+ fps: readFiniteNumber(entry.fps, `${field}.fps`),
124823
+ totalFrames: readNonNegativeInteger(entry.totalFrames, `${field}.totalFrames`),
124824
+ metadata: readVideoMetadata(entry.metadata, `${field}.metadata`)
124825
+ };
124826
+ });
124827
+ const videoIds = /* @__PURE__ */ new Set();
124828
+ for (const video of videos) {
124829
+ if (videoIds.has(video.id)) {
124830
+ metadataError("meta/videos.json.videos", `contains duplicate id ${JSON.stringify(video.id)}`);
124831
+ }
124832
+ videoIds.add(video.id);
124833
+ }
124834
+ const extractedIds = /* @__PURE__ */ new Set();
124835
+ for (const entry of extracted) {
124836
+ if (extractedIds.has(entry.videoId)) {
124837
+ metadataError(
124838
+ "meta/videos.json.extracted",
124839
+ `contains duplicate videoId ${JSON.stringify(entry.videoId)}`
124840
+ );
124841
+ }
124842
+ extractedIds.add(entry.videoId);
124843
+ if (!videoIds.has(entry.videoId)) {
124844
+ metadataError(
124845
+ "meta/videos.json.extracted",
124846
+ `references undeclared video ${JSON.stringify(entry.videoId)}`
124847
+ );
124848
+ }
124849
+ }
124850
+ for (const video of videos) {
124851
+ if (!extractedIds.has(video.id)) {
124852
+ metadataError(
124853
+ "meta/videos.json.extracted",
124854
+ `is missing declared video ${JSON.stringify(video.id)}`
124855
+ );
124856
+ }
124857
+ }
124858
+ return { videos, extracted };
124859
+ }
124860
+ function buildPlanVideosJson(input2) {
124861
+ const videos = input2.videos.map((video, index) => {
124862
+ if (Number.isFinite(video.end)) return { ...video };
124863
+ if (!Number.isFinite(input2.compositionEnd) || input2.compositionEnd <= 0 || input2.compositionEnd <= video.start) {
124864
+ metadataError(
124865
+ `meta/videos.json.videos[${index}].end`,
124866
+ "cannot be resolved without a finite composition end after its start"
124867
+ );
124868
+ }
124869
+ return { ...video, end: input2.compositionEnd };
124870
+ });
124871
+ return parsePlanVideosJson({ videos, extracted: input2.extracted });
124872
+ }
124544
124873
  async function readFfmpegVersion() {
124545
124874
  if (cachedFfmpegVersion !== null) return cachedFfmpegVersion;
124546
124875
  const { stdout: stdout2 } = await execFile6("ffmpeg", ["-version"], { maxBuffer: 1024 * 1024 });
@@ -124597,7 +124926,7 @@ function readProducerVersion() {
124597
124926
  cachedProducerVersion = "0.0.0-unknown";
124598
124927
  return cachedProducerVersion;
124599
124928
  }
124600
- var PLAN_VIDEOS_META_RELATIVE_PATH, PLAN_AUDIO_RELATIVE_PATH, execFile6, cachedFfmpegVersion, cachedProducerVersion;
124929
+ var PLAN_VIDEOS_META_RELATIVE_PATH, PLAN_AUDIO_RELATIVE_PATH, INVALID_VIDEO_METADATA, PlanVideosMetadataError, execFile6, cachedFfmpegVersion, cachedProducerVersion;
124601
124930
  var init_shared2 = __esm({
124602
124931
  "../producer/src/services/distributed/shared.ts"() {
124603
124932
  "use strict";
@@ -124605,6 +124934,16 @@ var init_shared2 = __esm({
124605
124934
  init_logger();
124606
124935
  PLAN_VIDEOS_META_RELATIVE_PATH = "meta/videos.json";
124607
124936
  PLAN_AUDIO_RELATIVE_PATH = "audio.aac";
124937
+ INVALID_VIDEO_METADATA = "INVALID_VIDEO_METADATA";
124938
+ PlanVideosMetadataError = class extends Error {
124939
+ // Read by cloud adapters across the package boundary to classify retries.
124940
+ // fallow-ignore-next-line unused-class-member
124941
+ code = INVALID_VIDEO_METADATA;
124942
+ constructor(message) {
124943
+ super(message);
124944
+ this.name = "PlanVideosMetadataError";
124945
+ }
124946
+ };
124608
124947
  execFile6 = promisify4(execFileCallback);
124609
124948
  cachedFfmpegVersion = null;
124610
124949
  cachedProducerVersion = null;
@@ -125080,6 +125419,9 @@ async function plan(projectDir, config, planDir) {
125080
125419
  materializeSymlinks: true
125081
125420
  });
125082
125421
  if (extractResult.failureToEnforce) throw extractResult.failureToEnforce;
125422
+ if (extractResult.extractionResult) {
125423
+ assertVideoExtractionSucceeded(extractResult.extractionResult);
125424
+ }
125083
125425
  const audioResult = await runAudioStage({
125084
125426
  projectDir,
125085
125427
  workDir,
@@ -125102,8 +125444,9 @@ async function plan(projectDir, config, planDir) {
125102
125444
  }
125103
125445
  if (existsSync57(finalCompiledDir)) rmSync18(finalCompiledDir, { recursive: true, force: true });
125104
125446
  renameSync12(compiledDir, finalCompiledDir);
125105
- const planVideosJson = {
125447
+ const planVideosJson = buildPlanVideosJson({
125106
125448
  videos: composition.videos,
125449
+ compositionEnd: job.duration ?? Number.NaN,
125107
125450
  extracted: (extractResult.extractionResult?.extracted ?? []).map((ext) => ({
125108
125451
  videoId: ext.videoId,
125109
125452
  srcPath: ext.srcPath,
@@ -125112,7 +125455,7 @@ async function plan(projectDir, config, planDir) {
125112
125455
  totalFrames: ext.totalFrames,
125113
125456
  metadata: ext.metadata
125114
125457
  }))
125115
- };
125458
+ });
125116
125459
  mkdirSync29(join65(planDir, "meta"), { recursive: true });
125117
125460
  writeFileSync20(
125118
125461
  join65(planDir, PLAN_VIDEOS_META_RELATIVE_PATH),
@@ -125598,90 +125941,20 @@ function listVideoFramePaths(planV1Dir, videos) {
125598
125941
  };
125599
125942
  });
125600
125943
  }
125601
- function readFiniteNumber(value, field) {
125602
- if (typeof value !== "number" || !Number.isFinite(value)) {
125603
- throw new PlanV2IntegrityError(`${field} must be a finite number`);
125604
- }
125605
- return value;
125606
- }
125607
- function readBoolean(value, field) {
125608
- if (typeof value !== "boolean") {
125609
- throw new PlanV2IntegrityError(`${field} must be boolean`);
125610
- }
125611
- return value;
125612
- }
125613
- function readVideoMetadata(value, field) {
125614
- if (!isRecord7(value)) {
125615
- throw new PlanV2IntegrityError(`${field} must be an object`);
125616
- }
125617
- let colorSpace = null;
125618
- if (value.colorSpace !== null) {
125619
- if (!isRecord7(value.colorSpace)) {
125620
- throw new PlanV2IntegrityError(`${field}.colorSpace must be an object or null`);
125944
+ function parsePlanVideosJson2(value) {
125945
+ try {
125946
+ return parsePlanVideosJson(value);
125947
+ } catch (err) {
125948
+ if (err instanceof PlanVideosMetadataError) {
125949
+ throw new PlanV2IntegrityError(err.message);
125621
125950
  }
125622
- colorSpace = {
125623
- colorTransfer: readColorComponent(
125624
- value.colorSpace.colorTransfer,
125625
- `${field}.colorSpace.colorTransfer`
125626
- ),
125627
- colorPrimaries: readColorComponent(
125628
- value.colorSpace.colorPrimaries,
125629
- `${field}.colorSpace.colorPrimaries`
125630
- ),
125631
- colorSpace: readColorComponent(value.colorSpace.colorSpace, `${field}.colorSpace.colorSpace`)
125632
- };
125633
- }
125634
- return {
125635
- durationSeconds: readFiniteNumber(value.durationSeconds, `${field}.durationSeconds`),
125636
- videoStreamDurationSeconds: readFiniteNumber(
125637
- value.videoStreamDurationSeconds,
125638
- `${field}.videoStreamDurationSeconds`
125639
- ),
125640
- width: readPositiveInteger(value.width, `${field}.width`),
125641
- height: readPositiveInteger(value.height, `${field}.height`),
125642
- fps: readFiniteNumber(value.fps, `${field}.fps`),
125643
- videoCodec: readString(value.videoCodec, `${field}.videoCodec`),
125644
- hasAudio: readBoolean(value.hasAudio, `${field}.hasAudio`),
125645
- isVFR: readBoolean(value.isVFR, `${field}.isVFR`),
125646
- hasAlpha: readBoolean(value.hasAlpha, `${field}.hasAlpha`),
125647
- colorSpace
125648
- };
125649
- }
125650
- function parsePlanVideosJson(value) {
125651
- if (!isRecord7(value) || !Array.isArray(value.videos) || !Array.isArray(value.extracted)) {
125652
- throw new PlanV2IntegrityError("meta/videos.json must contain videos and extracted arrays");
125951
+ throw err;
125653
125952
  }
125654
- const videos = value.videos.map((video, index) => {
125655
- const field = `meta/videos.json.videos[${index}]`;
125656
- if (!isRecord7(video)) throw new PlanV2IntegrityError(`${field} must be an object`);
125657
- return {
125658
- id: readString(video.id, `${field}.id`),
125659
- src: readString(video.src, `${field}.src`),
125660
- start: readFiniteNumber(video.start, `${field}.start`),
125661
- end: readFiniteNumber(video.end, `${field}.end`),
125662
- mediaStart: readFiniteNumber(video.mediaStart, `${field}.mediaStart`),
125663
- loop: readBoolean(video.loop, `${field}.loop`),
125664
- hasAudio: readBoolean(video.hasAudio, `${field}.hasAudio`)
125665
- };
125666
- });
125667
- const extracted = value.extracted.map((entry, index) => {
125668
- const field = `meta/videos.json.extracted[${index}]`;
125669
- if (!isRecord7(entry)) throw new PlanV2IntegrityError(`${field} must be an object`);
125670
- return {
125671
- videoId: readString(entry.videoId, `${field}.videoId`),
125672
- srcPath: readString(entry.srcPath, `${field}.srcPath`),
125673
- framePattern: readString(entry.framePattern, `${field}.framePattern`),
125674
- fps: readFiniteNumber(entry.fps, `${field}.fps`),
125675
- totalFrames: readNonNegativeInteger(entry.totalFrames, `${field}.totalFrames`),
125676
- metadata: readVideoMetadata(entry.metadata, `${field}.metadata`)
125677
- };
125678
- });
125679
- return { videos, extracted };
125680
125953
  }
125681
125954
  function materializeExtractedVideoDirectories(planDir) {
125682
125955
  const videosPath = join68(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
125683
125956
  if (!existsSync59(videosPath)) return;
125684
- const videos = parsePlanVideosJson(readJsonFile(videosPath, PLAN_VIDEOS_META_RELATIVE_PATH));
125957
+ const videos = parsePlanVideosJson2(readJsonFile(videosPath, PLAN_VIDEOS_META_RELATIVE_PATH));
125685
125958
  for (const video of videos.extracted) {
125686
125959
  mkdirSync31(resolveExtractedVideoOutputDir(planDir, video.videoId), { recursive: true });
125687
125960
  }
@@ -125694,9 +125967,9 @@ function parseChunkSlices(value) {
125694
125967
  return value.map((chunk, position) => {
125695
125968
  const field = `meta/chunks.json[${position}]`;
125696
125969
  if (!isRecord7(chunk)) throw new PlanV2IntegrityError(`${field} must be an object`);
125697
- const index = readNonNegativeInteger(chunk.index, `${field}.index`);
125698
- const startFrame = readNonNegativeInteger(chunk.startFrame, `${field}.startFrame`);
125699
- const endFrame = readPositiveInteger(chunk.endFrame, `${field}.endFrame`);
125970
+ const index = readNonNegativeInteger2(chunk.index, `${field}.index`);
125971
+ const startFrame = readNonNegativeInteger2(chunk.startFrame, `${field}.startFrame`);
125972
+ const endFrame = readPositiveInteger2(chunk.endFrame, `${field}.endFrame`);
125700
125973
  if (endFrame <= startFrame) {
125701
125974
  throw new PlanV2IntegrityError(`${field}.endFrame must be greater than startFrame`);
125702
125975
  }
@@ -125717,12 +125990,12 @@ function buildVideoChunkDependencies(planV1Dir, dimensions) {
125717
125990
  const chunksPath = join68(planV1Dir, "meta", "chunks.json");
125718
125991
  const videos = readJsonFile(videosPath, PLAN_VIDEOS_META_RELATIVE_PATH);
125719
125992
  const chunks = readJsonFile(chunksPath, "meta/chunks.json");
125720
- const parsedVideos = parsePlanVideosJson(videos);
125993
+ const parsedVideos = parsePlanVideosJson2(videos);
125721
125994
  const parsedChunks = parseChunkSlices(chunks);
125722
125995
  const extracted = listVideoFramePaths(planV1Dir, parsedVideos);
125723
125996
  const table = createFrameLookupTable(parsedVideos.videos, extracted);
125724
- const fpsNum = readPositiveInteger(dimensions.fpsNum, "dimensions.fpsNum");
125725
- const fpsDen = readPositiveInteger(dimensions.fpsDen, "dimensions.fpsDen");
125997
+ const fpsNum = readPositiveInteger2(dimensions.fpsNum, "dimensions.fpsNum");
125998
+ const fpsDen = readPositiveInteger2(dimensions.fpsDen, "dimensions.fpsDen");
125726
125999
  const mutable = /* @__PURE__ */ new Map();
125727
126000
  for (const chunk of parsedChunks) {
125728
126001
  for (let frame = chunk.startFrame; frame < chunk.endFrame; frame++) {
@@ -125811,14 +126084,14 @@ function buildPlanV2Publication(planV1Dir) {
125811
126084
  const base2 = {
125812
126085
  protocol: PLAN_PROTOCOL_V2,
125813
126086
  sourcePlanV1Hash,
125814
- chunkCount: readPositiveInteger(v1Plan.chunkCount, "chunkCount"),
125815
- totalFrames: readPositiveInteger(v1Plan.totalFrames, "totalFrames"),
126087
+ chunkCount: readPositiveInteger2(v1Plan.chunkCount, "chunkCount"),
126088
+ totalFrames: readPositiveInteger2(v1Plan.totalFrames, "totalFrames"),
125816
126089
  fps: readV1PlanFps(dimensions),
125817
- width: readPositiveInteger(dimensions.width, "dimensions.width"),
125818
- height: readPositiveInteger(dimensions.height, "dimensions.height"),
126090
+ width: readPositiveInteger2(dimensions.width, "dimensions.width"),
126091
+ height: readPositiveInteger2(dimensions.height, "dimensions.height"),
125819
126092
  format: readDistributedFormat(dimensions.format),
125820
- ffmpegVersion: readString(v1Plan.ffmpegVersion, "ffmpegVersion"),
125821
- producerVersion: readString(v1Plan.producerVersion, "producerVersion"),
126093
+ ffmpegVersion: readString2(v1Plan.ffmpegVersion, "ffmpegVersion"),
126094
+ producerVersion: readString2(v1Plan.producerVersion, "producerVersion"),
125822
126095
  limitations: { videoDependencyMode: videoDependencyPlan.mode },
125823
126096
  artifacts
125824
126097
  };
@@ -125921,25 +126194,19 @@ function resultFromManifest(planV2Dir, manifest) {
125921
126194
  limitations: manifest.limitations
125922
126195
  };
125923
126196
  }
125924
- function readString(value, field) {
126197
+ function readString2(value, field) {
125925
126198
  if (typeof value !== "string" || value.length === 0) {
125926
126199
  throw new PlanV2IntegrityError(`${field} must be a non-empty string`);
125927
126200
  }
125928
126201
  return value;
125929
126202
  }
125930
- function readColorComponent(value, field) {
125931
- if (typeof value !== "string") {
125932
- throw new PlanV2IntegrityError(`${field} must be a string`);
125933
- }
125934
- return value;
125935
- }
125936
- function readPositiveInteger(value, field) {
126203
+ function readPositiveInteger2(value, field) {
125937
126204
  if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
125938
126205
  throw new PlanV2IntegrityError(`${field} must be a positive integer`);
125939
126206
  }
125940
126207
  return value;
125941
126208
  }
125942
- function readNonNegativeInteger(value, field) {
126209
+ function readNonNegativeInteger2(value, field) {
125943
126210
  if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
125944
126211
  throw new PlanV2IntegrityError(`${field} must be a non-negative integer`);
125945
126212
  }
@@ -125952,7 +126219,7 @@ function readSupportedFps(value) {
125952
126219
  return value;
125953
126220
  }
125954
126221
  function readV1PlanFps(dimensions) {
125955
- const fpsDen = readPositiveInteger(dimensions.fpsDen, "dimensions.fpsDen");
126222
+ const fpsDen = readPositiveInteger2(dimensions.fpsDen, "dimensions.fpsDen");
125956
126223
  if (fpsDen !== 1) {
125957
126224
  throw new PlanV2IntegrityError("dimensions.fpsDen must be 1 for plan v2");
125958
126225
  }
@@ -125966,7 +126233,7 @@ function readDistributedFormat(value) {
125966
126233
  }
125967
126234
  function parseArtifact(value, index) {
125968
126235
  if (!isRecord7(value)) throw new PlanV2IntegrityError(`artifacts[${index}] must be an object`);
125969
- const path2 = readString(value.path, `artifacts[${index}].path`);
126236
+ const path2 = readString2(value.path, `artifacts[${index}].path`);
125970
126237
  assertSafeRelativePath(path2);
125971
126238
  if (!isSha256(value.sha256)) {
125972
126239
  throw new PlanV2IntegrityError(`artifacts[${index}].sha256 must be a lowercase sha256`);
@@ -126019,14 +126286,14 @@ function parsePlanV2Manifest(value) {
126019
126286
  protocol: PLAN_PROTOCOL_V2,
126020
126287
  planHash: value.planHash,
126021
126288
  sourcePlanV1Hash: value.sourcePlanV1Hash,
126022
- chunkCount: readPositiveInteger(value.chunkCount, "chunkCount"),
126023
- totalFrames: readPositiveInteger(value.totalFrames, "totalFrames"),
126289
+ chunkCount: readPositiveInteger2(value.chunkCount, "chunkCount"),
126290
+ totalFrames: readPositiveInteger2(value.totalFrames, "totalFrames"),
126024
126291
  fps: readSupportedFps(value.fps),
126025
- width: readPositiveInteger(value.width, "width"),
126026
- height: readPositiveInteger(value.height, "height"),
126292
+ width: readPositiveInteger2(value.width, "width"),
126293
+ height: readPositiveInteger2(value.height, "height"),
126027
126294
  format: readDistributedFormat(value.format),
126028
- ffmpegVersion: readString(value.ffmpegVersion, "ffmpegVersion"),
126029
- producerVersion: readString(value.producerVersion, "producerVersion"),
126295
+ ffmpegVersion: readString2(value.ffmpegVersion, "ffmpegVersion"),
126296
+ producerVersion: readString2(value.producerVersion, "producerVersion"),
126030
126297
  limitations: { videoDependencyMode: value.limitations.videoDependencyMode },
126031
126298
  artifacts
126032
126299
  };
@@ -126428,6 +126695,16 @@ var init_assemble = __esm({
126428
126695
  import { randomBytes as randomBytes2 } from "crypto";
126429
126696
  import { existsSync as existsSync61, mkdirSync as mkdirSync34, readFileSync as readFileSync36, readdirSync as readdirSync21, rmSync as rmSync23, writeFileSync as writeFileSync25 } from "fs";
126430
126697
  import { extname as extname15, join as join70 } from "path";
126698
+ function validatePlanVideosForChunk(value) {
126699
+ try {
126700
+ return parsePlanVideosJson(value);
126701
+ } catch (err) {
126702
+ throw new RenderChunkValidationError(
126703
+ INVALID_VIDEO_METADATA,
126704
+ `[renderChunk] invalid meta/videos.json: ${err instanceof Error ? err.message : String(err)}`
126705
+ );
126706
+ }
126707
+ }
126431
126708
  async function createVerifiedDistributedCaptureSession(serverUrl, framesDir, captureOptions, cfg, dependencies = distributedCaptureSessionDependencies) {
126432
126709
  const session = await dependencies.createCaptureSession(
126433
126710
  serverUrl,
@@ -126562,10 +126839,11 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
126562
126839
  let planVideos = null;
126563
126840
  if (existsSync61(videosJsonPath)) {
126564
126841
  try {
126565
- planVideos = JSON.parse(readFileSync36(videosJsonPath, "utf-8"));
126842
+ planVideos = validatePlanVideosForChunk(JSON.parse(readFileSync36(videosJsonPath, "utf-8")));
126566
126843
  } catch (err) {
126844
+ if (err instanceof RenderChunkValidationError) throw err;
126567
126845
  throw new RenderChunkValidationError(
126568
- MISSING_PLAN_ARTIFACT,
126846
+ INVALID_VIDEO_METADATA,
126569
126847
  `[renderChunk] failed to parse ${videosJsonPath}: ${err instanceof Error ? err.message : String(err)}`
126570
126848
  );
126571
126849
  }
@@ -127060,6 +127338,7 @@ var init_distributed = __esm({
127060
127338
  init_renderConfigValidation();
127061
127339
  init_projectHash();
127062
127340
  init_planProtocol();
127341
+ init_shared2();
127063
127342
  init_planValidation();
127064
127343
  }
127065
127344
  });
@@ -130086,7 +130365,7 @@ function applyResolutionPreset(destDir, resolution) {
130086
130365
  if (changed) writeFileSync27(file, html, "utf-8");
130087
130366
  }
130088
130367
  }
130089
- async function scaffoldProject(destDir, name, templateId, localVideoName, durationSeconds, tailwind = false, resolution) {
130368
+ async function scaffoldProject(destDir, name, templateId, localVideoName, durationSeconds, tailwind = false, resolution, authoringSkill) {
130090
130369
  mkdirSync38(destDir, { recursive: true });
130091
130370
  const templateDir = getStaticTemplateDir(templateId);
130092
130371
  if (existsSync67(join78(templateDir, "index.html"))) {
@@ -130112,7 +130391,12 @@ async function scaffoldProject(destDir, name, templateId, localVideoName, durati
130112
130391
  );
130113
130392
  if (!existsSync67(resolve39(destDir, "hyperframes.json"))) {
130114
130393
  const { writeProjectConfig: writeProjectConfig2, DEFAULT_PROJECT_CONFIG: DEFAULT_PROJECT_CONFIG2 } = await Promise.resolve().then(() => (init_projectConfig(), projectConfig_exports));
130115
- writeProjectConfig2(destDir, DEFAULT_PROJECT_CONFIG2);
130394
+ const { normalizeSkillSlug: normalizeSkillSlug2 } = await Promise.resolve().then(() => (init_skill(), skill_exports));
130395
+ const skill = normalizeSkillSlug2(authoringSkill);
130396
+ writeProjectConfig2(
130397
+ destDir,
130398
+ skill ? { ...DEFAULT_PROJECT_CONFIG2, authoringSkill: skill } : DEFAULT_PROJECT_CONFIG2
130399
+ );
130116
130400
  }
130117
130401
  writeDefaultPackageJson(destDir, name);
130118
130402
  const sharedDir = getSharedTemplateDir();
@@ -130261,6 +130545,10 @@ var init_init = __esm({
130261
130545
  resolution: {
130262
130546
  type: "string",
130263
130547
  description: "Canvas resolution preset: landscape (1920x1080), portrait (1080x1920), landscape-4k (3840x2160), portrait-4k (2160x3840), square (1080x1080), square-4k (2160x2160). Aliases: 1080p, 4k, uhd, 1080p-square, square-1080p, 4k-square. Default: keep template dimensions (typically 1920x1080)."
130548
+ },
130549
+ skill: {
130550
+ type: "string",
130551
+ description: "Owning authoring workflow slug (e.g. product-launch-video). Stamped into hyperframes.json so every render of this project is attributed to it on anonymous telemetry, without re-passing --skill on each render. Ignored unless it is a slug."
130264
130552
  }
130265
130553
  },
130266
130554
  async run({ args }) {
@@ -130395,7 +130683,8 @@ var init_init = __esm({
130395
130683
  localVideoName2,
130396
130684
  videoDuration2,
130397
130685
  tailwind,
130398
- resolutionPreset
130686
+ resolutionPreset,
130687
+ args.skill
130399
130688
  );
130400
130689
  } catch (err) {
130401
130690
  console.error(
@@ -130583,7 +130872,8 @@ var init_init = __esm({
130583
130872
  localVideoName,
130584
130873
  videoDuration,
130585
130874
  tailwind,
130586
- resolutionPreset
130875
+ resolutionPreset,
130876
+ args.skill
130587
130877
  );
130588
130878
  if (!isBundled) {
130589
130879
  spin.stop(c.success(`Downloaded ${templateId}`));
@@ -133785,20 +134075,6 @@ var init_renderArgs = __esm({
133785
134075
  }
133786
134076
  });
133787
134077
 
133788
- // src/telemetry/skill.ts
133789
- function normalizeSkillSlug(raw) {
133790
- if (typeof raw !== "string") return void 0;
133791
- const slug = raw.trim();
133792
- return SKILL_SLUG.test(slug) ? slug : void 0;
133793
- }
133794
- var SKILL_SLUG;
133795
- var init_skill = __esm({
133796
- "src/telemetry/skill.ts"() {
133797
- "use strict";
133798
- SKILL_SLUG = /^[a-z0-9][a-z0-9-]{0,63}$/;
133799
- }
133800
- });
133801
-
133802
134078
  // src/commands/render/plan.ts
133803
134079
  import { statSync as statSync27 } from "fs";
133804
134080
  import { dirname as dirname39, join as join85, resolve as resolve51 } from "path";
@@ -133848,8 +134124,9 @@ function createRenderPlan(args, now = /* @__PURE__ */ new Date()) {
133848
134124
  failUsage();
133849
134125
  }
133850
134126
  const quality = qualityRaw;
133851
- const authoringSkill = normalizeSkillSlug(args.skill);
133852
- const invalidAuthoringSkill = typeof args.skill === "string" && args.skill.trim() !== "" && !authoringSkill ? args.skill : void 0;
134127
+ const flagSkill = normalizeSkillSlug(args.skill);
134128
+ const authoringSkill = flagSkill ?? loadProjectConfig(project.dir).authoringSkill;
134129
+ const invalidAuthoringSkill = typeof args.skill === "string" && args.skill.trim() !== "" && !flagSkill ? args.skill : void 0;
133853
134130
  const formatRaw = args.format ?? "mp4";
133854
134131
  const format = parseRenderFormat(formatRaw);
133855
134132
  if (!format) {
@@ -134085,6 +134362,7 @@ var init_plan2 = __esm({
134085
134362
  init_project();
134086
134363
  init_renderArgs();
134087
134364
  init_skill();
134365
+ init_projectConfig();
134088
134366
  VALID_QUALITY = /* @__PURE__ */ new Set(["draft", "standard", "high"]);
134089
134367
  RENDER_FORMATS = ["mp4", "webm", "mov", "png-sequence", "gif"];
134090
134368
  VALID_FORMAT = new Set(RENDER_FORMATS);
@@ -135442,7 +135720,7 @@ __export(render_exports, {
135442
135720
  renderLocal: () => renderLocal,
135443
135721
  resolveBrowserGpuForCli: () => resolveBrowserGpuForCli
135444
135722
  });
135445
- import { mkdirSync as mkdirSync43, readdirSync as readdirSync27, readFileSync as readFileSync53, statSync as statSync28, writeFileSync as writeFileSync30, rmSync as rmSync26 } from "fs";
135723
+ import { mkdtempSync as mkdtempSync11, readdirSync as readdirSync27, readFileSync as readFileSync53, statSync as statSync28, writeFileSync as writeFileSync30, rmSync as rmSync26 } from "fs";
135446
135724
  import { freemem as freemem5, tmpdir as tmpdir12 } from "os";
135447
135725
  import { resolve as resolve54, dirname as dirname41, join as join87, basename as basename16 } from "path";
135448
135726
  import { execFileSync as execFileSync11, spawn as spawn14 } from "child_process";
@@ -135510,8 +135788,7 @@ function ensureDockerImage(version2, platform10, quiet) {
135510
135788
  }
135511
135789
  if (!quiet) console.log(c.dim(` Building Docker image: ${tag} (${platform10})...`));
135512
135790
  const dockerfilePath = resolveDockerfilePath();
135513
- const tmpDir = join87(tmpdir12(), `hyperframes-docker-${Date.now()}`);
135514
- mkdirSync43(tmpDir, { recursive: true });
135791
+ const tmpDir = mkdtempSync11(join87(tmpdir12(), "hyperframes-docker-"));
135515
135792
  writeFileSync30(join87(tmpDir, "Dockerfile"), readFileSync53(dockerfilePath));
135516
135793
  const targetArch = platform10 === "linux/arm64" ? "arm64" : "amd64";
135517
135794
  try {
@@ -136061,6 +136338,7 @@ function trackRenderMetrics(job, elapsedMs, options, docker) {
136061
136338
  deParallelRouter: perf?.drawElement?.parallelRouter,
136062
136339
  dePreRouterWorkers: perf?.drawElement?.preRouterWorkers,
136063
136340
  deGateReason: perf?.drawElement?.gateReason,
136341
+ gpuRenderer: perf?.drawElement?.gpuRenderer,
136064
136342
  deWorkerEncode: perf?.drawElement?.workerEncode,
136065
136343
  deVerifyArmed: perf?.drawElement?.verifyArmed,
136066
136344
  deVerifyChecked: perf?.drawElement?.verifyChecked,
@@ -136148,6 +136426,7 @@ var init_render = __esm({
136148
136426
  init_commandResult();
136149
136427
  init_dist();
136150
136428
  init_plan2();
136429
+ init_projectConfig();
136151
136430
  init_present2();
136152
136431
  init_execute();
136153
136432
  init_producer();
@@ -136403,6 +136682,7 @@ var init_render = __esm({
136403
136682
  // Keep the transport adapter thin: each phase has one ownership boundary.
136404
136683
  async run({ args }) {
136405
136684
  const plan2 = createRenderPlan(args);
136685
+ seedProjectAuthoringSkill(plan2.project.dir, args.skill);
136406
136686
  await presentRenderPlan(plan2);
136407
136687
  await executeRenderPlan(plan2, {
136408
136688
  renderDocker,
@@ -138086,7 +138366,7 @@ __export(validate_exports, {
138086
138366
  resolveNavigationTimeoutMs: () => resolveNavigationTimeoutMs,
138087
138367
  shouldIgnoreRequestFailure: () => shouldIgnoreRequestFailure
138088
138368
  });
138089
- import { existsSync as existsSync80, mkdtempSync as mkdtempSync11, readFileSync as readFileSync56, rmSync as rmSync27 } from "fs";
138369
+ import { existsSync as existsSync80, mkdtempSync as mkdtempSync12, readFileSync as readFileSync56, rmSync as rmSync27 } from "fs";
138090
138370
  import { tmpdir as tmpdir13 } from "os";
138091
138371
  import { join as join90, dirname as dirname43 } from "path";
138092
138372
  import { fileURLToPath as fileURLToPath12 } from "url";
@@ -138253,7 +138533,7 @@ async function localizeRemoteAssets(html) {
138253
138533
  try {
138254
138534
  const { loadProducer: loadProducer2 } = await Promise.resolve().then(() => (init_producer(), producer_exports));
138255
138535
  const { localizeRemoteMediaSources: localizeRemoteMediaSources2, localizeRemoteImageSources: localizeRemoteImageSources2, localizeRemoteFontFaces: localizeRemoteFontFaces2 } = await loadProducer2();
138256
- dir = mkdtempSync11(join90(tmpdir13(), "hf-validate-assets-"));
138536
+ dir = mkdtempSync12(join90(tmpdir13(), "hf-validate-assets-"));
138257
138537
  const assetDir = dir;
138258
138538
  const media = await localizeRemoteMediaSources2(html, assetDir);
138259
138539
  const images = await localizeRemoteImageSources2(media.html, assetDir);
@@ -138564,7 +138844,7 @@ __export(checkBrowser_exports, {
138564
138844
  preResolveHostileMediaProxies: () => preResolveHostileMediaProxies,
138565
138845
  runBrowserCheck: () => runBrowserCheck
138566
138846
  });
138567
- import { mkdirSync as mkdirSync44, writeFileSync as writeFileSync31 } from "fs";
138847
+ import { mkdirSync as mkdirSync43, writeFileSync as writeFileSync31 } from "fs";
138568
138848
  import { join as join91, resolve as resolve56 } from "path";
138569
138849
  async function preResolveHostileMediaProxies(projectDir, html, autoProxyOverride) {
138570
138850
  if (!resolveAutoProxy(projectDir, autoProxyOverride)) return;
@@ -138664,7 +138944,7 @@ async function captureFindingCrops(project, options, requests) {
138664
138944
  const page = session.page;
138665
138945
  await waitForPreferredSeekTarget(page);
138666
138946
  const snapshotDir = join91(project.dir, "snapshots");
138667
- mkdirSync44(snapshotDir, { recursive: true });
138947
+ mkdirSync43(snapshotDir, { recursive: true });
138668
138948
  for (const request of requests) {
138669
138949
  await seekCompositionTimeline(page, request.time, AUDIT_SEEK_OPTIONS);
138670
138950
  const canvas = await page.evaluate(() => ({
@@ -138769,7 +139049,7 @@ function createPageDriver(page, setTime) {
138769
139049
  setTime(time);
138770
139050
  await seekCompositionTimeline(page, time, DENSE_GEOMETRY_SEEK_OPTIONS);
138771
139051
  },
138772
- collectLayout: (time, tolerance) => collectLayout(page, time, tolerance),
139052
+ collectLayout: (time, tolerance, layout2) => collectLayout(page, time, tolerance, layout2),
138773
139053
  collectOverlap: (time) => collectOverlap(page, time),
138774
139054
  collectLayoutGeometry: () => collectLayoutGeometry(page),
138775
139055
  collectRotationSample: (time) => collectRotationSample(page, time),
@@ -138855,7 +139135,7 @@ async function collectTweenBoundaries2(page) {
138855
139135
  return boundaries.filter(Number.isFinite);
138856
139136
  });
138857
139137
  }
138858
- async function collectLayout(page, time, tolerance) {
139138
+ async function collectLayout(page, time, tolerance, layout2) {
138859
139139
  const raw = await page.evaluate(
138860
139140
  (options) => {
138861
139141
  const audit = Reflect.get(window, "__hyperframesLayoutAudit");
@@ -138863,7 +139143,11 @@ async function collectLayout(page, time, tolerance) {
138863
139143
  const result = Reflect.apply(audit, window, [options]);
138864
139144
  return Array.isArray(result) ? result : [];
138865
139145
  },
138866
- { time, tolerance }
139146
+ {
139147
+ time,
139148
+ tolerance,
139149
+ ...typeof layout2?.proseCoverageFloor === "number" ? { proseCoverageFloor: layout2.proseCoverageFloor } : {}
139150
+ }
138867
139151
  );
138868
139152
  return anchorLayoutIssues(page, raw.flatMap(parseLayoutIssue));
138869
139153
  }
@@ -139452,7 +139736,7 @@ var init_checkBrowser = __esm({
139452
139736
  });
139453
139737
 
139454
139738
  // src/utils/checkPipeline.ts
139455
- import { mkdirSync as mkdirSync45, writeFileSync as writeFileSync33 } from "fs";
139739
+ import { mkdirSync as mkdirSync44, writeFileSync as writeFileSync33 } from "fs";
139456
139740
  import { join as join93, relative as relative21 } from "path";
139457
139741
  function selectContrastTimes(grid) {
139458
139742
  if (grid.length <= 5) return [...grid];
@@ -139635,7 +139919,7 @@ async function collectGridSamples(driver, options, grid, motion) {
139635
139919
  await driver.seek(time);
139636
139920
  const issuesAtTime = [];
139637
139921
  if (layoutSet.has(time)) {
139638
- const layoutIssues = await driver.collectLayout(time, options.tolerance);
139922
+ const layoutIssues = await driver.collectLayout(time, options.tolerance, options.layout);
139639
139923
  collected.layoutIssues.push(...layoutIssues);
139640
139924
  issuesAtTime.push(...layoutIssues);
139641
139925
  collected.geometrySignatures.push(await driver.collectLayoutGeometry());
@@ -140394,7 +140678,7 @@ async function runBrowserCheck2(project, options, motion) {
140394
140678
  }
140395
140679
  async function writeSnapshot(projectDir, index, time, pngBase64) {
140396
140680
  const snapshotDir = join93(projectDir, "snapshots");
140397
- mkdirSync45(snapshotDir, { recursive: true });
140681
+ mkdirSync44(snapshotDir, { recursive: true });
140398
140682
  const filename = `frame-${String(index).padStart(2, "0")}-at-${time.toFixed(1)}s.png`;
140399
140683
  const path2 = join93(snapshotDir, filename);
140400
140684
  writeFileSync33(path2, Buffer.from(pngBase64, "base64"));
@@ -140476,7 +140760,8 @@ __export(check_exports, {
140476
140760
  createCheckCommand: () => createCheckCommand,
140477
140761
  default: () => check_default,
140478
140762
  examples: () => examples12,
140479
- parseFrameCheck: () => parseFrameCheck
140763
+ parseFrameCheck: () => parseFrameCheck,
140764
+ parseLayout: () => parseLayout
140480
140765
  });
140481
140766
  function createCheckCommand(dependencies = DEFAULT_COMMAND_DEPENDENCIES) {
140482
140767
  return defineCommand({
@@ -140552,6 +140837,10 @@ function createCheckCommand(dependencies = DEFAULT_COMMAND_DEPENDENCIES) {
140552
140837
  "frame-check": {
140553
140838
  type: "string",
140554
140839
  description: 'Bare --frame-check uses defaults (tol=2px, severity=warning, seek=.5; breach floor=max(120px, 6% of shorter canvas edge)); or pass "severity=error;seek=.25,.75;tol=4" to tune'
140840
+ },
140841
+ layout: {
140842
+ type: "string",
140843
+ description: 'Layout knobs: "proseCoverageFloor=0.05" (0\u20131; default 0.15).'
140555
140844
  }
140556
140845
  },
140557
140846
  async run({ args }) {
@@ -140599,6 +140888,7 @@ function parseCheckOptions(args) {
140599
140888
  snapshots: args.snapshots === true,
140600
140889
  captionZone: parseCaptionZone(args["caption-zone"]),
140601
140890
  frameCheck: parseFrameCheck(args["frame-check"]),
140891
+ layout: parseLayout(args.layout),
140602
140892
  autoProxy: args.proxy
140603
140893
  };
140604
140894
  }
@@ -140627,8 +140917,8 @@ function parseFrameCheckFields(value) {
140627
140917
  }
140628
140918
  function parseFrameCheckTolerance(raw) {
140629
140919
  if (raw === void 0) return void 0;
140630
- const tol = Number.parseFloat(raw);
140631
- if (!Number.isFinite(tol) || tol < 0) throw frameCheckError();
140920
+ const tol = parseNumberStrict(raw);
140921
+ if (tol === null || tol < 0) throw frameCheckError();
140632
140922
  return tol;
140633
140923
  }
140634
140924
  function frameCheckError() {
@@ -140636,6 +140926,45 @@ function frameCheckError() {
140636
140926
  'Invalid --frame-check: use bare --frame-check or "severity=warning|error;seek=.25,.75;tol=4" (all fields optional)'
140637
140927
  );
140638
140928
  }
140929
+ function parseLayout(value) {
140930
+ if (value === void 0 || value === null || value === false) return void 0;
140931
+ if (value === true || value === "") throw layoutError();
140932
+ if (typeof value !== "string") throw layoutError();
140933
+ const fields = parseLayoutFields(value);
140934
+ const proseCoverageFloor = parseProseCoverageFloor(fields.get("proseCoverageFloor"));
140935
+ if (proseCoverageFloor === void 0) throw layoutError();
140936
+ return { proseCoverageFloor };
140937
+ }
140938
+ function parseLayoutFields(value) {
140939
+ const fields = /* @__PURE__ */ new Map();
140940
+ for (const part of value.split(";")) {
140941
+ const trimmed = part.trim();
140942
+ if (!trimmed) continue;
140943
+ const separator = trimmed.indexOf("=");
140944
+ if (separator <= 0) throw layoutError();
140945
+ const key2 = trimmed.slice(0, separator).trim();
140946
+ const entry = trimmed.slice(separator + 1).trim();
140947
+ if (!LAYOUT_FIELDS.has(key2) || fields.has(key2)) throw layoutError();
140948
+ fields.set(key2, entry);
140949
+ }
140950
+ return fields;
140951
+ }
140952
+ function parseProseCoverageFloor(raw) {
140953
+ if (raw === void 0) return void 0;
140954
+ const floor = parseNumberStrict(raw);
140955
+ if (floor === null || floor < 0 || floor > 1) throw layoutError();
140956
+ return floor;
140957
+ }
140958
+ function layoutError() {
140959
+ return new Error(
140960
+ 'Invalid --layout: use "proseCoverageFloor=0.05" with a fraction from 0 to 1 (inclusive)'
140961
+ );
140962
+ }
140963
+ function parseNumberStrict(raw) {
140964
+ if (raw === "") return null;
140965
+ const value = Number(raw);
140966
+ return Number.isFinite(value) ? value : null;
140967
+ }
140639
140968
  function parseCaptionZone(value) {
140640
140969
  if (value === void 0 || value === null) return void 0;
140641
140970
  const fields = parseCaptionFields(captionZoneString(value));
@@ -140687,8 +141016,8 @@ function requiredCaptionFraction(fields, key2) {
140687
141016
  }
140688
141017
  function captionFraction(value) {
140689
141018
  if (value === void 0 || value === "") return null;
140690
- const parsed = Number(value);
140691
- return Number.isFinite(parsed) && parsed >= 0 && parsed <= 1 ? parsed : null;
141019
+ const parsed = parseNumberStrict(value);
141020
+ return parsed !== null && parsed >= 0 && parsed <= 1 ? parsed : null;
140692
141021
  }
140693
141022
  function captionSeverity(value) {
140694
141023
  if (value === void 0) return void 0;
@@ -140808,7 +141137,7 @@ function printCounts(section2) {
140808
141137
  ` ${c.dim(`${section2.errorCount} error(s), ${section2.warningCount} warning(s), ${section2.infoCount} info(s)`)}`
140809
141138
  );
140810
141139
  }
140811
- var examples12, DEFAULT_COMMAND_DEPENDENCIES, CAPTION_ZONE_FIELDS, FRAME_CHECK_FIELDS, check_default;
141140
+ var examples12, DEFAULT_COMMAND_DEPENDENCIES, CAPTION_ZONE_FIELDS, FRAME_CHECK_FIELDS, LAYOUT_FIELDS, check_default;
140812
141141
  var init_check = __esm({
140813
141142
  "src/commands/check.ts"() {
140814
141143
  "use strict";
@@ -140834,6 +141163,7 @@ var init_check = __esm({
140834
141163
  };
140835
141164
  CAPTION_ZONE_FIELDS = /* @__PURE__ */ new Set(["x0", "y0", "x1", "y1", "severity", "seek"]);
140836
141165
  FRAME_CHECK_FIELDS = /* @__PURE__ */ new Set(["severity", "seek", "tol"]);
141166
+ LAYOUT_FIELDS = /* @__PURE__ */ new Set(["proseCoverageFloor"]);
140837
141167
  check_default = createCheckCommand();
140838
141168
  }
140839
141169
  });
@@ -141045,7 +141375,7 @@ __export(beats_exports, {
141045
141375
  default: () => beats_default,
141046
141376
  examples: () => examples13
141047
141377
  });
141048
- import { existsSync as existsSync83, readFileSync as readFileSync58, mkdirSync as mkdirSync46, writeFileSync as writeFileSync34 } from "fs";
141378
+ import { existsSync as existsSync83, readFileSync as readFileSync58, mkdirSync as mkdirSync45, writeFileSync as writeFileSync34 } from "fs";
141049
141379
  import { resolve as resolve57, join as join95, dirname as dirname45 } from "path";
141050
141380
  function fail(message) {
141051
141381
  console.error(c.error(message));
@@ -141118,7 +141448,7 @@ var init_beats2 = __esm({
141118
141448
  fail(`No beats detected in ${rel} \u2014 nothing written. (Track may be silent/ambient.)`);
141119
141449
  }
141120
141450
  const outPath = join95(project.dir, "beats", `${rel}.json`);
141121
- mkdirSync46(dirname45(outPath), { recursive: true });
141451
+ mkdirSync45(dirname45(outPath), { recursive: true });
141122
141452
  writeFileSync34(outPath, serializeBeats(result.beatTimes, result.beatStrengths, rel));
141123
141453
  report(`beats/${rel}.json`, result, Boolean(args.json));
141124
141454
  }
@@ -142967,10 +143297,10 @@ __export(motionShot_exports, {
142967
143297
  captureMotionPathShot: () => captureMotionPathShot,
142968
143298
  ensureShotOutputDir: () => ensureShotOutputDir
142969
143299
  });
142970
- import { mkdirSync as mkdirSync47, writeFileSync as writeFileSync35 } from "fs";
143300
+ import { mkdirSync as mkdirSync46, writeFileSync as writeFileSync35 } from "fs";
142971
143301
  import { dirname as dirname46 } from "path";
142972
143302
  function ensureShotOutputDir(outPath) {
142973
- mkdirSync47(dirname46(outPath), { recursive: true });
143303
+ mkdirSync46(dirname46(outPath), { recursive: true });
142974
143304
  }
142975
143305
  function applyOrbitCamera(selectors, cam) {
142976
143306
  const first = document.querySelector(selectors[0] ?? "");
@@ -145043,7 +145373,7 @@ var init_remove_background = __esm({
145043
145373
 
145044
145374
  // src/whisper/parakeet.ts
145045
145375
  import { execFileSync as execFileSync12 } from "child_process";
145046
- import { existsSync as existsSync88, mkdtempSync as mkdtempSync12, readFileSync as readFileSync63, rmSync as rmSync28, writeFileSync as writeFileSync36 } from "fs";
145376
+ import { existsSync as existsSync88, mkdtempSync as mkdtempSync13, readFileSync as readFileSync63, rmSync as rmSync28, writeFileSync as writeFileSync36 } from "fs";
145047
145377
  import { homedir as homedir17, tmpdir as tmpdir14 } from "os";
145048
145378
  import { basename as basename19, extname as extname17, join as join99 } from "path";
145049
145379
  function isRunnable(bin) {
@@ -145113,7 +145443,7 @@ function transcribeWithParakeet(inputPath, dir, options) {
145113
145443
  options?.onProgress?.(
145114
145444
  cached2 ? "Transcribing with Parakeet..." : "Downloading Parakeet model (first run, ~600MB)..."
145115
145445
  );
145116
- const workDir = mkdtempSync12(join99(tmpdir14(), "hyperframes-parakeet-"));
145446
+ const workDir = mkdtempSync13(join99(tmpdir14(), "hyperframes-parakeet-"));
145117
145447
  try {
145118
145448
  const argv2 = [inputPath, "--model", model, "--output-format", "json", "--output-dir", workDir];
145119
145449
  if (options?.language) argv2.push("--language", options.language);
@@ -145420,7 +145750,7 @@ var init_transcribe2 = __esm({
145420
145750
  });
145421
145751
 
145422
145752
  // src/tts/manager.ts
145423
- import { existsSync as existsSync90, mkdirSync as mkdirSync48 } from "fs";
145753
+ import { existsSync as existsSync90, mkdirSync as mkdirSync47 } from "fs";
145424
145754
  import { homedir as homedir18 } from "os";
145425
145755
  import { join as join101 } from "path";
145426
145756
  function inferLangFromVoiceId(voiceId) {
@@ -145439,7 +145769,7 @@ async function ensureModel3(model = DEFAULT_MODEL4, options) {
145439
145769
  `Unknown TTS model: ${model}. Available: ${Object.keys(MODEL_URLS2).join(", ")}`
145440
145770
  );
145441
145771
  }
145442
- mkdirSync48(MODELS_DIR3, { recursive: true });
145772
+ mkdirSync47(MODELS_DIR3, { recursive: true });
145443
145773
  options?.onProgress?.(`Downloading TTS model ${model} (~311 MB)...`);
145444
145774
  await downloadFile(url, modelPath2);
145445
145775
  if (!existsSync90(modelPath2)) {
@@ -145450,7 +145780,7 @@ async function ensureModel3(model = DEFAULT_MODEL4, options) {
145450
145780
  async function ensureVoices(options) {
145451
145781
  const voicesPath = join101(VOICES_DIR, "voices-v1.0.bin");
145452
145782
  if (existsSync90(voicesPath)) return voicesPath;
145453
- mkdirSync48(VOICES_DIR, { recursive: true });
145783
+ mkdirSync47(VOICES_DIR, { recursive: true });
145454
145784
  options?.onProgress?.("Downloading voice data (~27 MB)...");
145455
145785
  await downloadFile(VOICES_URL, voicesPath);
145456
145786
  if (!existsSync90(voicesPath)) {
@@ -145594,12 +145924,12 @@ __export(synthesize_exports, {
145594
145924
  synthesize: () => synthesize
145595
145925
  });
145596
145926
  import { execFileSync as execFileSync14 } from "child_process";
145597
- import { existsSync as existsSync91, writeFileSync as writeFileSync38, mkdirSync as mkdirSync49, readdirSync as readdirSync30, unlinkSync as unlinkSync7 } from "fs";
145927
+ import { existsSync as existsSync91, writeFileSync as writeFileSync38, mkdirSync as mkdirSync48, readdirSync as readdirSync30, unlinkSync as unlinkSync7 } from "fs";
145598
145928
  import { join as join103, dirname as dirname50, basename as basename20 } from "path";
145599
145929
  import { homedir as homedir19 } from "os";
145600
145930
  function ensureSynthScript() {
145601
145931
  if (!existsSync91(SCRIPT_PATH)) {
145602
- mkdirSync49(SCRIPT_DIR, { recursive: true });
145932
+ mkdirSync48(SCRIPT_DIR, { recursive: true });
145603
145933
  writeFileSync38(SCRIPT_PATH, SYNTH_SCRIPT);
145604
145934
  const currentName = basename20(SCRIPT_PATH);
145605
145935
  try {
@@ -145640,7 +145970,7 @@ async function synthesize(text2, outputPath, options) {
145640
145970
  ensureVoices({ onProgress: options?.onProgress })
145641
145971
  ]);
145642
145972
  const scriptPath = ensureSynthScript();
145643
- mkdirSync49(dirname50(outputPath), { recursive: true });
145973
+ mkdirSync48(dirname50(outputPath), { recursive: true });
145644
145974
  options?.onProgress?.(`Generating speech with voice ${voice} (${lang})...`);
145645
145975
  try {
145646
145976
  const espeakLang = ESPEAK_LANG_OVERRIDES[lang] ?? lang;
@@ -187082,7 +187412,7 @@ __export(snapshot_exports, {
187082
187412
  resolveSnapshotVideoFrameTime: () => resolveSnapshotVideoFrameTime,
187083
187413
  tailFrameTime: () => tailFrameTime
187084
187414
  });
187085
- import { existsSync as existsSync98, mkdtempSync as mkdtempSync13, readFileSync as readFileSync68, mkdirSync as mkdirSync50, rmSync as rmSync29, writeFileSync as writeFileSync41 } from "fs";
187415
+ import { existsSync as existsSync98, mkdtempSync as mkdtempSync14, readFileSync as readFileSync68, mkdirSync as mkdirSync49, rmSync as rmSync29, writeFileSync as writeFileSync41 } from "fs";
187086
187416
  import { tmpdir as tmpdir15 } from "os";
187087
187417
  import { resolve as resolve67, join as join106, relative as relative24, isAbsolute as isAbsolute15, basename as basename25 } from "path";
187088
187418
  function orbitStageSource() {
@@ -187127,7 +187457,7 @@ function requireSnapshotFfmpeg(ffmpegPath) {
187127
187457
  );
187128
187458
  }
187129
187459
  async function extractVideoFrameToBuffer(videoPath, timeSeconds, useVp9AlphaDecoder = false) {
187130
- const tmp = mkdtempSync13(join106(tmpdir15(), "hf-snapshot-frame-"));
187460
+ const tmp = mkdtempSync14(join106(tmpdir15(), "hf-snapshot-frame-"));
187131
187461
  const outPath = join106(tmp, "frame.png");
187132
187462
  try {
187133
187463
  const ffmpegPath = requireSnapshotFfmpeg(findFFmpeg());
@@ -187241,7 +187571,7 @@ async function captureSnapshots(projectDir, opts) {
187241
187571
  }
187242
187572
  const cameraExpr = opts.angle && (opts.angle.yaw !== 0 || opts.angle.pitch !== 0) ? `(${orbitStageSource()})(${JSON.stringify(opts.angle)})` : null;
187243
187573
  const snapshotDir = opts.outputDir ?? join106(projectDir, "snapshots");
187244
- mkdirSync50(snapshotDir, { recursive: true });
187574
+ mkdirSync49(snapshotDir, { recursive: true });
187245
187575
  try {
187246
187576
  const { readdirSync: readdirSync38 } = await import("fs");
187247
187577
  for (const file of readdirSync38(snapshotDir)) {
@@ -188589,8 +188919,8 @@ __export(grade_compare_exports, {
188589
188919
  import {
188590
188920
  copyFileSync as copyFileSync10,
188591
188921
  existsSync as existsSync100,
188592
- mkdirSync as mkdirSync51,
188593
- mkdtempSync as mkdtempSync14,
188922
+ mkdirSync as mkdirSync50,
188923
+ mkdtempSync as mkdtempSync15,
188594
188924
  readFileSync as readFileSync70,
188595
188925
  rmSync as rmSync30,
188596
188926
  writeFileSync as writeFileSync44
@@ -188852,7 +189182,7 @@ function frameFileNameForPath(framePath) {
188852
189182
  return "frame.png";
188853
189183
  }
188854
189184
  async function prepareGradeCompareTempProject(opts) {
188855
- const tempDir = mkdtempSync14(join107(tmpdir16(), "hf-grade-compare-"));
189185
+ const tempDir = mkdtempSync15(join107(tmpdir16(), "hf-grade-compare-"));
188856
189186
  try {
188857
189187
  const frameFileName2 = opts.frameFileName ?? frameFileNameForPath(opts.framePath);
188858
189188
  writeFileSync44(join107(tempDir, frameFileName2), opts.frameBuffer);
@@ -188899,7 +189229,7 @@ function isVideoPath2(filePath) {
188899
189229
  return [".mp4", ".mov", ".m4v", ".webm", ".mkv", ".avi", ".mpeg", ".mpg", ".ogv"].includes(ext);
188900
189230
  }
188901
189231
  async function extractVideoFrameToBuffer2(videoPath) {
188902
- const tmp = mkdtempSync14(join107(tmpdir16(), "hf-grade-compare-frame-"));
189232
+ const tmp = mkdtempSync15(join107(tmpdir16(), "hf-grade-compare-frame-"));
188903
189233
  const outPath = join107(tmp, "frame.png");
188904
189234
  try {
188905
189235
  const ffmpegPath = findFFmpeg();
@@ -189096,7 +189426,7 @@ var init_grade_compare = __esm({
189096
189426
  prepared.tempDir,
189097
189427
  parsed.timeoutMs
189098
189428
  );
189099
- mkdirSync51(dirname54(parsed.outPath), { recursive: true });
189429
+ mkdirSync50(dirname54(parsed.outPath), { recursive: true });
189100
189430
  copyFileSync10(tempSheet, parsed.outPath);
189101
189431
  trackCompareSheet({
189102
189432
  command: "grade-compare",
@@ -189141,7 +189471,7 @@ __export(compare_exports, {
189141
189471
  parseCompareArgs: () => parseCompareArgs,
189142
189472
  prepareCompareVariantProjects: () => prepareCompareVariantProjects
189143
189473
  });
189144
- import { cpSync as cpSync6, existsSync as existsSync101, mkdirSync as mkdirSync53, mkdtempSync as mkdtempSync15, renameSync as renameSync16, rmSync as rmSync31, statSync as statSync35 } from "fs";
189474
+ import { cpSync as cpSync6, existsSync as existsSync101, mkdirSync as mkdirSync51, mkdtempSync as mkdtempSync16, renameSync as renameSync16, rmSync as rmSync31, statSync as statSync35 } from "fs";
189145
189475
  import { tmpdir as tmpdir17 } from "os";
189146
189476
  import { basename as basename28, dirname as dirname55, extname as extname24, join as join108 } from "path";
189147
189477
  function defaultLabelForPath(input2) {
@@ -189242,7 +189572,7 @@ function inputError(variant) {
189242
189572
  );
189243
189573
  }
189244
189574
  function stageHtmlVariant(variant) {
189245
- const stagedDir = mkdtempSync15(join108(tmpdir17(), "hf-compare-variant-"));
189575
+ const stagedDir = mkdtempSync16(join108(tmpdir17(), "hf-compare-variant-"));
189246
189576
  try {
189247
189577
  cpSync6(dirname55(variant.inputPath), stagedDir, {
189248
189578
  recursive: true,
@@ -189339,7 +189669,7 @@ async function renderCompareSheet(parsed) {
189339
189669
  const capResult = capCompareVariants(parsed.variants);
189340
189670
  const variants = capResult.variants;
189341
189671
  const prepared = prepareCompareVariantProjects(variants);
189342
- const frameDir = mkdtempSync15(join108(tmpdir17(), "hf-compare-frames-"));
189672
+ const frameDir = mkdtempSync16(join108(tmpdir17(), "hf-compare-frames-"));
189343
189673
  const framePaths = [];
189344
189674
  try {
189345
189675
  let renderReadyTimedOut = false;
@@ -189354,7 +189684,7 @@ async function renderCompareSheet(parsed) {
189354
189684
  framePaths.push(rendered.framePath);
189355
189685
  renderReadyTimedOut = renderReadyTimedOut || rendered.renderReadyTimedOut;
189356
189686
  }
189357
- mkdirSync53(dirname55(parsed.outPath), { recursive: true });
189687
+ mkdirSync51(dirname55(parsed.outPath), { recursive: true });
189358
189688
  await createContactSheet(framePaths, parsed.outPath, {
189359
189689
  cols: parsed.cols ?? defaultCompareCols(framePaths.length),
189360
189690
  maxImages: framePaths.length,
@@ -189473,7 +189803,7 @@ ${c.error("\u2717")} Compare failed: ${message}`);
189473
189803
  });
189474
189804
 
189475
189805
  // src/capture/assetDownloader.ts
189476
- import { writeFileSync as writeFileSync45, mkdirSync as mkdirSync54 } from "fs";
189806
+ import { writeFileSync as writeFileSync45, mkdirSync as mkdirSync53 } from "fs";
189477
189807
  import { join as join109, extname as extname25 } from "path";
189478
189808
  import { createHash as createHash18 } from "crypto";
189479
189809
  function svgContentHashSlug(svgSource, isLogo) {
@@ -189482,10 +189812,10 @@ function svgContentHashSlug(svgSource, isLogo) {
189482
189812
  }
189483
189813
  async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks) {
189484
189814
  const assetsDir = join109(outputDir, "assets");
189485
- mkdirSync54(assetsDir, { recursive: true });
189815
+ mkdirSync53(assetsDir, { recursive: true });
189486
189816
  const assets = [];
189487
189817
  const downloadedUrls = /* @__PURE__ */ new Set();
189488
- mkdirSync54(join109(outputDir, "assets", "svgs"), { recursive: true });
189818
+ mkdirSync53(join109(outputDir, "assets", "svgs"), { recursive: true });
189489
189819
  const usedSvgNames = /* @__PURE__ */ new Set();
189490
189820
  for (let i2 = 0; i2 < tokens.svgs.length && i2 < 30; i2++) {
189491
189821
  const svg = tokens.svgs[i2];
@@ -189616,7 +189946,7 @@ function normalizeUrl(u) {
189616
189946
  }
189617
189947
  async function downloadAndRewriteFonts(css, outputDir) {
189618
189948
  const assetsDir = join109(outputDir, "assets", "fonts");
189619
- mkdirSync54(assetsDir, { recursive: true });
189949
+ mkdirSync53(assetsDir, { recursive: true });
189620
189950
  const fontUrlRegex = /url\(['"]?(https?:\/\/[^'")\s]+\.(?:woff2?|ttf|otf)[^'")\s]*?)['"]?\)/g;
189621
189951
  const fontUrls = /* @__PURE__ */ new Set();
189622
189952
  let match;
@@ -189809,7 +190139,7 @@ __export(video_exports, {
189809
190139
  runVideoMode: () => runVideoMode,
189810
190140
  safeFilename: () => safeFilename
189811
190141
  });
189812
- import { createWriteStream as createWriteStream4, existsSync as existsSync103, mkdirSync as mkdirSync55, readFileSync as readFileSync71, unlinkSync as unlinkSync9 } from "fs";
190142
+ import { createWriteStream as createWriteStream4, existsSync as existsSync103, mkdirSync as mkdirSync54, readFileSync as readFileSync71, unlinkSync as unlinkSync9 } from "fs";
189813
190143
  import { resolve as resolve71, join as join110, basename as basename29 } from "path";
189814
190144
  async function streamToFile(url, destPath) {
189815
190145
  const r2 = await safeFetch(url, {
@@ -189985,7 +190315,7 @@ async function runVideoMode(args) {
189985
190315
  return;
189986
190316
  }
189987
190317
  const outDir = isW2hLayout ? join110(projectDir, "capture", "assets", "videos") : join110(projectDir, "assets", "videos");
189988
- mkdirSync55(outDir, { recursive: true });
190318
+ mkdirSync54(outDir, { recursive: true });
189989
190319
  const fname = safeFilename(entry.filename || basename29(entry.url));
189990
190320
  const outPath = join110(outDir, fname);
189991
190321
  const relPath = isW2hLayout ? `capture/assets/videos/${fname}` : `assets/videos/${fname}`;
@@ -191554,7 +191884,7 @@ var init_animationCataloger = __esm({
191554
191884
  });
191555
191885
 
191556
191886
  // src/capture/mediaCapture.ts
191557
- import { mkdirSync as mkdirSync56, writeFileSync as writeFileSync47, readdirSync as readdirSync34, readFileSync as readFileSync74, statSync as statSync36 } from "fs";
191887
+ import { mkdirSync as mkdirSync55, writeFileSync as writeFileSync47, readdirSync as readdirSync34, readFileSync as readFileSync74, statSync as statSync36 } from "fs";
191558
191888
  import { join as join113, extname as extname26 } from "path";
191559
191889
  async function saveLottieAnimations(discoveredLotties, lottieDir) {
191560
191890
  let savedCount = 0;
@@ -191616,7 +191946,7 @@ async function saveLottieAnimations(discoveredLotties, lottieDir) {
191616
191946
  async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir) {
191617
191947
  const manifest = [];
191618
191948
  const previewDir = join113(lottieDir, "previews");
191619
- mkdirSync56(previewDir, { recursive: true });
191949
+ mkdirSync55(previewDir, { recursive: true });
191620
191950
  for (const file of readdirSync34(lottieDir)) {
191621
191951
  if (!file.endsWith(".json")) continue;
191622
191952
  try {
@@ -191782,9 +192112,9 @@ async function captureVideoManifest(page, outputDir, progress, opts) {
191782
192112
  const merged = [...byKey.values()];
191783
192113
  if (merged.length === 0) return;
191784
192114
  const videoManifestDir = join113(outputDir, "assets", "videos");
191785
- mkdirSync56(videoManifestDir, { recursive: true });
192115
+ mkdirSync55(videoManifestDir, { recursive: true });
191786
192116
  const previewDir = join113(videoManifestDir, "previews");
191787
- mkdirSync56(previewDir, { recursive: true });
192117
+ mkdirSync55(previewDir, { recursive: true });
191788
192118
  const videoManifest = [];
191789
192119
  const dlStart = Date.now();
191790
192120
  for (let vi = 0; vi < merged.length && vi < 20; vi++) {
@@ -192490,11 +192820,11 @@ var screenshotCapture_exports = {};
192490
192820
  __export(screenshotCapture_exports, {
192491
192821
  captureScrollScreenshots: () => captureScrollScreenshots
192492
192822
  });
192493
- import { writeFileSync as writeFileSync50, mkdirSync as mkdirSync57 } from "fs";
192823
+ import { writeFileSync as writeFileSync50, mkdirSync as mkdirSync56 } from "fs";
192494
192824
  import { join as join117 } from "path";
192495
192825
  async function captureScrollScreenshots(page, outputDir) {
192496
192826
  const screenshotsDir = join117(outputDir, "screenshots");
192497
- mkdirSync57(screenshotsDir, { recursive: true });
192827
+ mkdirSync56(screenshotsDir, { recursive: true });
192498
192828
  const MAX_SCREENSHOTS = 20;
192499
192829
  const filePaths = [];
192500
192830
  try {
@@ -192934,7 +193264,7 @@ var capture_exports = {};
192934
193264
  __export(capture_exports, {
192935
193265
  captureWebsite: () => captureWebsite
192936
193266
  });
192937
- import { mkdirSync as mkdirSync58, writeFileSync as writeFileSync51, existsSync as existsSync108 } from "fs";
193267
+ import { mkdirSync as mkdirSync57, writeFileSync as writeFileSync51, existsSync as existsSync108 } from "fs";
192938
193268
  import { join as join118 } from "path";
192939
193269
  async function captureWebsite(opts, onProgress) {
192940
193270
  const {
@@ -192952,9 +193282,9 @@ async function captureWebsite(opts, onProgress) {
192952
193282
  onProgress?.(stage, detail);
192953
193283
  };
192954
193284
  loadEnvFile(outputDir);
192955
- mkdirSync58(join118(outputDir, "extracted"), { recursive: true });
192956
- mkdirSync58(join118(outputDir, "screenshots"), { recursive: true });
192957
- mkdirSync58(join118(outputDir, "assets"), { recursive: true });
193285
+ mkdirSync57(join118(outputDir, "extracted"), { recursive: true });
193286
+ mkdirSync57(join118(outputDir, "screenshots"), { recursive: true });
193287
+ mkdirSync57(join118(outputDir, "assets"), { recursive: true });
192958
193288
  progress("browser", "Launching headless Chrome...");
192959
193289
  const { ensureBrowser: ensureBrowser2 } = await Promise.resolve().then(() => (init_manager2(), manager_exports2));
192960
193290
  const browser = await ensureBrowser2();
@@ -193115,7 +193445,7 @@ async function captureWebsite(opts, onProgress) {
193115
193445
  }
193116
193446
  if (discoveredLotties.length > 0) {
193117
193447
  const lottieDir = join118(outputDir, "assets", "lottie");
193118
- mkdirSync58(lottieDir, { recursive: true });
193448
+ mkdirSync57(lottieDir, { recursive: true });
193119
193449
  const savedCount = await saveLottieAnimations(discoveredLotties, lottieDir);
193120
193450
  if (savedCount > 0) {
193121
193451
  await renderLottiePreviews(chromeBrowser, lottieDir, outputDir);
@@ -193619,8 +193949,8 @@ var init_capture2 = __esm({
193619
193949
  } catch (err) {
193620
193950
  const errMsg = normalizeErrorMessage(err);
193621
193951
  try {
193622
- const { mkdirSync: mkdirSync69, writeFileSync: writeFileSync61 } = await import("fs");
193623
- mkdirSync69(outputDir, { recursive: true });
193952
+ const { mkdirSync: mkdirSync68, writeFileSync: writeFileSync61 } = await import("fs");
193953
+ mkdirSync68(outputDir, { recursive: true });
193624
193954
  const isTimeout = /timeout|timed out/i.test(errMsg);
193625
193955
  const reason = isTimeout ? "Page navigation timed out \u2014 the site may be blocking headless browsers or requires authentication." : `Capture failed: ${errMsg}`;
193626
193956
  writeFileSync61(
@@ -193687,14 +194017,14 @@ __export(state_exports, {
193687
194017
  stateFilePath: () => stateFilePath,
193688
194018
  writeStackOutputs: () => writeStackOutputs
193689
194019
  });
193690
- import { existsSync as existsSync109, mkdirSync as mkdirSync59, readdirSync as readdirSync37, readFileSync as readFileSync77, rmSync as rmSync32, writeFileSync as writeFileSync53 } from "fs";
194020
+ import { existsSync as existsSync109, mkdirSync as mkdirSync58, readdirSync as readdirSync37, readFileSync as readFileSync77, rmSync as rmSync32, writeFileSync as writeFileSync53 } from "fs";
193691
194021
  import { dirname as dirname56, join as join119 } from "path";
193692
194022
  function stateFilePath(stackName = DEFAULT_STACK_NAME, cwd = process.cwd()) {
193693
194023
  return join119(cwd, STATE_DIR_NAME, `${STATE_FILE_PREFIX}${stackName}.json`);
193694
194024
  }
193695
194025
  function writeStackOutputs(outputs, cwd = process.cwd()) {
193696
194026
  const path2 = stateFilePath(outputs.stackName, cwd);
193697
- mkdirSync59(dirname56(path2), { recursive: true });
194027
+ mkdirSync58(dirname56(path2), { recursive: true });
193698
194028
  writeFileSync53(path2, JSON.stringify(outputs, null, 2) + "\n");
193699
194029
  return path2;
193700
194030
  }
@@ -195289,7 +195619,7 @@ __export(cloudrun_exports, {
195289
195619
  missingCloudRunAdapterMessage: () => missingCloudRunAdapterMessage
195290
195620
  });
195291
195621
  import { spawnSync as spawnSync6 } from "child_process";
195292
- import { existsSync as existsSync116, mkdirSync as mkdirSync60, readFileSync as readFileSync81, writeFileSync as writeFileSync54 } from "fs";
195622
+ import { existsSync as existsSync116, mkdirSync as mkdirSync59, readFileSync as readFileSync81, writeFileSync as writeFileSync54 } from "fs";
195293
195623
  import { homedir as homedir20 } from "os";
195294
195624
  import { join as join126, resolve as resolve76 } from "path";
195295
195625
  function loadCloudRunAdapter() {
@@ -195316,7 +195646,7 @@ function statePath() {
195316
195646
  return join126(stateDir(), "cloudrun-state.json");
195317
195647
  }
195318
195648
  function writeState(state) {
195319
- mkdirSync60(stateDir(), { recursive: true });
195649
+ mkdirSync59(stateDir(), { recursive: true });
195320
195650
  writeFileSync54(statePath(), JSON.stringify(state, null, 2));
195321
195651
  }
195322
195652
  function readState(args) {
@@ -195475,7 +195805,7 @@ function findRepoRoot(tfDir) {
195475
195805
  }
195476
195806
  function writeCloudBuildConfig(image) {
195477
195807
  const cfgPath = join126(stateDir(), "cloudrun-cloudbuild.yaml");
195478
- mkdirSync60(stateDir(), { recursive: true });
195808
+ mkdirSync59(stateDir(), { recursive: true });
195479
195809
  writeFileSync54(
195480
195810
  cfgPath,
195481
195811
  [
@@ -196128,7 +196458,7 @@ var init_poll = __esm({
196128
196458
  });
196129
196459
 
196130
196460
  // src/cloud/download.ts
196131
- import { createWriteStream as createWriteStream5, mkdirSync as mkdirSync61, unlinkSync as unlinkSync10 } from "fs";
196461
+ import { createWriteStream as createWriteStream5, mkdirSync as mkdirSync60, unlinkSync as unlinkSync10 } from "fs";
196132
196462
  import { dirname as dirname58 } from "path";
196133
196463
  async function downloadToFile(url, destPath, options = {}) {
196134
196464
  const fetchImpl = options.fetchImpl ?? fetch;
@@ -196139,7 +196469,7 @@ async function downloadToFile(url, destPath, options = {}) {
196139
196469
  if (!res.body) {
196140
196470
  throw new Error(`Failed to download ${url}: empty response body`);
196141
196471
  }
196142
- mkdirSync61(dirname58(destPath), { recursive: true });
196472
+ mkdirSync60(dirname58(destPath), { recursive: true });
196143
196473
  const totalHeader = res.headers.get("content-length");
196144
196474
  const total = totalHeader ? Number.parseInt(totalHeader, 10) : void 0;
196145
196475
  const totalOpt = total !== void 0 && Number.isFinite(total) ? total : void 0;
@@ -198696,7 +199026,7 @@ var init_parseFigmaRef = __esm({
198696
199026
  });
198697
199027
 
198698
199028
  // ../core/dist/figma/freeze.js
198699
- import { copyFileSync as copyFileSync11, mkdirSync as mkdirSync63, rmSync as rmSync33, statSync as statSync38, writeFileSync as writeFileSync55 } from "fs";
199029
+ import { copyFileSync as copyFileSync11, mkdirSync as mkdirSync61, rmSync as rmSync33, statSync as statSync38, writeFileSync as writeFileSync55 } from "fs";
198700
199030
  import { dirname as dirname59 } from "path";
198701
199031
  function exceedsFreezeCap(byteLength) {
198702
199032
  return byteLength > MAX_FREEZE_BYTES;
@@ -198706,7 +199036,7 @@ function freezeBytes(bytes, destPath) {
198706
199036
  throw new Error("freeze failed: empty bytes");
198707
199037
  if (exceedsFreezeCap(bytes.length))
198708
199038
  throw new Error(`freeze failed: ${bytes.length} bytes exceeds ${MAX_FREEZE_BYTES} cap`);
198709
- mkdirSync63(dirname59(destPath), { recursive: true });
199039
+ mkdirSync61(dirname59(destPath), { recursive: true });
198710
199040
  try {
198711
199041
  writeFileSync55(destPath, bytes, { flag: "wx" });
198712
199042
  } catch (err) {
@@ -198750,7 +199080,7 @@ var init_jsonl = __esm({
198750
199080
  });
198751
199081
 
198752
199082
  // ../core/dist/figma/manifest.js
198753
- import { appendFileSync as appendFileSync2, existsSync as existsSync119, mkdirSync as mkdirSync64, readFileSync as readFileSync85, writeFileSync as writeFileSync56 } from "fs";
199083
+ import { appendFileSync as appendFileSync2, existsSync as existsSync119, mkdirSync as mkdirSync63, readFileSync as readFileSync85, writeFileSync as writeFileSync56 } from "fs";
198754
199084
  import { join as join127 } from "path";
198755
199085
  function mediaDir(projectDir) {
198756
199086
  return join127(projectDir, ".media");
@@ -198783,7 +199113,7 @@ function readManifest(projectDir) {
198783
199113
  return readJsonlValues(manifestPath(projectDir)).filter(isFigmaManifestRecord);
198784
199114
  }
198785
199115
  function appendRecord(projectDir, record) {
198786
- mkdirSync64(typeDirPath(projectDir, record.type), { recursive: true });
199116
+ mkdirSync63(typeDirPath(projectDir, record.type), { recursive: true });
198787
199117
  appendFileSync2(manifestPath(projectDir), JSON.stringify(record) + "\n");
198788
199118
  }
198789
199119
  function findAllByFigmaNode(projectDir, fileKey, nodeId) {
@@ -198845,7 +199175,7 @@ var init_manifest = __esm({
198845
199175
  });
198846
199176
 
198847
199177
  // ../core/dist/figma/mediaIndex.js
198848
- import { mkdirSync as mkdirSync65, writeFileSync as writeFileSync57 } from "fs";
199178
+ import { mkdirSync as mkdirSync64, writeFileSync as writeFileSync57 } from "fs";
198849
199179
  import { dirname as dirname60, join as join128 } from "path";
198850
199180
  function isRow(value) {
198851
199181
  return typeof value === "object" && value !== null;
@@ -198896,7 +199226,7 @@ function regenerateIndex(projectDir) {
198896
199226
  const records = readJsonlValues(manifestPath(projectDir)).filter(isRow);
198897
199227
  const content = generateIndexContent(records);
198898
199228
  const p2 = indexPath(projectDir);
198899
- mkdirSync65(dirname60(p2), { recursive: true });
199229
+ mkdirSync64(dirname60(p2), { recursive: true });
198900
199230
  writeFileSync57(p2, content);
198901
199231
  return content;
198902
199232
  }
@@ -198962,7 +199292,7 @@ var init_sanitizeSvg = __esm({
198962
199292
  });
198963
199293
 
198964
199294
  // ../core/dist/figma/bindings.js
198965
- import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync66, writeFileSync as writeFileSync58 } from "fs";
199295
+ import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync65, writeFileSync as writeFileSync58 } from "fs";
198966
199296
  import { join as join129 } from "path";
198967
199297
  function bindingsPath(projectDir) {
198968
199298
  return join129(mediaDir(projectDir), BINDINGS_FILE);
@@ -198982,7 +199312,7 @@ function readBindings(projectDir) {
198982
199312
  function upsertBindings(projectDir, records) {
198983
199313
  const incoming = new Set(records.map((r2) => r2.figmaId));
198984
199314
  const survivors = readLines(projectDir).filter((line2) => !(isBindingRecord(line2) && incoming.has(line2.figmaId)));
198985
- mkdirSync66(mediaDir(projectDir), { recursive: true });
199315
+ mkdirSync65(mediaDir(projectDir), { recursive: true });
198986
199316
  const lines = [...survivors, ...records].map((r2) => JSON.stringify(r2)).join("\n");
198987
199317
  writeFileSync58(bindingsPath(projectDir), lines.length > 0 ? lines + "\n" : "");
198988
199318
  }
@@ -199851,7 +200181,7 @@ __export(component_exports, {
199851
200181
  default: () => component_default,
199852
200182
  runComponentImport: () => runComponentImport
199853
200183
  });
199854
- import { existsSync as existsSync121, mkdirSync as mkdirSync67, writeFileSync as writeFileSync60 } from "fs";
200184
+ import { existsSync as existsSync121, mkdirSync as mkdirSync66, writeFileSync as writeFileSync60 } from "fs";
199855
200185
  import { join as join133, relative as relative29 } from "path";
199856
200186
  function escapeAttr2(value) {
199857
200187
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
@@ -199868,7 +200198,7 @@ async function runComponentImport(refInput, deps) {
199868
200198
  console.warn(
199869
200199
  `component dir compositions/components/${name} already exists \u2014 overwriting (rename the figma frame for a separate import)`
199870
200200
  );
199871
- mkdirSync67(componentDir, { recursive: true });
200201
+ mkdirSync66(componentDir, { recursive: true });
199872
200202
  const { html, frozenAssets, failedRasterize } = await rasterizeFallback(
199873
200203
  mapped,
199874
200204
  ref2.fileKey,
@@ -200069,7 +200399,7 @@ __export(autoUpdate_exports, {
200069
200399
  scheduleBackgroundInstall: () => scheduleBackgroundInstall
200070
200400
  });
200071
200401
  import { spawn as spawn16 } from "child_process";
200072
- import { appendFileSync as appendFileSync4, mkdirSync as mkdirSync68, openSync as openSync8 } from "fs";
200402
+ import { appendFileSync as appendFileSync4, mkdirSync as mkdirSync67, openSync as openSync8 } from "fs";
200073
200403
  import { homedir as homedir21 } from "os";
200074
200404
  import { join as join134 } from "path";
200075
200405
  import { compareVersions as compareVersions3 } from "compare-versions";
@@ -200086,14 +200416,14 @@ function majorOf(version2) {
200086
200416
  }
200087
200417
  function log(line2) {
200088
200418
  try {
200089
- mkdirSync68(CONFIG_DIR3, { recursive: true, mode: 448 });
200419
+ mkdirSync67(CONFIG_DIR3, { recursive: true, mode: 448 });
200090
200420
  appendFileSync4(LOG_FILE, `${(/* @__PURE__ */ new Date()).toISOString()} ${line2}
200091
200421
  `, { mode: 384 });
200092
200422
  } catch {
200093
200423
  }
200094
200424
  }
200095
200425
  function launchDetachedInstall(invocation, displayCommand, version2) {
200096
- mkdirSync68(CONFIG_DIR3, { recursive: true, mode: 448 });
200426
+ mkdirSync67(CONFIG_DIR3, { recursive: true, mode: 448 });
200097
200427
  const configFile = join134(CONFIG_DIR3, "config.json");
200098
200428
  const nodeScript = `
200099
200429
  const { execFile } = require("node:child_process");