Commit Graph
4089 Commits
Author SHA1 Message Date
JamesandClaude Opus 4.6 09518890ea refactor(cli): migrate skills install to npx skills add
Replace custom git clone + copy logic with `npx skills add` from the
vercel-labs/skills ecosystem. This is the standard used by GSAP,
Remotion, and other skill providers.

- Primary path: `npx skills add heygen-com/hyperframes` and
  `npx skills add greensock/gsap-skills` with `-g -y -a <agent>` flags
- Git clone + copy kept as fallback if npx is unavailable
- Added `skillsAgent` field to targets mapping CLI flags to the correct
  agent names (e.g., gemini → gemini-cli)
- Removes ~80 lines of custom git/copy infrastructure

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 19:16:08 +00:00
JamesandClaude Opus 4.6 42c79f3bd3 feat(cli): scaffold CLAUDE.md and project-level skills into init
New HyperFrames projects created via `hyperframes init` now include:

- CLAUDE.md + AGENTS.md — teaches AI agents about skills, commands,
  project structure, and framework rules (class="clip", timeline
  registration, determinism). Agents know to invoke /compose-video
  before writing compositions.
- .claude/skills/{compose-video,captions} — project-level skills for
  immediate availability in the current agent session (global skills
  require a session restart to discover).
- Updated next-steps output with `hyperframes docs <topic>` and a
  link to hyperframes.heygen.com.
- Updated README with "AI Agent Skills" section documenting
  `npx hyperframes skills` and `npx skills add` install paths.
