Commit Graph
5 Commits
Author SHA1 Message Date
Miguel Ángel cf40ba987f Remove tunnel-based HyperFrames publish flow 2026-04-22 01:04:59 -04:00
Miguel Ángel 66df6fa922 feat(cli): hyperframes publish — share projects via a public URL
One-command share: `hyperframes publish` starts the preview server,
opens a public HTTPS tunnel to it, and prints a URL the user can paste
into Slack / Figma / iPhone Safari / anywhere. Token-gated, read-only
by default, explicit consent before exposure, auto-rebuilds the studio
if its assets are missing so users never see a broken tunnel.

# What changed

- packages/cli/src/commands/publish.ts — new command. Consent prompt,
  mint session token, start preview through a fetch wrapper that gates
  the studio server, open the tunnel, print the URL. Ctrl-C reaps both
  children cleanly.
- packages/cli/src/utils/tunnel.ts — provider detection
  (cloudflared preferred, tuns.sh ssh fallback) and openTunnel that
  spawns the child and resolves with the first emitted public URL.
- packages/cli/src/utils/publishSecurity.ts — shared-secret token gate,
  mutation gate (read-only by default), read-only UI injection,
  interactive consent prompt.
- packages/cli/src/server/studioServer.ts — StudioAssetsMissingError
  thrown at construction if dist is missing (no lazy-500 at request
  time). Fixed the dev-fallback path: was three levels up at
  `hyperframes-oss/studio/dist`, now correctly two at
  `packages/studio/dist`.
- packages/cli/scripts/build-copy.sh — replaces the old silent
  `cp -r ../studio/dist/*`. Runs under `set -euo pipefail`, asserts
  the source index.html exists before copying, asserts the
  destination index.html exists after. A zero-file copy can no longer
  ship a broken CLI bundle.
- Root package.json — build now serializes studio first, then the
  rest in parallel. CLI dropped its nested `build:studio`; the two
  parallel `vite build` invocations were clobbering each other's
  packages/studio/dist.
- packages/cli/src/cli.ts, src/help.ts — register publish in the
  CLI, add it to the Getting Started group and root examples.
- docs/packages/cli.mdx — documents the command, flags, and the
  read-only security model.

# Tunnel

Auto-picks the first available of:

1. cloudflared — Cloudflare's free quick tunnel. Most reliable.
   `brew install cloudflared` (Linux/Windows binaries equivalent). No
   account, no keys, no config.
2. tuns.sh — zero-install ssh -R fallback. Works on every machine
   with OpenSSH.

`--provider cloudflared | tuns | auto` forces one.

# Security hardening

