@scenar/cli 0.10.0 → 0.11.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 (164) hide show
  1. package/example-bundle/assets/index-DxmqjG-8.js +388 -0
  2. package/example-bundle/assets/style-B2sxnVVZ.css +1 -0
  3. package/example-bundle/embed.js +1 -1
  4. package/example-bundle/index.html +2 -2
  5. package/example-bundle/logo.png +0 -0
  6. package/example-bundle/pack-manifest.json +33 -9
  7. package/example-bundle/soundtrack/music.mp3 +0 -0
  8. package/example-bundle/soundtrack/sfx/click.mp3 +0 -0
  9. package/example-bundle/soundtrack/sfx/keystroke.mp3 +0 -0
  10. package/package.json +4 -4
  11. package/src/__tests__/collect-pack-shots.test.ts +8 -0
  12. package/src/__tests__/elevenlabs-provider.test.ts +187 -0
  13. package/src/__tests__/embed-snippet.test.ts +5 -0
  14. package/src/__tests__/find-authored-soundtrack.test.ts +54 -0
  15. package/src/__tests__/find-authored-title-cards.test.ts +65 -0
  16. package/src/__tests__/load-bundle.test.ts +67 -0
  17. package/src/__tests__/narrate-command.test.ts +65 -1
  18. package/src/__tests__/narration-cache.test.ts +134 -0
  19. package/src/__tests__/pack-bundle-contract.test.ts +2 -0
  20. package/src/__tests__/pack-generate-embed-entry.test.ts +95 -16
  21. package/src/__tests__/presenter-command.test.ts +366 -0
  22. package/src/__tests__/render-command.test.ts +11 -0
  23. package/src/__tests__/render-generate-entry.test.ts +102 -0
  24. package/src/__tests__/resolve-provider.test.ts +32 -3
  25. package/src/__tests__/scenario-validator.test.ts +174 -0
  26. package/src/__tests__/stage-assets.test.ts +269 -0
  27. package/src/commands/narrate.js +1 -1
  28. package/src/commands/narrate.js.map +1 -1
  29. package/src/commands/narrate.ts +1 -1
  30. package/src/commands/presenter.d.ts +3 -0
  31. package/src/commands/presenter.d.ts.map +1 -0
  32. package/src/commands/presenter.js +89 -0
  33. package/src/commands/presenter.js.map +1 -0
  34. package/src/commands/presenter.ts +107 -0
  35. package/src/commands/render.d.ts.map +1 -1
  36. package/src/commands/render.js +2 -0
  37. package/src/commands/render.js.map +1 -1
  38. package/src/commands/render.ts +6 -0
  39. package/src/embed/embed-snippet.d.ts +5 -0
  40. package/src/embed/embed-snippet.d.ts.map +1 -1
  41. package/src/embed/embed-snippet.js +6 -1
  42. package/src/embed/embed-snippet.js.map +1 -1
  43. package/src/embed/embed-snippet.ts +6 -1
  44. package/src/index.d.ts.map +1 -1
  45. package/src/index.js +2 -0
  46. package/src/index.js.map +1 -1
  47. package/src/index.ts +2 -0
  48. package/src/narrate/run-narrate.d.ts +15 -1
  49. package/src/narrate/run-narrate.d.ts.map +1 -1
  50. package/src/narrate/run-narrate.js +11 -4
  51. package/src/narrate/run-narrate.js.map +1 -1
  52. package/src/narrate/run-narrate.ts +15 -6
  53. package/src/pack/bundle-contract.d.ts +1 -0
  54. package/src/pack/bundle-contract.d.ts.map +1 -1
  55. package/src/pack/bundle-contract.js +3 -0
  56. package/src/pack/bundle-contract.js.map +1 -1
  57. package/src/pack/bundle-contract.ts +3 -0
  58. package/src/pack/collect-pack-shots.d.ts +5 -1
  59. package/src/pack/collect-pack-shots.d.ts.map +1 -1
  60. package/src/pack/collect-pack-shots.js +3 -1
  61. package/src/pack/collect-pack-shots.js.map +1 -1
  62. package/src/pack/collect-pack-shots.ts +15 -1
  63. package/src/pack/generate-embed-entry.d.ts +7 -0
  64. package/src/pack/generate-embed-entry.d.ts.map +1 -1
  65. package/src/pack/generate-embed-entry.js +102 -7
  66. package/src/pack/generate-embed-entry.js.map +1 -1
  67. package/src/pack/generate-embed-entry.ts +107 -7
  68. package/src/pack/run-pack.d.ts +1 -0
  69. package/src/pack/run-pack.d.ts.map +1 -1
  70. package/src/pack/run-pack.js +100 -9
  71. package/src/pack/run-pack.js.map +1 -1
  72. package/src/pack/run-pack.ts +133 -9
  73. package/src/presenter/heygen-client.d.ts +69 -0
  74. package/src/presenter/heygen-client.d.ts.map +1 -0
  75. package/src/presenter/heygen-client.js +154 -0
  76. package/src/presenter/heygen-client.js.map +1 -0
  77. package/src/presenter/heygen-client.ts +224 -0
  78. package/src/presenter/run-presenter.d.ts +67 -0
  79. package/src/presenter/run-presenter.d.ts.map +1 -0
  80. package/src/presenter/run-presenter.js +348 -0
  81. package/src/presenter/run-presenter.js.map +1 -0
  82. package/src/presenter/run-presenter.ts +536 -0
  83. package/src/render/generate-entry.d.ts +4 -0
  84. package/src/render/generate-entry.d.ts.map +1 -1
  85. package/src/render/generate-entry.js +75 -7
  86. package/src/render/generate-entry.js.map +1 -1
  87. package/src/render/generate-entry.ts +84 -7
  88. package/src/render/run-render.d.ts +6 -0
  89. package/src/render/run-render.d.ts.map +1 -1
  90. package/src/render/run-render.js +58 -0
  91. package/src/render/run-render.js.map +1 -1
  92. package/src/render/run-render.ts +72 -0
  93. package/src/render/stage-assets.d.ts +44 -0
  94. package/src/render/stage-assets.d.ts.map +1 -0
  95. package/src/render/stage-assets.js +91 -0
  96. package/src/render/stage-assets.js.map +1 -0
  97. package/src/render/stage-assets.ts +130 -0
  98. package/src/tts/echogarden.d.ts.map +1 -1
  99. package/src/tts/echogarden.js +4 -2
  100. package/src/tts/echogarden.js.map +1 -1
  101. package/src/tts/echogarden.ts +5 -2
  102. package/src/tts/edge-tts.d.ts.map +1 -1
  103. package/src/tts/edge-tts.js +2 -1
  104. package/src/tts/edge-tts.js.map +1 -1
  105. package/src/tts/edge-tts.ts +2 -1
  106. package/src/tts/elevenlabs.d.ts +15 -0
  107. package/src/tts/elevenlabs.d.ts.map +1 -0
  108. package/src/tts/elevenlabs.js +73 -0
  109. package/src/tts/elevenlabs.js.map +1 -0
  110. package/src/tts/elevenlabs.ts +105 -0
  111. package/src/tts/openai.d.ts.map +1 -1
  112. package/src/tts/openai.js +2 -1
  113. package/src/tts/openai.js.map +1 -1
  114. package/src/tts/openai.ts +2 -1
  115. package/src/tts/resolve-provider.d.ts +1 -1
  116. package/src/tts/resolve-provider.d.ts.map +1 -1
  117. package/src/tts/resolve-provider.js +10 -3
  118. package/src/tts/resolve-provider.js.map +1 -1
  119. package/src/tts/resolve-provider.ts +12 -4
  120. package/src/tts/types.d.ts +9 -0
  121. package/src/tts/types.d.ts.map +1 -1
  122. package/src/tts/types.ts +10 -0
  123. package/src/util/confirm.d.ts +11 -0
  124. package/src/util/confirm.d.ts.map +1 -0
  125. package/src/util/confirm.js +24 -0
  126. package/src/util/confirm.js.map +1 -0
  127. package/src/util/confirm.ts +23 -0
  128. package/src/util/load-bundle.d.ts +17 -1
  129. package/src/util/load-bundle.d.ts.map +1 -1
  130. package/src/util/load-bundle.js +19 -11
  131. package/src/util/load-bundle.js.map +1 -1
  132. package/src/util/load-bundle.ts +38 -12
  133. package/src/util/load-ts.d.ts +77 -0
  134. package/src/util/load-ts.d.ts.map +1 -1
  135. package/src/util/load-ts.js +125 -0
  136. package/src/util/load-ts.js.map +1 -1
  137. package/src/util/load-ts.ts +165 -0
  138. package/src/util/narration-cache.d.ts +13 -5
  139. package/src/util/narration-cache.d.ts.map +1 -1
  140. package/src/util/narration-cache.js +10 -8
  141. package/src/util/narration-cache.js.map +1 -1
  142. package/src/util/narration-cache.ts +22 -6
  143. package/src/util/presenter-cache.d.ts +44 -0
  144. package/src/util/presenter-cache.d.ts.map +1 -0
  145. package/src/util/presenter-cache.js +43 -0
  146. package/src/util/presenter-cache.js.map +1 -0
  147. package/src/util/presenter-cache.ts +101 -0
  148. package/src/util/scenario-assets.d.ts +47 -0
  149. package/src/util/scenario-assets.d.ts.map +1 -0
  150. package/src/util/scenario-assets.js +71 -0
  151. package/src/util/scenario-assets.js.map +1 -0
  152. package/src/util/scenario-assets.ts +111 -0
  153. package/src/util/soundtrack-assets.d.ts +34 -0
  154. package/src/util/soundtrack-assets.d.ts.map +1 -0
  155. package/src/util/soundtrack-assets.js +48 -0
  156. package/src/util/soundtrack-assets.js.map +1 -0
  157. package/src/util/soundtrack-assets.ts +69 -0
  158. package/src/validate/scenario-validator.d.ts.map +1 -1
  159. package/src/validate/scenario-validator.js +82 -0
  160. package/src/validate/scenario-validator.js.map +1 -1
  161. package/src/validate/scenario-validator.ts +110 -0
  162. package/tsconfig.tsbuildinfo +1 -1
  163. package/example-bundle/assets/index-DpJUOVBT.js +0 -388
  164. package/example-bundle/assets/style-ix_q0zsc.css +0 -1
@@ -0,0 +1,224 @@
1
+ /**
2
+ * Minimal HeyGen API client for `scenar presenter` — deliberately an
3
+ * internal module of the CLI, not published API. Shaped so a second
4
+ * avatar provider could slot in behind the same function signatures,
5
+ * but the interface is not exported until a second provider is a
6
+ * concrete need (the TTS-provider precedent).
7
+ *
8
+ * Endpoints and response fields follow the live Gate 0 transcript:
9
+ * `POST /v3/assets` (multipart) → `data.asset_id`;
10
+ * `POST /v3/videos` (`type: "avatar"`, `audio_asset_id`) →
11
+ * `data.video_id`; `GET /v3/videos/{id}` → `data.status`
12
+ * (`completed`/`failed`), `data.video_url`, `data.failure_message`.
13
+ *
14
+ * BYO key, no Scenar-side proxying — identical to the TTS providers.
15
+ * Native fetch, no new runtime dependencies.
16
+ */
17
+
18
+ const HEYGEN_BASE_URL = "https://api.heygen.com";
19
+
20
+ /** Engine flag values → HeyGen's API vocabulary. */
21
+ export const HEYGEN_ENGINE_TYPES = {
22
+ iii: "avatar_iii",
23
+ iv: "avatar_iv",
24
+ } as const;
25
+
26
+ export type HeygenEngineFlag = keyof typeof HEYGEN_ENGINE_TYPES;
27
+
28
+ /**
29
+ * Indicative $/minute of narration audio per engine, from the project
30
+ * cost analysis. The estimate the cost gate prints is indicative —
31
+ * HeyGen bills credits, not dollars.
32
+ */
33
+ export const HEYGEN_RATE_PER_MIN: Record<HeygenEngineFlag, number> = {
34
+ iii: 1.0,
35
+ iv: 4.0,
36
+ };
37
+
38
+ const MAX_ATTEMPTS_ON_429 = 3;
39
+ const DEFAULT_RETRY_AFTER_S = 30;
40
+
41
+ export interface HeygenRequestContext {
42
+ readonly apiKey: string;
43
+ /** Injectable for tests; defaults to a real timer. */
44
+ readonly sleep?: (ms: number) => Promise<void>;
45
+ /** Rate-limit feedback sink (stderr line per retry). */
46
+ readonly onRetry?: (message: string) => void;
47
+ }
48
+
49
+ const realSleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));
50
+
51
+ /**
52
+ * Run a HeyGen request, honoring 429 rate limits: wait `Retry-After`
53
+ * (default 30 s) and retry the same call, up to 3 attempts total.
54
+ */
55
+ async function heygenFetch(
56
+ ctx: HeygenRequestContext,
57
+ path: string,
58
+ init: RequestInit,
59
+ ): Promise<Response> {
60
+ const sleep = ctx.sleep ?? realSleep;
61
+ let response: Response;
62
+ for (let attempt = 1; ; attempt++) {
63
+ response = await fetch(`${HEYGEN_BASE_URL}${path}`, {
64
+ ...init,
65
+ headers: { "x-api-key": ctx.apiKey, ...(init.headers ?? {}) },
66
+ });
67
+ if (response.status !== 429 || attempt >= MAX_ATTEMPTS_ON_429) return response;
68
+
69
+ const retryAfterS =
70
+ Number(response.headers.get("retry-after")) || DEFAULT_RETRY_AFTER_S;
71
+ ctx.onRetry?.(
72
+ `rate-limited, retrying in ${retryAfterS}s (${attempt}/${MAX_ATTEMPTS_ON_429})`,
73
+ );
74
+ await sleep(retryAfterS * 1000);
75
+ }
76
+ }
77
+
78
+ async function readBody(response: Response): Promise<Record<string, unknown>> {
79
+ try {
80
+ return (await response.json()) as Record<string, unknown>;
81
+ } catch {
82
+ return {};
83
+ }
84
+ }
85
+
86
+ function apiError(operation: string, response: Response, body: unknown): Error {
87
+ return new Error(
88
+ `HeyGen ${operation} failed (${response.status}): ${JSON.stringify(body)}`,
89
+ );
90
+ }
91
+
92
+ /** Upload one narration MP3 as a HeyGen asset (free). Returns the asset id. */
93
+ export async function uploadAudioAsset(
94
+ ctx: HeygenRequestContext,
95
+ audio: Buffer,
96
+ filename: string,
97
+ ): Promise<string> {
98
+ const form = new FormData();
99
+ form.append(
100
+ "file",
101
+ new Blob([new Uint8Array(audio)], { type: "audio/mpeg" }),
102
+ filename,
103
+ );
104
+ const response = await heygenFetch(ctx, "/v3/assets", {
105
+ method: "POST",
106
+ body: form,
107
+ });
108
+ const body = await readBody(response);
109
+ if (!response.ok) throw apiError("asset upload", response, body);
110
+ const assetId = (body["data"] as Record<string, unknown> | undefined)?.["asset_id"];
111
+ if (typeof assetId !== "string" || assetId.length === 0) {
112
+ throw new Error(`HeyGen asset upload returned no asset_id: ${JSON.stringify(body)}`);
113
+ }
114
+ return assetId;
115
+ }
116
+
117
+ export interface CreateAvatarVideoInput {
118
+ readonly avatarId: string;
119
+ readonly audioAssetId: string;
120
+ readonly engine: HeygenEngineFlag;
121
+ readonly resolution: string;
122
+ /**
123
+ * Deterministic title (`scenar <scenarioId> step-<N> <hash8>`) so any
124
+ * duplicate generation is identifiable in the HeyGen dashboard.
125
+ */
126
+ readonly title: string;
127
+ }
128
+
129
+ /** Create one avatar video lip-synced to the uploaded audio. Returns the video id. */
130
+ export async function createAvatarVideo(
131
+ ctx: HeygenRequestContext,
132
+ input: CreateAvatarVideoInput,
133
+ ): Promise<string> {
134
+ const response = await heygenFetch(ctx, "/v3/videos", {
135
+ method: "POST",
136
+ headers: { "Content-Type": "application/json" },
137
+ body: JSON.stringify({
138
+ type: "avatar",
139
+ avatar_id: input.avatarId,
140
+ audio_asset_id: input.audioAssetId,
141
+ engine: { type: HEYGEN_ENGINE_TYPES[input.engine] },
142
+ output_format: "mp4",
143
+ aspect_ratio: "auto",
144
+ resolution: input.resolution,
145
+ title: input.title,
146
+ }),
147
+ });
148
+ const body = await readBody(response);
149
+ if (!response.ok) throw apiError("video create", response, body);
150
+ const videoId = (body["data"] as Record<string, unknown> | undefined)?.["video_id"];
151
+ if (typeof videoId !== "string" || videoId.length === 0) {
152
+ throw new Error(`HeyGen video create returned no video_id: ${JSON.stringify(body)}`);
153
+ }
154
+ return videoId;
155
+ }
156
+
157
+ export interface PollOutcome {
158
+ readonly status: "completed" | "failed" | "timeout";
159
+ readonly videoUrl?: string;
160
+ readonly failureMessage?: string;
161
+ }
162
+
163
+ export interface PollOptions {
164
+ readonly intervalMs: number;
165
+ readonly timeoutMs: number;
166
+ /** Elapsed-time feedback per tick (drives the progress line). */
167
+ readonly onTick?: (elapsedMs: number) => void;
168
+ }
169
+
170
+ /**
171
+ * Poll a generation to completion. Polling is read-only, so transient
172
+ * network errors retry on the next tick inside the step timeout.
173
+ */
174
+ export async function pollVideo(
175
+ ctx: HeygenRequestContext,
176
+ videoId: string,
177
+ options: PollOptions,
178
+ ): Promise<PollOutcome> {
179
+ const sleep = ctx.sleep ?? realSleep;
180
+ const startedAt = Date.now();
181
+
182
+ while (Date.now() - startedAt < options.timeoutMs) {
183
+ await sleep(options.intervalMs);
184
+ options.onTick?.(Date.now() - startedAt);
185
+
186
+ let body: Record<string, unknown>;
187
+ try {
188
+ const response = await heygenFetch(ctx, `/v3/videos/${videoId}`, { method: "GET" });
189
+ body = await readBody(response);
190
+ if (!response.ok) continue;
191
+ } catch {
192
+ continue;
193
+ }
194
+
195
+ const data = (body["data"] ?? {}) as Record<string, unknown>;
196
+ const status = data["status"];
197
+ if (status === "completed") {
198
+ return {
199
+ status: "completed",
200
+ videoUrl: typeof data["video_url"] === "string" ? data["video_url"] : undefined,
201
+ };
202
+ }
203
+ if (status === "failed") {
204
+ return {
205
+ status: "failed",
206
+ failureMessage:
207
+ typeof data["failure_message"] === "string"
208
+ ? data["failure_message"]
209
+ : JSON.stringify(data["failure_message"] ?? "unknown failure"),
210
+ };
211
+ }
212
+ }
213
+
214
+ return { status: "timeout" };
215
+ }
216
+
217
+ /** Download a completed clip. Returns the raw bytes. */
218
+ export async function downloadVideo(url: string): Promise<Buffer> {
219
+ const response = await fetch(url);
220
+ if (!response.ok) {
221
+ throw new Error(`HeyGen clip download failed (${response.status}) for ${url}`);
222
+ }
223
+ return Buffer.from(await response.arrayBuffer());
224
+ }
@@ -0,0 +1,67 @@
1
+ import { type HeygenEngineFlag } from "./heygen-client.js";
2
+ /** Options for {@link runPresenter}. Paths may be relative; resolved here. */
3
+ export interface RunPresenterOptions {
4
+ /** A scenario YAML file, a .ts steps file, or a directory of scenarios. */
5
+ readonly target: string;
6
+ /** HeyGen avatar id. Falls back to the HEYGEN_AVATAR_ID env var. */
7
+ readonly avatar?: string;
8
+ /** Generation engine (default: iii). */
9
+ readonly engine?: HeygenEngineFlag;
10
+ /** Output resolution tier (default: 720p). */
11
+ readonly resolution?: string;
12
+ /** Output directory for clips (default: presenter/ beside the scenario). */
13
+ readonly out?: string;
14
+ /** URL path prefix for the `src` fields written into the manifest. */
15
+ readonly baseUrl?: string;
16
+ /** Skip the cost confirmation (CI / scripts). */
17
+ readonly yes?: boolean;
18
+ /** Progress sink for per-step/per-scenario messages (stderr). */
19
+ readonly onLog?: (message: string) => void;
20
+ /** Injectable confirmation prompt (the command wires util/confirm). */
21
+ readonly confirmImpl?: (question: string) => Promise<boolean>;
22
+ /** Whether stdin can answer a prompt (default: process.stdin.isTTY). */
23
+ readonly isTty?: boolean;
24
+ /** Test seams for the minutes-long polling loop. */
25
+ readonly pollIntervalMs?: number;
26
+ readonly pollTimeoutMs?: number;
27
+ readonly sleep?: (ms: number) => Promise<void>;
28
+ }
29
+ /** Per-scenario presenter outcome. */
30
+ export interface PresentedScenario {
31
+ readonly id: string;
32
+ readonly generated: number;
33
+ readonly cached: number;
34
+ readonly failed: number;
35
+ /** True when no step opts into the presenter (nothing to generate). */
36
+ readonly skipped: boolean;
37
+ }
38
+ /** Aggregate result of a presenter run. */
39
+ export interface PresenterResult {
40
+ readonly mode: "file" | "directory";
41
+ readonly scenarios: PresentedScenario[];
42
+ readonly totalGenerated: number;
43
+ readonly totalCached: number;
44
+ readonly totalFailed: number;
45
+ readonly totalSkipped: number;
46
+ readonly errors: Array<{
47
+ id: string;
48
+ message: string;
49
+ }>;
50
+ /** True when the cost gate was declined or unanswerable. */
51
+ readonly aborted: boolean;
52
+ }
53
+ /**
54
+ * Generate presenter clips for a scenario file or a directory of
55
+ * scenarios — the orchestration behind `scenar presenter`. For each
56
+ * step marked `presenter: true`, uploads that step's narration audio
57
+ * to HeyGen, generates a lip-synced avatar clip, downloads it beside
58
+ * the narration output, and writes a positional `presenter/manifest.json`
59
+ * keyed to AUTHORED steps (like the narration manifest).
60
+ *
61
+ * Spend is visible and consented: before any paid call the batch
62
+ * estimate is printed and confirmed (or `--yes`). Failures never
63
+ * corrupt state — the manifest and cache are written from completed
64
+ * work only, so a rerun regenerates exactly the missing steps.
65
+ */
66
+ export declare function runPresenter(options: RunPresenterOptions): Promise<PresenterResult>;
67
+ //# sourceMappingURL=run-presenter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-presenter.d.ts","sourceRoot":"","sources":["../../../src/presenter/run-presenter.ts"],"names":[],"mappings":"AAcA,OAAO,EAEL,KAAK,gBAAgB,EAMtB,MAAM,oBAAoB,CAAC;AAU5B,8EAA8E;AAC9E,MAAM,WAAW,mBAAmB;IAClC,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,oEAAoE;IACpE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,wCAAwC;IACxC,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,CAAC;IACnC,8CAA8C;IAC9C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,4EAA4E;IAC5E,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,iDAAiD;IACjD,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC;IACvB,iEAAiE;IACjE,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,uEAAuE;IACvE,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9D,wEAAwE;IACxE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,oDAAoD;IACpD,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAED,sCAAsC;AACtC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,uEAAuE;IACvE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED,2CAA2C;AAC3C,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IACpC,QAAQ,CAAC,SAAS,EAAE,iBAAiB,EAAE,CAAC;IACxC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxD,4DAA4D;IAC5D,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAmBD;;;;;;;;;;;;GAYG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,eAAe,CAAC,CAgCzF"}
@@ -0,0 +1,348 @@
1
+ import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
2
+ import { basename, dirname, join, resolve } from "node:path";
3
+ import { discoverScenarios } from "../util/discover-scenarios.js";
4
+ import { loadStepsFromTs } from "../util/load-ts.js";
5
+ import { loadStepsFromFile } from "../narrate/run-narrate.js";
6
+ import { fileExists } from "../util/narration-cache.js";
7
+ import { buildPresenterCacheFile, computePresenterHash, isPresenterCached, loadPresenterCache, presenterFingerprint, savePresenterCache, } from "../util/presenter-cache.js";
8
+ import { HEYGEN_RATE_PER_MIN, createAvatarVideo, downloadVideo, pollVideo, uploadAudioAsset, } from "./heygen-client.js";
9
+ /** Default generation poll cadence and per-step budget. */
10
+ const POLL_INTERVAL_MS = 5_000;
11
+ const POLL_TIMEOUT_MS = 15 * 60 * 1_000;
12
+ /** Progress-line cadence during a minutes-long generation. */
13
+ const PROGRESS_LOG_EVERY_MS = 30_000;
14
+ const CONTAINER = "mp4"; // fixed for v1 (Safari renders VP9-alpha opaque)
15
+ /** Shorten an avatar id for display: de95f90d… → de95…8ad8. */
16
+ function shortAvatarId(id) {
17
+ return id.length > 10 ? `${id.slice(0, 4)}…${id.slice(-4)}` : id;
18
+ }
19
+ /**
20
+ * Generate presenter clips for a scenario file or a directory of
21
+ * scenarios — the orchestration behind `scenar presenter`. For each
22
+ * step marked `presenter: true`, uploads that step's narration audio
23
+ * to HeyGen, generates a lip-synced avatar clip, downloads it beside
24
+ * the narration output, and writes a positional `presenter/manifest.json`
25
+ * keyed to AUTHORED steps (like the narration manifest).
26
+ *
27
+ * Spend is visible and consented: before any paid call the batch
28
+ * estimate is printed and confirmed (or `--yes`). Failures never
29
+ * corrupt state — the manifest and cache are written from completed
30
+ * work only, so a rerun regenerates exactly the missing steps.
31
+ */
32
+ export async function runPresenter(options) {
33
+ const onLog = options.onLog ?? (() => { });
34
+ // Every prerequisite that needs no network is checked before any work.
35
+ const apiKey = process.env["HEYGEN_API_KEY"];
36
+ if (!apiKey) {
37
+ throw new Error("HEYGEN_API_KEY environment variable is not set.\n\n" +
38
+ "Set it before running presenter:\n" +
39
+ " export HEYGEN_API_KEY=...");
40
+ }
41
+ const avatarId = options.avatar ?? process.env["HEYGEN_AVATAR_ID"];
42
+ if (!avatarId) {
43
+ throw new Error("No avatar id. Pass --avatar <id> or set HEYGEN_AVATAR_ID.");
44
+ }
45
+ const engine = options.engine ?? "iii";
46
+ const resolution = options.resolution ?? "720p";
47
+ const config = {
48
+ apiKey,
49
+ avatarId,
50
+ engine,
51
+ resolution,
52
+ fingerprint: presenterFingerprint(engine, resolution, CONTAINER, avatarId),
53
+ };
54
+ const resolved = resolve(options.target);
55
+ const info = await stat(resolved);
56
+ return info.isDirectory()
57
+ ? presentDirectory(resolved, options, config, onLog)
58
+ : presentSingleFile(resolved, options, config, onLog);
59
+ }
60
+ async function presentSingleFile(filePath, options, config, onLog) {
61
+ const scenarioDir = dirname(filePath);
62
+ const id = basename(scenarioDir);
63
+ const { steps } = await loadStepsFromFile(filePath);
64
+ const outDir = options.out ?? join(scenarioDir, "presenter");
65
+ const outcome = await presentScenario({
66
+ scenarioId: id,
67
+ steps,
68
+ scenarioDir,
69
+ outDir,
70
+ options,
71
+ config,
72
+ onLog,
73
+ });
74
+ return {
75
+ mode: "file",
76
+ scenarios: [outcome.result],
77
+ totalGenerated: outcome.result.generated,
78
+ totalCached: outcome.result.cached,
79
+ totalFailed: outcome.result.failed,
80
+ totalSkipped: outcome.result.skipped ? 1 : 0,
81
+ errors: [],
82
+ aborted: outcome.aborted,
83
+ };
84
+ }
85
+ async function presentDirectory(dirPath, options, config, onLog) {
86
+ const scenarios = await discoverScenarios(dirPath);
87
+ const results = [];
88
+ const errors = [];
89
+ let totals = { generated: 0, cached: 0, failed: 0, skipped: 0 };
90
+ let aborted = false;
91
+ if (scenarios.length > 0) {
92
+ onLog(`Discovered ${scenarios.length} scenario(s)`);
93
+ }
94
+ for (const scenario of scenarios) {
95
+ onLog(` ${scenario.id}`);
96
+ try {
97
+ const steps = await loadStepsFromTs(scenario.stepsPath);
98
+ const scenarioDir = dirname(scenario.stepsPath);
99
+ const outDir = options.out
100
+ ? join(options.out, scenario.id)
101
+ : join(scenarioDir, "presenter");
102
+ const outcome = await presentScenario({
103
+ scenarioId: scenario.id,
104
+ steps,
105
+ scenarioDir,
106
+ outDir,
107
+ options,
108
+ config,
109
+ onLog,
110
+ });
111
+ results.push(outcome.result);
112
+ totals = {
113
+ generated: totals.generated + outcome.result.generated,
114
+ cached: totals.cached + outcome.result.cached,
115
+ failed: totals.failed + outcome.result.failed,
116
+ skipped: totals.skipped + (outcome.result.skipped ? 1 : 0),
117
+ };
118
+ if (outcome.aborted) {
119
+ aborted = true;
120
+ break; // a declined gate stops the batch — never spend past a "no"
121
+ }
122
+ }
123
+ catch (error) {
124
+ const message = error instanceof Error ? error.message : String(error);
125
+ onLog(` \x1b[31mfailed\x1b[0m: ${message}`);
126
+ errors.push({ id: scenario.id, message });
127
+ }
128
+ }
129
+ return {
130
+ mode: "directory",
131
+ scenarios: results,
132
+ totalGenerated: totals.generated,
133
+ totalCached: totals.cached,
134
+ totalFailed: totals.failed,
135
+ totalSkipped: totals.skipped,
136
+ errors,
137
+ aborted,
138
+ };
139
+ }
140
+ async function presentScenario(input) {
141
+ const { scenarioId, steps, scenarioDir, outDir, options, config, onLog } = input;
142
+ const optedIn = steps
143
+ .map((step, index) => ({ step, index }))
144
+ .filter(({ step }) => step.presenter === true);
145
+ if (optedIn.length === 0) {
146
+ return {
147
+ result: { id: scenarioId, generated: 0, cached: 0, failed: 0, skipped: true },
148
+ aborted: false,
149
+ };
150
+ }
151
+ // Authoring contradiction (load-time rule, guarded here too in the
152
+ // validator's wording): a presenter step must have a narration script.
153
+ const unscripted = optedIn.filter(({ step }) => {
154
+ const text = step.narration ?? step.narrationText;
155
+ return typeof text !== "string" || text.length === 0;
156
+ });
157
+ if (unscripted.length > 0) {
158
+ const indices = unscripted.map(({ index }) => index).join(", ");
159
+ throw new Error(`steps[${indices}].presenter: presenter requires narration_text — ` +
160
+ "a presenter clip is derived from the step's narration audio.");
161
+ }
162
+ // Operational prerequisite: narration audio must already exist —
163
+ // the clip is derived from it by definition.
164
+ const narrationDir = join(scenarioDir, "narration");
165
+ let narrationManifest = null;
166
+ try {
167
+ narrationManifest = JSON.parse(await readFile(join(narrationDir, "manifest.json"), "utf-8"));
168
+ }
169
+ catch {
170
+ // Reported below with the per-step gap list.
171
+ }
172
+ const missingAudio = [];
173
+ const ready = [];
174
+ for (const { index } of optedIn) {
175
+ const entry = narrationManifest?.steps[index];
176
+ const audioPath = join(narrationDir, `step-${index}.mp3`);
177
+ if (!entry || !(await fileExists(audioPath))) {
178
+ missingAudio.push(index);
179
+ }
180
+ else {
181
+ ready.push({ index, durationMs: entry.durationMs, audioPath });
182
+ }
183
+ }
184
+ if (missingAudio.length > 0) {
185
+ throw new Error(`steps ${missingAudio.join(", ")} opt into the presenter but have no narration audio. ` +
186
+ `Run 'scenar narrate ${options.target}' first — presenter clips are derived from narration audio.`);
187
+ }
188
+ // Cache pass: the per-step hash is the narration MP3 bytes, so
189
+ // re-narrating a step invalidates its clip automatically.
190
+ const existingCache = await loadPresenterCache(outDir);
191
+ const plan = [];
192
+ for (const step of ready) {
193
+ const audioBytes = await readFile(step.audioPath);
194
+ const hash = computePresenterHash(audioBytes);
195
+ const clipPath = join(outDir, `step-${step.index}.mp4`);
196
+ const cached = isPresenterCached(existingCache, step.index, hash, config.fingerprint) &&
197
+ (await fileExists(clipPath));
198
+ plan.push({ ...step, hash, cached });
199
+ }
200
+ const toGenerate = plan.filter((step) => !step.cached);
201
+ // The plan — printed before anything spends money.
202
+ onLog(`Presenter plan — ${scenarioId} (engine ${config.engine}, ${config.resolution}, ` +
203
+ `avatar ${shortAvatarId(config.avatarId)})`);
204
+ for (const step of plan) {
205
+ const seconds = (step.durationMs / 1000).toFixed(1);
206
+ onLog(` step ${step.index} ${seconds}s ${step.cached ? "cached" : "generate"}`);
207
+ }
208
+ if (toGenerate.length === 0) {
209
+ onLog("Nothing to generate — all clips cached.");
210
+ // Still (re)write manifest and cache from the cached clips, so a
211
+ // deleted manifest regenerates without a paid call.
212
+ await writeOutputs(outDir, scenarioId, steps.length, plan, [], options, config);
213
+ return {
214
+ result: { id: scenarioId, generated: 0, cached: plan.length, failed: 0, skipped: false },
215
+ aborted: false,
216
+ };
217
+ }
218
+ const totalAudioMs = toGenerate.reduce((sum, step) => sum + step.durationMs, 0);
219
+ const rate = HEYGEN_RATE_PER_MIN[config.engine];
220
+ const estimate = (totalAudioMs / 60_000) * rate;
221
+ onLog(`Estimated cost: ${toGenerate.length} clip(s), ${(totalAudioMs / 1000).toFixed(1)}s audio → ` +
222
+ `~$${estimate.toFixed(2)} (rate: ~$${rate.toFixed(2)}/min, engine ${config.engine})`);
223
+ // The cost gate. Never spend silently: a pipe that cannot answer must
224
+ // pass --yes explicitly.
225
+ if (!options.yes) {
226
+ const isTty = options.isTty ?? process.stdin.isTTY === true;
227
+ if (!isTty) {
228
+ onLog("stdin is not a TTY; pass --yes to confirm the estimated spend.");
229
+ return {
230
+ result: { id: scenarioId, generated: 0, cached: 0, failed: 0, skipped: false },
231
+ aborted: true,
232
+ };
233
+ }
234
+ const confirmed = await input.options.confirmImpl?.("Proceed?");
235
+ if (!confirmed) {
236
+ onLog("Aborted — nothing generated, nothing spent.");
237
+ return {
238
+ result: { id: scenarioId, generated: 0, cached: 0, failed: 0, skipped: false },
239
+ aborted: true,
240
+ };
241
+ }
242
+ }
243
+ // Sequential generation (HeyGen concurrency limits are unverified —
244
+ // the sequential loop is the honest v1).
245
+ const ctx = {
246
+ apiKey: config.apiKey,
247
+ sleep: options.sleep,
248
+ onRetry: (message) => onLog(` ${message}`),
249
+ };
250
+ await mkdir(outDir, { recursive: true });
251
+ const completed = [];
252
+ let failed = 0;
253
+ for (const step of toGenerate) {
254
+ try {
255
+ const audioBytes = await readFile(step.audioPath);
256
+ const assetId = await uploadAudioAsset(ctx, audioBytes, `step-${step.index}.mp3`);
257
+ const videoId = await createAvatarVideo(ctx, {
258
+ avatarId: config.avatarId,
259
+ audioAssetId: assetId,
260
+ engine: config.engine,
261
+ resolution: config.resolution,
262
+ title: `scenar ${scenarioId} step-${step.index} ${step.hash.slice(0, 8)}`,
263
+ });
264
+ onLog(` step ${step.index}: generating (video_id ${videoId.slice(0, 8)}…)`);
265
+ let lastProgressMs = 0;
266
+ const outcome = await pollVideo(ctx, videoId, {
267
+ intervalMs: options.pollIntervalMs ?? POLL_INTERVAL_MS,
268
+ timeoutMs: options.pollTimeoutMs ?? POLL_TIMEOUT_MS,
269
+ onTick: (elapsedMs) => {
270
+ if (elapsedMs - lastProgressMs >= PROGRESS_LOG_EVERY_MS) {
271
+ lastProgressMs = elapsedMs;
272
+ onLog(` step ${step.index}: generating (video_id ${videoId.slice(0, 8)}…) ` +
273
+ `${Math.round(elapsedMs / 1000)}s`);
274
+ }
275
+ },
276
+ });
277
+ if (outcome.status === "timeout") {
278
+ failed++;
279
+ onLog(`\x1b[31m step ${step.index}: timed out after ${Math.round((options.pollTimeoutMs ?? POLL_TIMEOUT_MS) / 60_000)}min — check video ${videoId} in the HeyGen dashboard\x1b[0m`);
280
+ continue;
281
+ }
282
+ if (outcome.status === "failed" || !outcome.videoUrl) {
283
+ failed++;
284
+ onLog(`\x1b[31m step ${step.index}: HeyGen generation failed: ` +
285
+ `${outcome.failureMessage ?? "no video_url returned"}\x1b[0m`);
286
+ continue;
287
+ }
288
+ const clip = await downloadVideo(outcome.videoUrl);
289
+ await writeFile(join(outDir, `step-${step.index}.mp4`), clip);
290
+ completed.push({ index: step.index, durationMs: step.durationMs, hash: step.hash });
291
+ onLog(`\x1b[32m+\x1b[0m step ${step.index}: ${step.durationMs}ms clip (${clip.length} bytes)`);
292
+ }
293
+ catch (error) {
294
+ failed++;
295
+ const message = error instanceof Error ? error.message : String(error);
296
+ onLog(`\x1b[31m step ${step.index}: ${message}\x1b[0m`);
297
+ }
298
+ }
299
+ await writeOutputs(outDir, scenarioId, steps.length, plan, completed, options, config);
300
+ const cachedCount = plan.filter((step) => step.cached).length;
301
+ onLog(`\x1b[32m+\x1b[0m ${scenarioId}: ${completed.length} generated, ` +
302
+ `${cachedCount} cached${failed > 0 ? `, \x1b[31m${failed} failed\x1b[0m` : ""}`);
303
+ return {
304
+ result: {
305
+ id: scenarioId,
306
+ generated: completed.length,
307
+ cached: cachedCount,
308
+ failed,
309
+ skipped: false,
310
+ },
311
+ aborted: false,
312
+ };
313
+ }
314
+ /**
315
+ * Write `manifest.json` and `.presenter-cache.json` from completed work
316
+ * only (cached steps + this run's successes). Failed and non-opted
317
+ * steps stay `null` — playback degrades to no-presenter on those steps
318
+ * by design, and a rerun regenerates exactly the missing ones.
319
+ */
320
+ async function writeOutputs(outDir, scenarioId, totalSteps, plan, completed, options, config) {
321
+ await mkdir(outDir, { recursive: true });
322
+ const srcPrefix = options.baseUrl
323
+ ? `${options.baseUrl.replace(/\/$/, "")}/${scenarioId}`
324
+ : ".";
325
+ const manifestEntries = new Map();
326
+ const cacheEntries = new Map();
327
+ for (const step of plan.filter((p) => p.cached)) {
328
+ manifestEntries.set(step.index, {
329
+ src: `${srcPrefix}/step-${step.index}.mp4`,
330
+ durationMs: step.durationMs,
331
+ });
332
+ cacheEntries.set(step.index, { hash: step.hash, durationMs: step.durationMs });
333
+ }
334
+ for (const step of completed) {
335
+ manifestEntries.set(step.index, {
336
+ src: `${srcPrefix}/step-${step.index}.mp4`,
337
+ durationMs: step.durationMs,
338
+ });
339
+ cacheEntries.set(step.index, { hash: step.hash, durationMs: step.durationMs });
340
+ }
341
+ const manifestSteps = Array.from({ length: totalSteps }, () => null);
342
+ for (const [index, entry] of manifestEntries) {
343
+ manifestSteps[index] = entry;
344
+ }
345
+ await writeFile(join(outDir, "manifest.json"), JSON.stringify({ steps: manifestSteps }, null, 2) + "\n");
346
+ await savePresenterCache(outDir, buildPresenterCacheFile(config.fingerprint, totalSteps, cacheEntries));
347
+ }
348
+ //# sourceMappingURL=run-presenter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-presenter.js","sourceRoot":"","sources":["../../../src/presenter/run-presenter.ts"],"names":[],"mappings":"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"}