- Repo-level CLAUDE.md for framework contributors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 19:15:54 +00:00
Miguel Ángel 1593055482 fix(core): filter decorative elements from timeline clip collection (#92)
## What

Enhanced timeline collection to discover GSAP-animated scene elements and persistent overlays through runtime introspection.

## Why

The existing timeline collection only captured elements with explicit timing attributes (`data-start`, `data-track-index`) or media elements, missing scene elements that are animated purely through GSAP tweens and persistent overlay elements that should appear for the full composition duration.

## How

Added two new discovery mechanisms to `collectRuntimeTimelinePayload`:

1. **GSAP Timeline Introspection**: Walks the master timeline's tweens using `getChildren()` to find animated elements, calculates absolute time ranges by traversing parent timelines, and bubbles child tween ranges up to their nearest scene-level ancestors (direct children of root with IDs).
2. **Persistent Overlay Detection**: Identifies direct children of the root composition that weren't captured by DOM queries or GSAP introspection, treating them as full-duration overlay elements while filtering out non-visual elements (script, style, meta tags) and hidden elements.

Both mechanisms respect existing track assignments and create new tracks when needed to avoid conflicts.

## Test plan

- [x] Unit tests added/updated
- [x] Manual testing performed
- [ ] Documentation updated (if applicable)

Added comprehensive test coverage for:

- GSAP-animated scene element discovery via timeline introspection
- Time range bubbling from child elements to scene ancestors
- Persistent overlay inclusion as full-duration clips
- Proper filtering of non-visual elements (script/style tags)

![image.png](https://app.graphite.com/user-attachments/assets/106ea699-e243-493d-aa76-a74f303b80c3.png)

![image.png](https://app.graphite.com/user-attachments/assets/2d124d15-c933-4e12-891c-06100e6241c3.png)
2026-03-27 18:31:46 +01:00
James Russo 4922700921 Merge pull request #90 from heygen-com/fix/simplify-release-workflow
chore(ci): simplify release to a single workflow
2026-03-27 00:35:05 -07:00
JamesandClaude Opus 4.6 bce1a11e71 chore(ci): simplify release process to a single workflow
The 3-workflow chain (release.yml → release-tag.yml → publish.yml) was
broken by design: tags created by GITHUB_TOKEN don't trigger other
workflows, so merging a release PR never actually published.

Consolidate into a single publish.yml that triggers on both tag push
and release PR merge. Delete the redundant prepare-release and
tag-release workflows.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 07:32:38 +00:00
James Russo b7ce417745 Merge pull request #86 from heygen-com/release/v0.1.6
chore: release v0.1.6
v0.1.6
2026-03-27 00:23:29 -07:00
James Russo 651e004a6c Merge pull request #88 from heygen-com/fix/blank-template-full-html-document
fix(cli): make blank template a full HTML document
2026-03-27 00:23:12 -07:00
Miguel Ángel 5fba109fee ci: scope regression tests to core/producer/engine packages only (#89)
## What

Updated the GitHub Actions regression workflow to monitor specific package directories instead of the entire packages folder.

## Why

This change provides more granular control over when regression tests are triggered, allowing the workflow to run only when changes are made to the core, producer, or engine packages rather than any package in the repository.

## How

Modified the path filters in the regression workflow to explicitly list the three critical package directories (`packages/core/**`, `packages/producer/**`, `packages/engine/**`) instead of using the broad `packages/**` pattern.

## Test plan

How was this tested?

- [ ] Unit tests added/updated
- [ ] Manual testing performed
- [ ] Documentation updated (if applicable)
2026-03-27 08:11:49 +01:00
James Russo d3262f78a0 Merge pull request #87 from heygen-com/fix/dev-sigint-exit
fix(cli): exit on Ctrl+C during `hyperframes dev`
2026-03-27 00:07:59 -07:00
Miguel ÁngelandClaude Opus 4.6 d52ea88df5 fix(cli): make blank template a full HTML document
The blank template was a bare `<div>` fragment without `<!DOCTYPE>`,
`<head>`, or `<body>`. This caused:
- Blank preview (bundler/runtime can't initialize from a fragment)
- "Failed to run lint" (parsing errors on the malformed document)

All other templates (swiss-grid, vignelli, warm-grain, play-mode) are
proper HTML documents — blank was the only outlier.

Also improves lint error reporting to show the actual error message
instead of the generic "Failed to run lint."

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 02:50:53 -04:00
JamesandClaude Opus 4.6 89beb95a87 fix(cli): exit on Ctrl+C during hyperframes dev
The SIGINT handlers that were meant to do graceful cleanup (close server,
remove symlinks) override Node's default exit behavior. If the cleanup
hangs (e.g. server.close() blocked by open connections), the process is
stuck and Ctrl+C does nothing.

Fix: don't intercept SIGINT at all. Node's default behavior exits the
process immediately on Ctrl+C. The OS reclaims the port and file handles.
Use process.on("exit") for best-effort symlink cleanup instead.

Also fixes running the CLI via `tsx` in dev mode — __CLI_VERSION__ is a
tsup build-time define that crashes at runtime without a fallback.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 06:35:41 +00:00
github-actions[bot] 55ff721552 chore: release v0.1.6 2026-03-27 06:04:18 +00:00
James Russo 2915b68633 fix(cli): handle port collisions in dev server with auto-increment (#85)
## Summary
- Fix silent failure when `hyperframes dev` port (default 3002) is already in use (e.g., by Cursor IDE)
- Replace TOCTOU-prone `isPortAvailable()` probe with `serveWithPortFallback()` that binds the real server directly
- Auto-increment to next available port with a visible yellow warning, or show a clear error when all ports (range of 10) are exhausted

## What changed
The old approach used a throwaway `net.createServer()` to test port availability, closed it, then opened the real Hono server — a classic TOCTOU race. The fallback also silently returned the original port when all 10 were taken.

Now we use `createAdaptorServer()` (creates the Hono HTTP server without binding) and manually call `.listen(port)`, catching `EADDRINUSE` to try the next port. This eliminates the race entirely.

**Before:** `hyperframes dev` says "Studio running at http://localhost:3002" even when port 3002 belongs to another process.

**After:**
- Port available: works as before
- Port taken: `Port 3002 is in use, using 3003 instead` (yellow warning)
- All ports taken: `Ports 3002–3011 are all in use. Use --port to specify a different port.` (error + exit)

## Testing
- Verified TypeScript compiles cleanly (`tsc --noEmit`)
- All pre-commit hooks (lint, format, commitlint) pass
- Manual test: run another server on 3002, then `hyperframes dev` → confirms auto-increment message appears

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-03-26 23:02:50 -07:00
JamesandClaude Opus 4.6 f3964f3e45 fix(cli): handle port collisions in dev server with auto-increment
The embedded dev server (hyperframes dev) silently reported success even
when the requested port was already in use (e.g., by Cursor IDE on 3002).
This happened because the old isPortAvailable() probe had a TOCTOU race
and findAvailablePort() silently fell back to the original port on exhaustion.

Replace with serveWithPortFallback() that binds the real Hono server
directly via createAdaptorServer + manual listen(), retrying on EADDRINUSE.
Shows a yellow warning when auto-incrementing and a clear error when all
ports are exhausted.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 05:15:30 +00:00
James Russo e0ee983e19 Merge pull request #81 from heygen-com/feat/webm-transparency
feat(render): WebM output with VP9 alpha transparency
2026-03-26 22:07:57 -07:00
JamesandClaude Opus 4.6 83d11ac595 refactor: simplify review fixes for WebM PR
- Use static import for copyFileSync (was unnecessary dynamic import)
- Shallow-copy config before mutating forceScreenshot (prevents
  caller-provided config from being permanently modified)
- Consolidate isWebm/isWebmRender/outputFormat into single early
  declaration in renderOrchestrator
- Fix debug output extension for WebM (was hardcoded .mp4)
- Log unexpected audio extraction errors instead of silently swallowing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 03:40:35 +00:00
Miguel Ángel dc0d69a21b feat(producer): support entryFile for rendering individual compositions (#42)
## Changes

- Add optional `entryFile` parameter to render API endpoints (`/v1/render` and `/v1/render-stream`)
- Enable rendering individual sub-compositions by extracting them from index.html context when the entry file is a `<template>` wrapper
- Change base64 audio/video linting from detecting "fabricated" media to prohibiting all inline base64 media
- Add manifest path resolution for bundled producer deployments

## API Changes

- `RenderConfig` — new optional `entryFile` field for specifying HTML file to render
- `server.ts` — parses `entryFile` from request body, validates file exists in project directory
- `executeRenderJob` — uses `entryFile` instead of hardcoded `"index.html"`

## Template Extraction

- `extractStandaloneEntryFromIndex` — extracts sub-composition hosts from index.html and creates standalone render context
- Handles `<template>` entry files by finding matching `data-composition-src` in index.html and isolating that host
- Resets `data-start` to 0 for standalone rendering

## Linting Updates

- Change rule #3.7 from detecting "fabricated" base64 media to prohibiting all inline base64 audio/video
- Lower detection threshold from 100+ to 20+ base64 characters
- All base64 media now triggers error severity with clearer messaging about file size bloat

## Usage

```json
POST /v1/render-stream
{ "projectDir": "/path/to/project", "entryFile": "compositions/intro.html" }
```

Omit `entryFile` for default behavior (renders `index.html`).
2026-03-27 04:28:51 +01:00
James Russo 244a9cbe60 Merge pull request #80 from heygen-com/docs/update-for-recent-features
docs: update CLI docs for dev server, version checks, and agent output
2026-03-26 20:13:15 -07:00
JamesandClaude Opus 4.6 684b103ba4 docs: add WebM transparency docs to engine, producer, and CLI examples
- Engine: document getEncoderPreset() for MP4/WebM, VP9 alpha flags,
  Opus audio in mux step
- Producer: document format field in RenderConfig, WebM usage example,
  pipeline steps updated for WebM
- CLI: add render command examples in --help (including WebM overlay)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 02:45:09 +00:00
JamesandClaude Opus 4.6 1ac9d27b45 docs: add WebM transparency documentation and examples
Document the --format webm flag, VP9 alpha output, overlay workflow
with FFmpeg, and transparent background requirement for compositions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 02:42:49 +00:00
JamesandClaude Opus 4.6 8d47665149 fix(render): update WebM golden baseline with working alpha
The VP9 alpha encoding works correctly — verified by overlaying on a
green background with explicit VP9 decoder. FFmpeg's default decoder
doesn't expose VP9 alpha through ffprobe, but browsers and VP9-aware
decoders read it correctly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 02:42:16 +00:00
JamesandClaude Opus 4.6 efc7b82755 fix(render): fix WebM transparency — use screenshot mode + proper CDP setup
- Force screenshot capture mode for WebM (beginFrame doesn't support
  alpha channel in chrome-headless-shell)
- Set Emulation.setDefaultBackgroundColorOverride once during session
  creation (matching experiment-framework's approach)
- Fix acquireBrowser to pass executable path in screenshot mode on
  Linux (was setting undefined, causing puppeteer-core to fail)
- Add Page.captureScreenshot params: fromSurface, captureBeyondViewport,
  optimizeForSpeed (matching experiment-framework)
- Fix regression harness extractMonoPcm16 for videos without audio
- Add getEncoderPreset unit tests
- Add webm-transparency regression test with golden baseline

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 02:35:03 +00:00
James Russo b02017f8e2 Merge pull request #79 from heygen-com/feat/version-check-system
feat(cli): version check system with agent-friendly output
2026-03-26 19:25:39 -07:00
JamesandClaude Opus 4.6 a146c9e527 test(render): add WebM regression test and getEncoderPreset unit tests
- New regression test `webm-transparency`: minimal transparent composition
  rendered to WebM, validates VP9 codec and visual quality at 100
  checkpoints against golden baseline
- Extend regression harness: `renderConfig.format` field ("mp4" | "webm"),
  format-aware output paths and snapshot filenames
- Fix `extractMonoPcm16` to gracefully handle videos without audio
  streams (WebM without audio was throwing instead of returning empty)
- Unit tests for `getEncoderPreset()`: VP9/yuva420p for WebM,
  h264/yuv420p for MP4, preset mapping, quality preservation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 02:19:43 +00:00
JamesandClaude Opus 4.6 15f6b86ac0 feat(render): add WebM output with VP9 alpha transparency
Support rendering compositions with transparent backgrounds via
`--format webm`. VP9+alpha is the standard format for overlayable
video (captions, lower thirds, overlays).

Changes by layer:
- CLI: `--format mp4|webm` flag on render command
- Producer: threads format through RenderConfig, switches to PNG
  capture and VP9 encoding when webm
- Engine: getEncoderPreset() returns VP9 config with yuva420p;
  transparent page background via CDP when capturing PNG;
  mux uses Opus audio for WebM; VP9 flags from production:
  -row-mt 1, -auto-alt-ref 0, alpha_mode=1 metadata
- Frame capture: Emulation.setDefaultBackgroundColorOverride a=0

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 02:12:39 +00:00
Miguel Ángel 9262d01109 fix(producer): fall back to data-duration when GSAP timeline is empty (#78)
## What

When a composition has an empty GSAP timeline (no animations), `window.__hf.duration` was always 0, causing `hyperframes render` to time out after 45 seconds waiting for `duration > 0`.

## Why

The HF bridge script reads duration from `window.__player.getDuration()`, which returns the GSAP timeline duration. An empty timeline has duration 0. The render engine waits for `window.__hf.duration > 0` to confirm the runtime is ready — so compositions with no animations would always deadlock.

This hits the `--template blank` scaffold immediately: it generates an empty GSAP timeline and relies solely on `data-duration="10"` for composition timing.

## How

One-line change to the bridge script: when `getDuration()` returns 0, fall back to reading `data-duration` from the root `[data-composition-id]` element. This is the same value the static compiler already extracted — so we get a correct duration without any extra browser round-trips.

```js
get duration() {
  var d = p.getDuration();
  return d > 0 ? d : getDeclaredDuration(); // reads data-duration from root element
}
```

## Test plan

- [x] `hyperframes init my-video --template blank && hyperframes render my-video` completes successfully
- [x] Output: `output.mp4` — 10s, 1920×1080, 30fps ✓  
- [x] Compositions with actual GSAP animations unaffected (fallback only triggers when timeline duration is 0)
- [x] Build passes, lint/format clean
2026-03-27 02:56:52 +01:00
James c463f8c699 ci: retrigger checks 2026-03-27 01:51:51 +00:00
JamesandClaude Opus 4.6 31aa45ba3a docs: update CLI docs for dev server, version checks, and --port flag
- Document the three dev server modes (embedded/local studio/monorepo)
- Add --port flag to dev command
- Document _meta envelope on all --json commands
- Document upgrade --check --json for agent consumption
- Document passive update notices and HYPERFRAMES_NO_UPDATE_CHECK
- Update doctor output example with Version check row
- Fix README default port from 3000 to 3002

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 01:44:28 +00:00
JamesandClaude Opus 4.6 cd15c68a2e feat(cli): add version check system with agent-friendly output
- New `updateCheck.ts` utility: cached npm registry check (24h TTL),
  sync `getUpdateMeta()` for _meta envelope, `printUpdateNotice()` for
  passive stderr banner
- `upgrade --check --json`: machine-readable version check for AI agents
  Returns { current, latest, updateAvailable }
- `_meta` envelope on all --json commands (info, lint, benchmark,
  compositions): includes version, latestVersion, updateAvailable
- `doctor` shows version check as first row
- Passive update notice on stderr after command completes (skipped in
  CI, non-TTY, --json, --quiet)
- Background check fires on startup (non-blocking, populates cache)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 01:41:34 +00:00
github-actions[bot]andgithub-actions[bot] ffd0fb678f chore: release v0.1.5 (#77)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
v0.1.5
2026-03-27 01:50:05 +01:00
James Russo 101ff633ca Merge pull request #76 from heygen-com/fix/cli-version-port-docker-hint
fix(cli): inject version from package.json, fix docker hint, add --port to dev
2026-03-26 17:46:44 -07:00
Miguel ÁngelandClaude Sonnet 4.6 9d2362990b fix(cli): inject version from package.json, fix docker hint, add --port to dev
- version.ts: replace hardcoded "0.1.0" with __CLI_VERSION__ injected by
  tsup at build time from package.json — fixes version mismatch where
  `hyperframes --version` reported 0.1.0 while package was 0.1.4
- tsup.config.ts: add define.__CLI_VERSION__ using package.json version
- render.ts: renderDocker error handler showed "Try --docker" even when
  already using --docker — changed to "Check Docker is running: docker info"
- dev.ts: add missing --port arg to embedded mode; findAvailablePort now
  starts from the user-supplied port instead of hardcoded 3002

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 00:45:36 +00:00
James Russo fd9d56f7e9 Merge pull request #75 from heygen-com/feat/embedded-dev-server
feat(cli): implement embedded dev server for hyperframes dev
2026-03-26 17:28:15 -07:00
JamesandClaude Opus 4.6 c34cbedbc7 fix(cli): resolve typecheck errors in studioServer
Add nullish coalescing for regex match groups that TypeScript flags as
possibly undefined.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 23:56:14 +00:00
JamesandClaude Opus 4.6 80cc643ac5 feat(cli): add render endpoint to embedded studio server
Wire up the "Export MP4" button in the studio UI. The render runs
async in the same process using @hyperframes/producer's executeRenderJob,
with SSE progress streaming and MP4 download on completion.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 23:53:29 +00:00
JamesandClaude Opus 4.6 5ace89d634 feat(cli): implement embedded dev server for hyperframes dev
When installed via npx, `hyperframes dev` now starts a standalone
Hono HTTP server that serves the pre-built studio SPA and implements
the project API (file listing, read/write, preview bundling,
sub-composition rendering, runtime serving, SSE file watching).

Three modes are auto-detected:
1. Monorepo dev (running from .ts source) → spawn Vite (existing)
2. Local @hyperframes/studio installed → spawn Vite via package (new)
3. Default → embedded Hono server (new, zero extra deps needed)

Also patches the studio SPA to use EventSource SSE fallback when
Vite HMR is unavailable (production/embedded builds).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 23:34:00 +00:00
Miguel Ángel e554710646 fix(producer): propagate data-start to inner composition element during compilation (#73)
## Summary
- copy data-start from the host sub-composition node to the inlined inner composition root
- preserve the correct runtime offset lookup for nested compositions after producer compilation
- avoid nested GSAP timelines snapping to their end state when the host starts later than t=0
2026-03-27 00:20:16 +01:00
James Russo 09fdc31e33 Merge pull request #72 from heygen-com/fix/cli-publish-name
fix(ci): rename CLI package to match npm and make publish idempotent
v0.1.4
2026-03-26 15:44:51 -07:00
JamesandClaude Opus 4.6 bac66c2152 fix(ci): keep @hyperframes/cli name, rewrite to hyperframes at publish time
Reverting the package rename — Vance needs @hyperframes/cli for local
dev workflow. Instead, rewrite the name to "hyperframes" in the publish
workflow just before npm publish, so the monorepo name stays intact.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 22:36:06 +00:00
JamesandClaude Opus 4.6 1e2875ab56 chore: update lockfile for CLI package rename
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 22:27:03 +00:00
JamesandClaude Opus 4.6 70e9dfca30 fix(ci): rename CLI package to match npm and make publish idempotent
The CLI is published to npm as unscoped `hyperframes` but the
package.json had `@hyperframes/cli`, causing ENEEDAUTH on publish
(wrong scope for the npm token).

Also replace per-step continue-on-error with a single publish script
that skips already-published versions and fails on real errors, making
re-runs safe.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 22:25:36 +00:00
James Russo 30f9ec4927 Merge pull request #70 from heygen-com/release/v0.1.4
chore: release v0.1.4
2026-03-26 15:10:58 -07:00
github-actions[bot] 87fc3e3d8f chore: release v0.1.4 2026-03-26 22:09:12 +00:00
James Russo 81e6cb92f0 Merge pull request #69 from heygen-com/fix/embed-fonts-at-build-time
fix(cli): make npx hyperframes render work outside monorepo
2026-03-26 15:08:22 -07:00
JamesandClaude Opus 4.6 e746debb1a fix(ci): generate font data in Docker build instead of committing
The generated font data is a pure function of the generator script +
@fontsource package versions — no reason to store 566KB of base64
blobs in git. Generate it during the Docker test image build instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 21:34:25 +00:00
JamesandClaude Opus 4.6 879a1a1ae5 fix(ci): commit generated font data for regression tests
The regression CI runs the producer source directly via tsx (not the
bundled CLI), so it needs fontData.generated.ts to exist at import
time. Remove from .gitignore and commit the generated file.

Mark as linguist-generated in .gitattributes so GitHub collapses it
in PR diffs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 21:22:32 +00:00
JamesandClaude Opus 4.6 c926985b63 fix(cli): copy pre-built runtime artifacts instead of regenerating
The previous approach called loadHyperframeRuntimeSource() to regenerate
the runtime IIFE, but its output lacked the trailing newline present in
the pre-built artifact, causing a SHA256 checksum mismatch with the
manifest. Copy the pre-built files from core/dist directly instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 21:15:56 +00:00
JamesandClaude Opus 4.6 2280273f79 fix(cli): remove empty src attrs from swiss-grid audio elements
Empty src="" on <audio> elements causes the browser to fetch the
current page URL as the audio source, producing 404 errors in the
console during rendering. These are placeholder sound effect slots
whose src should be set dynamically when audio is configured.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 21:06:00 +00:00
JamesandClaude Opus 4.6 e4a145a809 fix(cli): pass resolved browser path to engine for rendering
The CLI's ensureBrowser() finds Chrome via env var, cached download,
or system paths (including /Applications/Google Chrome.app on macOS).
But the result was discarded — the engine's acquireBrowser() only
checked the puppeteer cache for chrome-headless-shell, passing
undefined executablePath on macOS without headless-shell installed.

Bridge the two by setting PRODUCER_HEADLESS_SHELL_PATH from the CLI's
resolved browser path before creating the render job, so the engine's
resolveConfig() picks it up.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 21:05:36 +00:00
JamesandClaude Opus 4.6 68f86f66ac fix(cli): ship runtime manifest alongside CLI bundle
The hyperframeRuntimeLoader uses import.meta.url to resolve
hyperframe.manifest.json as a sibling file. When bundled into the CLI
via tsup, import.meta.url points to dist/cli.js, so the sibling lookup
checks dist/hyperframe.manifest.json — which wasn't being shipped.

Copy the manifest and the canonical-named runtime IIFE into dist/ during
the build-runtime step so the loader's sibling-path resolution works in
published npm packages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 21:02:36 +00:00