playhead-cli 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 (119) hide show
  1. package/Dockerfile +24 -0
  2. package/LICENSE +21 -0
  3. package/README.md +184 -0
  4. package/action.yml +71 -0
  5. package/dist/audio/mux.d.ts +29 -0
  6. package/dist/audio/mux.d.ts.map +1 -0
  7. package/dist/audio/timing.d.ts +6 -0
  8. package/dist/audio/timing.d.ts.map +1 -0
  9. package/dist/audio/tts.d.ts +23 -0
  10. package/dist/audio/tts.d.ts.map +1 -0
  11. package/dist/authoring/author.d.ts +40 -0
  12. package/dist/authoring/author.d.ts.map +1 -0
  13. package/dist/authoring/catalog.d.ts +31 -0
  14. package/dist/authoring/catalog.d.ts.map +1 -0
  15. package/dist/authoring/explore.d.ts +30 -0
  16. package/dist/authoring/explore.d.ts.map +1 -0
  17. package/dist/authoring/serialize.d.ts +26 -0
  18. package/dist/authoring/serialize.d.ts.map +1 -0
  19. package/dist/authoring/validate.d.ts +31 -0
  20. package/dist/authoring/validate.d.ts.map +1 -0
  21. package/dist/beforeafter.d.ts +25 -0
  22. package/dist/beforeafter.d.ts.map +1 -0
  23. package/dist/bundle/hash.d.ts +3 -0
  24. package/dist/bundle/hash.d.ts.map +1 -0
  25. package/dist/bundle/reader.d.ts +6 -0
  26. package/dist/bundle/reader.d.ts.map +1 -0
  27. package/dist/bundle/types.d.ts +54 -0
  28. package/dist/bundle/types.d.ts.map +1 -0
  29. package/dist/bundle/writer.d.ts +35 -0
  30. package/dist/bundle/writer.d.ts.map +1 -0
  31. package/dist/capture/driver-api.d.ts +190 -0
  32. package/dist/capture/driver-api.d.ts.map +1 -0
  33. package/dist/capture/events.d.ts +78 -0
  34. package/dist/capture/events.d.ts.map +1 -0
  35. package/dist/capture/executor.d.ts +19 -0
  36. package/dist/capture/executor.d.ts.map +1 -0
  37. package/dist/capture/playwright-driver.d.ts +122 -0
  38. package/dist/capture/playwright-driver.d.ts.map +1 -0
  39. package/dist/cli/index.d.ts +3 -0
  40. package/dist/cli/index.d.ts.map +1 -0
  41. package/dist/cli/index.js +6648 -0
  42. package/dist/cli/index.js.map +1 -0
  43. package/dist/compose/camera/interpolate.d.ts +9 -0
  44. package/dist/compose/camera/interpolate.d.ts.map +1 -0
  45. package/dist/compose/camera/planner.d.ts +19 -0
  46. package/dist/compose/camera/planner.d.ts.map +1 -0
  47. package/dist/compose/captions.d.ts +22 -0
  48. package/dist/compose/captions.d.ts.map +1 -0
  49. package/dist/compose/cursor/path.d.ts +17 -0
  50. package/dist/compose/cursor/path.d.ts.map +1 -0
  51. package/dist/compose/cursor/render.d.ts +10 -0
  52. package/dist/compose/cursor/render.d.ts.map +1 -0
  53. package/dist/compose/index.d.ts +25 -0
  54. package/dist/compose/index.d.ts.map +1 -0
  55. package/dist/compose/kinds.d.ts +27 -0
  56. package/dist/compose/kinds.d.ts.map +1 -0
  57. package/dist/compose/overlays/build.d.ts +7 -0
  58. package/dist/compose/overlays/build.d.ts.map +1 -0
  59. package/dist/compose/overlays/draw.d.ts +12 -0
  60. package/dist/compose/overlays/draw.d.ts.map +1 -0
  61. package/dist/compose/overlays/text.d.ts +18 -0
  62. package/dist/compose/overlays/text.d.ts.map +1 -0
  63. package/dist/compose/render/encoder.d.ts +13 -0
  64. package/dist/compose/render/encoder.d.ts.map +1 -0
  65. package/dist/compose/render/frameStore.d.ts +27 -0
  66. package/dist/compose/render/frameStore.d.ts.map +1 -0
  67. package/dist/compose/render/renderer.d.ts +5 -0
  68. package/dist/compose/render/renderer.d.ts.map +1 -0
  69. package/dist/compose/timeline/pacing.d.ts +36 -0
  70. package/dist/compose/timeline/pacing.d.ts.map +1 -0
  71. package/dist/compose/types.d.ts +236 -0
  72. package/dist/compose/types.d.ts.map +1 -0
  73. package/dist/index.d.ts +25 -0
  74. package/dist/index.d.ts.map +1 -0
  75. package/dist/index.js +4963 -0
  76. package/dist/index.js.map +1 -0
  77. package/dist/integrations/jira.d.ts +44 -0
  78. package/dist/integrations/jira.d.ts.map +1 -0
  79. package/dist/mcp/bin.d.ts +3 -0
  80. package/dist/mcp/bin.d.ts.map +1 -0
  81. package/dist/mcp/bin.js +5749 -0
  82. package/dist/mcp/bin.js.map +1 -0
  83. package/dist/mcp/server.d.ts +10 -0
  84. package/dist/mcp/server.d.ts.map +1 -0
  85. package/dist/shared/easing.d.ts +9 -0
  86. package/dist/shared/easing.d.ts.map +1 -0
  87. package/dist/shared/exit.d.ts +32 -0
  88. package/dist/shared/exit.d.ts.map +1 -0
  89. package/dist/shared/ffmpeg.d.ts +19 -0
  90. package/dist/shared/ffmpeg.d.ts.map +1 -0
  91. package/dist/shared/geometry.d.ts +47 -0
  92. package/dist/shared/geometry.d.ts.map +1 -0
  93. package/dist/shared/log.d.ts +10 -0
  94. package/dist/shared/log.d.ts.map +1 -0
  95. package/dist/shared/version.d.ts +4 -0
  96. package/dist/shared/version.d.ts.map +1 -0
  97. package/dist/spec/locators.d.ts +62 -0
  98. package/dist/spec/locators.d.ts.map +1 -0
  99. package/dist/spec/parse.d.ts +16 -0
  100. package/dist/spec/parse.d.ts.map +1 -0
  101. package/dist/spec/schema.d.ts +365 -0
  102. package/dist/spec/schema.d.ts.map +1 -0
  103. package/dist/theme/index.d.ts +23 -0
  104. package/dist/theme/index.d.ts.map +1 -0
  105. package/dist/verify/checks.d.ts +27 -0
  106. package/dist/verify/checks.d.ts.map +1 -0
  107. package/dist/verify/contact-sheet.d.ts +7 -0
  108. package/dist/verify/contact-sheet.d.ts.map +1 -0
  109. package/dist/verify/media.d.ts +42 -0
  110. package/dist/verify/media.d.ts.map +1 -0
  111. package/dist/verify/report.d.ts +12 -0
  112. package/dist/verify/report.d.ts.map +1 -0
  113. package/dist/verify/runner.d.ts +13 -0
  114. package/dist/verify/runner.d.ts.map +1 -0
  115. package/dist/verify/types.d.ts +63 -0
  116. package/dist/verify/types.d.ts.map +1 -0
  117. package/dist/verify/vision.d.ts +19 -0
  118. package/dist/verify/vision.d.ts.map +1 -0
  119. package/package.json +74 -0
