@skyphusion-labs/vivijure-core 0.9.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 (267) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +87 -0
  3. package/dist/audio-routing.d.ts +2 -0
  4. package/dist/audio-routing.d.ts.map +1 -0
  5. package/dist/audio-routing.js +4 -0
  6. package/dist/audio-routing.js.map +1 -0
  7. package/dist/audio-stage.d.ts +4 -0
  8. package/dist/audio-stage.d.ts.map +1 -0
  9. package/dist/audio-stage.js +25 -0
  10. package/dist/audio-stage.js.map +1 -0
  11. package/dist/beat-analyze.d.ts +22 -0
  12. package/dist/beat-analyze.d.ts.map +1 -0
  13. package/dist/beat-analyze.js +111 -0
  14. package/dist/beat-analyze.js.map +1 -0
  15. package/dist/beat-sync-types.d.ts +33 -0
  16. package/dist/beat-sync-types.d.ts.map +1 -0
  17. package/dist/beat-sync-types.js +4 -0
  18. package/dist/beat-sync-types.js.map +1 -0
  19. package/dist/bundle-assembler.d.ts +54 -0
  20. package/dist/bundle-assembler.d.ts.map +1 -0
  21. package/dist/bundle-assembler.js +376 -0
  22. package/dist/bundle-assembler.js.map +1 -0
  23. package/dist/bundle-durations.d.ts +5 -0
  24. package/dist/bundle-durations.d.ts.map +1 -0
  25. package/dist/bundle-durations.js +68 -0
  26. package/dist/bundle-durations.js.map +1 -0
  27. package/dist/bundle-storyboard.d.ts +11 -0
  28. package/dist/bundle-storyboard.d.ts.map +1 -0
  29. package/dist/bundle-storyboard.js +82 -0
  30. package/dist/bundle-storyboard.js.map +1 -0
  31. package/dist/captions.d.ts +25 -0
  32. package/dist/captions.d.ts.map +1 -0
  33. package/dist/captions.js +56 -0
  34. package/dist/captions.js.map +1 -0
  35. package/dist/cast-db.d.ts +61 -0
  36. package/dist/cast-db.d.ts.map +1 -0
  37. package/dist/cast-db.js +324 -0
  38. package/dist/cast-db.js.map +1 -0
  39. package/dist/cast-lora-train.d.ts +18 -0
  40. package/dist/cast-lora-train.d.ts.map +1 -0
  41. package/dist/cast-lora-train.js +215 -0
  42. package/dist/cast-lora-train.js.map +1 -0
  43. package/dist/cast-loras.d.ts +21 -0
  44. package/dist/cast-loras.d.ts.map +1 -0
  45. package/dist/cast-loras.js +69 -0
  46. package/dist/cast-loras.js.map +1 -0
  47. package/dist/clip-content-validate.d.ts +43 -0
  48. package/dist/clip-content-validate.d.ts.map +1 -0
  49. package/dist/clip-content-validate.js +115 -0
  50. package/dist/clip-content-validate.js.map +1 -0
  51. package/dist/clip-job-model.d.ts +42 -0
  52. package/dist/clip-job-model.d.ts.map +1 -0
  53. package/dist/clip-job-model.js +8 -0
  54. package/dist/clip-job-model.js.map +1 -0
  55. package/dist/clip-validate.d.ts +33 -0
  56. package/dist/clip-validate.d.ts.map +1 -0
  57. package/dist/clip-validate.js +247 -0
  58. package/dist/clip-validate.js.map +1 -0
  59. package/dist/d1-retry.d.ts +20 -0
  60. package/dist/d1-retry.d.ts.map +1 -0
  61. package/dist/d1-retry.js +67 -0
  62. package/dist/d1-retry.js.map +1 -0
  63. package/dist/db-env.d.ts +6 -0
  64. package/dist/db-env.d.ts.map +1 -0
  65. package/dist/db-env.js +2 -0
  66. package/dist/db-env.js.map +1 -0
  67. package/dist/dialogue-lines.d.ts +23 -0
  68. package/dist/dialogue-lines.d.ts.map +1 -0
  69. package/dist/dialogue-lines.js +84 -0
  70. package/dist/dialogue-lines.js.map +1 -0
  71. package/dist/film-advance-lease.d.ts +9 -0
  72. package/dist/film-advance-lease.d.ts.map +1 -0
  73. package/dist/film-advance-lease.js +19 -0
  74. package/dist/film-advance-lease.js.map +1 -0
  75. package/dist/film-model.d.ts +415 -0
  76. package/dist/film-model.d.ts.map +1 -0
  77. package/dist/film-model.js +541 -0
  78. package/dist/film-model.js.map +1 -0
  79. package/dist/film-orchestrator.d.ts +216 -0
  80. package/dist/film-orchestrator.d.ts.map +1 -0
  81. package/dist/film-orchestrator.js +2204 -0
  82. package/dist/film-orchestrator.js.map +1 -0
  83. package/dist/film-render-bridge.d.ts +26 -0
  84. package/dist/film-render-bridge.d.ts.map +1 -0
  85. package/dist/film-render-bridge.js +176 -0
  86. package/dist/film-render-bridge.js.map +1 -0
  87. package/dist/finish-hash.d.ts +10 -0
  88. package/dist/finish-hash.d.ts.map +1 -0
  89. package/dist/finish-hash.js +60 -0
  90. package/dist/finish-hash.js.map +1 -0
  91. package/dist/index.d.ts +48 -0
  92. package/dist/index.d.ts.map +1 -0
  93. package/dist/index.js +45 -0
  94. package/dist/index.js.map +1 -0
  95. package/dist/key-safety.d.ts +5 -0
  96. package/dist/key-safety.d.ts.map +1 -0
  97. package/dist/key-safety.js +23 -0
  98. package/dist/key-safety.js.map +1 -0
  99. package/dist/lora-bundle.d.ts +19 -0
  100. package/dist/lora-bundle.d.ts.map +1 -0
  101. package/dist/lora-bundle.js +75 -0
  102. package/dist/lora-bundle.js.map +1 -0
  103. package/dist/lora-keys.d.ts +2 -0
  104. package/dist/lora-keys.d.ts.map +1 -0
  105. package/dist/lora-keys.js +4 -0
  106. package/dist/lora-keys.js.map +1 -0
  107. package/dist/modules/conformance.d.ts +58 -0
  108. package/dist/modules/conformance.d.ts.map +1 -0
  109. package/dist/modules/conformance.js +369 -0
  110. package/dist/modules/conformance.js.map +1 -0
  111. package/dist/modules/manifest-validate.d.ts +5 -0
  112. package/dist/modules/manifest-validate.d.ts.map +1 -0
  113. package/dist/modules/manifest-validate.js +60 -0
  114. package/dist/modules/manifest-validate.js.map +1 -0
  115. package/dist/modules/registry.d.ts +184 -0
  116. package/dist/modules/registry.d.ts.map +1 -0
  117. package/dist/modules/registry.js +653 -0
  118. package/dist/modules/registry.js.map +1 -0
  119. package/dist/modules/render-pipeline.d.ts +38 -0
  120. package/dist/modules/render-pipeline.d.ts.map +1 -0
  121. package/dist/modules/render-pipeline.js +40 -0
  122. package/dist/modules/render-pipeline.js.map +1 -0
  123. package/dist/modules/types.d.ts +544 -0
  124. package/dist/modules/types.d.ts.map +1 -0
  125. package/dist/modules/types.js +57 -0
  126. package/dist/modules/types.js.map +1 -0
  127. package/dist/operator-config.d.ts +20 -0
  128. package/dist/operator-config.d.ts.map +1 -0
  129. package/dist/operator-config.js +85 -0
  130. package/dist/operator-config.js.map +1 -0
  131. package/dist/output-extract.d.ts +8 -0
  132. package/dist/output-extract.d.ts.map +1 -0
  133. package/dist/output-extract.js +129 -0
  134. package/dist/output-extract.js.map +1 -0
  135. package/dist/planner-prompt.d.ts +12 -0
  136. package/dist/planner-prompt.d.ts.map +1 -0
  137. package/dist/planner-prompt.js +337 -0
  138. package/dist/planner-prompt.js.map +1 -0
  139. package/dist/planner-yaml.d.ts +15 -0
  140. package/dist/planner-yaml.d.ts.map +1 -0
  141. package/dist/planner-yaml.js +157 -0
  142. package/dist/planner-yaml.js.map +1 -0
  143. package/dist/platform/fetcher.d.ts +3 -0
  144. package/dist/platform/fetcher.d.ts.map +1 -0
  145. package/dist/platform/fetcher.js +7 -0
  146. package/dist/platform/fetcher.js.map +1 -0
  147. package/dist/platform/index.d.ts +6 -0
  148. package/dist/platform/index.d.ts.map +1 -0
  149. package/dist/platform/index.js +5 -0
  150. package/dist/platform/index.js.map +1 -0
  151. package/dist/platform/object-store-r2.d.ts +26 -0
  152. package/dist/platform/object-store-r2.d.ts.map +1 -0
  153. package/dist/platform/object-store-r2.js +74 -0
  154. package/dist/platform/object-store-r2.js.map +1 -0
  155. package/dist/platform/orchestrator-context.d.ts +20 -0
  156. package/dist/platform/orchestrator-context.d.ts.map +1 -0
  157. package/dist/platform/orchestrator-context.js +28 -0
  158. package/dist/platform/orchestrator-context.js.map +1 -0
  159. package/dist/platform/r2-types.d.ts +41 -0
  160. package/dist/platform/r2-types.d.ts.map +1 -0
  161. package/dist/platform/r2-types.js +3 -0
  162. package/dist/platform/r2-types.js.map +1 -0
  163. package/dist/platform/types.d.ts +90 -0
  164. package/dist/platform/types.d.ts.map +1 -0
  165. package/dist/platform/types.js +23 -0
  166. package/dist/platform/types.js.map +1 -0
  167. package/dist/preflight.d.ts +57 -0
  168. package/dist/preflight.d.ts.map +1 -0
  169. package/dist/preflight.js +243 -0
  170. package/dist/preflight.js.map +1 -0
  171. package/dist/presign.d.ts +5 -0
  172. package/dist/presign.d.ts.map +1 -0
  173. package/dist/presign.js +24 -0
  174. package/dist/presign.js.map +1 -0
  175. package/dist/public-id.d.ts +8 -0
  176. package/dist/public-id.d.ts.map +1 -0
  177. package/dist/public-id.js +25 -0
  178. package/dist/public-id.js.map +1 -0
  179. package/dist/render-adopt.d.ts +3 -0
  180. package/dist/render-adopt.d.ts.map +1 -0
  181. package/dist/render-adopt.js +92 -0
  182. package/dist/render-adopt.js.map +1 -0
  183. package/dist/render-log.d.ts +25 -0
  184. package/dist/render-log.d.ts.map +1 -0
  185. package/dist/render-log.js +139 -0
  186. package/dist/render-log.js.map +1 -0
  187. package/dist/render-module-config.d.ts +28 -0
  188. package/dist/render-module-config.d.ts.map +1 -0
  189. package/dist/render-module-config.js +124 -0
  190. package/dist/render-module-config.js.map +1 -0
  191. package/dist/render-mux.d.ts +18 -0
  192. package/dist/render-mux.d.ts.map +1 -0
  193. package/dist/render-mux.js +69 -0
  194. package/dist/render-mux.js.map +1 -0
  195. package/dist/render-orchestrator.d.ts +71 -0
  196. package/dist/render-orchestrator.d.ts.map +1 -0
  197. package/dist/render-orchestrator.js +376 -0
  198. package/dist/render-orchestrator.js.map +1 -0
  199. package/dist/render-sweep.d.ts +5 -0
  200. package/dist/render-sweep.d.ts.map +1 -0
  201. package/dist/render-sweep.js +62 -0
  202. package/dist/render-sweep.js.map +1 -0
  203. package/dist/renders-db.d.ts +129 -0
  204. package/dist/renders-db.d.ts.map +1 -0
  205. package/dist/renders-db.js +752 -0
  206. package/dist/renders-db.js.map +1 -0
  207. package/dist/runpod-submit.d.ts +151 -0
  208. package/dist/runpod-submit.d.ts.map +1 -0
  209. package/dist/runpod-submit.js +469 -0
  210. package/dist/runpod-submit.js.map +1 -0
  211. package/dist/runpod-types.d.ts +2 -0
  212. package/dist/runpod-types.d.ts.map +1 -0
  213. package/dist/runpod-types.js +4 -0
  214. package/dist/runpod-types.js.map +1 -0
  215. package/dist/scatter-notify.d.ts +4 -0
  216. package/dist/scatter-notify.d.ts.map +1 -0
  217. package/dist/scatter-notify.js +43 -0
  218. package/dist/scatter-notify.js.map +1 -0
  219. package/dist/scatter-orchestrator-types.d.ts +56 -0
  220. package/dist/scatter-orchestrator-types.d.ts.map +1 -0
  221. package/dist/scatter-orchestrator-types.js +3 -0
  222. package/dist/scatter-orchestrator-types.js.map +1 -0
  223. package/dist/scatter-orchestrator.d.ts +70 -0
  224. package/dist/scatter-orchestrator.d.ts.map +1 -0
  225. package/dist/scatter-orchestrator.js +614 -0
  226. package/dist/scatter-orchestrator.js.map +1 -0
  227. package/dist/scatter.d.ts +35 -0
  228. package/dist/scatter.d.ts.map +1 -0
  229. package/dist/scatter.js +171 -0
  230. package/dist/scatter.js.map +1 -0
  231. package/dist/secret-store.d.ts +5 -0
  232. package/dist/secret-store.d.ts.map +1 -0
  233. package/dist/secret-store.js +19 -0
  234. package/dist/secret-store.js.map +1 -0
  235. package/dist/shot-durations-parse.d.ts +3 -0
  236. package/dist/shot-durations-parse.d.ts.map +1 -0
  237. package/dist/shot-durations-parse.js +43 -0
  238. package/dist/shot-durations-parse.js.map +1 -0
  239. package/dist/srt.d.ts +11 -0
  240. package/dist/srt.d.ts.map +1 -0
  241. package/dist/srt.js +54 -0
  242. package/dist/srt.js.map +1 -0
  243. package/dist/storyboard-ids.d.ts +2 -0
  244. package/dist/storyboard-ids.d.ts.map +1 -0
  245. package/dist/storyboard-ids.js +12 -0
  246. package/dist/storyboard-ids.js.map +1 -0
  247. package/dist/storyboard-projects-db.d.ts +31 -0
  248. package/dist/storyboard-projects-db.d.ts.map +1 -0
  249. package/dist/storyboard-projects-db.js +153 -0
  250. package/dist/storyboard-projects-db.js.map +1 -0
  251. package/dist/storyboard-validate.d.ts +72 -0
  252. package/dist/storyboard-validate.d.ts.map +1 -0
  253. package/dist/storyboard-validate.js +595 -0
  254. package/dist/storyboard-validate.js.map +1 -0
  255. package/dist/structured-events.d.ts +6 -0
  256. package/dist/structured-events.d.ts.map +1 -0
  257. package/dist/structured-events.js +15 -0
  258. package/dist/structured-events.js.map +1 -0
  259. package/dist/tar.d.ts +9 -0
  260. package/dist/tar.d.ts.map +1 -0
  261. package/dist/tar.js +151 -0
  262. package/dist/tar.js.map +1 -0
  263. package/dist/voices.d.ts +12 -0
  264. package/dist/voices.d.ts.map +1 -0
  265. package/dist/voices.js +37 -0
  266. package/dist/voices.js.map +1 -0
  267. package/package.json +71 -0
