Skip to content

feat(cli): capture-video on-demand fetcher + capture pipeline robustness#1447

Open
ukimsanov wants to merge 1 commit into
mainfrom
feat/cli-capture-video
Open

feat(cli): capture-video on-demand fetcher + capture pipeline robustness#1447
ukimsanov wants to merge 1 commit into
mainfrom
feat/cli-capture-video

Conversation

@ukimsanov

@ukimsanov ukimsanov commented Jun 14, 2026

Copy link
Copy Markdown
Collaborator

What

  • New hyperframes capture-video <project> — on-demand mp4 fetch from the capture manifest.
  • Capture pipeline robustness: structural logo signals, content-hash SVG slugs, SVG → PNG rasterization for Gemini Vision, regex-escape fix.
  • New lintMissingLocalAsset rule — flags <video> / <img> / <source> src pointing at missing local files.

Why

heygen-logo.svg on main today contains Google's wordmark — label-derived slugs picked the wrong DOM context. The new website-to-video skill inherits this and would ship off-brand videos. Same root for: class-substring isLogo catching 0/32 SVGs on heygen.com, Vision hallucinating wordmarks from raw SVG path text, and capture being 100% blocked by a \/ regex collapse in page.evaluate.

How

  • capture-video: safeFetch (SSRF + redirect-hop revalidation), 250 MB cap, content-type whitelist, flag: "wx" exclusive-create write, lookup by entry.index (not array offset — manifest can have gaps).
  • Content-hash SVG slugs (svg-<8-char-sha1>.svg) so filename can never drift from content.
  • sharp rasterizes SVGs to PNG before Vision; polarity detection flips white-glyph SVGs to a dark background.
  • lintMissingLocalAsset masks <!-- --> / <style> / <script> ranges before scanning so commented-out examples aren't false positives.

Test plan

  • bun run --filter @hyperframes/cli typecheck + test pass (17 new capture-video + 70 lintProject cases)
  • Manual: captured 9 sites (heygen, vercel, stripe, linear, notion, huggingface, remotion, posthog, anthropic, airbnb) — all clean; brand mark findable via description grep on each
  • Manual: hyperframes capture-video <dir> --index 0 downloads the heygen.com Orb mp4

// `flag: "wx"` = exclusive create: throws EEXIST if outPath exists.
// Race-free check-and-create in one syscall.
try {
writeFileSync(outPath, buf, { flag: "wx" });
@ukimsanov ukimsanov force-pushed the feat/cli-capture-video branch 3 times, most recently from aaefdfd to e4e8b44 Compare June 14, 2026 22:47
For the hyperframes.dev website-to-video flow. Real-AI-test runs against
heygen.com, huly.io, and heygen-showcase surfaced two gaps: (1) capture's
logo / asset-captioning signals missed modern React/Tailwind builds; and
(2) there was no CLI surface to pull the videos the manifest references.

New command:

  • `hyperframes capture-video <project>` — on-demand downloader for
    entries in capture/extracted/video-manifest.json. Capture writes the
    manifest + preview PNGs but skips the mp4s; this pulls one entry by
    `--index N` (matched against the entry's `index` field, NOT array
    offset — gaps are possible when a preview screenshot fails). SSRF-safe
    via safeFetch, 250 MB cap, content-type whitelist, race-free
    exclusive-create write. Layout-aware (handles both standalone capture
    and W2H project layouts).

Capture pipeline fixes:

  • Structural logo signals (assetCataloger + tokenExtractor): inBanner /
    inHomeLink / matchesTitleBrand. Class-substring alone caught 0/32 SVGs
    on heygen.com — modern builds don't put 'logo' / 'brand' in any
    className.

  • Content-hash SVG slugs (assetDownloader): `svg-<8char-sha1>.svg` —
    label-derived slugs mis-attributed partner-logo carousels
    (heygen-logo.svg actually contained Google, hubspot-logo.svg contained
    Trivago, etc.). Content-hash names are invariant by construction.

  • SVG → PNG rasterization before Gemini Vision (contentExtractor): the
    raw-SVG-as-text path was hallucinating wordmarks (VIVIENNE for HubSpot,
    'wrestling' for Workday). Adds polarity detection so a white-glyph SVG
    flattened to a blank PNG gets inverted before captioning. LOGO tag in
    asset-descriptions.md when structural signals fire (independent of
    Gemini key presence).

  • Double-escape \/ inside the page.evaluate template literal in
    assetCataloger + tokenExtractor: the original `/^https?:\/\/.../`
    collapsed to `/` mid-template and threw `Unexpected token ^`. Capture
    was 100% blocked on this until the escape was fixed.

  • `asset-descriptions.md` header branches on Gemini-key presence with
    an explicit 'Vision OFF — catalog-derived descriptions' warning.

New lint rule:

  • `lintMissingLocalAsset` (cli/utils/lintProject): scans <video> / <img>
    / <source> src for local files that don't exist in the project.
    Empirically the most common sub-agent mistake across multi-URL runs
    (~5+ per run). Uses `resolveExistingLocalAsset` so the existence check
    matches the bundler's notion of 'resolves'. Masks comment / style /
    script ranges before scanning so a literal `<img src=missing.png>`
    inside a tutorial comment isn't reported.

Tests: 17 new for capture-video (safeFilename decoding/sanitization,
VIDEO_CONTENT_TYPE_RE accept/reject, pickManifestEntry index-field lookup
with gaps, URL-mismatch + bad-index rejection, --index over --url
priority); 70 cases under lintProject.test.ts covering the new rule and
existing rules.

Sibling PRs in this stack:
  • #PR_A1 — fix(producer): __dirname ESM banner shim
  • #PR_A2 — fix(core/lint): findRootTag masks comment/style/script
@ukimsanov ukimsanov force-pushed the feat/cli-capture-video branch from e4e8b44 to b56b7c5 Compare June 14, 2026 23:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants