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.
- package/CHANGELOG.md +42 -0
- package/GLOSSARY.md +145 -0
- package/LICENSE +21 -0
- package/README.md +302 -2
- package/dist/assets/executable.d.ts +16 -0
- package/dist/assets/executable.d.ts.map +1 -0
- package/dist/assets/executable.js +59 -0
- package/dist/assets/executable.js.map +1 -0
- package/dist/assets/files.d.ts +4 -0
- package/dist/assets/files.d.ts.map +1 -0
- package/dist/assets/files.js +30 -0
- package/dist/assets/files.js.map +1 -0
- package/dist/assets/integrations.d.ts +15 -0
- package/dist/assets/integrations.d.ts.map +1 -0
- package/dist/assets/integrations.js +15 -0
- package/dist/assets/integrations.js.map +1 -0
- package/dist/assets/process.d.ts +29 -0
- package/dist/assets/process.d.ts.map +1 -0
- package/dist/assets/process.js +97 -0
- package/dist/assets/process.js.map +1 -0
- package/dist/assets/provenance.d.ts +77 -0
- package/dist/assets/provenance.d.ts.map +1 -0
- package/dist/assets/provenance.js +302 -0
- package/dist/assets/provenance.js.map +1 -0
- package/dist/assets/transcribe.d.ts +38 -0
- package/dist/assets/transcribe.d.ts.map +1 -0
- package/dist/assets/transcribe.js +354 -0
- package/dist/assets/transcribe.js.map +1 -0
- package/dist/assets/tts.d.ts +29 -0
- package/dist/assets/tts.d.ts.map +1 -0
- package/dist/assets/tts.js +182 -0
- package/dist/assets/tts.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +878 -0
- package/dist/cli.js.map +1 -0
- package/dist/contracts.d.ts +106 -0
- package/dist/contracts.d.ts.map +1 -0
- package/dist/contracts.js +298 -0
- package/dist/contracts.js.map +1 -0
- package/dist/doctor.d.ts +56 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +471 -0
- package/dist/doctor.js.map +1 -0
- package/dist/errors.d.ts +10 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +18 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/preview/main.d.ts +19 -0
- package/dist/preview/main.d.ts.map +1 -0
- package/dist/preview/main.js +480 -0
- package/dist/preview/main.js.map +1 -0
- package/dist/project.d.ts +16 -0
- package/dist/project.d.ts.map +1 -0
- package/dist/project.js +88 -0
- package/dist/project.js.map +1 -0
- package/dist/renderer/audio.d.ts +31 -0
- package/dist/renderer/audio.d.ts.map +1 -0
- package/dist/renderer/audio.js +89 -0
- package/dist/renderer/audio.js.map +1 -0
- package/dist/renderer/browser.d.ts +22 -0
- package/dist/renderer/browser.d.ts.map +1 -0
- package/dist/renderer/browser.js +175 -0
- package/dist/renderer/browser.js.map +1 -0
- package/dist/renderer/ffmpeg.d.ts +58 -0
- package/dist/renderer/ffmpeg.d.ts.map +1 -0
- package/dist/renderer/ffmpeg.js +266 -0
- package/dist/renderer/ffmpeg.js.map +1 -0
- package/dist/renderer/index.d.ts +72 -0
- package/dist/renderer/index.d.ts.map +1 -0
- package/dist/renderer/index.js +279 -0
- package/dist/renderer/index.js.map +1 -0
- package/dist/renderer/transcripts.d.ts +30 -0
- package/dist/renderer/transcripts.d.ts.map +1 -0
- package/dist/renderer/transcripts.js +88 -0
- package/dist/renderer/transcripts.js.map +1 -0
- package/dist/runtime/clock.d.ts +5 -0
- package/dist/runtime/clock.d.ts.map +1 -0
- package/dist/runtime/clock.js +16 -0
- package/dist/runtime/clock.js.map +1 -0
- package/dist/runtime/index.d.ts +29 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +287 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/random.d.ts +8 -0
- package/dist/runtime/random.d.ts.map +1 -0
- package/dist/runtime/random.js +21 -0
- package/dist/runtime/random.js.map +1 -0
- package/dist/server/preview-html.d.ts +2 -0
- package/dist/server/preview-html.d.ts.map +1 -0
- package/dist/server/preview-html.js +229 -0
- package/dist/server/preview-html.js.map +1 -0
- package/dist/server/server.d.ts +17 -0
- package/dist/server/server.d.ts.map +1 -0
- package/dist/server/server.js +237 -0
- package/dist/server/server.js.map +1 -0
- package/dist/transcript.d.ts +44 -0
- package/dist/transcript.d.ts.map +1 -0
- package/dist/transcript.js +130 -0
- package/dist/transcript.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +2 -0
- package/dist/version.js.map +1 -0
- package/docs/adr/0001-keep-composition-authoring-web-native.md +3 -0
- package/docs/adr/0002-make-frame-and-props-the-only-animation-state.md +3 -0
- package/docs/adr/0003-render-browser-frames-offline.md +3 -0
- package/docs/adr/0004-produce-local-ai-assets-outside-rendering.md +3 -0
- package/docs/v0.1-design.md +238 -0
- package/examples/demo/README.md +24 -0
- package/examples/demo/assets/chime.wav +0 -0
- package/examples/demo/compositions/demo.js +209 -0
- package/examples/demo/props/demo.json +5 -0
- package/examples/demo/video.config.js +5 -0
- package/package.json +68 -3
- package/skill/README.md +29 -0
- package/skill/SKILL.md +57 -0
- package/skill/references/audio-assets.md +86 -0
- package/skill/references/deterministic-authoring.md +107 -0
- package/skill/references/failure-recovery.md +57 -0
- package/skill/references/json-results.md +203 -0
- package/skill/references/subtitles-karaoke.md +95 -0
- 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
|
-
#
|
|
1
|
+
# Frameyard
|
|
2
2
|
|
|
3
|
-
|
|
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
|