@clipwright/core 0.34.0 → 0.35.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 (179) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/README.md +3 -1
  3. package/dist/account-actors.d.ts +173 -14
  4. package/dist/account-actors.js +142 -10
  5. package/dist/account-actors.js.map +1 -1
  6. package/dist/actor-bounds.d.ts +4 -0
  7. package/dist/actor-bounds.js +4 -0
  8. package/dist/actor-bounds.js.map +1 -1
  9. package/dist/actors.d.ts +31 -0
  10. package/dist/actors.js +26 -0
  11. package/dist/actors.js.map +1 -1
  12. package/dist/assets.d.ts +17 -1
  13. package/dist/assets.js +19 -2
  14. package/dist/assets.js.map +1 -1
  15. package/dist/billing-terms.d.ts +69 -0
  16. package/dist/billing-terms.js +8 -0
  17. package/dist/billing-terms.js.map +1 -1
  18. package/dist/contract-dispositions.d.ts +485 -1
  19. package/dist/contract-dispositions.js +27 -3
  20. package/dist/contract-dispositions.js.map +1 -1
  21. package/dist/contracts/v1/workflow-brief.d.ts +1420 -0
  22. package/dist/contracts/v1/workflow-brief.js +4 -0
  23. package/dist/contracts/v1/workflow-brief.js.map +1 -1
  24. package/dist/contracts/v1/workflow-catalog.d.ts +2141 -105
  25. package/dist/contracts/v1/workflow-catalog.js +4 -2
  26. package/dist/contracts/v1/workflow-catalog.js.map +1 -1
  27. package/dist/contracts/v1/workflow-client.d.ts +1631 -3
  28. package/dist/contracts/v1/workflow-client.js +6 -3
  29. package/dist/contracts/v1/workflow-client.js.map +1 -1
  30. package/dist/contracts/v1/workflow-execution.d.ts +161 -9
  31. package/dist/contracts/v1/workflow-execution.js +15 -7
  32. package/dist/contracts/v1/workflow-execution.js.map +1 -1
  33. package/dist/contracts/v1/workflow-plan.d.ts +3167 -142
  34. package/dist/contracts/v1/workflow-primitives.d.ts +3 -1
  35. package/dist/contracts/v1/workflow-primitives.js +1 -1
  36. package/dist/contracts/v1/workflow-primitives.js.map +1 -1
  37. package/dist/contracts/v1/workflow-run-read.d.ts +114 -1
  38. package/dist/edit-billing.d.ts +12 -0
  39. package/dist/edit-billing.js +8 -0
  40. package/dist/edit-billing.js.map +1 -1
  41. package/dist/edit-long-form.d.ts +1 -1
  42. package/dist/edit-long-form.js +12 -2
  43. package/dist/edit-long-form.js.map +1 -1
  44. package/dist/endpoint-notes.d.ts +3 -0
  45. package/dist/endpoint-notes.js +66 -0
  46. package/dist/endpoint-notes.js.map +1 -0
  47. package/dist/endpoints.d.ts +4 -0
  48. package/dist/endpoints.js +13 -0
  49. package/dist/endpoints.js.map +1 -1
  50. package/dist/errors.d.ts +1 -1
  51. package/dist/errors.js +6 -0
  52. package/dist/errors.js.map +1 -1
  53. package/dist/face-billing.d.ts +55 -0
  54. package/dist/face-billing.js +22 -0
  55. package/dist/face-billing.js.map +1 -1
  56. package/dist/faceless-price.d.ts +26 -2
  57. package/dist/faceless-price.js +33 -6
  58. package/dist/faceless-price.js.map +1 -1
  59. package/dist/faceless-public.d.ts +6 -1
  60. package/dist/faceless-public.js +2 -2
  61. package/dist/faceless-public.js.map +1 -1
  62. package/dist/generations.js +15 -16
  63. package/dist/generations.js.map +1 -1
  64. package/dist/idempotency.d.ts +2 -0
  65. package/dist/idempotency.js +7 -4
  66. package/dist/idempotency.js.map +1 -1
  67. package/dist/index.d.ts +11 -0
  68. package/dist/index.js +13 -0
  69. package/dist/index.js.map +1 -1
  70. package/dist/long-form-pricing.d.ts +7 -4
  71. package/dist/long-form-pricing.js +13 -10
  72. package/dist/long-form-pricing.js.map +1 -1
  73. package/dist/long-form-revision.js +1 -1
  74. package/dist/long-form-revision.js.map +1 -1
  75. package/dist/openapi.d.ts +154 -0
  76. package/dist/openapi.js +334 -0
  77. package/dist/openapi.js.map +1 -0
  78. package/dist/pacing.d.ts +15 -0
  79. package/dist/pacing.js +30 -0
  80. package/dist/pacing.js.map +1 -1
  81. package/dist/parts-pricing.d.ts +84 -0
  82. package/dist/parts-pricing.js +105 -0
  83. package/dist/parts-pricing.js.map +1 -0
  84. package/dist/presenter-beats.d.ts +45 -0
  85. package/dist/presenter-beats.js +144 -0
  86. package/dist/presenter-beats.js.map +1 -0
  87. package/dist/prices.d.ts +98 -0
  88. package/dist/prices.js +189 -0
  89. package/dist/prices.js.map +1 -0
  90. package/dist/public-responses.d.ts +23 -0
  91. package/dist/public-responses.js +11 -0
  92. package/dist/public-responses.js.map +1 -0
  93. package/dist/reference-analysis.d.ts +683 -0
  94. package/dist/reference-analysis.js +91 -0
  95. package/dist/reference-analysis.js.map +1 -0
  96. package/dist/reference-kinds.d.ts +6 -0
  97. package/dist/reference-kinds.js +8 -0
  98. package/dist/reference-kinds.js.map +1 -0
  99. package/dist/reference-profile.d.ts +633 -0
  100. package/dist/reference-profile.js +151 -0
  101. package/dist/reference-profile.js.map +1 -0
  102. package/dist/reference-staging.d.ts +571 -0
  103. package/dist/reference-staging.js +151 -0
  104. package/dist/reference-staging.js.map +1 -0
  105. package/dist/reference-voice.d.ts +305 -0
  106. package/dist/reference-voice.js +108 -0
  107. package/dist/reference-voice.js.map +1 -0
  108. package/dist/resolved-timeline.d.ts +53 -1
  109. package/dist/resolved-timeline.js +50 -4
  110. package/dist/resolved-timeline.js.map +1 -1
  111. package/dist/run-static-warnings.js +14 -5
  112. package/dist/run-static-warnings.js.map +1 -1
  113. package/dist/runs.d.ts +75 -3
  114. package/dist/runs.js +6 -1
  115. package/dist/runs.js.map +1 -1
  116. package/dist/skills.d.ts +1805 -24
  117. package/dist/skills.js +129 -19
  118. package/dist/skills.js.map +1 -1
  119. package/dist/templates/callout.d.ts +2 -2
  120. package/dist/templates/callout.js +6 -6
  121. package/dist/templates/callout.js.map +1 -1
  122. package/dist/templates/checklist.js +2 -2
  123. package/dist/templates/checklist.js.map +1 -1
  124. package/dist/templates/index.d.ts +14 -7
  125. package/dist/templates/index.js +29 -17
  126. package/dist/templates/index.js.map +1 -1
  127. package/dist/templates/notification.js +2 -2
  128. package/dist/templates/notification.js.map +1 -1
  129. package/dist/templates/phone-call.d.ts +3 -0
  130. package/dist/templates/phone-call.js +14 -7
  131. package/dist/templates/phone-call.js.map +1 -1
  132. package/dist/templates/progress.js +2 -2
  133. package/dist/templates/progress.js.map +1 -1
  134. package/dist/templates/sounds.d.ts +357 -7
  135. package/dist/templates/sounds.js +34 -12
  136. package/dist/templates/sounds.js.map +1 -1
  137. package/dist/ugc-call.d.ts +89 -0
  138. package/dist/ugc-call.js +111 -0
  139. package/dist/ugc-call.js.map +1 -0
  140. package/dist/ugc-framing.d.ts +26 -0
  141. package/dist/ugc-framing.js +64 -0
  142. package/dist/ugc-framing.js.map +1 -0
  143. package/dist/ugc-layers.d.ts +5 -1
  144. package/dist/ugc-layers.js +6 -1
  145. package/dist/ugc-layers.js.map +1 -1
  146. package/dist/vendor-response.d.ts +4 -0
  147. package/dist/vendor-response.js +12 -0
  148. package/dist/vendor-response.js.map +1 -1
  149. package/dist/voice-admission.d.ts +409 -0
  150. package/dist/voice-listing.js +7 -1
  151. package/dist/voice-listing.js.map +1 -1
  152. package/dist/voice-pace.d.ts +34 -0
  153. package/dist/voice-pace.generated.json +72 -0
  154. package/dist/voice-pace.js +34 -0
  155. package/dist/voice-pace.js.map +1 -0
  156. package/dist/voices.d.ts +21 -8
  157. package/dist/voices.js +44 -19
  158. package/dist/voices.js.map +1 -1
  159. package/dist/workflows/catalog.js +2 -1
  160. package/dist/workflows/catalog.js.map +1 -1
  161. package/dist/workflows/index.d.ts +2 -0
  162. package/dist/workflows/index.js +2 -0
  163. package/dist/workflows/index.js.map +1 -1
  164. package/dist/workflows/methods/faceless-explainer.d.ts +377 -10
  165. package/dist/workflows/methods/kinetic-launch.d.ts +377 -10
  166. package/dist/workflows/methods/product-image-story.d.ts +377 -10
  167. package/dist/workflows/methods/reference-remake.d.ts +870 -0
  168. package/dist/workflows/methods/reference-remake.js +26 -0
  169. package/dist/workflows/methods/reference-remake.js.map +1 -0
  170. package/dist/workflows/methods/shared.d.ts +377 -10
  171. package/dist/workflows/methods/ugc-faq-answer.d.ts +377 -10
  172. package/dist/workflows/methods/ugc-product-intro.d.ts +377 -10
  173. package/dist/workflows/methods/website-proof-demo.d.ts +377 -10
  174. package/dist/workflows/reference-remake.d.ts +663 -0
  175. package/dist/workflows/reference-remake.js +598 -0
  176. package/dist/workflows/reference-remake.js.map +1 -0
  177. package/dist/workflows/validate.js +7 -4
  178. package/dist/workflows/validate.js.map +1 -1
  179. package/package.json +8 -2
