frameyard 0.0.0-stage → 0.1.0

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.
Files changed (127) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/GLOSSARY.md +145 -0
  3. package/LICENSE +21 -0
  4. package/README.md +302 -2
  5. package/dist/assets/executable.d.ts +16 -0
  6. package/dist/assets/executable.d.ts.map +1 -0
  7. package/dist/assets/executable.js +59 -0
  8. package/dist/assets/executable.js.map +1 -0
  9. package/dist/assets/files.d.ts +4 -0
  10. package/dist/assets/files.d.ts.map +1 -0
  11. package/dist/assets/files.js +30 -0
  12. package/dist/assets/files.js.map +1 -0
  13. package/dist/assets/integrations.d.ts +15 -0
  14. package/dist/assets/integrations.d.ts.map +1 -0
  15. package/dist/assets/integrations.js +15 -0
  16. package/dist/assets/integrations.js.map +1 -0
  17. package/dist/assets/process.d.ts +29 -0
  18. package/dist/assets/process.d.ts.map +1 -0
  19. package/dist/assets/process.js +97 -0
  20. package/dist/assets/process.js.map +1 -0
  21. package/dist/assets/provenance.d.ts +77 -0
  22. package/dist/assets/provenance.d.ts.map +1 -0
  23. package/dist/assets/provenance.js +302 -0
  24. package/dist/assets/provenance.js.map +1 -0
  25. package/dist/assets/transcribe.d.ts +38 -0
  26. package/dist/assets/transcribe.d.ts.map +1 -0
  27. package/dist/assets/transcribe.js +354 -0
  28. package/dist/assets/transcribe.js.map +1 -0
  29. package/dist/assets/tts.d.ts +29 -0
  30. package/dist/assets/tts.d.ts.map +1 -0
  31. package/dist/assets/tts.js +182 -0
  32. package/dist/assets/tts.js.map +1 -0
  33. package/dist/cli.d.ts +3 -0
  34. package/dist/cli.d.ts.map +1 -0
  35. package/dist/cli.js +878 -0
  36. package/dist/cli.js.map +1 -0
  37. package/dist/contracts.d.ts +106 -0
  38. package/dist/contracts.d.ts.map +1 -0
  39. package/dist/contracts.js +298 -0
  40. package/dist/contracts.js.map +1 -0
  41. package/dist/doctor.d.ts +56 -0
  42. package/dist/doctor.d.ts.map +1 -0
  43. package/dist/doctor.js +471 -0
  44. package/dist/doctor.js.map +1 -0
  45. package/dist/errors.d.ts +10 -0
  46. package/dist/errors.d.ts.map +1 -0
  47. package/dist/errors.js +18 -0
  48. package/dist/errors.js.map +1 -0
  49. package/dist/index.d.ts +8 -0
  50. package/dist/index.d.ts.map +1 -0
  51. package/dist/index.js +5 -0
  52. package/dist/index.js.map +1 -0
  53. package/dist/preview/main.d.ts +19 -0
  54. package/dist/preview/main.d.ts.map +1 -0
  55. package/dist/preview/main.js +480 -0
  56. package/dist/preview/main.js.map +1 -0
  57. package/dist/project.d.ts +16 -0
  58. package/dist/project.d.ts.map +1 -0
  59. package/dist/project.js +88 -0
  60. package/dist/project.js.map +1 -0
  61. package/dist/renderer/audio.d.ts +31 -0
  62. package/dist/renderer/audio.d.ts.map +1 -0
  63. package/dist/renderer/audio.js +89 -0
  64. package/dist/renderer/audio.js.map +1 -0
  65. package/dist/renderer/browser.d.ts +22 -0
  66. package/dist/renderer/browser.d.ts.map +1 -0
  67. package/dist/renderer/browser.js +175 -0
  68. package/dist/renderer/browser.js.map +1 -0
  69. package/dist/renderer/ffmpeg.d.ts +58 -0
  70. package/dist/renderer/ffmpeg.d.ts.map +1 -0
  71. package/dist/renderer/ffmpeg.js +266 -0
  72. package/dist/renderer/ffmpeg.js.map +1 -0
  73. package/dist/renderer/index.d.ts +72 -0
  74. package/dist/renderer/index.d.ts.map +1 -0
  75. package/dist/renderer/index.js +279 -0
  76. package/dist/renderer/index.js.map +1 -0
  77. package/dist/renderer/transcripts.d.ts +30 -0
  78. package/dist/renderer/transcripts.d.ts.map +1 -0
  79. package/dist/renderer/transcripts.js +88 -0
  80. package/dist/renderer/transcripts.js.map +1 -0
  81. package/dist/runtime/clock.d.ts +5 -0
  82. package/dist/runtime/clock.d.ts.map +1 -0
  83. package/dist/runtime/clock.js +16 -0
  84. package/dist/runtime/clock.js.map +1 -0
  85. package/dist/runtime/index.d.ts +29 -0
  86. package/dist/runtime/index.d.ts.map +1 -0
  87. package/dist/runtime/index.js +287 -0
  88. package/dist/runtime/index.js.map +1 -0
  89. package/dist/runtime/random.d.ts +8 -0
  90. package/dist/runtime/random.d.ts.map +1 -0
  91. package/dist/runtime/random.js +21 -0
  92. package/dist/runtime/random.js.map +1 -0
  93. package/dist/server/preview-html.d.ts +2 -0
  94. package/dist/server/preview-html.d.ts.map +1 -0
  95. package/dist/server/preview-html.js +229 -0
  96. package/dist/server/preview-html.js.map +1 -0
  97. package/dist/server/server.d.ts +17 -0
  98. package/dist/server/server.d.ts.map +1 -0
  99. package/dist/server/server.js +237 -0
  100. package/dist/server/server.js.map +1 -0
  101. package/dist/transcript.d.ts +44 -0
  102. package/dist/transcript.d.ts.map +1 -0
  103. package/dist/transcript.js +130 -0
  104. package/dist/transcript.js.map +1 -0
  105. package/dist/version.d.ts +2 -0
  106. package/dist/version.d.ts.map +1 -0
  107. package/dist/version.js +2 -0
  108. package/dist/version.js.map +1 -0
  109. package/docs/adr/0001-keep-composition-authoring-web-native.md +3 -0
  110. package/docs/adr/0002-make-frame-and-props-the-only-animation-state.md +3 -0
  111. package/docs/adr/0003-render-browser-frames-offline.md +3 -0
  112. package/docs/adr/0004-produce-local-ai-assets-outside-rendering.md +3 -0
  113. package/docs/v0.1-design.md +238 -0
  114. package/examples/demo/README.md +24 -0
  115. package/examples/demo/assets/chime.wav +0 -0
  116. package/examples/demo/compositions/demo.js +209 -0
  117. package/examples/demo/props/demo.json +5 -0
  118. package/examples/demo/video.config.js +5 -0
  119. package/package.json +68 -3
  120. package/skill/README.md +29 -0
  121. package/skill/SKILL.md +57 -0
  122. package/skill/references/audio-assets.md +86 -0
  123. package/skill/references/deterministic-authoring.md +107 -0
  124. package/skill/references/failure-recovery.md +57 -0
  125. package/skill/references/json-results.md +203 -0
  126. package/skill/references/subtitles-karaoke.md +95 -0
  127. package/skill/scripts/frameyard.mjs +60 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,42 @@