package/Dockerfile ADDED
@@ -0,0 +1,24 @@
1
+ # Playhead in a container: capture + compose + verify with no host dependencies.
2
+ # The Playwright base image ships Chromium, its sandbox deps, and system fonts.
3
+ #
4
+ # docker build -t playhead .
5
+ # docker run --rm -v "$PWD:/work" -w /work --shm-size=1g playhead \
6
+ # render specs/examples/add-user.yaml -o out --json
7
+ #
8
+ # Notes:
9
+ # - --shm-size=1g (or --disable-dev-shm-usage) keeps Chromium from OOMing in /dev/shm.
10
+ # - TTS narration (`output.audio.narration: tts`) uses macOS `say` and degrades LOUDLY to a
11
+ # silent render on Linux; a Linux TTS provider is planned behind the same seam.
12
+ # - ffmpeg/ffprobe come from the npm static binaries — nothing to apt-install.
13
+ FROM mcr.microsoft.com/playwright:v1.49.0-noble
14
+
15
+ WORKDIR /app
16
+ COPY package.json package-lock.json ./
17
+ RUN npm ci --omit=dev --ignore-scripts \
18
+ && npx playwright install chromium
19
+
20
+ COPY dist ./dist
21
+ COPY demo ./demo
22
+
23
+ ENTRYPOINT ["node", "dist/cli/index.js"]
24
+ CMD ["--help"]
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Aman Rai
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 ADDED
@@ -0,0 +1,184 @@
1
+ # Playhead
2
+
3
+ **Product video as a build artifact, not a performance.** Give Playhead a small text
4
+ spec and a URL; it drives your real web app with real input, records clean footage plus a
5
+ structured event log, applies all presentation at compose time, and reviews its own output
6
+ before delivering a verified MP4.
7
+
8
+ ```
9
+ spec + live product → capture → compose → verify → out.mp4 + verdict.json
10
+ ```
11
+
12
+ Change the spec or the product, regenerate in minutes. **Re-render, don't re-record.**
13
+
14
+ ---
15
+
16
+ ## Quick start
17
+
18
+ ```bash
19
+ npm install
20
+ npx playwright install chromium
21
+ npm run demo & # bundled demo app on http://localhost:4173
22
+ npm run playhead -- render specs/examples/add-user.yaml -o out
23
+ ```
24
+
25
+ ## Let an agent write the spec
26
+
27
+ You don't have to hand-write locators. Point the agent at a running app and describe the
28
+ flow — it walks the app, reads each screen's accessibility tree, and authors a spec where
29
+ **every locator is validated against the live DOM** (no more "the name was `☰Contacts`, not
30
+ `Contacts`" surprises):
31
+
32
+ ```bash
33
+ export ANTHROPIC_API_KEY=... # the agent that drafts the spec
34
+ npx playhead author http://localhost:4173/crm.html "create a new deal for Globex" -o deal.yaml
35
+ npx playhead render deal.yaml -o out
36
+ ```
37
+
38
+ Or just inspect what's addressable on a screen (great input for writing specs by hand):
39
+
40
+ ```bash
41
+ npx playhead explore http://localhost:4173/crm.html
42
+ ```
43
+
44
+ See [VISION.md](VISION.md) for where this is going — Playhead as the video-evidence layer
45
+ for the whole development lifecycle (CI, Jira, PRs, and an MCP tool for coding agents).
46
+
47
+ ## Use it from your coding agent (MCP)
48
+
49
+ Playhead ships an MCP server, so any agent (Claude Code, Cursor, …) can explore your app,
50
+ write a grounded spec, render it, and read the verdict — inside your own repo. Register it:
51
+
52
+ ```jsonc
53
+ // .mcp.json
54
+ {
55
+ "mcpServers": {
56
+ "playhead": { "command": "npx", "args": ["playhead", "mcp"] }
57
+ }
58
+ }
59
+ ```
60
+
61
+ Tools the agent gets: `playhead_explore` (grounded, validated locators for the current
62
+ screen), `playhead_validate_spec` (schema + locator check, no render), `playhead_author`
63
+ (auto-draft a spec toward a goal), and `playhead_render` (capture → compose → verify,
64
+ returning the publish/no-publish verdict with reasons and artifact paths). The agent's loop
65
+ becomes: *explore → write spec → render → read verdict → fix → re-render.*
66
+
67
+ Outputs land in `out/`:
68
+
69
+ - `out/out.mp4` — the finished 1080p walkthrough (title card, planned camera moves, rendered
70
+ cursor, auto-written numbered captions).
71
+ - `out/capture/` — the re-renderable **capture bundle** (frames + `events.json` + manifest).
72
+ - `out/compose-manifest.json` — everything the compositor rendered, and what verification checks against.
73
+ - `out/verify/verdict.json` — `publishable` / `not-publishable` with per-check reasons.
74
+ - `out/verify/contact-sheet.png` — key moments as an annotated grid, so you can trust the
75
+ output without watching it end to end.
76
+
77
+ `render` exits `0` only when the verdict is publishable — drop it in CI as an end-to-end test
78
+ with video evidence attached.
79
+
80
+ ## The spec
81
+
82
+ A spec is small and reviewable on purpose. Elements are addressed the resilient way — by
83
+ role, label, and text, never brittle CSS — which is also what lets Playhead auto-write the
84
+ instruction captions.
85
+
86
+ ```yaml
87
+ playhead: 1
88
+ title: "Add a user in Acme Admin"
89
+ subtitle: "Admin in 30 seconds" # title-card subtitle (defaults to the app host)
90
+ vars:
91
+ base: ${env.APP_URL:-http://localhost:4173}
92
+ app:
93
+ url: "{{base}}"
94
+ viewport: 1280x720
95
+ storageState: session.json # from `playhead login` (authed apps)
96
+ assertLoggedIn: 'role=link[name="Deals"]' # fail fast on stale auth
97
+ environment: { timezone: UTC, locale: en-US, fixedTime: "2026-01-01T12:00:00Z" }
98
+ network:
99
+ block: ["**analytics**"] # never film third-party noise
100
+ output:
101
+ kind: demo # walkthrough | demo | bug-repro | before-after | release | social
102
+ audio: { narration: tts, sfx: true } # spoken captions + click ticks, loudness-normalized
103
+ endCard: { title: "Acme Admin", subtitle: "acme.dev" }
104
+ masking:
105
+ - target: 'testid=env-banner' # redacted at capture; never reaches a frame
106
+ style: solid
107
+ scenes:
108
+ - id: add-user
109
+ title: "Add a user"
110
+ steps:
111
+ - click: 'role=button[name="Add user"]'
112
+ - type: { target: 'label=Full name', text: "Jane Doe" }
113
+ - type: { target: 'label=Temporary password', text: "s3cret!", mask: true }
114
+ - click: 'role=button[name="Create user"]'
115
+ caption: "Create it."
116
+ narration: "One click, and the account is live." # spoken line ≠ on-screen card
117
+ focus: 'text="User added"' # act→react: click here, camera lands THERE
118
+ - expect: { target: 'text="User added"', visible: true }
119
+ - wait: { for: 'text="Jane Doe"', state: visible } # structured wait, never a sleep
120
+ ```
121
+
122
+ Elements inside iframes are addressed with a frame chain: `frame=#preview >> role=button[name="Save"]`.
123
+ Per-step extras: `caption`, `narration`, `focus` (`target`/`wide`/a locator), `shot: cut|continue`,
124
+ `timeout`, `mask`. Specs compose: `extends: ./base.yaml` merges a base file underneath.
125
+ Scaffold one with `npx playhead init`.
126
+
127
+ ## Commands
128
+
129
+ | Command | What it does |
130
+ |---|---|
131
+ | `playhead render <specs...>` | capture → compose → verify; `--workers N` pools multiple specs; `--json` / `--junit <path>` for CI |
132
+ | `playhead validate <spec> --live` | preflight EVERY locator against the running app in one pass, with suggestions — before any frame is captured |
133
+ | `playhead capture <spec>` | produce a capture bundle only; `--from-scene <id>` fast-forwards earlier steps unfilmed |
134
+ | `playhead compose <bundle>` | re-render from a bundle — **no live app needed**; `--spec edited.yaml` re-composes with new text/theme/audio; `--expect-sha` asserts determinism |
135
+ | `playhead verify <bundle> <mp4>` | run the verification suite; `--json` / `--junit` |
136
+ | `playhead jira report <KEY>` | attach evidence (video/failure clip, verdict, contact sheet, console log) to an existing Jira issue + post a verdict comment; or `render --jira <KEY>` (env: JIRA_BASE_URL/EMAIL/API_TOKEN) |
137
+ | `playhead attest <verify-dir>` | re-verify a verdict: artifact hashes + HMAC signature (`PLAYHEAD_SIGNING_KEY`) |
138
+ | `playhead explore <url>` | grounded locator catalog (regions, stability marks, iframes); `--storage-state` for authed screens |
139
+ | `playhead login <url>` | you log in in a headed browser; the session is saved for `app.storageState` |
140
+ | `playhead init [path]` | scaffold a starter spec |
141
+ | `playhead doctor [--ci]` | check ffmpeg, browser, fonts — and container/TTS/signing prerequisites |
142
+
143
+ Add `--vision` to `render`/`verify` for an optional Claude review of the contact sheet (needs
144
+ `ANTHROPIC_API_KEY`; skipped cleanly without one, so CI stays deterministic).
145
+
146
+ **Exit codes are a contract:** `0` publishable · `1` the app broke the flow (a partial bundle
147
+ **and a watchable failure clip** are written) · `2` rendered but failed verification ·
148
+ `3` infra · `4` bad spec/arguments.
149
+
150
+ ## CI
151
+
152
+ `action.yml` ships a composite GitHub Action (render → JUnit → artifact upload), and the
153
+ `Dockerfile` runs everything in the Playwright base image. Verdicts carry provenance (version,
154
+ spec hash, capture time, host) and an HMAC signature when `PLAYHEAD_SIGNING_KEY` is set —
155
+ `playhead attest` proves a verdict describes exactly these files and was never edited.
156
+
157
+ ## How it works
158
+
159
+ 1. **Capture** (`src/capture/`) drives the app through an in-house `Driver` interface
160
+ (Playwright behind it, swappable). It records 2×-resolution frames on a timestamp index,
161
+ an event log (action kind, accessible label, measured bounding box, timestamps, cursor
162
+ path), and applies masking in the DOM before any pixel is painted.
163
+ 2. **Compose** (`src/compose/`) is a pure function of the bundle. It plans pacing (holds,
164
+ trimmed idle, min step duration), a camera path from measured UI geometry (quantized zoom,
165
+ never cropping a target), a re-synthesized cursor, and auto-written captions — then renders
166
+ per frame with skia canvas piped to ffmpeg. All presentation is here, so any visual change
167
+ is a re-render.
168
+ 3. **Verify** (`src/verify/`) runs ten deterministic checks (target framed and legible, cursor
169
+ on target, action visibly effective, delivered pixels match the plan, no frozen/blank
170
+ footage, captions fit, pacing sane, encode intact, masks opaque + log clean, narration
171
+ audible in its windows at target loudness) and emits a signed verdict and contact sheet.
172
+
173
+ The **capture bundle is the only artifact that touches the live app** — compose and verify
174
+ never hit the network, which is what makes re-render deterministic and CI-safe.
175
+
176
+ ## Development
177
+
178
+ ```bash
179
+ npm run typecheck
180
+ npm test # unit + seeded-failure fixtures + full e2e against the demo app
181
+ npm run build
182
+ ```
183
+
184
+ See [AGENTS.md](AGENTS.md) for architecture notes and conventions.
package/action.yml ADDED
@@ -0,0 +1,71 @@
1
+ name: 'Playhead Render'
2
+ description: >
3
+ Render verified product videos from Playhead specs: capture the live app, compose the video,
4
+ verify it, and publish a JUnit report. Exit codes distinguish flow failure (the app broke — a
5
+ failure clip is rendered) from video-quality failure from infra problems.
6
+ branding:
7
+ icon: 'film'
8
+ color: 'purple'
9
+
10
+ inputs:
11
+ specs:
12
+ description: 'Space-separated spec paths (or a glob expanded by the shell step calling this action)'
13
+ required: true
14
+ out:
15
+ description: 'Output directory for bundles, videos, verdicts'
16
+ default: 'playhead-out'
17
+ workers:
18
+ description: 'Concurrent renders when given multiple specs'
19
+ default: '2'
20
+ signing-key:
21
+ description: 'PLAYHEAD_SIGNING_KEY — when set, verdicts are HMAC-signed and attestable'
22
+ required: false
23
+
24
+ outputs:
25
+ junit:
26
+ description: 'Path to the JUnit XML report'
27
+ value: ${{ steps.render.outputs.junit }}
28
+
29
+ runs:
30
+ using: 'composite'
31
+ steps:
32
+ - name: Cache Chromium
33
+ uses: actions/cache@v4
34
+ with:
35
+ path: ~/.cache/ms-playwright
36
+ key: playwright-chromium-${{ runner.os }}
37
+
38
+ - name: Install browsers
39
+ shell: bash
40
+ run: npx playwright install --with-deps chromium
41
+
42
+ - name: Render + verify
43
+ id: render
44
+ shell: bash
45
+ env:
46
+ PLAYHEAD_SIGNING_KEY: ${{ inputs.signing-key }}
47
+ run: |
48
+ set +e
49
+ npx playhead render ${{ inputs.specs }} \
50
+ -o "${{ inputs.out }}" \
51
+ --workers "${{ inputs.workers }}" \
52
+ --junit "${{ inputs.out }}/junit.xml" \
53
+ --json > "${{ inputs.out }}/verdict-stream.json"
54
+ code=$?
55
+ echo "junit=${{ inputs.out }}/junit.xml" >> "$GITHUB_OUTPUT"
56
+ # 0 pass · 1 flow failure (failure clip rendered) · 2 quality · 3 infra · 4 usage
57
+ echo "playhead exit code: $code"
58
+ exit $code
59
+
60
+ - name: Upload artifacts (videos, verdicts, contact sheets, failure clips)
61
+ if: always()
62
+ uses: actions/upload-artifact@v4
63
+ with:
64
+ name: playhead-${{ github.run_id }}
65
+ path: |
66
+ ${{ inputs.out }}/**/*.mp4
67
+ ${{ inputs.out }}/**/verdict.json
68
+ ${{ inputs.out }}/**/contact-sheet.png
69
+ ${{ inputs.out }}/**/failure.json
70
+ ${{ inputs.out }}/junit.xml
71
+ if-no-files-found: ignore
@@ -0,0 +1,29 @@
1
+ import type { StepMapEntry, ClickRipple } from '../compose/types.js';
2
+ import type { NarrationClip } from './tts.js';
3
+ export interface PlacedClip {
4
+ stepRef: string;
5
+ /** Output-clock ms the clip starts playing. */
6
+ tStart: number;
7
+ durMs: number;
8
+ }
9
+ export interface AudioBedOptions {
10
+ /** Narration clips per stepRef (absent = no narration). */
11
+ narration?: Map<string, NarrationClip>;
12
+ /** Click-beat times (output clock) that get a soft synthesized tick. */
13
+ sfxBeats?: ClickRipple[];
14
+ /** User-supplied music bed (no asset is bundled). */
15
+ music?: {
16
+ file: string;
17
+ gainDb: number;
18
+ };
19
+ /** Directory for the synthesized click sample (cached). */
20
+ cacheDir: string;
21
+ }
22
+ /**
23
+ * Build the full audio track — narration clips at their step starts, click ticks at ripple
24
+ * beats, an optional music bed ducked under narration — normalize loudness (EBU R128, I=-16),
25
+ * and mux it onto the rendered MP4. Returns where each narration clip landed so verification
26
+ * can reason about voice windows.
27
+ */
28
+ export declare function mixAudio(videoPath: string, steps: StepMapEntry[], durationMs: number, opts: AudioBedOptions): Promise<PlacedClip[]>;
29
+ //# sourceMappingURL=mux.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mux.d.ts","sourceRoot":"","sources":["../../src/audio/mux.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAO9C,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,2DAA2D;IAC3D,SAAS,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACvC,wEAAwE;IACxE,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;IACzB,qDAAqD;IACrD,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,YAAY,EAAE,EACrB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,eAAe,GACpB,OAAO,CAAC,UAAU,EAAE,CAAC,CAyFvB"}
@@ -0,0 +1,6 @@
1
+ /** Narration placement constants, shared by the timeline (dwell fitting) and the muxer. */
2
+ /** A step's spoken line starts this long after the step (and its caption) begin. */
3
+ export declare const AUDIO_LEAD_MS = 150;
4
+ /** Silence kept after a spoken line finishes before the step is allowed to end. */
5
+ export declare const AUDIO_TAIL_MS = 450;
6
+ //# sourceMappingURL=timing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timing.d.ts","sourceRoot":"","sources":["../../src/audio/timing.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAE3F,oFAAoF;AACpF,eAAO,MAAM,aAAa,MAAM,CAAC;AACjC,mFAAmF;AACnF,eAAO,MAAM,aAAa,MAAM,CAAC"}
@@ -0,0 +1,23 @@
1
+ export interface NarrationOptions {
2
+ /** Only 'say' (macOS local TTS) is implemented today; the seam is here for cloud providers. */
3
+ provider: 'say';
4
+ voice?: string;
5
+ /** Words per minute (say-specific). */
6
+ rate?: number;
7
+ }
8
+ export interface NarrationLine {
9
+ stepRef: string;
10
+ text: string;
11
+ }
12
+ export interface NarrationClip {
13
+ stepRef: string;
14
+ file: string;
15
+ durationMs: number;
16
+ }
17
+ /**
18
+ * Synthesize one clip per line. Clips are cached by a content hash of
19
+ * (provider, voice, rate, text) so re-renders reuse them — keeping compose offline
20
+ * and deterministic (same text+voice → same audio → same timing).
21
+ */
22
+ export declare function synthesizeNarration(lines: NarrationLine[], opts: NarrationOptions, cacheDir: string): Promise<Map<string, NarrationClip>>;
23
+ //# sourceMappingURL=tts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tts.d.ts","sourceRoot":"","sources":["../../src/audio/tts.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,gBAAgB;IAC/B,+FAA+F;IAC/F,QAAQ,EAAE,KAAK,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAqCD;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,aAAa,EAAE,EACtB,IAAI,EAAE,gBAAgB,EACtB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAWrC"}
@@ -0,0 +1,40 @@
1
+ import type { Driver } from '../capture/driver-api.js';
2
+ import { type PageSnapshot } from './explore.js';
3
+ import { type AuthoredStep } from './serialize.js';
4
+ export interface StepDecision {
5
+ done: boolean;
6
+ reason?: string;
7
+ scene: string;
8
+ action?: AuthoredStep['action'];
9
+ locator?: string;
10
+ text?: string;
11
+ value?: string;
12
+ keys?: string;
13
+ caption?: string;
14
+ mask?: boolean;
15
+ }
16
+ /** Chooses the next step from the current screen. Default is Claude; injectable for tests/policies. */
17
+ export type Decider = (goal: string, snap: PageSnapshot, soFar: AuthoredStep[]) => Promise<StepDecision>;
18
+ export interface AuthorOptions {
19
+ url: string;
20
+ goal: string;
21
+ outPath: string;
22
+ title?: string;
23
+ viewport: {
24
+ w: number;
25
+ h: number;
26
+ };
27
+ maxSteps: number;
28
+ headless: boolean;
29
+ driver?: Driver;
30
+ /** Override the step decider (defaults to the Claude-backed one). */
31
+ decide?: Decider;
32
+ }
33
+ /** Agent-author a grounded, validated spec by walking the app toward the goal. Needs ANTHROPIC_API_KEY. */
34
+ export declare function authorSpec(opts: AuthorOptions): Promise<{
35
+ specPath: string;
36
+ steps: AuthoredStep[];
37
+ }>;
38
+ /** The default decider: ask Claude for the next step, forcing a structured tool call. */
39
+ export declare function claudeDecider(): Decider;
40
+ //# sourceMappingURL=author.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"author.d.ts","sourceRoot":"","sources":["../../src/authoring/author.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAGvD,OAAO,EAAgB,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,EAAoC,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGrF,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,uGAAuG;AACvG,MAAM,MAAM,OAAO,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;AAEzG,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAsCD,2GAA2G;AAC3G,wBAAsB,UAAU,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,YAAY,EAAE,CAAA;CAAE,CAAC,CAgD1G;AAED,yFAAyF;AACzF,wBAAgB,aAAa,IAAI,OAAO,CA0CvC"}
@@ -0,0 +1,31 @@
1
+ import type { RawInteractable } from '../capture/driver-api.js';
2
+ export type CatalogKind = 'button' | 'link' | 'input' | 'select' | 'toggle' | 'heading' | 'other';
3
+ export interface CatalogEntry {
4
+ kind: CatalogKind;
5
+ role: string | null;
6
+ name: string | null;
7
+ /** The chosen, validated Playhead locator string. */
8
+ locator: string;
9
+ /** Whether `locator` resolves to exactly one element. */
10
+ unique: boolean;
11
+ /** How likely the locator survives app changes: testid/role+name high, label medium, text low. */
12
+ stability: 'high' | 'medium' | 'low';
13
+ /** Landmark region: nav | header | sidebar | main | dialog | footer | page. */
14
+ region: string;
15
+ bbox: {
16
+ x: number;
17
+ y: number;
18
+ w: number;
19
+ h: number;
20
+ };
21
+ visible: boolean;
22
+ options?: string[];
23
+ }
24
+ /** Stability of a locator string by its engine — a maintenance signal for authors. */
25
+ export declare function stabilityOf(locatorRaw: string): 'high' | 'medium' | 'low';
26
+ /** Ordered locator candidates for an element, best (most resilient/readable) first. */
27
+ export declare function candidateLocators(el: RawInteractable): string[];
28
+ export declare function kindOf(el: RawInteractable): CatalogKind;
29
+ /** Quote for an EXACT locator match; fall back to a regex when the text has awkward characters. */
30
+ export declare function quote(s: string): string;
31
+ //# sourceMappingURL=catalog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/authoring/catalog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhE,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;AAElG,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,MAAM,EAAE,OAAO,CAAC;IAChB,kGAAkG;IAClG,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACrC,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACrD,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,sFAAsF;AACtF,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAMzE;AAKD,uFAAuF;AACvF,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,eAAe,GAAG,MAAM,EAAE,CAkB/D;AAED,wBAAgB,MAAM,CAAC,EAAE,EAAE,eAAe,GAAG,WAAW,CAQvD;AAED,mGAAmG;AACnG,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAMvC"}
@@ -0,0 +1,30 @@
1
+ import type { Driver } from '../capture/driver-api.js';
2
+ import { type CatalogEntry } from './catalog.js';
3
+ export interface PageSnapshot {
4
+ url: string;
5
+ title: string;
6
+ aria: string;
7
+ catalog: CatalogEntry[];
8
+ }
9
+ /**
10
+ * Explore the app in its current state: read every interactable, choose the first locator
11
+ * candidate that resolves uniquely against the live DOM, and return a grounded catalog.
12
+ * This is what makes authored specs correct by construction — locators are validated here,
13
+ * not discovered to be wrong at render time.
14
+ */
15
+ export declare function snapshotPage(driver: Driver): Promise<PageSnapshot>;
16
+ /** One-shot exploration: launch, navigate, snapshot, close. Used by `playhead explore`. */
17
+ export declare function exploreUrl(url: string, opts?: {
18
+ viewport?: {
19
+ w: number;
20
+ h: number;
21
+ };
22
+ headless?: boolean;
23
+ driver?: Driver;
24
+ /** storageState JSON (from `playhead login`) so authed screens are exploreable. */
25
+ storageStatePath?: string;
26
+ }): Promise<PageSnapshot>;
27
+ /** Render a catalog as a compact, human/agent-readable table: grouped by page REGION (where
28
+ * the element lives), then by kind, with a stability signal per locator. */
29
+ export declare function formatCatalog(snap: PageSnapshot): string;
30
+ //# sourceMappingURL=explore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"explore.d.ts","sourceRoot":"","sources":["../../src/authoring/explore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAGvD,OAAO,EAA0C,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAEzF,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CA+CxE;AAED,2FAA2F;AAC3F,wBAAsB,UAAU,CAC9B,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE;IACL,QAAQ,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,GACA,OAAO,CAAC,YAAY,CAAC,CAkBvB;AAED;6EAC6E;AAC7E,wBAAgB,aAAa,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAyBxD"}
@@ -0,0 +1,26 @@
1
+ /** An action the author loop decided on and (usually) executed to advance the app. */
2
+ export interface AuthoredStep {
3
+ action: 'goto' | 'click' | 'dblclick' | 'hover' | 'type' | 'press' | 'select' | 'scroll' | 'expect';
4
+ scene: string;
5
+ locator?: string;
6
+ text?: string;
7
+ value?: string;
8
+ keys?: string;
9
+ url?: string;
10
+ visible?: boolean;
11
+ caption?: string;
12
+ mask?: boolean;
13
+ }
14
+ export interface AuthoredSpec {
15
+ title: string;
16
+ url: string;
17
+ viewport: {
18
+ w: number;
19
+ h: number;
20
+ };
21
+ kind: string;
22
+ steps: AuthoredStep[];
23
+ }
24
+ /** Serialize an authored spec into clean, human-editable Playhead YAML, grouped into scenes. */
25
+ export declare function serializeSpec(spec: AuthoredSpec): string;
26
+ //# sourceMappingURL=serialize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../../src/authoring/serialize.ts"],"names":[],"mappings":"AAEA,sFAAsF;AACtF,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,UAAU,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACpG,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB;AA6BD,gGAAgG;AAChG,wBAAgB,aAAa,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CA4BxD"}
@@ -0,0 +1,31 @@
1
+ import type { Spec } from '../spec/schema.js';
2
+ import type { Driver } from '../capture/driver-api.js';
3
+ export interface ValidateIssue {
4
+ stepRef: string;
5
+ step: string;
6
+ locator: string;
7
+ severity: 'error' | 'warn';
8
+ message: string;
9
+ suggestions: string[];
10
+ }
11
+ export interface ValidateResult {
12
+ ok: boolean;
13
+ checked: number;
14
+ issues: ValidateIssue[];
15
+ }
16
+ /**
17
+ * Live preflight: walk the spec against the running app in ONE browser pass, checking every
18
+ * locator (the action target, the focus hint, waits, expects) *before* any frame is ever
19
+ * captured — and report ALL problems at once with nearest-candidate suggestions from the
20
+ * current screen. Steps are executed fast (no cursor choreography, no filming) so later
21
+ * locators are checked against the state they'll actually meet.
22
+ *
23
+ * This collapses "one bad locator = one full capture run" into a single cheap pass.
24
+ */
25
+ export declare function validateLive(spec: Spec, opts?: {
26
+ headless?: boolean;
27
+ driver?: Driver;
28
+ }): Promise<ValidateResult>;
29
+ /** Human-readable report for the CLI. */
30
+ export declare function formatValidateResult(res: ValidateResult): string;
31
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/authoring/validate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAG9C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAMvD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB;AAED;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,IAAI,EACV,IAAI,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7C,OAAO,CAAC,cAAc,CAAC,CAsFzB;AAsFD,yCAAyC;AACzC,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,MAAM,CAiBhE"}
@@ -0,0 +1,25 @@
1
+ import type { Spec } from './spec/schema.js';
2
+ import type { Verdict } from './verify/types.js';
3
+ export interface BeforeAfterResult {
4
+ videoPath: string;
5
+ before: {
6
+ videoPath: string;
7
+ verdict: Verdict;
8
+ };
9
+ after: {
10
+ videoPath: string;
11
+ verdict: Verdict;
12
+ };
13
+ verdict: 'publishable' | 'not-publishable';
14
+ }
15
+ /**
16
+ * Render a before/after comparison: run the SAME spec against two versions of the app
17
+ * (app.compareUrl = before, app.url = after), compose each with the full pipeline, verify both,
18
+ * and stitch them into one titled clip. Publishable only if both halves are.
19
+ */
20
+ export declare function renderBeforeAfter(spec: Spec, opts: {
21
+ outDir: string;
22
+ headless?: boolean;
23
+ vision?: boolean;
24
+ }): Promise<BeforeAfterResult>;
25
+ //# sourceMappingURL=beforeafter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"beforeafter.d.ts","sourceRoot":"","sources":["../src/beforeafter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAK7C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAIjD,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IAChD,KAAK,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IAC/C,OAAO,EAAE,aAAa,GAAG,iBAAiB,CAAC;CAC5C;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,IAAI,EACV,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAC7D,OAAO,CAAC,iBAAiB,CAAC,CAiC5B"}
@@ -0,0 +1,3 @@
1
+ export declare function sha256Json(value: unknown): string;
2
+ export declare function sha256File(path: string): Promise<string>;
3
+ //# sourceMappingURL=hash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../../src/bundle/hash.ts"],"names":[],"mappings":"AAGA,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEjD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQxD"}
@@ -0,0 +1,6 @@
1
+ import type { BundleData, FrameIndexEntry } from './types.js';
2
+ export declare function openBundle(dir: string): Promise<BundleData>;
3
+ /** Index of the latest frame with t ≤ tMs (hold semantics); clamps to the first frame. */
4
+ export declare function frameIndexForTime(frames: FrameIndexEntry[], tMs: number): number;
5
+ export declare function framePath(bundle: BundleData, index: number): string;
6
+ //# sourceMappingURL=reader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reader.d.ts","sourceRoot":"","sources":["../../src/bundle/reader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAkB,eAAe,EAAc,MAAM,YAAY,CAAC;AAG1F,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAgBjE;AAED,0FAA0F;AAC1F,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,eAAe,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAWhF;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEnE"}