@@ -0,0 +1,105 @@
1
+ import { z } from "zod";
2
+ import { identifier, partsFaceTariffSchema, presenterRatesSchema, PRESENTER_ENGINES, } from "./face-billing.js";
3
+ import { unionFaceFrames } from "./long-form-pricing.js";
4
+ // Parts prices (owner, 29.10, #800): ×2.5 over our cost at the largest pack's credit ($50 for 6000), payment fees inside.
5
+ // One tariff for short and long make_ugc; it replaces 30 credits a second and long-form-v1's 10 a face-second with 400 at least.
6
+ export const PARTS_PRICING_VERSION = "parts-v1";
7
+ /** Credits per second a presenter is in the video, covered or not (owner 22.10), by the engine that rendered the take. */
8
+ export const PRESENTER_CREDITS_PER_SECOND = Object.freeze({ heygen: 12, omnihuman: 22 });
9
+ /** Voice and render, per second of the finished video. */
10
+ export const PRODUCTION_CREDITS_PER_SECOND = 1;
11
+ /** A call scene's live avatar: one HeyGen photo avatar of the still, per still in the delivered video. */
12
+ export const LIVE_AVATAR_STILL_CREDITS = 400;
13
+ /** The least a delivered video costs. */
14
+ export const DELIVERED_VIDEO_MINIMUM_CREDITS = 50;
15
+ export const PRESENTER_ENGINE_LABELS = Object.freeze({
16
+ heygen: "presenter, HeyGen Avatar IV", omnihuman: "presenter, OmniHuman 2.0",
17
+ });
18
+ /** The long-form run's tariff, frozen at admission in `run_billing_terms` like long-form-v1 before it. */
19
+ export const PARTS_FACE_TARIFF = Object.freeze({
20
+ version: 3, pricingVersion: PARTS_PRICING_VERSION, presenterCreditsPerSecond: PRESENTER_CREDITS_PER_SECOND,
21
+ productionCreditsPerSecond: PRODUCTION_CREDITS_PER_SECOND, liveAvatarStillCredits: LIVE_AVATAR_STILL_CREDITS,
22
+ minimumCredits: DELIVERED_VIDEO_MINIMUM_CREDITS, rounding: "ceil_aggregate_seconds", failurePolicy: "verified_delivery_only",
23
+ });
24
+ /** The face tariffs new runs are admitted on, as a LIST: public text walks it, so another tariff cannot ship silently. */
25
+ export const FACE_BILLING_TARIFFS = Object.freeze([PARTS_FACE_TARIFF]);
26
+ /** A short run's tariff: one take, so the presenter is in every second of the video. Its failures keep the short run's outcomes. */
27
+ export const durationPartsTariffSchema = z.object({
28
+ version: z.literal(1),
29
+ pricingVersion: identifier,
30
+ presenterCreditsPerSecond: presenterRatesSchema,
31
+ productionCreditsPerSecond: z.number().int().nonnegative().max(2_147_483_647),
32
+ minimumCredits: z.number().int().nonnegative().max(2_147_483_647),
33
+ }).strict().readonly();
34
+ export const PARTS_DURATION_TARIFF = Object.freeze({
35
+ version: 1, pricingVersion: PARTS_PRICING_VERSION, presenterCreditsPerSecond: PRESENTER_CREDITS_PER_SECOND,
36
+ productionCreditsPerSecond: PRODUCTION_CREDITS_PER_SECOND, minimumCredits: DELIVERED_VIDEO_MINIMUM_CREDITS,
37
+ });
38
+ /** One line of an itemized price: what was counted, how many, at what rate, and the product. */
39
+ export const partsLineSchema = z.object({
40
+ label: z.string().min(1),
41
+ unit: z.enum(["face_second", "second", "still"]),
42
+ quantity: z.number().int().nonnegative(),
43
+ unit_credits: z.number().int().nonnegative(),
44
+ credits: z.number().int().nonnegative(),
45
+ }).strict();
46
+ const MAX_CREDITS = 2147483647n;
47
+ function settle(lines, minimum, delivered, faceFrames, billableFaceSeconds) {
48
+ const usage = lines.reduce((sum, line) => sum + BigInt(line.credits), 0n);
49
+ // The minimum is per DELIVERED video: a failed run that still bills is charged its parts only.
50
+ const minimumApplied = delivered && usage < BigInt(minimum);
51
+ const credits = minimumApplied ? BigInt(minimum) : usage;
52
+ if (credits > MAX_CREDITS)
53
+ throw new RangeError("parts billing credit overflow");
54
+ return { lines, usageCredits: Number(usage), credits: Number(credits), minimumApplied, faceFrames, billableFaceSeconds };
55
+ }
56
+ function line(label, unit, quantity, unitCredits) {
57
+ const credits = quantity * BigInt(unitCredits);
58
+ if (credits > MAX_CREDITS)
59
+ throw new RangeError("parts billing credit overflow");
60
+ return { label, unit, quantity: Number(quantity), unit_credits: unitCredits, credits: Number(credits) };
61
+ }
62
+ const ceilSeconds = (frames, fps) => (frames + fps - 1n) / fps;
63
+ /** A long-form video's price under a parts tariff: each engine's presenter seconds rounded up once over the video, every */
64
+ /** second of the video, each witnessed still; the minimum when delivered. Failure policy is settlement's, not this function's. */
65
+ export function calculatePartsCharge(measure, tariffInput, delivered) {
66
+ const tariff = partsFaceTariffSchema.parse(tariffInput);
67
+ const fps = BigInt(z.number().int().positive().max(Number.MAX_SAFE_INTEGER).parse(measure.fps));
68
+ const outputFrames = BigInt(z.number().int().positive().max(Number.MAX_SAFE_INTEGER).parse(measure.outputFrames));
69
+ const stills = BigInt(z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).parse(measure.liveAvatarStills));
70
+ const lines = [];
71
+ let billableFaceSeconds = 0n;
72
+ for (const engine of PRESENTER_ENGINES) {
73
+ const intervals = measure.presenter[engine];
74
+ if (intervals === undefined || intervals.length === 0)
75
+ continue;
76
+ const seconds = ceilSeconds(unionFaceFrames(intervals), fps);
77
+ billableFaceSeconds += seconds;
78
+ lines.push(line(PRESENTER_ENGINE_LABELS[engine], "face_second", seconds, tariff.presenterCreditsPerSecond[engine]));
79
+ }
80
+ lines.push(line("production (voice and render)", "second", ceilSeconds(outputFrames, fps), tariff.productionCreditsPerSecond));
81
+ if (stills > 0n)
82
+ lines.push(line("live avatar on a call", "still", stills, tariff.liveAvatarStillCredits));
83
+ const faceFrames = unionFaceFrames(PRESENTER_ENGINES.flatMap((engine) => [...(measure.presenter[engine] ?? [])]));
84
+ return settle(lines, tariff.minimumCredits, delivered, Number(faceFrames), Number(billableFaceSeconds));
85
+ }
86
+ /** A short video's price: the presenter is in every second of its one take. Rounding is the legacy rule: whole seconds, at least one. */
87
+ export function calculateDurationPartsCharge(durationSec, engine, tariffInput, delivered) {
88
+ const tariff = durationPartsTariffSchema.parse(tariffInput);
89
+ if (!Number.isFinite(durationSec) || durationSec < 0)
90
+ throw new RangeError("duration must be a finite, non-negative number of seconds");
91
+ const seconds = BigInt(Math.max(1, Math.ceil(durationSec)));
92
+ const lines = [line(PRESENTER_ENGINE_LABELS[engine], "face_second", seconds, tariff.presenterCreditsPerSecond[engine]),
93
+ line("production (voice and render)", "second", seconds, tariff.productionCreditsPerSecond)];
94
+ return settle(lines, tariff.minimumCredits, delivered, 0, Number(seconds));
95
+ }
96
+ /** What a delivered parts-tariff video records about its price. */
97
+ export const partsBillingSummarySchema = z.object({
98
+ pricingVersion: identifier,
99
+ lines: z.array(partsLineSchema),
100
+ minimumCredits: z.number().int().nonnegative(),
101
+ minimumApplied: z.boolean(),
102
+ actualDurationSec: z.number().positive().max(Number.MAX_SAFE_INTEGER),
103
+ finalCreditsCharged: z.number().int().nonnegative(),
104
+ }).strict().readonly();
105
+ //# sourceMappingURL=parts-pricing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parts-pricing.js","sourceRoot":"","sources":["../src/parts-pricing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ import type { GenerateVideoRequest, GenerationReference } from "./generations.js";
2
+ import type { ReferenceProfile } from "./reference-profile.js";
3
+ import type { UgcInsert, UgcTemplate } from "./skills.js";
4
+ /** Silent presenter beats of a reference (#857 S3): the presenter listens or reacts while the narration goes on. */
5
+ /** A beat is a seedance-2.0 clip whose first frame is the actor portrait, placed as a video insert on its words. */
6
+ export declare const PRESENTER_BEAT_BEHAVIOURS: readonly ["listening", "reacting"];
7
+ export type PresenterBeatBehaviour = (typeof PRESENTER_BEAT_BEHAVIOURS)[number];
8
+ /** The seedance-2.0 row both routes measured live: 720p 9:16, 4–15 s (`GENERATION_CAPABILITIES`). */
9
+ export declare const PRESENTER_BEAT_MODEL = "seedance-2.0";
10
+ export declare const PRESENTER_BEAT_MIN_SEC = 4;
11
+ export declare function presenterBeatPrompt(behaviour: PresenterBeatBehaviour): string;
12
+ /** Long enough for the words at the slowest speech the admission assumes; the insert cuts the rest where its words end. */
13
+ export declare function presenterBeatSeconds(words: number): number;
14
+ export interface PresenterBeat {
15
+ id: string;
16
+ beatIndex: number;
17
+ behaviour: PresenterBeatBehaviour;
18
+ /** Script words as make_ugc counts them; `endWord` is exclusive. */
19
+ startWord: number;
20
+ endWord: number;
21
+ durationS: number;
22
+ prompt: string;
23
+ }
24
+ /** The profile's listening and reacting beats on their script spans, one span per profile beat (null: no words). */
25
+ export declare function planPresenterBeats(input: {
26
+ script: string;
27
+ profile: Pick<ReferenceProfile, "beats">;
28
+ spans: readonly ({
29
+ start: number;
30
+ end: number;
31
+ } | null)[];
32
+ }): {
33
+ beats: PresenterBeat[];
34
+ warnings: string[];
35
+ };
36
+ /** Over-presenter templates that meet a beat move to the free words right before it, else right after, else are dropped and named. */
37
+ /** A full-frame template stays: it is drawn over inserts. */
38
+ export declare function fitTemplatesAroundBeats(script: string, templates: readonly UgcTemplate[], beats: readonly PresenterBeat[]): {
39
+ templates: UgcTemplate[];
40
+ warnings: string[];
41
+ };
42
+ /** The generate_video request of one beat: frame 0 is the actor portrait, so the identity is the portrait's. */
43
+ export declare function presenterBeatGeneration(beat: PresenterBeat, portrait: GenerationReference): GenerateVideoRequest;
44
+ /** The beat as a video insert over the narration: it starts on its first word and ends where its last word ends. */
45
+ export declare function presenterBeatInsert(beat: PresenterBeat, assetId: string): UgcInsert;
@@ -0,0 +1,144 @@
1
+ import { WORDS_PER_SECOND_MIN } from "./pacing.js";
2
+ import { lexicalTokens, quoteOccurrences } from "./semantic-inserts.js";
3
+ import { TEMPLATE_KINDS, templateFieldValues } from "./templates/index.js";
4
+ /** Silent presenter beats of a reference (#857 S3): the presenter listens or reacts while the narration goes on. */
5
+ /** A beat is a seedance-2.0 clip whose first frame is the actor portrait, placed as a video insert on its words. */
6
+ export const PRESENTER_BEAT_BEHAVIOURS = ["listening", "reacting"];
7
+ /** The seedance-2.0 row both routes measured live: 720p 9:16, 4–15 s (`GENERATION_CAPABILITIES`). */
8
+ export const PRESENTER_BEAT_MODEL = "seedance-2.0";
9
+ export const PRESENTER_BEAT_MIN_SEC = 4;
10
+ const PRESENTER_BEAT_MAX_SEC = 15;
11
+ const KEEP = "Same person, same face, hair, clothes, props and room as the first frame. Locked-off camera, vertical UGC video, " +
12
+ "natural light, realistic skin.";
13
+ /** An open-mouth surprise under a voiceover reads as broken lip-sync (24.10 probe): every action is asked closed-mouthed. */
14
+ const SILENT = "The person never speaks: the mouth stays closed or in a closed-lip smile the whole time, even when surprised. " +
15
+ "No talking, no lip movement, no captions, no text overlays.";
16
+ const ACTIONS = {
17
+ listening: "The person lifts a smartphone to the ear with one hand and listens silently to a caller, giving a few small nods, " +
18
+ "eyes alive and attentive, brows reacting slightly to what they hear.",
19
+ reacting: "The person glances to the side, the eyebrows rise in quiet closed-mouth surprise, then the face relaxes into a pleased " +
20
+ "closed-lip smile; they look straight at the camera and give a small thumbs-up.",
21
+ };
22
+ export function presenterBeatPrompt(behaviour) {
23
+ return `${KEEP} ${ACTIONS[behaviour]} ${SILENT}`;
24
+ }
25
+ /** Long enough for the words at the slowest speech the admission assumes; the insert cuts the rest where its words end. */
26
+ export function presenterBeatSeconds(words) {
27
+ return Math.min(PRESENTER_BEAT_MAX_SEC, Math.max(PRESENTER_BEAT_MIN_SEC, Math.ceil(words / WORDS_PER_SECOND_MIN)));
28
+ }
29
+ /** The profile's listening and reacting beats on their script spans, one span per profile beat (null: no words). */
30
+ export function planPresenterBeats(input) {
31
+ const words = lexicalTokens(input.script).length;
32
+ if (input.spans.length !== input.profile.beats.length)
33
+ throw new Error("presenter beats need one span per profile beat");
34
+ const beats = [];
35
+ const warnings = [];
36
+ input.profile.beats.forEach((beat, beatIndex) => {
37
+ const behaviour = beat.presenter_behaviour;
38
+ if (!PRESENTER_BEAT_BEHAVIOURS.includes(behaviour))
39
+ return;
40
+ const span = input.spans[beatIndex];
41
+ if (!span) {
42
+ warnings.push(`beat ${beatIndex} (${behaviour}) has no words in the script, so the presenter does not ${behaviour === "listening" ? "listen" : "react"} there`);
43
+ return;
44
+ }
45
+ if (!Number.isSafeInteger(span.start) || !Number.isSafeInteger(span.end) || span.start < 0 || span.end <= span.start || span.end > words) {
46
+ throw new Error(`beat ${beatIndex} span ${span.start}..${span.end} is outside the ${words} script words`);
47
+ }
48
+ const previous = beats.at(-1);
49
+ if (previous && span.start < previous.endWord)
50
+ throw new Error(`beat ${beatIndex} overlaps the beat before it`);
51
+ const count = span.end - span.start;
52
+ if (count / WORDS_PER_SECOND_MIN > PRESENTER_BEAT_MAX_SEC) {
53
+ warnings.push(`beat ${beatIndex} (${behaviour}) may outlast a ${PRESENTER_BEAT_MAX_SEC} s clip; its last frame holds to the end of its words`);
54
+ }
55
+ beats.push({ id: `beat-${beatIndex}`, beatIndex, behaviour: behaviour, startWord: span.start, endWord: span.end,
56
+ durationS: presenterBeatSeconds(count), prompt: presenterBeatPrompt(behaviour) });
57
+ });
58
+ if (beats.length && beats[0].startWord === 0 && beats.at(-1).endWord === words && beats.every((beat, i) => i === 0 || beat.startWord === beats[i - 1].endWord)) {
59
+ throw new Error("presenter beats cover every word and leave the speaking presenter no shot");
60
+ }
61
+ return { beats, warnings };
62
+ }
63
+ /** A beat's insert is a cutaway: an over-presenter template under it would be refused, so it moves next to the beat. */
64
+ const MIN_TEMPLATE_WORDS = 2;
65
+ function templateSpan(tokens, template) {
66
+ const anchor = template.anchor;
67
+ const range = "startWord" in anchor ? anchor : quoteOccurrences(tokens, anchor.quote)[anchor.occurrence ?? 0];
68
+ if (!range)
69
+ return null;
70
+ return { start: range.startWord, end: template.cover_words === undefined ? range.endWord : range.startWord + template.cover_words };
71
+ }
72
+ /** The template moved onto `span`: anchored on those words, its inner quotes spread over them in order. */
73
+ function moved(template, tokens, span) {
74
+ const { cover_words: _cover, ...rest } = template;
75
+ let data = structuredClone(template.data);
76
+ const fields = TEMPLATE_KINDS.find(kind => kind.kind === template.kind)?.quotes ?? [];
77
+ const values = fields.flatMap(field => templateFieldValues(data, field));
78
+ let from = span.start;
79
+ values.forEach(({ path }, index) => {
80
+ const target = Math.max(from, span.start + Math.floor(((index + 0.5) * (span.end - span.start)) / values.length));
81
+ // The shortest phrase from `target` whose first match inside the span, after the previous one, is `target` itself.
82
+ let length = 1;
83
+ while (target + length < span.end && quoteOccurrences(tokens.slice(from, span.end), tokens.slice(target, target + length).join(" "))[0]
84
+ .startWord + from !== target)
85
+ length += 1;
86
+ data = setAt(data, path, tokens.slice(target, target + length).join(" "));
87
+ from = target + length;
88
+ });
89
+ return { ...rest, anchor: { startWord: span.start, endWord: span.end }, data };
90
+ }
91
+ function setAt(data, path, value) {
92
+ const [head, ...tail] = path;
93
+ if (head === undefined)
94
+ return data;
95
+ const copy = Array.isArray(data) ? [...data] : { ...data };
96
+ copy[head] = tail.length ? setAt(data[head], tail, value) : value;
97
+ return copy;
98
+ }
99
+ /** Over-presenter templates that meet a beat move to the free words right before it, else right after, else are dropped and named. */
100
+ /** A full-frame template stays: it is drawn over inserts. */
101
+ export function fitTemplatesAroundBeats(script, templates, beats) {
102
+ const tokens = lexicalTokens(script);
103
+ const spans = templates.map(template => templateSpan(tokens, template));
104
+ const busy = (word, self) => beats.some(beat => word >= beat.startWord && word < beat.endWord) ||
105
+ spans.some((span, index) => index !== self && span !== null && word >= span.start && word < span.end);
106
+ const warnings = [];
107
+ const out = [];
108
+ templates.forEach((template, index) => {
109
+ const span = spans[index];
110
+ const placement = template.placement ?? TEMPLATE_KINDS.find(kind => kind.kind === template.kind)?.placements[0];
111
+ const beat = span && placement !== "full" && beats.find(one => span.start < one.endWord && span.end > one.startWord);
112
+ if (!span || !beat) {
113
+ out.push(template);
114
+ return;
115
+ }
116
+ const want = Math.max(MIN_TEMPLATE_WORDS, span.end - span.start);
117
+ let before = beat.startWord;
118
+ while (before > 0 && beat.startWord - before < want && !busy(before - 1, index))
119
+ before -= 1;
120
+ let after = beat.endWord;
121
+ while (after < tokens.length && after - beat.endWord < want && !busy(after, index))
122
+ after += 1;
123
+ const room = beat.startWord - before >= MIN_TEMPLATE_WORDS ? { start: before, end: beat.startWord }
124
+ : after - beat.endWord >= MIN_TEMPLATE_WORDS ? { start: beat.endWord, end: after } : null;
125
+ if (!room) {
126
+ warnings.push(`template "${template.id}" lies under the ${beat.behaviour} beat and has no free words beside it; it is left out`);
127
+ return;
128
+ }
129
+ spans[index] = room;
130
+ out.push(moved(template, tokens, room));
131
+ warnings.push(`template "${template.id}" moved to the words ${room.end === beat.startWord ? "right before" : "right after"} the ${beat.behaviour} beat, which covers its own`);
132
+ });
133
+ return { templates: out, warnings };
134
+ }
135
+ /** The generate_video request of one beat: frame 0 is the actor portrait, so the identity is the portrait's. */
136
+ export function presenterBeatGeneration(beat, portrait) {
137
+ return { prompt: beat.prompt, model: PRESENTER_BEAT_MODEL, mode: "first_frame", aspect_ratio: "9:16", resolution: "720p",
138
+ duration_s: beat.durationS, first_frame: portrait };
139
+ }
140
+ /** The beat as a video insert over the narration: it starts on its first word and ends where its last word ends. */
141
+ export function presenterBeatInsert(beat, assetId) {
142
+ return { id: beat.id, asset_id: assetId, media_type: "video", anchor: { startWord: beat.startWord, endWord: beat.endWord } };
143
+ }
144
+ //# sourceMappingURL=presenter-beats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presenter-beats.js","sourceRoot":"","sources":["../src/presenter-beats.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,98 @@
1
+ import { z } from "zod";
2
+ /** The public price list (#927): every number is read from the constant billing charges by, never typed here. */
3
+ export declare const PRICES_PATH = "/v1/public/prices";
4
+ export declare const PRICE_UNITS: readonly ["call", "second", "face_second", "image", "clip", "clip_second", "still"];
5
+ export type PriceUnit = (typeof PRICE_UNITS)[number];
6
+ /** One part of a price: credits per unit. A price of several parts adds them, then applies the entry's minimum. */
7
+ export declare const priceEntryLine: z.ZodObject<{
8
+ label: z.ZodNullable<z.ZodString>;
9
+ unit: z.ZodEnum<{
10
+ call: "call";
11
+ clip: "clip";
12
+ clip_second: "clip_second";
13
+ face_second: "face_second";
14
+ image: "image";
15
+ second: "second";
16
+ still: "still";
17
+ }>;
18
+ credits: z.ZodNumber;
19
+ }, z.core.$strict>;
20
+ export type PriceEntryLine = z.infer<typeof priceEntryLine>;
21
+ export declare const priceEntry: z.ZodObject<{
22
+ action: z.ZodString;
23
+ tool: z.ZodNullable<z.ZodString>;
24
+ route: z.ZodNullable<z.ZodString>;
25
+ variant: z.ZodNullable<z.ZodString>;
26
+ lines: z.ZodArray<z.ZodObject<{
27
+ label: z.ZodNullable<z.ZodString>;
28
+ unit: z.ZodEnum<{
29
+ call: "call";
30
+ clip: "clip";
31
+ clip_second: "clip_second";
32
+ face_second: "face_second";
33
+ image: "image";
34
+ second: "second";
35
+ still: "still";
36
+ }>;
37
+ credits: z.ZodNumber;
38
+ }, z.core.$strict>>;
39
+ formula: z.ZodNullable<z.ZodString>;
40
+ minimum_credits: z.ZodNullable<z.ZodNumber>;
41
+ quote_tool: z.ZodNullable<z.ZodString>;
42
+ notes: z.ZodNullable<z.ZodString>;
43
+ }, z.core.$strict>;
44
+ export type PriceEntry = z.infer<typeof priceEntry>;
45
+ export declare const pricesResponse: z.ZodObject<{
46
+ prices: z.ZodArray<z.ZodObject<{
47
+ action: z.ZodString;
48
+ tool: z.ZodNullable<z.ZodString>;
49
+ route: z.ZodNullable<z.ZodString>;
50
+ variant: z.ZodNullable<z.ZodString>;
51
+ lines: z.ZodArray<z.ZodObject<{
52
+ label: z.ZodNullable<z.ZodString>;
53
+ unit: z.ZodEnum<{
54
+ call: "call";
55
+ clip: "clip";
56
+ clip_second: "clip_second";
57
+ face_second: "face_second";
58
+ image: "image";
59
+ second: "second";
60
+ still: "still";
61
+ }>;
62
+ credits: z.ZodNumber;
63
+ }, z.core.$strict>>;
64
+ formula: z.ZodNullable<z.ZodString>;
65
+ minimum_credits: z.ZodNullable<z.ZodNumber>;
66
+ quote_tool: z.ZodNullable<z.ZodString>;
67
+ notes: z.ZodNullable<z.ZodString>;
68
+ }, z.core.$strict>>;
69
+ }, z.core.$strict>;
70
+ export type PricesResponse = z.infer<typeof pricesResponse>;
71
+ /** What a client reads: a unit or a field it does not know yet must not break it. */
72
+ export declare const pricesResponseRead: z.ZodObject<{
73
+ prices: z.ZodArray<z.ZodObject<{
74
+ action: z.ZodString;
75
+ tool: z.ZodNullable<z.ZodString>;
76
+ route: z.ZodNullable<z.ZodString>;
77
+ variant: z.ZodNullable<z.ZodString>;
78
+ lines: z.ZodArray<z.ZodObject<{
79
+ label: z.ZodNullable<z.ZodString>;
80
+ unit: z.ZodString;
81
+ credits: z.ZodNumber;
82
+ }, z.core.$loose>>;
83
+ formula: z.ZodNullable<z.ZodString>;
84
+ minimum_credits: z.ZodNullable<z.ZodNumber>;
85
+ quote_tool: z.ZodNullable<z.ZodString>;
86
+ notes: z.ZodNullable<z.ZodString>;
87
+ }, z.core.$loose>>;
88
+ }, z.core.$loose>;
89
+ export type PricesResponseRead = z.infer<typeof pricesResponseRead>;
90
+ /** Whether calling the entry's action may cost credits. */
91
+ export declare const isPaidEntry: (entry: PriceEntry) => boolean;
92
+ export declare const PRICE_LIST: readonly PriceEntry[];
93
+ /** One entry's price in words: its lines added up and its minimum, or its formula. */
94
+ export declare const priceText: (entry: PriceEntry) => string;
95
+ /** The price sentence a billed tool's description ends with; `null` for a free tool. */
96
+ export declare function priceLine(tool: string, list?: readonly PriceEntry[]): string | null;
97
+ /** A billed tool's description with its price line at the end. */
98
+ export declare const withPriceLine: (tool: string, description: string, list?: readonly PriceEntry[]) => string;
package/dist/prices.js ADDED
@@ -0,0 +1,189 @@
1
+ import { z } from "zod";
2
+ import { ACTOR_PORTRAIT_ASPECT, ACTOR_UNIT_CREDITS } from "./account-actors.js";
3
+ import { CANDIDATE_CHARGE_TEXT } from "./edit-candidate.js";
4
+ import { EDIT_RENDER_PRICE, EDIT_UNIT_PRICES, EDIT_ZERO_UNIT_PRICE } from "./edit-billing.js";
5
+ import { REVISION_CHARGE_TEXT } from "./edit-revision.js";
6
+ import { FACELESS_CREDITS_PER_1500_FRAMES, FACELESS_IMAGE_CREDITS, FACELESS_MAX_SELECTED_SECONDS, FACELESS_MIN_BASE_CREDITS, FACELESS_MIN_SELECTED_SECONDS, FACELESS_OPENER_CREDITS, quoteFaceless, } from "./faceless-price.js";
7
+ import { PRESENTER_ENGINES } from "./face-billing.js";
8
+ import { listGenerationModels } from "./generations.js";
9
+ import { PARTS_FACE_TARIFF, PRESENTER_ENGINE_LABELS } from "./parts-pricing.js";
10
+ import { REFERENCE_ANALYSIS_CREDITS } from "./reference-analysis.js";
11
+ /** The public price list (#927): every number is read from the constant billing charges by, never typed here. */
12
+ export const PRICES_PATH = "/v1/public/prices";
13
+ export const PRICE_UNITS = ["call", "second", "face_second", "image", "clip", "clip_second", "still"];
14
+ /** One part of a price: credits per unit. A price of several parts adds them, then applies the entry's minimum. */
15
+ export const priceEntryLine = z.strictObject({
16
+ label: z.string().min(1).nullable(),
17
+ unit: z.enum(PRICE_UNITS),
18
+ credits: z.number().int().nonnegative(),
19
+ });
20
+ export const priceEntry = z.strictObject({
21
+ action: z.string().min(1),
22
+ tool: z.string().min(1).nullable(),
23
+ /** The REST route as `endpointLabel` writes it. */
24
+ route: z.string().min(1).nullable(),
25
+ variant: z.string().min(1).nullable(),
26
+ /** Empty when the price is a formula the parts cannot state. */
27
+ lines: z.array(priceEntryLine),
28
+ formula: z.string().min(1).nullable(),
29
+ minimum_credits: z.number().int().positive().nullable(),
30
+ quote_tool: z.string().min(1).nullable(),
31
+ notes: z.string().min(1).nullable(),
32
+ }).refine((entry) => entry.lines.length > 0 || entry.formula !== null, "an entry states lines or a formula");
33
+ export const pricesResponse = z.strictObject({ prices: z.array(priceEntry).min(1) });
34
+ /** What a client reads: a unit or a field it does not know yet must not break it. */
35
+ export const pricesResponseRead = z.looseObject({ prices: z.array(z.looseObject({
36
+ action: z.string().min(1), tool: z.string().nullable(), route: z.string().nullable(), variant: z.string().nullable(),
37
+ lines: z.array(z.looseObject({ label: z.string().nullable(), unit: z.string().min(1), credits: z.number().int().nonnegative() })),
38
+ formula: z.string().nullable(), minimum_credits: z.number().int().positive().nullable(), quote_tool: z.string().nullable(),
39
+ notes: z.string().nullable(),
40
+ })) });
41
+ /** Whether calling the entry's action may cost credits. */
42
+ export const isPaidEntry = (entry) => entry.formula !== null || entry.lines.some((line) => line.credits > 0);
43
+ const paid = (entry) => ({
44
+ action: entry.variant === undefined ? entry.tool : `${entry.tool} (${entry.variant})`,
45
+ tool: entry.tool, route: entry.route, variant: entry.variant ?? null,
46
+ lines: "credits" in entry ? [{ label: null, unit: entry.unit, credits: entry.credits }] : "lines" in entry ? entry.lines : [],
47
+ formula: "formula" in entry ? entry.formula : null,
48
+ minimum_credits: entry.minimum_credits ?? null, quote_tool: entry.quote_tool, notes: entry.notes ?? null,
49
+ });
50
+ /** Every free MCP tool and the route it calls. */
51
+ const FREE_TOOLS = {
52
+ list_prices: `GET ${PRICES_PATH}`,
53
+ get_account: "GET /v1/account",
54
+ quote_ugc: "POST /v1/skills/make_ugc/quote",
55
+ quote_analysis: "POST /v1/reference-analyses/quote",
56
+ quote_faceless: "POST /v1/skills/make_faceless/quote",
57
+ quote_actor: "POST /v1/skills/create_actor/quote",
58
+ quote_image: "POST /v1/skills/generate_image/quote",
59
+ quote_video: "POST /v1/skills/generate_video/quote",
60
+ quote_revision: "POST /v1/runs/{id}/revisions/quote",
61
+ quote_candidate: "POST /v1/runs/{id}/candidates/quote",
62
+ quote_workflow_plan: "POST /v1/workflow-plans/{id}/quote",
63
+ get_run: "GET /v1/runs/{id}",
64
+ get_run_shots: "GET /v1/runs/{id}/shots",
65
+ list_voices: "GET /v1/voices",
66
+ list_template_sounds: "GET /v1/template-sounds",
67
+ list_generation_models: "GET /v1/generations/models",
68
+ list_actors: "GET /v1/actors",
69
+ delete_actor: "DELETE /v1/actors/{id}",
70
+ get_actor_defaults: "GET /v1/actors/{id}/defaults",
71
+ set_actor_defaults: "POST /v1/actors/{id}/defaults",
72
+ upload_image: "POST /v1/uploads/import",
73
+ create_upload: "POST /v1/uploads/intents",
74
+ get_upload: "GET /v1/uploads/{id}",
75
+ create_asset: "POST /v1/assets",
76
+ list_assets: "GET /v1/assets",
77
+ get_asset: "GET /v1/assets/{id}",
78
+ update_asset: "POST /v1/assets/{id}",
79
+ delete_asset: "DELETE /v1/assets/{id}",
80
+ get_reference_analysis: "GET /v1/reference-analyses/{id}",
81
+ list_workflows: "GET /v1/public/workflows",
82
+ get_workflow: "GET /v1/public/workflows/{id}",
83
+ prepare_workflow_plan: "POST /v1/workflow-plans",
84
+ plan_reference_remake: "POST /v1/workflow-plans/reference-remake",
85
+ get_workflow_plan: "GET /v1/workflow-plans/{id}",
86
+ approve_workflow_plan: "POST /v1/workflow-plans/{id}/approve",
87
+ };
88
+ const facelessAt = (seconds) => quoteFaceless({ outputFrames: seconds * 25, captions: true }).totalCredits;
89
+ function generationEntries() {
90
+ return listGenerationModels().flatMap((model) => {
91
+ const video = model.kind === "video";
92
+ const groups = new Map();
93
+ for (const row of model.rows) {
94
+ const key = `${row.resolution}|${row.unit_credits}`;
95
+ const group = groups.get(key) ?? { size: row.resolution, credits: row.unit_credits, aspects: [] };
96
+ group.aspects.push(row.aspect_ratio);
97
+ groups.set(key, group);
98
+ }
99
+ const priced = [...groups.values()];
100
+ return priced.map((group) => {
101
+ const split = priced.filter((other) => other.size === group.size).length > 1;
102
+ const variant = [model.model, group.size, split ? group.aspects.join(", ") : null].filter((part) => part !== null).join(" ");
103
+ return paid(video
104
+ ? { tool: "generate_video", route: "POST /v1/skills/generate_video/run", variant, unit: "clip_second", credits: group.credits,
105
+ quote_tool: "quote_video", notes: "Per second of the requested duration, charged once the clip is delivered." }
106
+ : { tool: "generate_image", route: "POST /v1/skills/generate_image/run", variant, unit: "image", credits: group.credits,
107
+ quote_tool: "quote_image", notes: "Charged once the image is delivered." });
108
+ });
109
+ });
110
+ }
111
+ function buildPriceList() {
112
+ const unit = EDIT_UNIT_PRICES;
113
+ const parts = PARTS_FACE_TARIFF;
114
+ return [
115
+ ...PRESENTER_ENGINES.map((engine) => paid({ tool: "make_ugc", route: "POST /v1/skills/make_ugc/run",
116
+ variant: engine === "heygen" ? "HeyGen presenter" : "OmniHuman presenter", quote_tool: "quote_ugc", minimum_credits: parts.minimumCredits,
117
+ lines: [{ label: PRESENTER_ENGINE_LABELS[engine], unit: "face_second", credits: parts.presenterCreditsPerSecond[engine] },
118
+ { label: "production (voice and render)", unit: "second", credits: parts.productionCreditsPerSecond },
119
+ { label: "live avatar on a call", unit: "still", credits: parts.liveAvatarStillCredits }],
120
+ notes: "Short and long videos alike. Presenter seconds are the seconds a presenter take fills, covered by an insert, overlay, " +
121
+ "window or template or not, counted once over the video and rounded up; the engine that renders each take sets its rate " +
122
+ (engine === "heygen" ? "(today every take renders on HeyGen). " : "(no take renders on OmniHuman today). ") +
123
+ "Production seconds are every second of the finished video. A live avatar is charged per call still in the video." })),
124
+ paid({ tool: "analyze_reference", route: "POST /v1/reference-analyses", unit: "call", credits: REFERENCE_ANALYSIS_CREDITS,
125
+ quote_tool: "quote_analysis", notes: "Held at the start and charged once the analysis succeeds; a failed one costs nothing. " +
126
+ "Starting it again on the same asset answers the same analysis for free." }),
127
+ paid({ tool: "make_faceless", route: "POST /v1/skills/make_faceless/run", quote_tool: "quote_faceless",
128
+ lines: [{ label: "generated picture", unit: "image", credits: FACELESS_IMAGE_CREDITS },
129
+ { label: "opening clip", unit: "clip", credits: FACELESS_OPENER_CREDITS },
130
+ { label: "production (story, voice and render)", unit: "second", credits: FACELESS_CREDITS_PER_1500_FRAMES / 60 }],
131
+ minimum_credits: FACELESS_MIN_BASE_CREDITS,
132
+ notes: `Pictures we generated for the delivered video and its seconds, never above the selected length. The quote is the most a ` +
133
+ `selection is charged, every picture it may generate: ${facelessAt(FACELESS_MIN_SELECTED_SECONDS)} for ` +
134
+ `${FACELESS_MIN_SELECTED_SECONDS} s, ${facelessAt(FACELESS_MAX_SELECTED_SECONDS)} for ${FACELESS_MAX_SELECTED_SECONDS} s. ` +
135
+ "Captions do not change the price." }),
136
+ paid({ tool: "revise_run", route: "POST /v1/runs/{id}/revisions", quote_tool: "quote_revision",
137
+ formula: `each shot generated anew: picture ${unit.picture}, clip ${unit.clip}, clip with a new picture ${unit.clip_with_new_picture}; ` +
138
+ `plus ${EDIT_RENDER_PRICE} to render. A revision that generates nothing: ${EDIT_ZERO_UNIT_PRICE}. A kept candidate adds nothing`,
139
+ notes: REVISION_CHARGE_TEXT }),
140
+ ...[["picture", "image", unit.picture], ["clip", "clip", unit.clip], ["clip with a new picture", "clip", unit.clip_with_new_picture]]
141
+ .map(([variant, priceUnit, credits]) => paid({ tool: "create_candidate", route: "POST /v1/runs/{id}/candidates", variant,
142
+ unit: priceUnit, credits, quote_tool: "quote_candidate", notes: CANDIDATE_CHARGE_TEXT })),
143
+ ...Object.entries(ACTOR_UNIT_CREDITS).flatMap(([quality, credits]) => [
144
+ paid({ tool: "create_actor", route: "POST /v1/skills/create_actor/run", variant: `${quality} portrait`, unit: "image",
145
+ credits: credits.portrait, quote_tool: "quote_actor", notes: "Each published image; a refused description or an unusable portrait costs nothing." }),
146
+ paid({ tool: "create_actor", route: "POST /v1/skills/create_actor/run", variant: `${quality} other format`, unit: "image",
147
+ credits: credits.variant, quote_tool: "quote_actor", notes: `Each requested format besides the ${ACTOR_PORTRAIT_ASPECT} portrait, once published.` }),
148
+ ]),
149
+ ...generationEntries(),
150
+ paid({ tool: "run_workflow_plan", route: "POST /v1/workflow-plans/{id}/run", quote_tool: "quote_workflow_plan",
151
+ formula: "the price of the native run it starts, make_ugc or make_faceless, as its quote states it" }),
152
+ ...Object.entries(FREE_TOOLS).map(([tool, route]) => ({ action: tool, tool, route, variant: null,
153
+ lines: [{ label: null, unit: "call", credits: 0 }], formula: null, minimum_credits: null, quote_tool: null, notes: null })),
154
+ ];
155
+ }
156
+ export const PRICE_LIST = Object.freeze(pricesResponse.parse({ prices: buildPriceList() }).prices);
157
+ const UNIT_TEXT = {
158
+ call: "call", second: "second of video", face_second: "second the presenter is in the video, covered or not", image: "image", clip: "clip",
159
+ clip_second: "second of clip", still: "call still in the video",
160
+ };
161
+ /** Past this many one-line prices of one unit, the sentence names their range and list_prices names each. */
162
+ const LISTED_PRICES = 4;
163
+ /** One entry's price in words: its lines added up and its minimum, or its formula. */
164
+ export const priceText = (entry) => entry.lines.length === 0 ? entry.formula ?? "" :
165
+ entry.lines.map((line) => `${line.label === null ? "" : `${line.label} `}${line.credits} credits per ${UNIT_TEXT[line.unit]}`).join(" plus ") +
166
+ (entry.minimum_credits === null ? "" : `, at least ${entry.minimum_credits}`);
167
+ /** The price sentence a billed tool's description ends with; `null` for a free tool. */
168
+ export function priceLine(tool, list = PRICE_LIST) {
169
+ const entries = list.filter((entry) => entry.tool === tool && isPaidEntry(entry));
170
+ const [first] = entries;
171
+ if (first === undefined)
172
+ return null;
173
+ const unit = first.lines[0]?.unit;
174
+ const single = entries.flatMap((entry) => (entry.formula === null && entry.lines.length === 1 && entry.lines[0].unit === unit
175
+ ? [entry.lines[0].credits] : []));
176
+ const text = unit !== undefined && single.length === entries.length && single.length > LISTED_PRICES
177
+ ? `${Math.min(...single)} to ${Math.max(...single)} credits per ${UNIT_TEXT[unit]}, by model and size`
178
+ : entries.map((entry) => `${entry.variant === null ? "" : `${entry.variant}: `}${priceText(entry)}`)
179
+ .join("; ");
180
+ return `Price: ${text}. Quote first with ${first.quote_tool}; list_prices names every price.`;
181
+ }
182
+ /** A billed tool's description with its price line at the end. */
183
+ export const withPriceLine = (tool, description, list = PRICE_LIST) => {
184
+ const line = priceLine(tool, list);
185
+ if (line === null)
186
+ throw new Error(`${tool} has no price in PRICE_LIST`);
187
+ return `${description} ${line}`;
188
+ };
189
+ //# sourceMappingURL=prices.js.map