The studio server was built for localhost and everything is
unauthenticated. PUT/POST/DELETE/PATCH on /api/projects/:id/files/*
reads and overwrites the project directory. Exposing that surface
unguarded over a public URL is how you ship a "drop a keylogger in
their index.html" bug. Four composed defences:

1. Token gate. On startup, mint a 32-byte base64url token via
   crypto.randomBytes. Public URL is
   `${tunnelUrl}/?t=${token}#project/${name}`. First hit validates in
   constant time (timingSafeEqual), sets an httpOnly; Secure;
   SameSite=Lax session cookie, and 302-redirects to a clean URL so
   the token never lands in browser history or Referer. Every
   subsequent request needs the cookie. Anything else returns 404
   (deliberately, not 401/403 — do not advertise that a server is
   here). Cookie TTL: 12 h. Implemented as a fetch wrapper rather
   than a Hono middleware because the studio routes are registered
   before publish gets the app; middlewares only apply to routes
   registered after them. Wrapping fetch gates traffic at the HTTP
   boundary and sidesteps ordering entirely.
2. Mutation gate. When --allow-edit is not set, refuse writes/deletes
   on `/api/projects/:id/files/*`,
   `POST /api/projects/:id/duplicate-file`, and
   `POST /api/projects/:id/render`. Returns
   `403 { "error": "forbidden", "reason": "…re-run with --allow-edit…" }`.
   Authenticated reads still work.
3. Read-only UI enforcement. The mutation gate closes the server
   side, but the studio UI does not know about the mode. Without
   this layer, visitors would type into a Monaco editor whose saves
   silently 403. Every response carries
   `X-HF-Publish-Readonly: 1`. HTML responses from the SPA (NOT
   `/api/*`, so the composition bundle served inside the player
   iframe is untouched) get a small inline <style> + <script>
   injected before `</head>`. The style neutralises Monaco /
   CodeMirror surfaces via pointer-events: none,
   caret-color: transparent, user-select: none. The script sets
   window.__HF_PUBLISH_READONLY__ = true, renders a compact pill
   centered at the top of the viewport ("READ-ONLY · Published
   tunnel"), and monkey-patches window.fetch to short-circuit
   mutating calls client-side so the UI does not loop through
   doomed 403s.
4. Explicit consent on startup. Before the server binds, a clack
   confirm prompt spells out the exposure: token-is-a-password
   guidance, the 12 h session lifetime, and — when --allow-edit is
   set — a warning that visitors can read/write/delete project
   files. --yes / -y skips for scripts. In non-TTY environments
   without --yes, publish refuses to proceed rather than silently
   exposing.

# Auto-repair for missing studio assets

Previously, running publish against a dev checkout without a built
studio produced "Studio not found. Rebuild with: pnpm run build" on
every request — after the tunnel was open and the URL shared. We
fix it instead: createStudioServer throws
StudioAssetsMissingError at construction, publish catches it,
spawns `bun run build` in packages/studio with a spinner, retries
createStudioServer once the build succeeds. The error surface only
fires when auto-rebuild itself fails (broken global install, etc.),
and lists every path searched plus the `bun install && bun run
build` guidance. End-to-end: deleting both packages/studio/dist
and packages/cli/dist/studio and running publish now rebuilds the
studio and opens the tunnel without user intervention.

# Flags

- --port <n>               Preview port (default 3002).
- --provider <p>           cloudflared | tuns | auto.
- --allow-edit             Remote visitors can write/delete files.
                           Off by default.
- --yes / -y               Skip the consent prompt.

# Properties worth calling out

- No info leak on unauthenticated probes — random visitors see 404
  whether a path exists or not.
- Token never leaves the first URL — redirect strips it before any
  HTML is served, so no iframe Referer can leak it.
- Constant-time comparison for both token and cookie checks.
- No persisted state — token lives for the lifetime of the command,
  Ctrl-C nukes it.
- Read-only cannot be bypassed by a malicious client; the
  server-side 403 is the real stop.
- zero-file build:copy can no longer ship a broken CLI.

# Tests

- src/utils/publishSecurity.test.ts — 23 cases: token exchange,
  cookie flow, mutation-gate allow/deny matrix,
  X-HF-Publish-Readonly header, HTML-injection + no-op on
  --allow-edit / non-HTML / /api/* paths.
- src/utils/tunnel.test.ts — 7 cases for the URL extractor
  (per-provider banner parsing, cross-provider contamination
  guard) and provider picker semantics.
- src/server/studioServer.test.ts — StudioAssetsMissingError shape
  and export for downstream catch blocks.
- Full CLI suite: 144/144 pass.
- tsc --noEmit clean, tsup build clean.
2026-04-18 02:52:12 +02: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 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
Vance IngallsandClaude Opus 4.6 9f8e5ba5a1 initial code (#2)
* feat: initial code port from hyperframes-internal

Port all OSS-ready packages from the internal monorepo:
- @hyperframes/core — shared types, HTML generation, GSAP utilities, runtime
- @hyperframes/cli — CLI for creating, previewing, and rendering compositions
- @hyperframes/engine — framework-agnostic rendering engine (BeginFrame + FFmpeg)
- @hyperframes/producer — video rendering pipeline (Puppeteer + FFmpeg)
- @hyperframes/ui-player — browser-based video player component
- @hyperframes/studio — composition editor (React frontend + Hono backend)

Includes regression test suite with Docker-based test harness.

All HeyGen-internal references, deployment infrastructure, and
proprietary assets have been removed. Package names migrated
from @app/* to @hyperframes/*.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: scrub internal codenames and stale references from OSS port

- Replace static.heygen.ai runtime URLs in test fixtures
- Remove internal CDN publish script (publish-hyperframe-runtime.ts)
- Replace sandbox-studio, sandbox-interceptor, __magicEditRuntime
  with neutral names (studio, hyperframe-runtime, __hyperframeRuntime)
- Fix stale Vault API / localhost references in docs
- Remove broken deprecated_studio link

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: remove remaining internal codenames and stale references

- Delete stale producer README.md and PIPELINE.md (referenced nonexistent files)
- Replace "Cerberus" codename with "HyperFrames" in test design reviews
- Replace magic-edit postMessage identifiers with hf-preview/hf-parent
- Rename debug-magic-edit-timeline.ts to debug-timeline.ts
- Replace "Motion Cut" with "HyperFrames" in Timeline comments
- Fix studio/CLI references to nonexistent archive package
  (use local data/projects/ dir, stub render proxy)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 22:43:56 -07:00