4 Commits
Author SHA1 Message Date
Miguel Ángel 045b3a4fd7 feat(cli): report which catalog items a render actually used (#3470)
`registry_item_added` fires when a catalog block is installed and
`render_complete` fires when a video is produced, but nothing joined them, so
"did this video use the catalog?" had no answer.

`hyperframes add` now records each installed item in `hyperframes.json`
(installed files are plain composition HTML with no provenance marker, so this
manifest is the only record that a file came from the registry), and
`render_complete` reports both the items the project installed and the blocks
the rendered composition actually reaches. An item installed and then never
mounted was tried and dropped, which no add-time event can express.

The scan answering "which sub-compositions does this file mount" now has one
owner, `collectSubCompositionSrcs` in `@hyperframes/parsers`, shared with
lint's `lintMissingOrEmptySubComposition`. It holds two invariants that were
previously restated per call site and got re-derived wrongly: it is a text scan
rather than a DOM query, because `<template>` content is inert and every
sub-composition except the render entry is wrapped in one; and references
resolve root-relative at every nesting level, matching `parseSubCompositions`.
It walks tag by tag rather than running open-ended spans across the whole file,
so a malformed composition cannot stall the render plan.

Also: `registryItems` is declared in the config schema, which closes with
`additionalProperties: false`, with an ajv-backed test pinning every key the CLI
writes; counts are never truncated by the name cap, and the reported used blocks
stay a subset of the reported installed ones, with `registry_items_truncated`
marking a windowed list; and an unreadable manifest reports itself rather than
posing as a project that never used the catalog.
2026-08-24 19:56:06 -04:00
WaterrrForever d287e5244c fix(cli): persist authoring skill in hyperframes.json for durable render attribution (#2762)
* fix(cli): persist authoring skill in hyperframes.json for durable render attribution

authoring_skill was stamped only on the first render through a workflow
passing --skill, so re-renders, `npm run render`, --batch, existing-project
renders, and general-video lost it — leaving 77-96% of real-human render
volume un-attributed and the skills-penetration metric misleadingly low.

Persist the owning skill in hyperframes.json: `init --skill` stamps it at
creation, `render` resolves the flag then falls back to the stored value, and
an explicit --skill seeds it (seed-once, never overwriting the creating
workflow's identity). Activate all render-producing creation workflows to
declare their skill at init.

Forward-only: does not rewrite historical telemetry.

* fix(cli): patch hyperframes.json in place when seeding the authoring skill

seedProjectAuthoringSkill is the only writer that touches an already existing
hyperframes.json — every other writeProjectConfig call site is guarded to write
only when the file is absent, which made the whole-file overwrite safe by
construction. Round-tripping the seed through normalizeConfig broke that: it
rebuilds the object from a field whitelist with no rest-spread, so any key
outside the schema was silently dropped, a media block was materialized in
projects that never had one, and key order was rewritten. hyperframes.json is
normally committed, so a render introduced a diff the user never asked for, and
any field added to the schema later would be deleted by a render on an older
CLI.

Parse the raw JSON, set authoringSkill, write it back, reusing the file's own
indentation. Unknown keys and formatting survive; the only delta is the key
being added. A corrupt config is now left untouched instead of clobbered.

Seed-once semantics are unchanged, still normalized so a hand-edited garbage
slug neither reaches telemetry nor wedges the seed.

Reported independently by both reviewers on #2762.

* fix(cli): create the docker build context with mkdtempSync

The `--docker` build context was created at a guessable path derived from
`Date.now()` in the world-writable OS temp dir. Another local user can
pre-create or symlink that path and have the build read a Dockerfile they
control. mkdtempSync gets a random suffix and 0o700 from the kernel, and it
creates the directory itself, so the separate mkdirSync goes away.

Pre-existing on main (alert #432, 2026-06-04, packages/cli/src/commands/render.ts),
surfaced against this branch only because the seed commit shifted line numbers in
the same file. Fixed here to unblock the CodeQL gate on #2762 rather than left for
a follow-up; the remaining 10 js/insecure-temporary-file alerts elsewhere in the
repo are untouched and still want their own pass.

* fix(cli): drop the check-then-use race when seeding the authoring skill

The seed tested for the config with existsSync and then wrote, which is a
check-then-use race: the file can be created or swapped between the check and
the write (CodeQL js/file-system-race).

Read once and branch on the failure reason instead. Only ENOENT creates a
config from scratch; any other read failure (permissions, I/O) now leaves an
existing file alone rather than overwriting it with a default, so this is also
strictly safer than the version it replaces.

Also replaces the `as Record<string, unknown>` assertion with an isJsonObject
type guard, per the repo's no-assertion convention.

Behaviour unchanged: all 4 seed regression tests still pass, and the
create/preserve/seed-once/corrupt-untouched paths were re-verified end to end.
2026-07-28 19:27:09 +08:00
Miguel Ángel 6458807066 feat(cli): let projects opt out of automatic proxying (#2591)
* feat(studio-server): serve H.264 proxies from the preview route

Wires the codec manifest and the transcoder into the preview surface: the route
negotiates a proxy via a query param and serves it through the existing range
and ETag machinery, composition HTML carries a codec map for the runtime, and
hostile assets pre-warm so a first play does not wait on a cold transcode.
Exposes the three subpath exports the CLI surfaces consume upstack.

Drops the TEMP fallow entry added with the transcoder: it has real importers now.

* fix(studio-server): publish media proxy exports

* fix(parsers): scan HTML comments linearly

* feat(cli): let projects opt out of automatic proxying

Adds media.autoProxy to hyperframes.json plus --proxy/--no-proxy flags, and
forwards the resolved value into the studio and preview servers and the vite
adapter. Lands before the runtime slice that turns auto-proxying on, so the
switch exists before there is any behavior to switch off.

* fix(cli): align media config schema
2026-07-16 23:01:14 -04:00
Miguel Ángel 9ef864d1f2 fix(docs): serve hyperframes.json / registry JSON schemas (#304) (#305)
Closes #304.

## Summary

The three `/schema/*.json` URLs baked into every Hyperframes project as `\$schema` references are 404ing on the live docs site — blocking editor autocomplete and validation.

- \`https://hyperframes.heygen.com/schema/hyperframes.json\` — **404** (missing entirely)
- \`https://hyperframes.heygen.com/schema/registry.json\` — **404** (only in npm package)
- \`https://hyperframes.heygen.com/schema/registry-item.json\` — **404** (only in npm package)

Mintlify serves top-level non-MDX dirs in \`docs/\` at \`/\<dir>/*\` (confirmed by \`docs/logo/*.svg\` → \`/logo/*.svg\`). This PR drops the three schemas into \`docs/schema/\` so the URLs resolve.

## What changed

| File | Role |
|---|---|
| \`docs/schema/hyperframes.json\` | **New.** Authored from the \`ProjectConfig\` type in \`packages/cli/src/utils/projectConfig.ts\`. |
| \`docs/schema/registry.json\` | Mirror of \`packages/core/schemas/registry.json\`. |
| \`docs/schema/registry-item.json\` | Mirror of \`packages/core/schemas/registry-item.json\`. |
| \`scripts/sync-schemas.ts\` | Keeps the registry mirrors in lockstep with their authoritative copies in \`packages/core/schemas/\`. \`--check\` mode fails the Docs workflow on drift. |
| \`.github/workflows/docs.yml\` | Runs \`tsx scripts/sync-schemas.ts --check\` on every PR touching docs or core schemas. |
| \`package.json\` | \`sync-schemas\` / \`sync-schemas:check\` npm scripts. |

## Why not make \`packages/core/schemas/\` authoritative for \`hyperframes.json\` too?

\`hyperframes.json\` is CLI config, not a core type. Keeping the schema in \`docs/\` avoids an artificial dependency between \`@hyperframes/core\` and \`@hyperframes/cli\`. If the two ever need to align, we can flip the direction then.

## Verification

- \`bun run sync-schemas:check\` → \`2/2 in sync\`.
- Ajv (draft 2020-12, in-process) validation against 9 cases:
  - ✓ real factory-series-c-video config
  - ✓ default shape from \`hyperframes init\`
  - ✓ \`\$schema\` is optional
  - ✓ missing registry → rejected
  - ✓ missing paths.assets → rejected
  - ✓ extra top-level key → rejected
  - ✓ empty registry string → rejected
  - ✓ empty block path → rejected
  - ✓ missing paths entirely → rejected

## Test plan

- [x] \`tsx scripts/sync-schemas.ts --check\` passes locally
- [x] Schemas parse as valid JSON and validate real/default project configs
- [x] After merge: \`curl -sI https://hyperframes.heygen.com/schema/hyperframes.json\` returns 200 once Mintlify redeploys
- [x] Same check for \`/schema/registry.json\` and \`/schema/registry-item.json\`
- [x] VS Code autocomplete and error-highlighting work on \`hyperframes.json\` without extra config

## Notes

- The Docs workflow now triggers on \`packages/core/schemas/**\` and \`scripts/sync-schemas.ts\` in addition to \`docs/**\`, so a core-schemas change that forgets to run \`sync-schemas\` will fail CI instead of silently publishing stale docs.
- No runtime / API changes to any package; ship independent of a version bump.
2026-04-17 17:43:43 +02:00