1
+ # Changelog
2
+
3
+ All notable changes to Frameyard are recorded here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow
5
+ [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ Frameyard uses `0.x` versions until the composition, configuration, CLI,
8
+ JSON-result, transcript, and skill contracts stabilize. Before `1.0.0`, a
9
+ contract change may ship in a minor release and is called out explicitly below.
10
+
11
+ ## [Unreleased]
12
+
13
+ ## [0.1.0] - 2026-10-28
14
+
15
+ ### Added
16
+
17
+ - Native JavaScript composition contract with fixed `width`, `height`, `fps`,
18
+ and `durationInFrames`, JSON props, and synchronous seek-safe `update()`.
19
+ - Browser runtime with deterministic frame time, keyed `random()`, CSS and Web
20
+ Animations seeking, and preview diagnostics for non-deterministic clocks,
21
+ randomness, and CSS transitions.
22
+ - Interactive preview with composition selection, prop editing, frame
23
+ scrubbing, playback, and audio synchronization.
24
+ - CLI commands: `init`, `dev`, `check`, `still`, `render`, `tts`, `transcribe`,
25
+ and `doctor`, with `--json` result envelopes and stable error codes.
26
+ - Deterministic Chromium capture through Playwright and FFmpeg H.264/AAC
27
+ encoding, with atomic output publication and explicit overwrite flags.
28
+ - Audio clips placed on composition frames with source trimming in seconds and
29
+ an offline FFmpeg mix.
30
+ - Canonical transcript validation, measured word timings, and SRT/VTT
31
+ formatting, including transcript requirements attached to compositions.
32
+ - Local `qwen-tts` speech synthesis and `whisper-cli` transcription wrappers
33
+ with executable resolution, atomic publishing, and a versioned provenance
34
+ manifest.
35
+ - `frameyard doctor` environment, integration, and manifest inspection without
36
+ downloading, repairing, or modifying anything.
37
+ - Host-neutral agent skill under `skill/`, versioned with the npm package API.
38
+ - 30-second reference composition, deterministic render benchmark, public-seam
39
+ test suite, design baseline, and architecture decision records.
40
+
41
+ [Unreleased]: https://github.com/vladiantio/frameyard/compare/v0.1.0...HEAD
42
+ [0.1.0]: https://github.com/vladiantio/frameyard/releases/tag/v0.1.0
package/GLOSSARY.md ADDED
@@ -0,0 +1,145 @@
1
+ # Frameyard
2
+
3
+ Frameyard is a local-first framework for creating short, parameterized videos with HTML, CSS, and JavaScript. Its domain separates reusable compositions from the fixed inputs and deterministic renders that turn them into videos.
4
+
5
+ ## Language
6
+
7
+ ### Authoring
8
+
9
+ **Project**:
10
+ A collection of related compositions and the assets and configuration they share.
11
+ _Avoid_: Video, render
12
+
13
+ **Composition**:
14
+ A uniquely identified, reusable video definition with fixed dimensions, frame rate, duration in frames, and content parameterized by props.
15
+ _Avoid_: Scene, template, video
16
+
17
+ **Composition metadata**:
18
+ The fixed dimensions, frame rate, and duration in frames that define a composition's output contract.
19
+ _Avoid_: Render options, props
20
+
21
+ **Composition lifecycle**:
22
+ The `mount → update → dispose` sequence that governs a composition's frame production.
23
+ _Avoid_: Render loop, playback lifecycle
24
+
25
+ **Composition revision**:
26
+ A specific version of a composition's source and metadata.
27
+ _Avoid_: Build, release
28
+
29
+ **Props**:
30
+ The immutable, JSON-serializable inputs supplied to a composition for one render.
31
+ _Avoid_: Options, settings, mutable state
32
+
33
+ **Asset**:
34
+ A local file required by a composition, whether static or generated.
35
+ _Avoid_: Resource, media file
36
+
37
+ ### Deterministic execution
38
+
39
+ **Frame**:
40
+ An integer-addressed position on a composition's timeline.
41
+ _Avoid_: Timestamp, animation tick
42
+
43
+ **Deterministic execution**:
44
+ The rule that a composition's visual state at any frame depends only on that frame, its props, and pre-loaded data and assets.
45
+ _Avoid_: Playback-driven animation
46
+
47
+ **Frame clock**:
48
+ The single source of animation time, derived from the current frame and the composition's frame rate.
49
+ _Avoid_: Wall clock, playback timer
50
+
51
+ **Seek safety**:
52
+ The property that any frame can be produced correctly without depending on previously produced frames.
53
+ _Avoid_: History-dependent updates
54
+
55
+ **Deterministic randomness**:
56
+ Random values derived statelessly from a key so they do not depend on call order or rendering history.
57
+ _Avoid_: Unseeded randomness
58
+
59
+ **Frame production**:
60
+ The phase in which the runtime produces the visual state of a requested frame, without network access or model inference.
61
+ _Avoid_: Real-time playback
62
+
63
+ **Loading phase**:
64
+ The phase before frame production when required data and assets become available.
65
+ _Avoid_: Per-frame fetching
66
+
67
+ ### Audio
68
+
69
+ **Audio clip**:
70
+ An audio source placed on a composition's timeline with a start frame, source trim, and volume.
71
+ _Avoid_: Audio track
72
+
73
+ **Audio mix**:
74
+ The single offline mix of a composition's audio clips for one render.
75
+ _Avoid_: Soundtrack, audio track, final cut
76
+
77
+ ### Rendering
78
+
79
+ **Preview**:
80
+ An interactive, frame-accurate inspection of a composition using the same composition behavior as a render.
81
+ _Avoid_: Visual editor
82
+
83
+ **Check**:
84
+ A validation operation that checks configuration, props, assets, audio, and transcript requirements, then mounts a composition and evaluates every frame without capturing or encoding.
85
+ _Avoid_: Test render, dry run
86
+
87
+ **Render**:
88
+ The production of one complete video from a composition and a fixed set of props.
89
+ _Avoid_: Recording, export
90
+
91
+ **Video**:
92
+ The finished audiovisual output of a successful render.
93
+ _Avoid_: Composition, project
94
+
95
+ **Still**:
96
+ A single frame captured as a PNG through the production rendering path for debugging.
97
+ _Avoid_: Frame export, image sequence
98
+
99
+ **Reproducibility**:
100
+ The guarantee that, for the same composition revision, props, assets, and frame under a pinned runtime/browser environment, the visual state is identical. It does not imply identical encoded bytes, even on the same machine; encoded output depends on the encoder and its runtime.
101
+ _Avoid_: Bit-for-bit encoding equality
102
+
103
+ ### Agent operation
104
+
105
+ **Agent-assisted authoring and operation**:
106
+ The creation and operation of a Frameyard project by an AI agent acting on a user's behalf.
107
+ _Avoid_: Built-in generative AI
108
+
109
+ **Agent skill**:
110
+ An API-version-matched set of instructions and resources through which an AI agent uses Frameyard's public authoring and operation contracts.
111
+ _Avoid_: Agent runtime, MCP server
112
+
113
+ ### Local asset production
114
+
115
+ **Generated asset**:
116
+ A frozen local input produced by speech synthesis or transcription before a composition is loaded or rendered.
117
+ _Avoid_: Per-frame generation
118
+
119
+ **Provenance manifest**:
120
+ A versioned, project-local record of generated assets, their content hashes, and the inputs and processes that produced them.
121
+ _Avoid_: Render cache
122
+
123
+ **Speech synthesis** (TTS):
124
+ The local production of a spoken audio asset from text using a named speaker.
125
+ _Avoid_: Voice generation, voice cloning, voice design
126
+
127
+ **Transcription**:
128
+ The local production of timed text from source audio or video.
129
+ _Avoid_: Subtitle rendering
130
+
131
+ **Transcript**:
132
+ The canonical, versioned artifact containing full text, source identity, and source-relative timed segments, with optional word timings.
133
+ _Avoid_: Subtitle file, raw generator output
134
+
135
+ **Transcript segment**:
136
+ A timed span of transcript text with source-relative start and end times.
137
+ _Avoid_: Subtitle cue, line
138
+
139
+ **Word timing**:
140
+ The measured, source-relative start and end of a word in source audio.
141
+ _Avoid_: Interpolated segment timing
142
+
143
+ **Karaoke subtitle**:
144
+ A subtitle presentation pattern that highlights words according to actual word timings.
145
+ _Avoid_: Segment-timed highlighting
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Frameyard contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,303 @@
1
- # Temporary Holding Version
1
+ # Frameyard
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Frameyard is a local-first framework for building short, parameterized videos with HTML, CSS, and JavaScript and rendering them deterministically.
4
+
5
+ The v0.1 design is implemented: composition contracts, browser runtime and preview, `init`, `dev`, `check`, `still`, `render`, `tts`, `transcribe`, `doctor`, deterministic Chromium capture, FFmpeg output, provenance manifests, transcript validation, and the official agent skill.
6
+
7
+ ## Requirements
8
+
9
+ - Node.js 22.12 or newer
10
+ - FFmpeg and FFprobe on `PATH`, built with H.264 (`libx264`) and AAC support
11
+ - The Playwright-managed Chromium build
12
+ - Optional: `qwen-tts` from [`qwentts.cpp`](https://github.com/ServeurpersoCom/qwentts.cpp) for local speech synthesis
13
+ - Optional: `whisper-cli` from [`whisper.cpp`](https://github.com/ggml-org/whisper.cpp) for local transcription
14
+
15
+ ```sh
16
+ pnpm install
17
+ pnpm exec playwright install chromium
18
+ pnpm build
19
+ ```
20
+
21
+ Run `node dist/cli.js doctor` to inspect the environment without repairing or downloading anything. The wrappers target the CLI surfaces of qwentts.cpp commit `5916bf7c79dbb4e66fc059cdc507a79a23a28bb4` and whisper.cpp commit `284029c07012d2000941a08d38a31ffb71f4734e`.
22
+
23
+ CI uses fake `qwen-tts` and `whisper-cli` executables. Real-model tests are opt-in:
24
+
25
+ ```sh
26
+ FRAMEYARD_REAL_INTEGRATIONS=1 \
27
+ FRAMEYARD_REAL_TALKER=/path/to/talker.gguf \
28
+ FRAMEYARD_REAL_CODEC=/path/to/codec.gguf \
29
+ FRAMEYARD_REAL_WHISPER_MODEL=/path/to/ggml-model.bin \
30
+ FRAMEYARD_REAL_WHISPER_SOURCE=/path/to/source.wav \
31
+ # optional, for GPUs with too little VRAM:
32
+ FRAMEYARD_REAL_TTS_DEVICE=CPU \
33
+ pnpm vitest run test/real-integrations.test.ts
34
+ ```
35
+
36
+ ## Quick start
37
+
38
+ Scaffold a project:
39
+
40
+ ```sh
41
+ node dist/cli.js init my-video
42
+ cd my-video
43
+ node ../dist/cli.js dev
44
+ ```
45
+
46
+ `dev` prints a local preview URL. Select a composition, edit its JSON props, scrub frames, and play the composition with its audio timeline.
47
+
48
+ Validate and render:
49
+
50
+ ```sh
51
+ node ../dist/cli.js doctor
52
+ node ../dist/cli.js check hello
53
+ node ../dist/cli.js render hello --props props/hello.json --out hello.mp4
54
+ node ../dist/cli.js still hello --frame 42 --out hello-frame.png
55
+ ```
56
+
57
+ The 30-second reference composition lives in [`examples/demo`](./examples/demo):
58
+
59
+ ```sh
60
+ node dist/cli.js check demo --project examples/demo
61
+ node dist/cli.js render demo --project examples/demo --props examples/demo/props/demo.json --out demo.mp4
62
+ ```
63
+
64
+ ## Project format
65
+
66
+ A project is ordinary HTML, CSS, assets, and native JavaScript ES modules. `video.config.js` explicitly lists composition entry points; there is no directory scanning:
67
+
68
+ ```js
69
+ export default {
70
+ compositions: {
71
+ hello: "./compositions/hello.js",
72
+ },
73
+ };
74
+ ```
75
+
76
+ A composition default-exports a definition. `defineComposition` is optional but gives early metadata validation and editor types:
77
+
78
+ ```js
79
+ import { defineComposition } from "frameyard";
80
+ import { random } from "frameyard/runtime";
81
+
82
+ let title;
83
+
84
+ export default defineComposition({
85
+ width: 1920,
86
+ height: 1080,
87
+ fps: 30,
88
+ durationInFrames: 900,
89
+ defaultProps: { title: "Hello" },
90
+ validateProps(props) {
91
+ return typeof props.title === "string" ? [] : ["title must be a string"];
92
+ },
93
+ audio: [{ src: "assets/hit.wav", startFrame: 15, volume: 0.8 }],
94
+
95
+ mount({ container }) {
96
+ container.innerHTML = "<h1 id='title'></h1>";
97
+ title = container.querySelector("#title");
98
+ },
99
+
100
+ update({ frame, props }) {
101
+ title.textContent = props.title;
102
+ title.style.transform = `translateX(${random("title:" + frame) * 40}px)`;
103
+ },
104
+
105
+ dispose() {
106
+ title = undefined;
107
+ },
108
+ });
109
+ ```
110
+
111
+ The runtime supplies an import map for `frameyard` and `frameyard/runtime`, so project code needs no bundling step and no `node_modules` import.
112
+
113
+ ### Composition lifecycle
114
+
115
+ - `mount({ container, props })` creates the visual structure once and may finish asynchronous work.
116
+ - `update({ frame, props })` makes that structure represent the frame. It must be synchronous and seek-safe.
117
+ - `dispose()` releases resources after the last frame.
118
+
119
+ The only animation inputs are the integer frame and immutable props. Metadata (`width`, `height`, `fps`, `durationInFrames`) is fixed and validated before mounting. Props are JSON-serializable, deep-frozen, and merged shallowly over `defaultProps`.
120
+
121
+ Use `random("stable:key")` instead of `Math.random()`. The runtime pauses CSS animations and the Web Animations API inside the composition container and seeks them to the current frame. Independent `requestAnimationFrame` loops, timers, transitions, and wall-clock reads are not deterministic animation drivers.
122
+
123
+ ## Audio timeline
124
+
125
+ Audio clips are declarative:
126
+
127
+ ```js
128
+ audio: [
129
+ {
130
+ src: "assets/voice.wav", // project-relative local file
131
+ startFrame: 30, // composition timeline position
132
+ trimStart: 0.25, // seconds trimmed from the source
133
+ trimDuration: 4.2, // optional seconds to use
134
+ volume: 0.8, // 0..1
135
+ },
136
+ ];
137
+ ```
138
+
139
+ Preview synchronizes the timeline in the browser. Production mixes the same clips offline with FFmpeg. `check` reports clip durations and warnings when a clip extends beyond the composition. Frameyard never time-stretches audio or changes composition duration to fit it.
140
+
141
+ ## Transcripts
142
+
143
+ A composition can declare canonical transcript assets that `check`, `still`, and `render` must validate:
144
+
145
+ ```js
146
+ transcripts: [
147
+ { src: "assets/voice.transcript.json", words: true },
148
+ ],
149
+ ```
150
+
151
+ Transcript paths are project-relative. `words: true` requires actual measured word timings for karaoke-style presentation; Frameyard fails with `TRANSCRIPT_WORDS_UNAVAILABLE` rather than interpolating them. See [subtitles and karaoke](./skill/references/subtitles-karaoke.md) for the canonical JSON shape and the seek-safe frame conversion recipe.
152
+
153
+ ## Commands
154
+
155
+ | Command | Purpose |
156
+ | --- | --- |
157
+ | `frameyard init [directory]` | Scaffold a minimal project |
158
+ | `frameyard dev [--port <port>]` | Start the preview server |
159
+ | `frameyard check <id> [--props <file>]` | Validate props, assets, audio, transcripts, and every frame without capture |
160
+ | `frameyard still <id> --frame <n>` | Capture one PNG through the production path |
161
+ | `frameyard render <id> --out <video.mp4>` | Capture frames with Chromium and encode with FFmpeg |
162
+ | `frameyard tts ...` | Produce one frozen 24 kHz mono WAV with local qwentts.cpp |
163
+ | `frameyard transcribe ...` | Produce canonical transcript JSON with optional word timings and SRT/VTT exports |
164
+ | `frameyard doctor` | Inspect core requirements, optional integrations, and manifest integrity |
165
+
166
+ Common options: `--project <dir>`, `--json`, `--help`, and `--version`. `still` and `render` accept `--overwrite`; `tts` and `transcribe` accept `--force`. `init` takes the target directory as a positional argument. Output paths are relative to the current working directory.
167
+
168
+ A render fails immediately on an uncaught error, unhandled rejection, missing required asset, or invalid composition output. It writes to a temporary file and publishes only after FFmpeg succeeds. Existing outputs are not replaced unless `--overwrite` is supplied.
169
+
170
+ ### JSON results
171
+
172
+ Non-interactive commands support `--json`. Results go to stdout and human progress/diagnostics go to stderr:
173
+
174
+ ```json
175
+ {
176
+ "ok": true,
177
+ "command": "render",
178
+ "composition": "hello",
179
+ "path": "/absolute/path/hello.mp4",
180
+ "width": 1920,
181
+ "height": 1080,
182
+ "fps": 30,
183
+ "durationInFrames": 900,
184
+ "framesRendered": 900,
185
+ "audio": { "clips": 1, "warnings": [] },
186
+ "transcripts": { "clips": 1, "warnings": [] }
187
+ }
188
+ ```
189
+
190
+ Errors use the same envelope:
191
+
192
+ ```json
193
+ {
194
+ "ok": false,
195
+ "command": "render",
196
+ "error": { "code": "OUTPUT_EXISTS", "message": "Output already exists: ..." }
197
+ }
198
+ ```
199
+
200
+ `doctor --json` reports findings rather than throwing for environment problems: `ok: true` means inspection completed, while `healthy` reports whether required checks, manifest integrity, explicitly supplied models, and integration compatibility passed. See the [JSON result reference](./skill/references/json-results.md) for all command shapes.
201
+
202
+ ## Local asset production
203
+
204
+ `tts` and `transcribe` wrap optional local executables. Frameyard never builds upstream tools, downloads models, or runs inference during composition loading or frame production.
205
+
206
+ ```sh
207
+ # One frozen 24 kHz mono WAV. Text comes from --text-file or stdin.
208
+ node dist/cli.js tts \
209
+ --speaker vivian \
210
+ --talker-model models/qwen-talker-1.7b-customvoice-Q8_0.gguf \
211
+ --codec-model models/qwen-tokenizer-12hz-Q8_0.gguf \
212
+ --language English \
213
+ --text-file script.txt \
214
+ --out assets/voice.wav
215
+
216
+ # Canonical transcript with measured word timings and derived SRT/VTT.
217
+ node dist/cli.js transcribe assets/voice.wav \
218
+ --model models/ggml-base.en.bin \
219
+ --out assets/voice.transcript.json \
220
+ --words --export srt,vtt
221
+ ```
222
+
223
+ - Executable resolution: `--executable`, then `FRAMEYARD_QWEN_TTS` or `FRAMEYARD_WHISPER_CLI`, then `PATH`.
224
+ - `tts` supports CustomVoice only: a named speaker is required. Reference-audio cloning and VoiceDesign are deferred.
225
+ - `--device CPU` forces qwentts.cpp onto the CPU when a GPU does not have enough VRAM.
226
+ - `transcribe` normalizes the source with FFmpeg to 16 kHz mono PCM WAV before `whisper-cli`.
227
+ - Outputs are atomic. Existing assets are replaced only with `--force`.
228
+ - A partial operation leaves no apparently valid artifact and no manifest entry.
229
+ - Transcription never fabricates word timings and never executes Whisper's generated karaoke script.
230
+
231
+ ### Provenance
232
+
233
+ Successful operations update `frameyard.assets.json` in the project root, or the file passed to `--manifest`. The versioned manifest records artifact SHA-256 hashes and sizes, generator command and executable, Frameyard version, integration revision, model identities, operation parameters, and (for transcription) source identity and derived exports. Commit this manifest with the project.
234
+
235
+ ## Doctor
236
+
237
+ ```sh
238
+ node dist/cli.js doctor
239
+ node dist/cli.js doctor --json
240
+ node dist/cli.js doctor --talker-model models/talker.gguf --codec-model models/codec.gguf
241
+ ```
242
+
243
+ `doctor` checks Node, FFmpeg and required encoders, FFprobe, Playwright Chromium, manifest integrity (artifact, source, model, and derived-export hashes), explicitly supplied model readability, and optional `qwen-tts`/`whisper-cli` capabilities. It does not repair, download, or modify anything. Missing optional integrations do not fail, but a found executable with incompatible capabilities does.
244
+
245
+ ## Agent skill
246
+
247
+ The optional host-neutral agent skill lives in [`skill/`](./skill). It uses the same project format and CLI as humans, documents deterministic authoring, audio assets, subtitle/karaoke recipes, JSON results, and failure recovery, and ships a thin CLI wrapper. The npm package includes the skill files matching its API version. See [`skill/README.md`](./skill/README.md) for host-specific installation or referencing.
248
+
249
+ ## Rendering and reproducibility
250
+
251
+ Production rendering:
252
+
253
+ 1. Starts the same preview server and browser runtime used by `dev`.
254
+ 2. Launches pinned Chromium through Playwright.
255
+ 3. Seeks explicitly to each frame and waits for composition updates.
256
+ 4. Captures the viewport through the Chrome DevTools Protocol.
257
+ 5. Pipes PNG frames and the offline audio mix to FFmpeg.
258
+ 6. Encodes H.264 video with `yuv420p` and AAC audio into an MP4.
259
+
260
+ Rendering is sequential in v0.1. For the same composition revision, props, assets, frame, and pinned runtime/browser environment, the visual state is identical. Encoded bytes are not a reproducibility guarantee, even on the same machine; they depend on the FFmpeg build, encoder threading, and runtime environment.
261
+
262
+ For a repeatable local measurement of seek, capture, encode, and full-render time, run `pnpm bench`. The demo and the controlled fixtures under `bench/fixtures/` are the reference workloads.
263
+
264
+ ## Current limits
265
+
266
+ Deferred from this implementation slice:
267
+
268
+ - Built-in subtitle rendering, automatic caption burn-in, and karaoke rendering
269
+ - `qwentts.cpp` reference-audio voice cloning and VoiceDesign
270
+ - Automatic long-text chunking, streaming synthesis, and alternative generated-audio formats
271
+ - Automatic upstream builds, model downloads, environment repair, and hosted rendering
272
+ - Parallel rendering, frame caches, and general image-sequence output
273
+ - React or other UI-framework adapters, plugin APIs, and agent-only runtimes
274
+ - TypeScript transpilation for project compositions and npm dependency optimization
275
+
276
+ See the [v0.1 design baseline](./docs/v0.1-design.md) for the full product contract and the [domain glossary](./GLOSSARY.md) for terminology.
277
+
278
+ ## Repository layout
279
+
280
+ - `src/contracts.ts`: shared composition, props, audio, and transcript requirement contracts
281
+ - `src/transcript.ts`: canonical transcript validation and SRT/VTT formatting
282
+ - `src/runtime/`: browser lifecycle, frame clock, deterministic randomness
283
+ - `src/preview/`: interactive preview client
284
+ - `src/server/`: native ES-module server and import map
285
+ - `src/renderer/`: Chromium capture, audio mixing, transcript inspection, FFmpeg encoding
286
+ - `src/assets/`: local TTS/transcription wrappers, executable resolution, atomic publishing, provenance
287
+ - `src/doctor.ts`: environment, integration, and manifest inspection
288
+ - `src/cli.ts`: command-line entry point
289
+ - `skill/`: official host-neutral agent skill
290
+ - `examples/demo/`: the 30-second reference composition
291
+ - `bench/fixtures/`: controlled compositions for the render benchmark
292
+ - `scripts/`: benchmark, versioning, and git-hook installation
293
+ - `docs/adr/`: architecture decision records
294
+
295
+ ## Development
296
+
297
+ `pnpm install` installs the repository's git hooks. Pre-commit runs typecheck, test typecheck, and unit tests; pre-push runs the full suite. CI runs the same checks on Linux with Node 22 and 24, using fake model executables.
298
+
299
+ Releases are tag-driven. See [`RELEASING.md`](./RELEASING.md) for version policy, npm trusted publishing, and the release checklist, and [`CHANGELOG.md`](./CHANGELOG.md) for release notes.
300
+
301
+ ## License
302
+
303
+ MIT
@@ -0,0 +1,16 @@
1
+ export interface ResolvedExecutable {
2
+ /** The default command name, such as "qwen-tts". */
3
+ command: string;
4
+ /** Absolute path to the executable that will be used. */
5
+ path: string;
6
+ source: "option" | "environment" | "path";
7
+ }
8
+ /** Resolves an integration executable: explicit option, environment, then PATH. */
9
+ export declare function resolveExecutable(options: {
10
+ command: string;
11
+ explicit?: string;
12
+ envVar: string;
13
+ cwd?: string;
14
+ installHint: string;
15
+ }): Promise<ResolvedExecutable>;
16
+ //# sourceMappingURL=executable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executable.d.ts","sourceRoot":"","sources":["../../src/assets/executable.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,kBAAkB;IACjC,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,QAAQ,GAAG,aAAa,GAAG,MAAM,CAAC;CAC3C;AAwCD,mFAAmF;AACnF,wBAAsB,iBAAiB,CAAC,OAAO,EAAE;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA2B9B"}
@@ -0,0 +1,59 @@
1
+ import { constants } from "node:fs";
2
+ import { access, stat } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { FrameyardError } from "../errors.js";
5
+ async function isExecutable(filePath) {
6
+ try {
7
+ const info = await stat(filePath);
8
+ if (!info.isFile()) {
9
+ return false;
10
+ }
11
+ await access(filePath, constants.X_OK);
12
+ return true;
13
+ }
14
+ catch {
15
+ return false;
16
+ }
17
+ }
18
+ function hasPathSeparator(value) {
19
+ return value.includes("/") || value.includes("\\");
20
+ }
21
+ function pathCandidates(command) {
22
+ if (process.platform !== "win32") {
23
+ return [command];
24
+ }
25
+ const extensions = (process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").split(";").filter(Boolean);
26
+ return path.extname(command) ? [command] : extensions.map((extension) => `${command}${extension}`);
27
+ }
28
+ async function findOnPath(command, cwd) {
29
+ const entries = (process.env.PATH ?? "").split(path.delimiter).filter(Boolean);
30
+ for (const entry of entries) {
31
+ for (const candidate of pathCandidates(command)) {
32
+ const absolute = path.resolve(cwd, entry, candidate);
33
+ if (await isExecutable(absolute)) {
34
+ return absolute;
35
+ }
36
+ }
37
+ }
38
+ return undefined;
39
+ }
40
+ /** Resolves an integration executable: explicit option, environment, then PATH. */
41
+ export async function resolveExecutable(options) {
42
+ const cwd = options.cwd ?? process.cwd();
43
+ const configured = options.explicit ?? process.env[options.envVar];
44
+ const source = options.explicit !== undefined ? "option" : process.env[options.envVar] !== undefined ? "environment" : "path";
45
+ if (configured !== undefined) {
46
+ const absolute = hasPathSeparator(configured) ? path.resolve(cwd, configured) : await findOnPath(configured, cwd);
47
+ if (absolute && (await isExecutable(absolute))) {
48
+ return { command: options.command, path: absolute, source };
49
+ }
50
+ const sourceLabel = source === "environment" ? ` in ${options.envVar}` : "";
51
+ throw new FrameyardError("EXECUTABLE_NOT_FOUND", `Could not find an executable at "${configured}"${sourceLabel}. ${options.installHint}`);
52
+ }
53
+ const fromPath = await findOnPath(options.command, cwd);
54
+ if (fromPath) {
55
+ return { command: options.command, path: fromPath, source: "path" };
56
+ }
57
+ throw new FrameyardError("EXECUTABLE_NOT_FOUND", `${options.command} was not found on PATH. ${options.installHint} You can also pass --executable or set ${options.envVar}.`);
58
+ }
59
+ //# sourceMappingURL=executable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executable.js","sourceRoot":"","sources":["../../src/assets/executable.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ /** Fails early with an actionable error when an input file is missing or unreadable. */
2
+ export declare function assertReadableFile(filePath: string, label: string): Promise<number>;
3
+ export declare function requireExtension(filePath: string, extension: string, label: string): void;
4
+ //# sourceMappingURL=files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/assets/files.ts"],"names":[],"mappings":"AAKA,wFAAwF;AACxF,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAgBzF;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAIzF"}
@@ -0,0 +1,30 @@
1
+ import { access, stat } from "node:fs/promises";
2
+ import { constants } from "node:fs";
3
+ import path from "node:path";
4
+ import { FrameyardError } from "../errors.js";
5
+ /** Fails early with an actionable error when an input file is missing or unreadable. */
6
+ export async function assertReadableFile(filePath, label) {
7
+ let info;
8
+ try {
9
+ info = await stat(filePath);
10
+ }
11
+ catch {
12
+ throw new FrameyardError("ASSET_NOT_FOUND", `${label} does not exist: ${filePath}`);
13
+ }
14
+ if (!info.isFile()) {
15
+ throw new FrameyardError("ASSET_NOT_FOUND", `${label} is not a file: ${filePath}`);
16
+ }
17
+ try {
18
+ await access(filePath, constants.R_OK);
19
+ }
20
+ catch {
21
+ throw new FrameyardError("ASSET_NOT_FOUND", `${label} is not readable: ${filePath}`);
22
+ }
23
+ return info.size;
24
+ }
25
+ export function requireExtension(filePath, extension, label) {
26
+ if (path.extname(filePath).toLowerCase() !== extension) {
27
+ throw new FrameyardError("USAGE", `${label} must use a ${extension} extension: ${filePath}`);
28
+ }
29
+ }
30
+ //# sourceMappingURL=files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/assets/files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,wFAAwF;AACxF,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,QAAgB,EAAE,KAAa;IACtE,IAAI,IAAI,CAAC;IACT,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,cAAc,CAAC,iBAAiB,EAAE,GAAG,KAAK,oBAAoB,QAAQ,EAAE,CAAC,CAAC;IACtF,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;QACnB,MAAM,IAAI,cAAc,CAAC,iBAAiB,EAAE,GAAG,KAAK,mBAAmB,QAAQ,EAAE,CAAC,CAAC;IACrF,CAAC;IACD,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,cAAc,CAAC,iBAAiB,EAAE,GAAG,KAAK,qBAAqB,QAAQ,EAAE,CAAC,CAAC;IACvF,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,QAAgB,EAAE,SAAiB,EAAE,KAAa;IACjF,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,KAAK,SAAS,EAAE,CAAC;QACvD,MAAM,IAAI,cAAc,CAAC,OAAO,EAAE,GAAG,KAAK,eAAe,SAAS,eAAe,QAAQ,EAAE,CAAC,CAAC;IAC/F,CAAC;AACH,CAAC"}
@@ -0,0 +1,15 @@
1
+ export declare const QWEN_TTS_ENV = "FRAMEYARD_QWEN_TTS";
2
+ export declare const WHISPER_CLI_ENV = "FRAMEYARD_WHISPER_CLI";
3
+ /**
4
+ * The qwentts.cpp and whisper.cpp revisions whose CLI surfaces this wrapper
5
+ * targets. Real-model integration tests are opt-in and must name the exact
6
+ * revision they exercise; doctor reports these values so generated provenance
7
+ * is interpretable.
8
+ */
9
+ export declare const QWEN_TTS_REFERENCE_REVISION = "5916bf7c79dbb4e66fc059cdc507a79a23a28bb4";
10
+ export declare const WHISPER_CPP_REFERENCE_REVISION = "284029c07012d2000941a08d38a31ffb71f4734e";
11
+ export declare const QWEN_TTS_COMMAND = "qwen-tts";
12
+ export declare const WHISPER_CLI_COMMAND = "whisper-cli";
13
+ export declare const QWEN_TTS_INSTALL_HINT = "Build qwentts.cpp and make its qwen-tts executable available on PATH.";
14
+ export declare const WHISPER_CLI_INSTALL_HINT = "Build whisper.cpp and make its whisper-cli executable available on PATH.";
15
+ //# sourceMappingURL=integrations.d.ts.map