@@ -0,0 +1,243 @@
1
+ // Pre-render preflight (v0.54.0).
2
+ //
3
+ // Pure checks over a validated storyboard plus optional context (bundle
4
+ // key, audio key, cast bindings). The Worker route in src/index.ts adds
5
+ // the R2 HEAD checks (bundle / audio existence) on top; this module
6
+ // owns the "is the storyboard shape itself ready to render?" decision
7
+ // so vitest can cover it without env.
8
+ //
9
+ // Issue model: each finding is one entry with a level (error / warning
10
+ // / info), a scope tag for the UI to group on, and a human-readable
11
+ // message. Errors must be resolved before render (the UI gates the
12
+ // submit button); warnings are advisory.
13
+ // A storyboard is considered "renderable" when every scene has a prompt,
14
+ // every referenced character slot is loaded, and no scene is
15
+ // pathologically short (which usually means the user forgot to set
16
+ // target_seconds and is leaning on the clip_seconds default).
17
+ export function checkStoryboardShape(storyboard) {
18
+ const issues = [];
19
+ const scenes = Array.isArray(storyboard.scenes) ? storyboard.scenes : [];
20
+ const loadedSlots = new Set(Array.isArray(storyboard.use_characters) ? storyboard.use_characters : []);
21
+ if (scenes.length === 0) {
22
+ issues.push({ level: "error", scope: "scenes", message: "storyboard has no scenes" });
23
+ return issues;
24
+ }
25
+ if (scenes.length > 24) {
26
+ issues.push({
27
+ level: "warning",
28
+ scope: "scenes",
29
+ message: `${scenes.length} scenes is a lot for one render; consider splitting (>15 min Wan I2V time)`,
30
+ });
31
+ }
32
+ scenes.forEach((scene, idx) => {
33
+ const sid = scene.id || `scene_${(idx + 1).toString().padStart(2, "0")}`;
34
+ const scope = `scene[${sid}]`;
35
+ if (!scene.prompt || !scene.prompt.trim()) {
36
+ issues.push({ level: "error", scope, message: `${sid} has an empty prompt` });
37
+ }
38
+ else if (scene.prompt.trim().length < 8) {
39
+ issues.push({
40
+ level: "warning",
41
+ scope,
42
+ message: `${sid} prompt is very short (${scene.prompt.trim().length} chars); the keyframe model may underspecify`,
43
+ });
44
+ }
45
+ if (Array.isArray(scene.character_slots)) {
46
+ for (const slot of scene.character_slots) {
47
+ if (!loadedSlots.has(slot)) {
48
+ issues.push({
49
+ level: "error",
50
+ scope,
51
+ message: `${sid} references slot "${slot}" which is not in use_characters`,
52
+ });
53
+ }
54
+ }
55
+ }
56
+ if (typeof scene.target_seconds === "number") {
57
+ if (scene.target_seconds <= 0) {
58
+ issues.push({
59
+ level: "error",
60
+ scope,
61
+ message: `${sid} has target_seconds <= 0 (got ${scene.target_seconds})`,
62
+ });
63
+ }
64
+ else if (scene.target_seconds < 1.5) {
65
+ issues.push({
66
+ level: "warning",
67
+ scope,
68
+ message: `${sid} target_seconds is ${scene.target_seconds}s; Wan I2V default minimum is ~1.5s`,
69
+ });
70
+ }
71
+ else if (scene.target_seconds > 12) {
72
+ issues.push({
73
+ level: "warning",
74
+ scope,
75
+ message: `${sid} target_seconds is ${scene.target_seconds}s; long clips often look static`,
76
+ });
77
+ }
78
+ }
79
+ });
80
+ return issues;
81
+ }
82
+ // Cast-binding readiness. If a slot was bound to a persisted cast
83
+ // member at plan time (planState.castBindings on the planner), the
84
+ // member needs a portrait (used as the SDXL start image) and a
85
+ // non-empty reference set (used by LoRA training). Both missing =
86
+ // error; portrait present but refs sparse = warning.
87
+ export function checkCastBindingsReady(bindings, catalog,
88
+ // #454: the keyframe-stage backend display token (the route resolves it from the module registry;
89
+ // default "SDXL" keeps the pure-function callers and their tests backend-agnostic).
90
+ keyframeLabel = "SDXL") {
91
+ const issues = [];
92
+ if (!bindings)
93
+ return issues;
94
+ const byId = new Map(catalog.map((c) => [c.id, c]));
95
+ for (const slot of Object.keys(bindings)) {
96
+ const id = bindings[slot];
97
+ const member = byId.get(id);
98
+ const scope = `cast[${slot}]`;
99
+ if (!member) {
100
+ issues.push({
101
+ level: "error",
102
+ scope,
103
+ message: `slot ${slot} is bound to cast id ${id} which no longer exists`,
104
+ });
105
+ continue;
106
+ }
107
+ const refCount = member.ref_keys?.length ?? 0;
108
+ if (!member.portrait_key) {
109
+ issues.push({
110
+ level: "error",
111
+ scope,
112
+ message: `${member.name} (slot ${slot}) has no portrait; render will fail at the ${keyframeLabel} keyframe stage`,
113
+ });
114
+ }
115
+ if (refCount === 0) {
116
+ issues.push({
117
+ level: "error",
118
+ scope,
119
+ message: `${member.name} (slot ${slot}) has no training refs; LoRA training will fail`,
120
+ });
121
+ }
122
+ else if (refCount < 4) {
123
+ issues.push({
124
+ level: "warning",
125
+ scope,
126
+ message: `${member.name} (slot ${slot}) has only ${refCount} training refs; 4-8 is recommended for stable LoRAs`,
127
+ });
128
+ }
129
+ }
130
+ return issues;
131
+ }
132
+ export function resolveCastBindings(bindings, catalog) {
133
+ const resolved = {};
134
+ const unresolved = [];
135
+ if (!bindings)
136
+ return { resolved, unresolved };
137
+ const byNumericId = new Map(catalog.map((c) => [c.id, c]));
138
+ const byPublicId = new Map();
139
+ for (const c of catalog) {
140
+ if (typeof c.public_id === "string" && c.public_id)
141
+ byPublicId.set(c.public_id, c);
142
+ }
143
+ for (const slot of Object.keys(bindings)) {
144
+ const value = bindings[slot];
145
+ const scope = `cast[${slot}]`;
146
+ // Numeric row id, or the numeric-string form of one.
147
+ if (typeof value === "number" || (typeof value === "string" && /^[0-9]+$/.test(value))) {
148
+ const numeric = typeof value === "number" ? value : Number(value);
149
+ if (byNumericId.has(numeric)) {
150
+ resolved[slot] = numeric;
151
+ }
152
+ else {
153
+ unresolved.push({
154
+ level: "error",
155
+ scope,
156
+ message: `slot ${slot} is bound to unknown cast id ${numeric} (no cast member has this numeric id)`,
157
+ });
158
+ }
159
+ continue;
160
+ }
161
+ // Public UUID -- the id the API returns to clients.
162
+ if (typeof value === "string") {
163
+ const member = byPublicId.get(value);
164
+ if (member) {
165
+ resolved[slot] = member.id;
166
+ }
167
+ else {
168
+ unresolved.push({
169
+ level: "error",
170
+ scope,
171
+ message: `slot ${slot} is bound to unknown cast id "${value}" (no cast member has this public id)`,
172
+ });
173
+ }
174
+ continue;
175
+ }
176
+ // Neither a number nor a string: a wrong id kind, not a lookup miss.
177
+ unresolved.push({
178
+ level: "error",
179
+ scope,
180
+ message: `slot ${slot} is bound to an invalid cast id (${value === null ? "null" : typeof value}); expected a cast public id or numeric row id`,
181
+ });
182
+ }
183
+ return { resolved, unresolved };
184
+ }
185
+ export function checkDurationGrid(storyboard, grid, quality, backendName = "the selected motion backend",
186
+ // #751: the per-shot duration FLOOR fraction (FILM_CLIP_DURATION_FLOOR; default 0.5, 0 disables the
187
+ // gate). When the route passes it, a clamp that would land BELOW floor x planned is not a warning --
188
+ // that render is guaranteed to hard-fail the #697 duration gate, so we escalate the issue to `error`
189
+ // (which blocks the submit) instead of telling the user the clip is merely "clamped" and "unblocked".
190
+ // Omitted (pure-function callers / older clients) keeps the pre-#751 warning-only behavior.
191
+ floorFraction) {
192
+ const issues = [];
193
+ if (!grid || typeof grid.fps !== "number" || !(grid.fps > 0) || !grid.tiers)
194
+ return issues;
195
+ const caps = Object.entries(grid.tiers)
196
+ .filter((e) => typeof e[1]?.max_frames === "number" && e[1].max_frames > 0);
197
+ if (caps.length === 0)
198
+ return issues;
199
+ // The declared tier when the caller names one; otherwise the LOOSEST declared cap, so with an
200
+ // unknown tier we only warn on shots that get clamped no matter which tier renders (no false alarms).
201
+ const declared = quality ? caps.find(([t]) => t === quality) : undefined;
202
+ const maxFrames = declared ? declared[1].max_frames : Math.max(...caps.map(([, t]) => t.max_frames));
203
+ const maxSeconds = Math.round((maxFrames / grid.fps) * 1000) / 1000;
204
+ const tierPhrase = declared ? `at the ${declared[0]} tier` : "even at its largest tier";
205
+ const gateArmed = typeof floorFraction === "number" && floorFraction > 0;
206
+ const scenes = Array.isArray(storyboard.scenes) ? storyboard.scenes : [];
207
+ scenes.forEach((scene, idx) => {
208
+ const sid = scene.id || `scene_${(idx + 1).toString().padStart(2, "0")}`;
209
+ const planned = typeof scene.target_seconds === "number" ? scene.target_seconds
210
+ : typeof storyboard.clip_seconds === "number" ? storyboard.clip_seconds
211
+ : undefined;
212
+ if (planned === undefined || !(planned > 0))
213
+ return; // no planned duration to compare (shape checks own <=0)
214
+ if (planned > maxSeconds + 0.001) {
215
+ // #751: a clamp that breaches the duration floor is a guaranteed hard-fail, not a warning.
216
+ const floorSeconds = gateArmed ? Math.round(floorFraction * planned * 1000) / 1000 : 0;
217
+ const breachesFloor = gateArmed && maxSeconds < floorSeconds - 0.001;
218
+ issues.push(breachesFloor
219
+ ? {
220
+ level: "error",
221
+ scope: `scene[${sid}]`,
222
+ message: `${sid} plans ${planned}s but ${backendName} delivers at most ${maxSeconds}s ${tierPhrase} (${maxFrames} frames at ${grid.fps}fps) -- below the ${Math.round(floorFraction * 100)}% duration floor (${floorSeconds}s), so this render would fail the duration gate. Shorten the shot to <= ${maxSeconds}s or choose a backend/tier that delivers more frames.`,
223
+ }
224
+ : {
225
+ level: "warning",
226
+ scope: `scene[${sid}]`,
227
+ message: `${sid} plans ${planned}s but ${backendName} delivers at most ${maxSeconds}s ${tierPhrase} (${maxFrames} frames at ${grid.fps}fps); the clip will be clamped`,
228
+ });
229
+ }
230
+ });
231
+ return issues;
232
+ }
233
+ export function summarize(issues) {
234
+ const counts = { error: 0, warning: 0, info: 0 };
235
+ for (const i of issues)
236
+ counts[i.level]++;
237
+ return {
238
+ ok: counts.error === 0,
239
+ counts,
240
+ issues,
241
+ };
242
+ }
243
+ //# sourceMappingURL=preflight.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preflight.js","sourceRoot":"","sources":["../src/preflight.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { OrchestratorEnv } from "./platform/orchestrator-context.js";
2
+ export declare const FILM_DOWNLOAD_TTL_SECONDS = 3600;
3
+ export declare function presignR2Get(env: OrchestratorEnv, key: string, expiresSec?: number): Promise<string>;
4
+ export declare function presignR2Put(env: OrchestratorEnv, key: string, expiresSec?: number, contentType?: string): Promise<string>;
5
+ //# sourceMappingURL=presign.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presign.d.ts","sourceRoot":"","sources":["../src/presign.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAE1E,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAU9C,wBAAsB,YAAY,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,SAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGvG;AAED,wBAAsB,YAAY,CAChC,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,MAAM,EACX,UAAU,SAAM,EAChB,WAAW,SAA6B,GACvC,OAAO,CAAC,MAAM,CAAC,CAGjB"}
@@ -0,0 +1,24 @@
1
+ // Presign helpers for orchestrators (delegates to platform presigner).
2
+ export const FILM_DOWNLOAD_TTL_SECONDS = 3600;
3
+ function isPresignSafeKey(key) {
4
+ if (typeof key !== "string" || key.length === 0 || key.length > 1024)
5
+ return false;
6
+ if (key.startsWith("/"))
7
+ return false;
8
+ if (key.includes("://"))
9
+ return false;
10
+ if (/[^ -~]/.test(key))
11
+ return false;
12
+ return !key.split("/").includes("..");
13
+ }
14
+ export async function presignR2Get(env, key, expiresSec = 300) {
15
+ if (!isPresignSafeKey(key))
16
+ throw new Error("R2 presign: refusing to sign an unsafe object key");
17
+ return env.PRESIGNER.presignGet(key, expiresSec);
18
+ }
19
+ export async function presignR2Put(env, key, expiresSec = 300, contentType = "application/octet-stream") {
20
+ if (!isPresignSafeKey(key))
21
+ throw new Error("R2 presign: refusing to sign an unsafe object key");
22
+ return env.PRESIGNER.presignPut(key, contentType, expiresSec);
23
+ }
24
+ //# sourceMappingURL=presign.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presign.js","sourceRoot":"","sources":["../src/presign.ts"],"names":[],"mappings":"AAAA,uEAAuE;AAIvE,MAAM,CAAC,MAAM,yBAAyB,GAAG,IAAI,CAAC;AAE9C,SAAS,gBAAgB,CAAC,GAAY;IACpC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,IAAI,GAAG,CAAC,MAAM,GAAG,IAAI;QAAE,OAAO,KAAK,CAAC;IACnF,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACtC,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACtC,IAAI,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACrC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAoB,EAAE,GAAW,EAAE,UAAU,GAAG,GAAG;IACpF,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACjG,OAAO,GAAG,CAAC,SAAS,CAAC,UAAU,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,GAAoB,EACpB,GAAW,EACX,UAAU,GAAG,GAAG,EAChB,WAAW,GAAG,0BAA0B;IAExC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACjG,OAAO,GAAG,CAAC,SAAS,CAAC,UAAU,CAAC,GAAG,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC;AAChE,CAAC"}
@@ -0,0 +1,8 @@
1
+ /** Mint a new public id for a row insert. Canonical lowercase UUID v4 from the platform CSPRNG. */
2
+ export declare function newPublicId(): string;
3
+ /** True iff `raw` is shaped like a public id. A bare integer (or any non-UUID string) is false, so a
4
+ * route can reject an enumeration probe (e.g. "5") with a 404 before touching the database. This is
5
+ * a cheap shape gate, NOT the security boundary -- the authoritative check is that the public_id
6
+ * matches a stored row (resolve* in the db layer). */
7
+ export declare function isPublicId(raw: unknown): raw is string;
8
+ //# sourceMappingURL=public-id.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public-id.d.ts","sourceRoot":"","sources":["../src/public-id.ts"],"names":[],"mappings":"AAWA,mGAAmG;AACnG,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAMD;;;uDAGuD;AACvD,wBAAgB,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,MAAM,CAEtD"}
@@ -0,0 +1,25 @@
1
+ // Unguessable public resource ids (security finding F13, sprint S9).
2
+ //
3
+ // The externally-addressable resources (storyboard_projects, cast_members, renders) expose a
4
+ // `public_id` -- a canonical UUID v4 (122 bits of entropy) -- as the ONLY id that leaves the core
5
+ // over the API. The internal INTEGER PK stays the join/FK key; public_id is what every `:id` route
6
+ // accepts and resolves. A bare sequential integer is not a valid public id, so it can never address
7
+ // a row: the enumeration attack (walk 1, 2, 3) is dead at the shape level and again at the lookup.
8
+ //
9
+ // One format serves both freshly-inserted rows (crypto.randomUUID() here) and rows backfilled by
10
+ // migration 0010 (a pure-SQL v4 generator emitting the identical 8-4-4-4-12 canonical shape).
11
+ /** Mint a new public id for a row insert. Canonical lowercase UUID v4 from the platform CSPRNG. */
12
+ export function newPublicId() {
13
+ return crypto.randomUUID();
14
+ }
15
+ // Canonical UUID v4: 8-4-4-4-12 lowercase hex, version nibble 4, variant nibble in [89ab]. Matches
16
+ // both crypto.randomUUID() and the migration 0010 backfill expression.
17
+ const PUBLIC_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
18
+ /** True iff `raw` is shaped like a public id. A bare integer (or any non-UUID string) is false, so a
19
+ * route can reject an enumeration probe (e.g. "5") with a 404 before touching the database. This is
20
+ * a cheap shape gate, NOT the security boundary -- the authoritative check is that the public_id
21
+ * matches a stored row (resolve* in the db layer). */
22
+ export function isPublicId(raw) {
23
+ return typeof raw === "string" && PUBLIC_ID_RE.test(raw);
24
+ }
25
+ //# sourceMappingURL=public-id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public-id.js","sourceRoot":"","sources":["../src/public-id.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,EAAE;AACF,6FAA6F;AAC7F,kGAAkG;AAClG,mGAAmG;AACnG,oGAAoG;AACpG,mGAAmG;AACnG,EAAE;AACF,iGAAiG;AACjG,8FAA8F;AAE9F,mGAAmG;AACnG,MAAM,UAAU,WAAW;IACzB,OAAO,MAAM,CAAC,UAAU,EAAE,CAAC;AAC7B,CAAC;AAED,mGAAmG;AACnG,uEAAuE;AACvE,MAAM,YAAY,GAAG,uEAAuE,CAAC;AAE7F;;;uDAGuD;AACvD,MAAM,UAAU,UAAU,CAAC,GAAY;IACrC,OAAO,OAAO,GAAG,KAAK,QAAQ,IAAI,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC3D,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Env } from "./platform/orchestrator-context.js";
2
+ export declare function handleAdoptRender(request: Request, env: Env): Promise<Response>;
3
+ //# sourceMappingURL=render-adopt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-adopt.d.ts","sourceRoot":"","sources":["../src/render-adopt.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,oCAAoC,CAAC;AAK9D,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,GAAG,GACP,OAAO,CAAC,QAAQ,CAAC,CAoGnB"}
@@ -0,0 +1,92 @@
1
+ // Backfill a render row for a job submitted outside the worker (idempotent adopt).
2
+ import { insertRender, markFinishDone } from "./renders-db.js";
3
+ import { coerceQualityTier, deriveProjectFromBundleKey } from "./runpod-submit.js";
4
+ import { isSafeBundleKey } from "./key-safety.js";
5
+ export async function handleAdoptRender(request, env) {
6
+ let body;
7
+ try {
8
+ body = (await request.json());
9
+ }
10
+ catch {
11
+ return json({ error: "Invalid JSON" }, 400);
12
+ }
13
+ if (typeof body.jobId !== "string" || body.jobId.trim().length === 0) {
14
+ return json({ error: "jobId is required (non-empty string)" }, 400);
15
+ }
16
+ const jobId = body.jobId.trim();
17
+ const outputKey = typeof body.outputKey === "string" && body.outputKey.trim().length > 0
18
+ ? body.outputKey.trim()
19
+ : null;
20
+ if (body.seconds !== undefined && (typeof body.seconds !== "number" || !Number.isFinite(body.seconds))) {
21
+ return json({ error: "seconds must be a finite number if provided" }, 400);
22
+ }
23
+ if (body.hasAudio !== undefined && typeof body.hasAudio !== "boolean") {
24
+ return json({ error: "hasAudio must be a boolean if provided" }, 400);
25
+ }
26
+ if (body.qualityTier !== undefined && coerceQualityTier(body.qualityTier) === undefined) {
27
+ return json({ error: "qualityTier must be 'draft' | 'standard' | 'final' if provided" }, 400);
28
+ }
29
+ if (body.mode !== undefined && body.mode !== "full" && body.mode !== "keyframes-only") {
30
+ return json({ error: "mode must be 'full' | 'keyframes-only' if provided" }, 400);
31
+ }
32
+ const bundleKey = typeof body.bundleKey === "string" ? body.bundleKey : "";
33
+ // Optional field, but when present it lands in the renders row and can later be read back as a
34
+ // storage key (regen-shot): require the canonical bundle shape, like the submit handlers.
35
+ if (bundleKey && !isSafeBundleKey(bundleKey)) {
36
+ return json({ error: "bundleKey must be a plain relative key under bundles/" }, 400);
37
+ }
38
+ const project = typeof body.project === "string" && body.project.trim().length > 0
39
+ ? body.project.trim()
40
+ : bundleKey
41
+ ? deriveProjectFromBundleKey(bundleKey)
42
+ : jobId;
43
+ const outJson = () => {
44
+ const out = { output_key: outputKey };
45
+ if (typeof body.seconds === "number")
46
+ out.seconds = body.seconds;
47
+ if (typeof body.hasAudio === "boolean")
48
+ out.has_audio = body.hasAudio;
49
+ return JSON.stringify(out);
50
+ };
51
+ try {
52
+ const existing = await env.DB.prepare("SELECT id FROM renders WHERE job_id = ? LIMIT 1")
53
+ .bind(jobId)
54
+ .first();
55
+ if (existing) {
56
+ if (outputKey)
57
+ await markFinishDone(env, jobId, outputKey, outJson());
58
+ return json({
59
+ ok: true,
60
+ jobId,
61
+ project,
62
+ adopted: true,
63
+ completed: !!outputKey,
64
+ deduped: true,
65
+ });
66
+ }
67
+ await insertRender(env, {
68
+ jobId,
69
+ project,
70
+ bundleKey,
71
+ qualityTier: coerceQualityTier(body.qualityTier) ?? "final",
72
+ status: outputKey ? "COMPLETED" : "SUBMITTED",
73
+ mode: body.mode ?? "full",
74
+ projectId: null,
75
+ });
76
+ if (outputKey) {
77
+ await markFinishDone(env, jobId, outputKey, outJson());
78
+ }
79
+ }
80
+ catch (err) {
81
+ console.error("adopt render insert failed:", err);
82
+ return json({ error: "could not adopt render" }, 500);
83
+ }
84
+ return json({ ok: true, jobId, project, adopted: true, completed: !!outputKey });
85
+ }
86
+ function json(body, status = 200) {
87
+ return new Response(JSON.stringify(body), {
88
+ status,
89
+ headers: { "content-type": "application/json; charset=utf-8" },
90
+ });
91
+ }
92
+ //# sourceMappingURL=render-adopt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-adopt.js","sourceRoot":"","sources":["../src/render-adopt.ts"],"names":[],"mappings":"AAAA,mFAAmF;AAGnF,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAAgB,EAChB,GAAQ;IAER,IAAI,IASH,CAAC;IACF,IAAI,CAAC;QACH,IAAI,GAAG,CAAC,MAAM,OAAO,CAAC,IAAI,EAAE,CAAgB,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE,GAAG,CAAC,CAAC;IAC9C,CAAC;IACD,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrE,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,sCAAsC,EAAE,EAAE,GAAG,CAAC,CAAC;IACtE,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IAEhC,MAAM,SAAS,GACb,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;QACpE,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE;QACvB,CAAC,CAAC,IAAI,CAAC;IACX,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;QACvG,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,6CAA6C,EAAE,EAAE,GAAG,CAAC,CAAC;IAC7E,CAAC;IACD,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,IAAI,OAAO,IAAI,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QACtE,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,wCAAwC,EAAE,EAAE,GAAG,CAAC,CAAC;IACxE,CAAC;IACD,IAAI,IAAI,CAAC,WAAW,KAAK,SAAS,IAAI,iBAAiB,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,SAAS,EAAE,CAAC;QACxF,OAAO,IAAI,CACT,EAAE,KAAK,EAAE,gEAAgE,EAAE,EAC3E,GAAG,CACJ,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;QACtF,OAAO,IAAI,CACT,EAAE,KAAK,EAAE,oDAAoD,EAAE,EAC/D,GAAG,CACJ,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3E,+FAA+F;IAC/F,0FAA0F;IAC1F,IAAI,SAAS,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7C,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,uDAAuD,EAAE,EAAE,GAAG,CAAC,CAAC;IACvF,CAAC;IACD,MAAM,OAAO,GACX,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;QAChE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE;QACrB,CAAC,CAAC,SAAS;YACT,CAAC,CAAC,0BAA0B,CAAC,SAAS,CAAC;YACvC,CAAC,CAAC,KAAK,CAAC;IAEd,MAAM,OAAO,GAAG,GAAW,EAAE;QAC3B,MAAM,GAAG,GAA4B,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;QAC/D,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ;YAAE,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QACjE,IAAI,OAAO,IAAI,CAAC,QAAQ,KAAK,SAAS;YAAE,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC;QACtE,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC,CAAC;IAEF,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,OAAO,CACnC,iDAAiD,CAClD;aACE,IAAI,CAAC,KAAK,CAAC;aACX,KAAK,EAAkB,CAAC;QAC3B,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,SAAS;gBAAE,MAAM,cAAc,CAAC,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC;YACtE,OAAO,IAAI,CAAC;gBACV,EAAE,EAAE,IAAI;gBACR,KAAK;gBACL,OAAO;gBACP,OAAO,EAAE,IAAI;gBACb,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,OAAO,EAAE,IAAI;aACd,CAAC,CAAC;QACL,CAAC;QAED,MAAM,YAAY,CAAC,GAAG,EAAE;YACtB,KAAK;YACL,OAAO;YACP,SAAS;YACT,WAAW,EAAE,iBAAiB,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,OAAO;YAC3D,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW;YAC7C,IAAI,EAAG,IAAI,CAAC,IAA8C,IAAI,MAAM;YACpE,SAAS,EAAE,IAAI;SAChB,CAAC,CAAC;QACH,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,cAAc,CAAC,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC;QACzD,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,wBAAwB,EAAE,EAAE,GAAG,CAAC,CAAC;IACxD,CAAC;IAED,OAAO,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;AACnF,CAAC;AAED,SAAS,IAAI,CAAC,IAAa,EAAE,MAAM,GAAG,GAAG;IACvC,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE;QACxC,MAAM;QACN,OAAO,EAAE,EAAE,cAAc,EAAE,iCAAiC,EAAE;KAC/D,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,25 @@
1
+ import type { Env } from "./platform/orchestrator-context.js";
2
+ import type { RunpodJobView } from "./runpod-types.js";
3
+ export declare function renderLogKey(jobId: string): string;
4
+ export declare function buildRenderLogText(view: RunpodJobView, generatedAtIso: string): string;
5
+ export interface CloudAnimateLogShot {
6
+ shot_id: string;
7
+ model: string;
8
+ status: "ok" | "failed";
9
+ log_id: string | null;
10
+ video_url?: string | null;
11
+ error?: string | null;
12
+ gateway_log?: unknown;
13
+ }
14
+ export interface CloudAnimateLogInput {
15
+ jobId: string;
16
+ model: string;
17
+ status: string;
18
+ executionTimeMs?: number | null;
19
+ error?: string | null;
20
+ shots: CloudAnimateLogShot[];
21
+ }
22
+ export declare function buildCloudAnimateLogText(p: CloudAnimateLogInput, generatedAtIso: string): string;
23
+ export declare function writeCloudAnimateLog(env: Env, input: CloudAnimateLogInput): Promise<string | null>;
24
+ export declare function writeRenderLog(env: Env, view: RunpodJobView): Promise<string | null>;
25
+ //# sourceMappingURL=render-log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-log.d.ts","sourceRoot":"","sources":["../src/render-log.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,oCAAoC,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAKvD,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD;AAcD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CA6BtF;AAWD,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,IAAI,GAAG,QAAQ,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAGtB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,mBAAmB,EAAE,CAAC;CAC9B;AAID,wBAAgB,wBAAwB,CACtC,CAAC,EAAE,oBAAoB,EACvB,cAAc,EAAE,MAAM,GACrB,MAAM,CA2BR;AAMD,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,oBAAoB,GAC1B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA0CxB;AAID,wBAAsB,cAAc,CAClC,GAAG,EAAE,GAAG,EACR,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAcxB"}
@@ -0,0 +1,139 @@
1
+ import { secretValue } from "./secret-store.js";
2
+ // Conventional R2 key for a job's log. The History UI derives the same key from
3
+ // the row's job_id, so there is nothing to store in D1.
4
+ export function renderLogKey(jobId) {
5
+ return `renders/logs/${jobId}.txt`;
6
+ }
7
+ // Bound free-text fields (an error string, an output blob) so a pathological GPU diagnostics dump
8
+ // cannot produce an unbounded R2 log object (issue #17). Generous enough to keep real diagnostics
9
+ // intact; a marker records how much was dropped.
10
+ const MAX_LOG_FIELD = 4000;
11
+ function clampLog(s) {
12
+ return s.length > MAX_LOG_FIELD
13
+ ? `${s.slice(0, MAX_LOG_FIELD)}\n...[truncated ${s.length - MAX_LOG_FIELD} chars]`
14
+ : s;
15
+ }
16
+ // Pure: format a RunPod job view into a readable per-render log. Kept pure so it
17
+ // is unit-testable; the caller supplies the timestamp.
18
+ export function buildRenderLogText(view, generatedAtIso) {
19
+ const lines = [];
20
+ lines.push(`Render log - job ${view.jobId}`);
21
+ lines.push(`Generated: ${generatedAtIso}`);
22
+ const raw = view.statusRaw && view.statusRaw !== view.status ? ` (${view.statusRaw})` : "";
23
+ lines.push(`Status: ${view.status}${raw}`);
24
+ if (typeof view.executionTimeMs === "number") {
25
+ lines.push(`Execution: ${(view.executionTimeMs / 1000).toFixed(1)}s`);
26
+ }
27
+ if (typeof view.delayTimeMs === "number") {
28
+ lines.push(`Queue delay: ${(view.delayTimeMs / 1000).toFixed(1)}s`);
29
+ }
30
+ if (view.error) {
31
+ lines.push("", "Error:", clampLog(view.error));
32
+ }
33
+ if (view.output !== undefined && view.output !== null) {
34
+ lines.push("", "Output / diagnostics:");
35
+ if (typeof view.output === "string") {
36
+ lines.push(clampLog(view.output));
37
+ }
38
+ else {
39
+ try {
40
+ lines.push(clampLog(JSON.stringify(view.output, null, 2)));
41
+ }
42
+ catch {
43
+ lines.push(clampLog(String(view.output)));
44
+ }
45
+ }
46
+ }
47
+ return lines.join("\n") + "\n";
48
+ }
49
+ // Pure: format a cloud-animate run into a readable per-render log. Caller
50
+ // supplies the timestamp so this stays unit-testable.
51
+ export function buildCloudAnimateLogText(p, generatedAtIso) {
52
+ const lines = [];
53
+ lines.push(`Render log - cloud animation job ${p.jobId}`);
54
+ lines.push(`Generated: ${generatedAtIso}`);
55
+ lines.push(`Status: ${p.status}`);
56
+ lines.push(`Model: ${p.model}`);
57
+ if (typeof p.executionTimeMs === "number") {
58
+ lines.push(`Execution: ${(p.executionTimeMs / 1000).toFixed(1)}s`);
59
+ }
60
+ lines.push(`Shots: ${p.shots.length}`);
61
+ if (p.error)
62
+ lines.push("", "Error:", clampLog(p.error));
63
+ for (const s of p.shots) {
64
+ lines.push("", `--- ${s.shot_id} (${s.status}) ---`);
65
+ lines.push(`Model: ${s.model}`);
66
+ lines.push(`AI Gateway log id: ${s.log_id ?? "(none captured)"}`);
67
+ if (s.video_url)
68
+ lines.push(`Provider clip URL: ${s.video_url}`);
69
+ if (s.error)
70
+ lines.push(`Error: ${clampLog(s.error)}`);
71
+ if (s.gateway_log !== undefined && s.gateway_log !== null) {
72
+ lines.push("AI Gateway log:");
73
+ try {
74
+ lines.push(clampLog(JSON.stringify(s.gateway_log, null, 2)));
75
+ }
76
+ catch {
77
+ lines.push(clampLog(String(s.gateway_log)));
78
+ }
79
+ }
80
+ }
81
+ return lines.join("\n") + "\n";
82
+ }
83
+ // Best-effort: enrich each shot with its AI Gateway log object, then write the
84
+ // cloud-animate log to R2 at the conventional key. NEVER throws. Returns the
85
+ // key on success, null on failure. The gateway lookups are individually guarded
86
+ // so one missing/expired log id cannot drop the whole file.
87
+ export async function writeCloudAnimateLog(env, input) {
88
+ try {
89
+ const gw = env.AI.gateway;
90
+ // Enrich each shot with its AI Gateway log object on a LOCAL COPY -- never mutate
91
+ // the caller's shot objects (issue #15). The lookups run CONCURRENTLY (one per
92
+ // shot) instead of serially, and each is individually guarded, so one slow or
93
+ // missing log id neither blocks the others nor drops the file; a failed lookup
94
+ // just leaves gateway_log undefined and is logged rather than swallowed silently.
95
+ let shots = input.shots;
96
+ const gatewayId = await secretValue(env.GATEWAY_ID);
97
+ if (gw && gatewayId) {
98
+ const gateway = gw(gatewayId);
99
+ shots = await Promise.all(input.shots.map(async (s) => {
100
+ if (!s.log_id)
101
+ return { ...s };
102
+ try {
103
+ return { ...s, gateway_log: await gateway.getLog(s.log_id) };
104
+ }
105
+ catch (e) {
106
+ console.warn(`cloud-animate log: AI Gateway getLog failed for shot ${s.shot_id} (log ${s.log_id}): ${e instanceof Error ? e.message : String(e)}`);
107
+ return { ...s };
108
+ }
109
+ }));
110
+ }
111
+ const key = renderLogKey(input.jobId);
112
+ const text = buildCloudAnimateLogText({ ...input, shots }, new Date().toISOString());
113
+ await env.R2_RENDERS.put(key, text, {
114
+ httpMetadata: { contentType: "text/plain; charset=utf-8" },
115
+ });
116
+ return key;
117
+ }
118
+ catch (e) {
119
+ console.warn(`cloud-animate log write failed for job ${input.jobId}: ${e instanceof Error ? e.message : String(e)}`);
120
+ return null;
121
+ }
122
+ }
123
+ // Best-effort: write the per-render log to R2. NEVER throws; logging must not
124
+ // break the render-resolve path. Returns the key on success, null on failure.
125
+ export async function writeRenderLog(env, view) {
126
+ try {
127
+ const key = renderLogKey(view.jobId);
128
+ const text = buildRenderLogText(view, new Date().toISOString());
129
+ await env.R2_RENDERS.put(key, text, {
130
+ httpMetadata: { contentType: "text/plain; charset=utf-8" },
131
+ });
132
+ return key;
133
+ }
134
+ catch (e) {
135
+ console.warn(`render log write failed for job ${view.jobId}: ${e instanceof Error ? e.message : String(e)}`);
136
+ return null;
137
+ }
138
+ }
139
+ //# sourceMappingURL=render-log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-log.js","sourceRoot":"","sources":["../src/render-log.ts"],"names":[],"mappings":"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"}