videobook 0.0.0-stage → 0.2.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 (565) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/LICENSE +21 -0
  3. package/README.md +195 -2
  4. package/bin/videobook.js +7 -0
  5. package/dist/acestep/client.js +203 -0
  6. package/dist/acestep/config.js +62 -0
  7. package/dist/acestep/errors.js +8 -0
  8. package/dist/acestep/process.js +175 -0
  9. package/dist/alive-captions/analyze.js +117 -0
  10. package/dist/alive-captions/audio.js +179 -0
  11. package/dist/alive-captions/frames.js +59 -0
  12. package/dist/alive-captions/model.js +37 -0
  13. package/dist/alive-captions/schema.js +49 -0
  14. package/dist/alive-captions/track.js +9 -0
  15. package/dist/api-key.js +33 -0
  16. package/dist/app-state.js +56 -0
  17. package/dist/app.js +257 -0
  18. package/dist/archive-b2.js +332 -0
  19. package/dist/archive-store.js +169 -0
  20. package/dist/artifact-label.js +36 -0
  21. package/dist/artifact-refs.js +49 -0
  22. package/dist/asset-tag-extraction.js +169 -0
  23. package/dist/asset-tags.js +228 -0
  24. package/dist/asset-usage.js +23 -0
  25. package/dist/audio-cue.js +71 -0
  26. package/dist/audio-encoding.js +32 -0
  27. package/dist/audio-sync-request.js +1 -0
  28. package/dist/audio-sync.js +223 -0
  29. package/dist/audio-trim-range.js +31 -0
  30. package/dist/book-context.js +31 -0
  31. package/dist/book-manager.js +374 -0
  32. package/dist/catalog-gc.js +26 -0
  33. package/dist/chain-executor.js +815 -0
  34. package/dist/chain-generation.js +115 -0
  35. package/dist/chain-plan.js +746 -0
  36. package/dist/chain-step-review.js +60 -0
  37. package/dist/character-name.js +25 -0
  38. package/dist/chat-generation-routing.js +73 -0
  39. package/dist/chat-generation-settings.js +78 -0
  40. package/dist/chat-models.js +44 -0
  41. package/dist/chat-prompt-rewrite.js +67 -0
  42. package/dist/chat-router.js +628 -0
  43. package/dist/chat-view-actions.js +49 -0
  44. package/dist/cli/args.js +242 -0
  45. package/dist/cli/config-command.js +103 -0
  46. package/dist/cli/doctor-command.js +289 -0
  47. package/dist/cli/entry.js +2 -0
  48. package/dist/cli/key-report.js +16 -0
  49. package/dist/cli/main.js +104 -0
  50. package/dist/cli/mcp-command.js +88 -0
  51. package/dist/cli/mcp-relay.js +51 -0
  52. package/dist/cli/open-browser.js +14 -0
  53. package/dist/cli/run-command.js +231 -0
  54. package/dist/cli/serve-command.js +62 -0
  55. package/dist/cli/setup-command.js +60 -0
  56. package/dist/cli/stdio-guard.js +8 -0
  57. package/dist/cold-open-model.js +7 -0
  58. package/dist/cold-open.js +79 -0
  59. package/dist/command-reference.js +95 -0
  60. package/dist/content-store.js +74 -0
  61. package/dist/cost.js +8 -0
  62. package/dist/crop-geometry.js +23 -0
  63. package/dist/dancing-workflow.js +447 -0
  64. package/dist/data-home.js +44 -0
  65. package/dist/diagnostics.js +109 -0
  66. package/dist/dialog-quality.js +34 -0
  67. package/dist/downloads-dest.js +56 -0
  68. package/dist/editing-moments.js +194 -0
  69. package/dist/elevenlabs-audio-extension.js +66 -0
  70. package/dist/engine.js +54 -0
  71. package/dist/env-file.js +91 -0
  72. package/dist/env-secrets.js +148 -0
  73. package/dist/ffmpeg-binary.js +12 -0
  74. package/dist/ffmpeg.js +425 -0
  75. package/dist/ffprobe-binary.js +77 -0
  76. package/dist/forget-asset.js +216 -0
  77. package/dist/frame-utils.js +27 -0
  78. package/dist/generation-credit-error.js +11 -0
  79. package/dist/generation-input-mentions.js +117 -0
  80. package/dist/generation-input-policy.js +68 -0
  81. package/dist/generation-reference-mentions.js +97 -0
  82. package/dist/goal-characters.js +49 -0
  83. package/dist/goal-plan-v3.js +273 -0
  84. package/dist/goal-plan.js +220 -0
  85. package/dist/goal-planner-prompt.js +28 -0
  86. package/dist/goal-prompt-budget.js +4 -0
  87. package/dist/goal-reference-images.js +25 -0
  88. package/dist/goal-state.js +261 -0
  89. package/dist/goal-stills.js +153 -0
  90. package/dist/goal-store.js +144 -0
  91. package/dist/goal-styles.js +144 -0
  92. package/dist/goal-v3-runtime.js +157 -0
  93. package/dist/guides/character_schema_guide.md +70 -0
  94. package/dist/guides/gpt_image_2_5_prompting_guide.md +21 -0
  95. package/dist/guides/grok_imagine_prompting_guide.md +72 -0
  96. package/dist/guides/image_reproduction_guide.md +84 -0
  97. package/dist/guides/kling_3_0_prompting_guide.md +136 -0
  98. package/dist/guides/nano_banana_pro_prompting_guide.md +28 -0
  99. package/dist/guides/seedance_2_prompting_guide.md +172 -0
  100. package/dist/http-access.js +49 -0
  101. package/dist/http-byte-range.js +31 -0
  102. package/dist/hydration.js +56 -0
  103. package/dist/image-compress.js +77 -0
  104. package/dist/image-edit-default.js +1 -0
  105. package/dist/image-edit-models.js +41 -0
  106. package/dist/image-edit-settings.js +11 -0
  107. package/dist/image-region-edit.js +32 -0
  108. package/dist/image-subject-map.js +523 -0
  109. package/dist/image.js +46 -0
  110. package/dist/index.js +12 -0
  111. package/dist/job-deadline.js +60 -0
  112. package/dist/jobs/_alibaba-upload.js +30 -0
  113. package/dist/jobs/_audio-chunks.js +31 -0
  114. package/dist/jobs/_download-into-asset.js +15 -0
  115. package/dist/jobs/_fal-shared.js +22 -0
  116. package/dist/jobs/_fal-upload.js +35 -0
  117. package/dist/jobs/_image-edit-adapter.js +213 -0
  118. package/dist/jobs/_kie-poll.js +119 -0
  119. package/dist/jobs/_kie-upload.js +32 -0
  120. package/dist/jobs/_lease.js +163 -0
  121. package/dist/jobs/_persist-output.js +19 -0
  122. package/dist/jobs/_storage-retry.js +21 -0
  123. package/dist/jobs/_transcribe.js +245 -0
  124. package/dist/jobs/_translate-transcript.js +270 -0
  125. package/dist/jobs/analyze-alive-captions.js +10 -0
  126. package/dist/jobs/analyze-editing-moments.js +104 -0
  127. package/dist/jobs/analyze-jokes.js +143 -0
  128. package/dist/jobs/analyze-keyword-placement.js +155 -0
  129. package/dist/jobs/analyze-music.js +14 -0
  130. package/dist/jobs/analyze-video.js +282 -0
  131. package/dist/jobs/apply-cuts.js +83 -0
  132. package/dist/jobs/apply-sfx.js +107 -0
  133. package/dist/jobs/archive-clip.js +458 -0
  134. package/dist/jobs/assemble-goal.js +98 -0
  135. package/dist/jobs/audio-boost.js +71 -0
  136. package/dist/jobs/audio-mix.js +161 -0
  137. package/dist/jobs/backfill-asset-tags.js +144 -0
  138. package/dist/jobs/burn-watermark.js +316 -0
  139. package/dist/jobs/change-video-speed.js +176 -0
  140. package/dist/jobs/clean-video-audio.js +207 -0
  141. package/dist/jobs/cold-open.js +166 -0
  142. package/dist/jobs/combine-goal.js +108 -0
  143. package/dist/jobs/complete-provider-task.js +185 -0
  144. package/dist/jobs/copy-library-asset.js +33 -0
  145. package/dist/jobs/cost-estimate-assessment.js +113 -0
  146. package/dist/jobs/crop-image.js +47 -0
  147. package/dist/jobs/crop-video.js +71 -0
  148. package/dist/jobs/describe-image.js +181 -0
  149. package/dist/jobs/detect-scenes.js +33 -0
  150. package/dist/jobs/doctor-fix.js +100 -0
  151. package/dist/jobs/download-video.js +427 -0
  152. package/dist/jobs/enqueue-search-index.js +24 -0
  153. package/dist/jobs/even-split.js +122 -0
  154. package/dist/jobs/export-sequence.js +365 -0
  155. package/dist/jobs/extend-audio.js +229 -0
  156. package/dist/jobs/extract-audio.js +72 -0
  157. package/dist/jobs/extract-frame.js +58 -0
  158. package/dist/jobs/extract-notebook-audio.js +82 -0
  159. package/dist/jobs/extract-notebook-frame.js +93 -0
  160. package/dist/jobs/extract-video-clip.js +8 -0
  161. package/dist/jobs/extract-video-poster.js +55 -0
  162. package/dist/jobs/extract-waveform.js +25 -0
  163. package/dist/jobs/finish-goal.js +255 -0
  164. package/dist/jobs/forget-asset.js +11 -0
  165. package/dist/jobs/generate-acestep-cover.js +219 -0
  166. package/dist/jobs/generate-media.js +70 -0
  167. package/dist/jobs/image-subjects.js +1059 -0
  168. package/dist/jobs/index.js +18 -0
  169. package/dist/jobs/inject-first-frame.js +175 -0
  170. package/dist/jobs/isolate-vocals.js +69 -0
  171. package/dist/jobs/kie-paygate.js +177 -0
  172. package/dist/jobs/label-music-sections.js +5 -0
  173. package/dist/jobs/osg-placement.js +135 -0
  174. package/dist/jobs/pad-video.js +72 -0
  175. package/dist/jobs/plan-goal.js +97 -0
  176. package/dist/jobs/post-record.js +21 -0
  177. package/dist/jobs/post-to-x.js +21 -0
  178. package/dist/jobs/prepare-search-reference.js +190 -0
  179. package/dist/jobs/prepare-temporal-search.js +16 -0
  180. package/dist/jobs/process-audio.js +67 -0
  181. package/dist/jobs/process-image.js +58 -0
  182. package/dist/jobs/process-upload.js +166 -0
  183. package/dist/jobs/process-video.js +438 -0
  184. package/dist/jobs/register-all.js +161 -0
  185. package/dist/jobs/relayout-notebook.js +78 -0
  186. package/dist/jobs/remove-sound.js +299 -0
  187. package/dist/jobs/render-project.js +380 -0
  188. package/dist/jobs/render-qr-outro.js +138 -0
  189. package/dist/jobs/render-timeline.js +283 -0
  190. package/dist/jobs/repair-search-indexes.js +42 -0
  191. package/dist/jobs/replace-video-audio.js +64 -0
  192. package/dist/jobs/restore-archive-clip.js +53 -0
  193. package/dist/jobs/reverse-video.js +53 -0
  194. package/dist/jobs/rewrite-script.js +173 -0
  195. package/dist/jobs/run-notebook.js +3335 -0
  196. package/dist/jobs/semantic-index.js +797 -0
  197. package/dist/jobs/smooth-scene-concat.js +257 -0
  198. package/dist/jobs/splice-video.js +280 -0
  199. package/dist/jobs/split-video.js +127 -0
  200. package/dist/jobs/suggest-cuts.js +149 -0
  201. package/dist/jobs/suggest-sfx.js +135 -0
  202. package/dist/jobs/sync-video-to-audio.js +329 -0
  203. package/dist/jobs/tag-asset.js +266 -0
  204. package/dist/jobs/task-failure.js +16 -0
  205. package/dist/jobs/timewarp-video.js +152 -0
  206. package/dist/jobs/transcribe-video-audio.js +117 -0
  207. package/dist/jobs/transcribe.js +35 -0
  208. package/dist/jobs/translate-transcript.js +55 -0
  209. package/dist/jobs/trim-audio.js +133 -0
  210. package/dist/jobs/trim-video.js +291 -0
  211. package/dist/jobs/xai-paygate.js +26 -0
  212. package/dist/keyword-placement-cache.js +74 -0
  213. package/dist/lazy-render.js +142 -0
  214. package/dist/library-browse.js +78 -0
  215. package/dist/lightning-defaults.js +206 -0
  216. package/dist/load-env.js +12 -0
  217. package/dist/local-search-models.js +206 -0
  218. package/dist/local-upload.js +318 -0
  219. package/dist/logger.js +69 -0
  220. package/dist/media/adapters/alibaba.js +20 -0
  221. package/dist/media/adapters/elevenlabs.js +58 -0
  222. package/dist/media/adapters/fal.js +45 -0
  223. package/dist/media/adapters/index.js +26 -0
  224. package/dist/media/adapters/kie.js +120 -0
  225. package/dist/media/adapters/pending.js +21 -0
  226. package/dist/media/adapters/types.js +5 -0
  227. package/dist/media/adapters/xai.js +18 -0
  228. package/dist/media/catalog.js +236 -0
  229. package/dist/media/complete.js +191 -0
  230. package/dist/media/cost.js +45 -0
  231. package/dist/media/descriptor.js +157 -0
  232. package/dist/media/endpoints.js +11 -0
  233. package/dist/media/frame-options.js +18 -0
  234. package/dist/media/insert-video-timing.js +17 -0
  235. package/dist/media/json-schema.js +75 -0
  236. package/dist/media/legacy-task-types.js +129 -0
  237. package/dist/media/listing.js +24 -0
  238. package/dist/media/model-id.js +14 -0
  239. package/dist/media/preview.js +86 -0
  240. package/dist/media/prompt-dialects.js +65 -0
  241. package/dist/media/ref-fields.js +106 -0
  242. package/dist/media/refs.js +131 -0
  243. package/dist/media/request.js +188 -0
  244. package/dist/media/types.js +6 -0
  245. package/dist/media/wire.js +161 -0
  246. package/dist/media-library.js +535 -0
  247. package/dist/media-output-path.js +33 -0
  248. package/dist/multyculty-workflow.js +757 -0
  249. package/dist/notebook-action-catalog.js +608 -0
  250. package/dist/notebook-cell-outputs.js +726 -0
  251. package/dist/notebook-credit-retry.js +45 -0
  252. package/dist/notebook-fixtures.js +8 -0
  253. package/dist/notebook-generation-contract.js +77 -0
  254. package/dist/notebook-generation-controls.js +145 -0
  255. package/dist/notebook-goal-layout.js +40 -0
  256. package/dist/notebook-graph.js +37 -0
  257. package/dist/notebook-image-edit.js +84 -0
  258. package/dist/notebook-initial-generation-prompt.js +222 -0
  259. package/dist/notebook-live-analysis.js +941 -0
  260. package/dist/notebook-media-ops.js +579 -0
  261. package/dist/notebook-mutations.js +20 -0
  262. package/dist/notebook-placement.js +41 -0
  263. package/dist/notebook-provider-failure.js +87 -0
  264. package/dist/notebook-published-post.js +26 -0
  265. package/dist/notebook-reference-resolver.js +227 -0
  266. package/dist/notebook-relayout.js +270 -0
  267. package/dist/notebook-run-plan.js +519 -0
  268. package/dist/notebook-shots.js +162 -0
  269. package/dist/notebook-workflow.js +856 -0
  270. package/dist/one-password.js +200 -0
  271. package/dist/openai-intelligence.js +99 -0
  272. package/dist/original-media-file.js +12 -0
  273. package/dist/osg-assets.js +36 -0
  274. package/dist/osg-placement-cache.js +64 -0
  275. package/dist/osg-settings.js +27 -0
  276. package/dist/paid-tools.js +22 -0
  277. package/dist/platform-guidance.js +26 -0
  278. package/dist/posts/dropbox-auth.js +91 -0
  279. package/dist/posts/dropbox-upload.js +132 -0
  280. package/dist/posts/icloud-drive.js +134 -0
  281. package/dist/posts/icloud-status.js +64 -0
  282. package/dist/posts/post-contract.js +2 -0
  283. package/dist/posts/post-copy-format.js +43 -0
  284. package/dist/posts/post-copy.js +193 -0
  285. package/dist/posts/post-folder.js +18 -0
  286. package/dist/posts/post-metrics.js +119 -0
  287. package/dist/posts/post-store.js +340 -0
  288. package/dist/posts/x-post-target.js +43 -0
  289. package/dist/preview-payload.js +90 -0
  290. package/dist/prompt-surfaces.js +22 -0
  291. package/dist/provider-prompt-transform.js +77 -0
  292. package/dist/provider-status-link.js +21 -0
  293. package/dist/provider-task-record.js +78 -0
  294. package/dist/provider-task-status.js +47 -0
  295. package/dist/provider-usage.js +294 -0
  296. package/dist/queue.js +810 -0
  297. package/dist/reference-video-duration.js +17 -0
  298. package/dist/remotion/BlurFillVideo.js +51 -0
  299. package/dist/remotion/KeywordGraphicsOverlay.js +68 -0
  300. package/dist/remotion/OsgOverlay.js +30 -0
  301. package/dist/remotion/QrOutroComposition.js +47 -0
  302. package/dist/remotion/RenderComposition.js +11 -0
  303. package/dist/remotion/browser.js +59 -0
  304. package/dist/remotion/constants.js +35 -0
  305. package/dist/remotion/index.js +66 -0
  306. package/dist/remotion/keyword-graphics.js +151 -0
  307. package/dist/remotion/keyword-placement.js +182 -0
  308. package/dist/remotion/osg-spec.js +198 -0
  309. package/dist/remotion/public/qr-outro-impact.wav +0 -0
  310. package/dist/remotion/qr-outro-audio.js +1 -0
  311. package/dist/remotion/qr-outro-spec.js +211 -0
  312. package/dist/remotion/shared-bundle.js +37 -0
  313. package/dist/remotion/subtitles/AliveCaptions.js +83 -0
  314. package/dist/remotion/subtitles/Highlight.js +58 -0
  315. package/dist/remotion/subtitles/Impact.js +67 -0
  316. package/dist/remotion/subtitles/SubtitleOverlay.js +9 -0
  317. package/dist/remotion/subtitles/alive-layout.js +321 -0
  318. package/dist/remotion/subtitles/alive-types.js +17 -0
  319. package/dist/remotion/subtitles/chunker.js +68 -0
  320. package/dist/remotion/subtitles/segments.js +42 -0
  321. package/dist/remotion/subtitles/styles.js +4 -0
  322. package/dist/remotion/subtitles/types.js +1 -0
  323. package/dist/remotion/util.js +18 -0
  324. package/dist/remotion-cancellation.js +15 -0
  325. package/dist/rename-helpers.js +204 -0
  326. package/dist/rhythm/analyze.js +128 -0
  327. package/dist/rhythm/contract.js +14 -0
  328. package/dist/rhythm/draft-model.js +19 -0
  329. package/dist/rhythm/energy.js +133 -0
  330. package/dist/rhythm/fft.js +44 -0
  331. package/dist/rhythm/model.js +71 -0
  332. package/dist/rhythm/music-analysis.js +124 -0
  333. package/dist/rhythm/music-draft.js +261 -0
  334. package/dist/rhythm/onset.js +101 -0
  335. package/dist/rhythm/run-worker.js +51 -0
  336. package/dist/rhythm/score.js +47 -0
  337. package/dist/rhythm/section-labels.js +168 -0
  338. package/dist/rhythm/sequence-rhythm.js +67 -0
  339. package/dist/rhythm/synthesize.js +103 -0
  340. package/dist/rhythm/timing.js +98 -0
  341. package/dist/rhythm/track.js +320 -0
  342. package/dist/rhythm/visual-fit.js +4 -0
  343. package/dist/rhythm/worker.js +25 -0
  344. package/dist/scene-detect.js +200 -0
  345. package/dist/scene-transition.js +12 -0
  346. package/dist/scheduled-posts.js +200 -0
  347. package/dist/search-index-coverage.js +13 -0
  348. package/dist/search-index-media.js +71 -0
  349. package/dist/search-reference-store.js +60 -0
  350. package/dist/search-scoring.js +17 -0
  351. package/dist/serve.js +125 -0
  352. package/dist/server-lock.js +143 -0
  353. package/dist/server.js +42 -0
  354. package/dist/srt.js +16 -0
  355. package/dist/tag-backfill-state.js +121 -0
  356. package/dist/tag-settings.js +30 -0
  357. package/dist/tag-vocabulary.js +237 -0
  358. package/dist/test-queue-cleanup.js +1 -0
  359. package/dist/time-parse.js +50 -0
  360. package/dist/timeline-helpers.js +554 -0
  361. package/dist/timewarp.js +87 -0
  362. package/dist/tools/_image-inputs.js +9 -0
  363. package/dist/tools/_media-refs.js +84 -0
  364. package/dist/tools/_shared.js +59 -0
  365. package/dist/tools/_suggest-shared.js +279 -0
  366. package/dist/tools/add-timeline-audio.js +73 -0
  367. package/dist/tools/alive-captions.js +157 -0
  368. package/dist/tools/analyze-jokes.js +56 -0
  369. package/dist/tools/analyze-video.js +66 -0
  370. package/dist/tools/animate-keywords.js +217 -0
  371. package/dist/tools/api-status.js +42 -0
  372. package/dist/tools/application-commands.js +34 -0
  373. package/dist/tools/apply-cuts.js +112 -0
  374. package/dist/tools/apply-sfx.js +152 -0
  375. package/dist/tools/apply-watermark.js +670 -0
  376. package/dist/tools/archive-clip.js +74 -0
  377. package/dist/tools/asset-history.js +61 -0
  378. package/dist/tools/asset-manifest.js +24 -0
  379. package/dist/tools/asset-tags.js +151 -0
  380. package/dist/tools/assign-slot.js +97 -0
  381. package/dist/tools/audio-boost.js +107 -0
  382. package/dist/tools/audio-mix.js +121 -0
  383. package/dist/tools/book-usage.js +16 -0
  384. package/dist/tools/chain.js +136 -0
  385. package/dist/tools/change-video-speed.js +105 -0
  386. package/dist/tools/chat-history.js +5 -0
  387. package/dist/tools/chat.js +848 -0
  388. package/dist/tools/clean-video-audio.js +97 -0
  389. package/dist/tools/clear-failed-tasks.js +66 -0
  390. package/dist/tools/cold-open.js +114 -0
  391. package/dist/tools/command-plan.js +1680 -0
  392. package/dist/tools/commands.js +1666 -0
  393. package/dist/tools/concat-images.js +236 -0
  394. package/dist/tools/connect-service.js +55 -0
  395. package/dist/tools/copy-file-to-clipboard.js +58 -0
  396. package/dist/tools/create-character.js +114 -0
  397. package/dist/tools/create-qart.js +253 -0
  398. package/dist/tools/create-qr-outro.js +149 -0
  399. package/dist/tools/create-qrbtf.js +215 -0
  400. package/dist/tools/create-watermark.js +97 -0
  401. package/dist/tools/crop-asset.js +18 -0
  402. package/dist/tools/crop-image.js +120 -0
  403. package/dist/tools/crop-video.js +97 -0
  404. package/dist/tools/delete-asset.js +104 -0
  405. package/dist/tools/describe-image.js +69 -0
  406. package/dist/tools/detect-scenes.js +44 -0
  407. package/dist/tools/doctor.js +126 -0
  408. package/dist/tools/download-video.js +114 -0
  409. package/dist/tools/duplicate-asset.js +184 -0
  410. package/dist/tools/edit-timeline.js +59 -0
  411. package/dist/tools/editing-moments.js +105 -0
  412. package/dist/tools/estimate-cost.js +171 -0
  413. package/dist/tools/even-split.js +295 -0
  414. package/dist/tools/export-asset-file.js +22 -0
  415. package/dist/tools/export-asset.js +140 -0
  416. package/dist/tools/export-sequence.js +250 -0
  417. package/dist/tools/export-to-downloads.js +54 -0
  418. package/dist/tools/export-transcript-srt.js +79 -0
  419. package/dist/tools/extend-audio.js +100 -0
  420. package/dist/tools/extract-audio.js +174 -0
  421. package/dist/tools/extract-frame.js +108 -0
  422. package/dist/tools/extract-notebook-audio.js +173 -0
  423. package/dist/tools/extract-notebook-frame.js +160 -0
  424. package/dist/tools/extract-video-clip.js +101 -0
  425. package/dist/tools/generate-media.js +157 -0
  426. package/dist/tools/generation-payload.js +48 -0
  427. package/dist/tools/get-timeline.js +12 -0
  428. package/dist/tools/get-watermark-config.js +38 -0
  429. package/dist/tools/goal.js +644 -0
  430. package/dist/tools/image-subjects.js +711 -0
  431. package/dist/tools/index.js +323 -0
  432. package/dist/tools/inject-first-frame.js +154 -0
  433. package/dist/tools/invert-image.js +70 -0
  434. package/dist/tools/isolate-vocals.js +66 -0
  435. package/dist/tools/list-assets.js +98 -0
  436. package/dist/tools/list-cell-generations.js +225 -0
  437. package/dist/tools/list-elevenlabs-voices.js +163 -0
  438. package/dist/tools/log-wrapper.js +29 -0
  439. package/dist/tools/make-cover-song.js +95 -0
  440. package/dist/tools/mark-asset-seen.js +23 -0
  441. package/dist/tools/media-library.js +245 -0
  442. package/dist/tools/move-timeline-audio.js +35 -0
  443. package/dist/tools/music-analysis.js +60 -0
  444. package/dist/tools/music-draft.js +29 -0
  445. package/dist/tools/pad-video.js +87 -0
  446. package/dist/tools/post-record.js +83 -0
  447. package/dist/tools/post.js +168 -0
  448. package/dist/tools/prompt-history.js +164 -0
  449. package/dist/tools/provider-key-status.js +248 -0
  450. package/dist/tools/qrbtf-render.js +849 -0
  451. package/dist/tools/read-asset-metadata.js +26 -0
  452. package/dist/tools/read-file-at-commit.js +29 -0
  453. package/dist/tools/read-transcript.js +41 -0
  454. package/dist/tools/reconcile-project-work.js +1452 -0
  455. package/dist/tools/recover-asset.js +28 -0
  456. package/dist/tools/remove-sound.js +72 -0
  457. package/dist/tools/remove-timeline-audio.js +34 -0
  458. package/dist/tools/remove-timeline-slot.js +40 -0
  459. package/dist/tools/rename-all.js +434 -0
  460. package/dist/tools/rename-asset.js +34 -0
  461. package/dist/tools/render-settings.js +47 -0
  462. package/dist/tools/render-timeline.js +120 -0
  463. package/dist/tools/render.js +384 -0
  464. package/dist/tools/reorder-character-images.js +45 -0
  465. package/dist/tools/replace-video-audio.js +133 -0
  466. package/dist/tools/restore-archive-clips.js +209 -0
  467. package/dist/tools/restore-asset-version.js +37 -0
  468. package/dist/tools/retry-generation.js +54 -0
  469. package/dist/tools/reverse-video.js +74 -0
  470. package/dist/tools/rewrite-prompt.js +388 -0
  471. package/dist/tools/rewrite-script.js +67 -0
  472. package/dist/tools/schema-from-apicity.js +66 -0
  473. package/dist/tools/search-archive.js +150 -0
  474. package/dist/tools/semantic-search.js +1214 -0
  475. package/dist/tools/sequence-rhythm.js +43 -0
  476. package/dist/tools/set-character-metadata.js +84 -0
  477. package/dist/tools/set-env-secret.js +40 -0
  478. package/dist/tools/set-osg.js +143 -0
  479. package/dist/tools/show-folder.js +37 -0
  480. package/dist/tools/smooth-scene-concat.js +122 -0
  481. package/dist/tools/splice-video.js +149 -0
  482. package/dist/tools/split-video.js +202 -0
  483. package/dist/tools/suggest-cuts.js +45 -0
  484. package/dist/tools/suggest-sfx.js +43 -0
  485. package/dist/tools/sync-video-to-audio.js +115 -0
  486. package/dist/tools/tag-backfill.js +177 -0
  487. package/dist/tools/timewarp-video.js +85 -0
  488. package/dist/tools/tool-def.js +1 -0
  489. package/dist/tools/tool-json-schema.js +116 -0
  490. package/dist/tools/transcribe.js +90 -0
  491. package/dist/tools/translate-subtitles.js +59 -0
  492. package/dist/tools/trim-audio.js +139 -0
  493. package/dist/tools/trim-video.js +168 -0
  494. package/dist/tools/update-script.js +25 -0
  495. package/dist/tools/update-timeline-audio.js +40 -0
  496. package/dist/tools/upload-file.js +41 -0
  497. package/dist/tools/verify-x-mentions.js +141 -0
  498. package/dist/tools/video-editing-command-contract.js +291 -0
  499. package/dist/tools/video-source-reference.js +30 -0
  500. package/dist/tools/video-time-input.js +18 -0
  501. package/dist/tools/videobook-domain.js +2021 -0
  502. package/dist/tools/wait-for-asset-ready.js +143 -0
  503. package/dist/tools/workspace-actions.js +54 -0
  504. package/dist/transcript-projection.js +202 -0
  505. package/dist/transcript-srt.js +33 -0
  506. package/dist/upload.js +76 -0
  507. package/dist/usage-providers.js +25 -0
  508. package/dist/video-analysis-projection.js +46 -0
  509. package/dist/video-poster.js +33 -0
  510. package/dist/video-proxy.js +99 -0
  511. package/dist/vision-regions.js +111 -0
  512. package/dist/voice-actions.js +90 -0
  513. package/dist/voice-audio.js +55 -0
  514. package/dist/voice-config.js +122 -0
  515. package/dist/voice-continuation.js +54 -0
  516. package/dist/voice-contract.js +1 -0
  517. package/dist/voice-http.js +145 -0
  518. package/dist/voice-reply-progress.js +55 -0
  519. package/dist/voice-session.js +992 -0
  520. package/dist/watermark-badge.js +259 -0
  521. package/dist/watermark-config.js +111 -0
  522. package/dist/watermark-handle-position.js +55 -0
  523. package/dist/watermark-platform.js +15 -0
  524. package/dist/watermark-timing.js +16 -0
  525. package/dist/workspace-action-catalog.js +315 -0
  526. package/dist/workspace-action-contract.js +21 -0
  527. package/dist/workspace-action-groups.js +98 -0
  528. package/dist/workspace-action-surfaces.js +7 -0
  529. package/dist/x/account.js +88 -0
  530. package/dist/x/authenticated-handle.js +30 -0
  531. package/dist/x/oauth-flow.js +191 -0
  532. package/dist/x/post-contract.js +1 -0
  533. package/dist/x/post-metadata.js +44 -0
  534. package/dist/x/post-to-x.js +456 -0
  535. package/dist/x/published-post-cell.js +90 -0
  536. package/dist/x/x-tokens.js +69 -0
  537. package/dist/ytdlp-binary.js +65 -0
  538. package/dist/zod-slots.js +334 -0
  539. package/package.json +84 -4
  540. package/ui/assets/alive-captions-BWb5nLKj.css +1 -0
  541. package/ui/assets/alive-captions-DPEr1nKM.js +1 -0
  542. package/ui/assets/confirm-bodies-D8ouHXKy.js +1 -0
  543. package/ui/assets/confirm-bodies-DrMhaXC6.css +1 -0
  544. package/ui/assets/fullscreen-B7nw4YbE.js +1 -0
  545. package/ui/assets/goal-ui-DK-xTCK0.js +2 -0
  546. package/ui/assets/goal-ui-DXdUM2KF.css +1 -0
  547. package/ui/assets/index-DzPAnCUj.css +1 -0
  548. package/ui/assets/index-kS1905s5.js +37 -0
  549. package/ui/assets/inspector-MlzQbTVo.js +1 -0
  550. package/ui/assets/jobs-ui-CoinOWx0.js +1 -0
  551. package/ui/assets/music-video-Cbe80kmQ.js +1 -0
  552. package/ui/assets/music-video-DPaiKKEq.css +1 -0
  553. package/ui/assets/ui-Oirr9VOf.css +1 -0
  554. package/ui/assets/ui-xo_Y1yQg.js +2 -0
  555. package/ui/assets/vendor-D2rfiZ5j.css +1 -0
  556. package/ui/assets/vendor-DfTCWgKm.js +19 -0
  557. package/ui/assets/vendor-flow-BXbeWxW7.js +7 -0
  558. package/ui/assets/vendor-flow-BnuhLJ6X.css +1 -0
  559. package/ui/assets/vendor-icons-CGqhCIXV.js +1 -0
  560. package/ui/assets/vendor-mcp-BTlEJcHV.js +67 -0
  561. package/ui/assets/vendor-react-Dj7D1fD4.js +9 -0
  562. package/ui/assets/vendor-remotion-CH1HXrlc.js +95 -0
  563. package/ui/assets/videobook-Qlp9HQFu.js +20 -0
  564. package/ui/favicon.svg +8 -0
  565. package/ui/index.html +126 -0
@@ -0,0 +1,47 @@
1
+ import { z } from "zod";
2
+ import { readLanguageCode, iso1FromIso3 } from "../jobs/_translate-transcript.js";
3
+ import { readRenderSettings, RENDER_ORIENTATIONS } from "./reconcile-project-work.js";
4
+ const TRANSLATED_SIDECAR_RE = /^elevenlabs\.([a-z]{2})\.json$/;
5
+ async function readTranscriptInfo(engine, assetId) {
6
+ const info = { sourceLanguageCode: null, sourceLanguage: null, cachedTranslations: [] };
7
+ const manifestResult = await engine.files.manifest(assetId);
8
+ if (manifestResult.ok) {
9
+ for (const file of manifestResult.value.files) {
10
+ const match = TRANSLATED_SIDECAR_RE.exec(file.name);
11
+ if (match)
12
+ info.cachedTranslations.push(match[1]);
13
+ }
14
+ }
15
+ const transcriptResult = await engine.files.read(assetId, "elevenlabs.json");
16
+ if (transcriptResult.ok) {
17
+ try {
18
+ const raw = JSON.parse(transcriptResult.value.toString());
19
+ info.sourceLanguageCode = readLanguageCode(raw);
20
+ info.sourceLanguage = iso1FromIso3(info.sourceLanguageCode);
21
+ }
22
+ catch {
23
+ // unreadable transcript — leave language unknown
24
+ }
25
+ }
26
+ return info;
27
+ }
28
+ export function getRenderSettingsDef(engine) {
29
+ return {
30
+ name: "get_render_settings",
31
+ description: "Fetch stored render settings for a single asset across all orientations, plus transcript language info (detected source language and cached subtitle translations)",
32
+ schema: {
33
+ asset_id: z.string().describe("Asset ID (e.g. vid-running-dog)"),
34
+ book_id: z.string().describe("Book id"),
35
+ },
36
+ handler: async (args) => {
37
+ const { asset_id } = args;
38
+ const [settings, transcript] = await Promise.all([
39
+ readRenderSettings(engine, asset_id, [...RENDER_ORIENTATIONS]),
40
+ readTranscriptInfo(engine, asset_id),
41
+ ]);
42
+ return {
43
+ content: [{ type: "text", text: JSON.stringify({ settings, transcript }) }],
44
+ };
45
+ },
46
+ };
47
+ }
@@ -0,0 +1,120 @@
1
+ import { join } from "node:path";
2
+ import { findAudioFile } from "videobook-engine";
3
+ import { z } from "zod";
4
+ import { queue } from "../queue.js";
5
+ import { readTimeline } from "../timeline-helpers.js";
6
+ const TIMELINE_FPS = 30;
7
+ const RENDER_FILENAMES = {
8
+ landscape: "landscape.mp4",
9
+ portrait: "portrait.mp4",
10
+ square: "square.mp4",
11
+ };
12
+ async function runRenderTimeline(engine, bookId, orientationOverride) {
13
+ const timeline = readTimeline(engine);
14
+ const orientation = orientationOverride ?? timeline.render;
15
+ if (timeline.slots.length === 0) {
16
+ return { status: "empty", message: "Timeline has no assigned artifacts." };
17
+ }
18
+ const renderFilename = RENDER_FILENAMES[orientation];
19
+ const resolvedSlots = [];
20
+ const missingArtifactIds = [];
21
+ for (const slot of timeline.slots) {
22
+ const artifact = engine.artifacts.get(slot.artifactId);
23
+ if (!artifact.ok)
24
+ return { status: "error", message: artifact.error.message };
25
+ const manifest = await engine.files.manifest(artifact.value.artifactId);
26
+ if (!manifest.ok)
27
+ return { status: "error", message: manifest.error.message };
28
+ if (!manifest.value.files.some((file) => file.name === renderFilename)) {
29
+ missingArtifactIds.push(artifact.value.artifactId);
30
+ continue;
31
+ }
32
+ resolvedSlots.push({
33
+ artifactId: artifact.value.artifactId,
34
+ volume: slot.volume ?? 100,
35
+ fadeIn: slot.audioFadeIn ?? 0,
36
+ fadeOut: slot.audioFadeOut ?? 0,
37
+ assetDir: manifest.value.path,
38
+ });
39
+ }
40
+ if (missingArtifactIds.length > 0) {
41
+ return {
42
+ status: "missing_renders",
43
+ orientation,
44
+ missingArtifactIds,
45
+ message: `Missing ${renderFilename} for ${missingArtifactIds.join(", ")}`,
46
+ };
47
+ }
48
+ const resolvedAudio = [];
49
+ for (const clip of timeline.audio) {
50
+ const artifact = engine.artifacts.get(clip.artifactId);
51
+ if (!artifact.ok)
52
+ return { status: "error", message: artifact.error.message };
53
+ const manifest = await engine.files.manifest(artifact.value.artifactId);
54
+ if (!manifest.ok)
55
+ return { status: "error", message: manifest.error.message };
56
+ const audioFile = findAudioFile(manifest.value.files, artifact.value.kind);
57
+ if (!audioFile)
58
+ return { status: "error", message: `No audio file found in ${artifact.value.artifactId}` };
59
+ resolvedAudio.push({
60
+ id: clip.id,
61
+ artifactId: artifact.value.artifactId,
62
+ audioPath: join(manifest.value.path, audioFile.name),
63
+ startMs: Math.round((clip.startFrame / TIMELINE_FPS) * 1000),
64
+ durationMs: Math.round((clip.durationFrames / TIMELINE_FPS) * 1000),
65
+ volume: (clip.volume ?? 100) / 100,
66
+ fadeInMs: clip.fadeIn ?? 0,
67
+ fadeOutMs: clip.fadeOut ?? 0,
68
+ });
69
+ }
70
+ const jobId = `render-timeline-${bookId}-${Date.now()}`;
71
+ await queue.enqueue(jobId, {
72
+ type: "render_timeline",
73
+ bookId,
74
+ orientation,
75
+ resolvedSlots,
76
+ resolvedAudio,
77
+ });
78
+ return { status: "enqueued", jobId, slotCount: resolvedSlots.length, orientation };
79
+ }
80
+ export function renderTimelineDef(engine) {
81
+ return {
82
+ name: "render_timeline",
83
+ description: "Render the selected book's typed v2 timeline",
84
+ schema: {
85
+ book_id: z.string().uuid(),
86
+ orientation: z.enum(["landscape", "portrait", "square"]).optional(),
87
+ },
88
+ handler: async (args) => {
89
+ const outcome = await runRenderTimeline(engine, String(args.book_id), args.orientation);
90
+ if (outcome.status === "error" || outcome.status === "empty") {
91
+ return { isError: true, content: [{ type: "text", text: outcome.message }] };
92
+ }
93
+ if (outcome.status === "missing_renders") {
94
+ return {
95
+ isError: true,
96
+ content: [{
97
+ type: "text",
98
+ text: JSON.stringify({
99
+ status: outcome.status,
100
+ orientation: outcome.orientation,
101
+ missing_artifact_ids: outcome.missingArtifactIds,
102
+ message: outcome.message,
103
+ }),
104
+ }],
105
+ };
106
+ }
107
+ return {
108
+ content: [{
109
+ type: "text",
110
+ text: JSON.stringify({
111
+ status: outcome.status,
112
+ job_id: outcome.jobId,
113
+ slot_count: outcome.slotCount,
114
+ orientation: outcome.orientation,
115
+ }),
116
+ }],
117
+ };
118
+ },
119
+ };
120
+ }
@@ -0,0 +1,384 @@
1
+ import { z } from "zod";
2
+ import { join } from "node:path";
3
+ import { logger } from "../logger.js";
4
+ import { queue } from "../queue.js";
5
+ import { findVideoFile } from "videobook-engine";
6
+ import { isValidHex } from "../remotion/util.js";
7
+ import { requireArtifactKind } from "./_shared.js";
8
+ import { isOsgPosition, isOsgSize, resolveOsgImagePath } from "../osg-assets.js";
9
+ import { readTimeline, timelineInput, writeTimeline, } from "../timeline-helpers.js";
10
+ import { DEFAULT_KEYWORD_GRAPHICS_LINGER_SECONDS, DEFAULT_KEYWORD_GRAPHICS_MAX_COUNT, } from "../remotion/keyword-graphics.js";
11
+ export function renderVideoDef(engine) {
12
+ return {
13
+ name: "render_video",
14
+ description: "Render a video artifact into landscape.mp4, portrait.mp4, or square.mp4. Renders all 3 orientations when orientation is omitted.",
15
+ schema: {
16
+ asset_id: z.string().describe("Video artifact id"),
17
+ book_id: z.string().describe("Book id"),
18
+ orientation: z
19
+ .enum(["landscape", "portrait", "square"])
20
+ .optional()
21
+ .describe("Render one orientation; omit to render all three orientations"),
22
+ subtitles_enabled: z
23
+ .boolean()
24
+ .optional()
25
+ .describe("Enable subtitle overlay (default: false)"),
26
+ subtitle_language: z
27
+ .string()
28
+ .regex(/^(original|[a-z]{2})$/)
29
+ .optional()
30
+ .describe("Subtitle language: 'original' or a two-letter ISO 639-1 code (e.g. 'en'). Lazily translates and caches elevenlabs.<lang>.json (default: original)"),
31
+ highlight_style: z
32
+ .enum(["impact", "highlight", "alive"])
33
+ .optional()
34
+ .describe("Subtitle animation style (default: impact). alive renders stacked, frame-aware, audio-timed captions configured by set_alive_captions"),
35
+ position: z
36
+ .enum(["top", "middle", "bottom"])
37
+ .optional()
38
+ .describe("Subtitle vertical position (default: bottom)"),
39
+ font_size: z
40
+ .enum(["small", "medium", "large"])
41
+ .optional()
42
+ .describe("Subtitle font size (default: medium)"),
43
+ text_color: z
44
+ .string()
45
+ .optional()
46
+ .describe("Subtitle text color hex (default: #F8FAFC)"),
47
+ highlight_color: z
48
+ .string()
49
+ .optional()
50
+ .describe("Active word box color hex for highlight style (default: #991B1B)"),
51
+ audio_fade_in: z
52
+ .number()
53
+ .optional()
54
+ .describe("Audio fade-in in ms (default: 0)"),
55
+ audio_fade_out: z
56
+ .number()
57
+ .optional()
58
+ .describe("Audio fade-out in ms (default: 0)"),
59
+ audio_volume: z
60
+ .number()
61
+ .optional()
62
+ .describe("Audio volume percent 0-100 (default: 100)"),
63
+ osg_asset_id: z
64
+ .string()
65
+ .optional()
66
+ .describe("Image asset ID (img- prefix) for on-screen graphic overlay"),
67
+ osg_position: z
68
+ .enum([
69
+ "top-left", "top-center", "top-right",
70
+ "middle-left", "middle-center", "middle-right",
71
+ "bottom-left", "bottom-center", "bottom-right",
72
+ ])
73
+ .optional()
74
+ .describe("OSG position (default: top-right)"),
75
+ osg_size: z
76
+ .enum(["small", "medium", "large"])
77
+ .optional()
78
+ .describe("OSG size preset; small ≈ half (default: medium)"),
79
+ osg_scale: z
80
+ .number()
81
+ .min(0.1)
82
+ .max(2)
83
+ .optional()
84
+ .describe("OSG size multiplier applied on top of osg_size (default: 1)"),
85
+ osg_opacity: z
86
+ .number()
87
+ .min(0.05)
88
+ .max(1)
89
+ .optional()
90
+ .describe("OSG opacity 0.05-1; the floating watermark uses 0.5 (default: 1)"),
91
+ osg_start_seconds: z
92
+ .number()
93
+ .min(0)
94
+ .nullable()
95
+ .optional()
96
+ .describe("OSG visibility window start in seconds; null/omit ⇒ from clip start"),
97
+ osg_end_seconds: z
98
+ .number()
99
+ .min(0)
100
+ .nullable()
101
+ .optional()
102
+ .describe("OSG visibility window end in seconds; null/omit ⇒ to clip end"),
103
+ keyword_graphics_enabled: z
104
+ .boolean()
105
+ .optional()
106
+ .describe("Enable transcript-timed cinematic keyword graphics (default: stored setting or false)"),
107
+ keyword_graphics_max_count: z
108
+ .number()
109
+ .int()
110
+ .min(1)
111
+ .max(20)
112
+ .optional()
113
+ .describe("Maximum automatic keyword graphics (default: stored setting or 8)"),
114
+ keyword_graphics_linger_seconds: z
115
+ .number()
116
+ .min(0.8)
117
+ .max(5)
118
+ .optional()
119
+ .describe("How long each keyword lingers after it is spoken (default: stored setting or 2.4 seconds)"),
120
+ keyword_graphics_keywords: z
121
+ .array(z.string().min(1))
122
+ .max(20)
123
+ .nullable()
124
+ .optional()
125
+ .describe("Exact transcript keywords to animate; null selects keywords automatically"),
126
+ background_mode: z
127
+ .enum(["blur", "black"])
128
+ .optional()
129
+ .describe("Background fill mode (default: blur)"),
130
+ stroke_width: z
131
+ .number()
132
+ .optional()
133
+ .describe("Subtitle stroke width in px, 0-20 (default: 6)"),
134
+ stroke_color: z
135
+ .string()
136
+ .optional()
137
+ .describe("Subtitle stroke color hex (default: #000000)"),
138
+ shadow_blur: z
139
+ .number()
140
+ .optional()
141
+ .describe("Subtitle shadow blur in px, 0-30 (default: 6)"),
142
+ shadow_opacity: z
143
+ .number()
144
+ .optional()
145
+ .describe("Subtitle shadow opacity 0-100 (default: 60)"),
146
+ },
147
+ handler: async (args) => {
148
+ const { asset_id, book_id, orientation: explicitOrientation, subtitles_enabled, subtitle_language = "original", highlight_style = "impact", position = "bottom", font_size = "medium", text_color = "#F8FAFC", highlight_color = "#991B1B", audio_fade_in = 0, audio_fade_out = 0, audio_volume = 100, osg_asset_id, osg_position, osg_size, osg_scale, osg_opacity, osg_start_seconds, osg_end_seconds, keyword_graphics_enabled, keyword_graphics_max_count, keyword_graphics_linger_seconds, keyword_graphics_keywords, background_mode = "blur", stroke_width = 6, stroke_color = "#000000", shadow_blur = 6, shadow_opacity = 60, } = args;
149
+ const kindGate = await requireArtifactKind(engine, asset_id, ["video"], "render_video");
150
+ if (kindGate)
151
+ return kindGate;
152
+ if (text_color && !isValidHex(text_color)) {
153
+ return {
154
+ isError: true,
155
+ content: [{ type: "text", text: `Invalid text_color hex: ${text_color}` }],
156
+ };
157
+ }
158
+ if (highlight_color && !isValidHex(highlight_color)) {
159
+ return {
160
+ isError: true,
161
+ content: [{ type: "text", text: `Invalid highlight_color hex: ${highlight_color}` }],
162
+ };
163
+ }
164
+ if (audio_volume < 0 || audio_volume > 100) {
165
+ return {
166
+ isError: true,
167
+ content: [{ type: "text", text: "audio_volume must be 0-100" }],
168
+ };
169
+ }
170
+ if (audio_fade_in < 0 || audio_fade_out < 0) {
171
+ return {
172
+ isError: true,
173
+ content: [{ type: "text", text: "audio fade values must be >= 0" }],
174
+ };
175
+ }
176
+ if (stroke_width < 0 || stroke_width > 20) {
177
+ return {
178
+ isError: true,
179
+ content: [{ type: "text", text: "stroke_width must be 0-20" }],
180
+ };
181
+ }
182
+ if (stroke_color && !isValidHex(stroke_color)) {
183
+ return {
184
+ isError: true,
185
+ content: [{ type: "text", text: `Invalid stroke_color hex: ${stroke_color}` }],
186
+ };
187
+ }
188
+ if (shadow_blur < 0 || shadow_blur > 30) {
189
+ return {
190
+ isError: true,
191
+ content: [{ type: "text", text: "shadow_blur must be 0-30" }],
192
+ };
193
+ }
194
+ if (shadow_opacity < 0 || shadow_opacity > 100) {
195
+ return {
196
+ isError: true,
197
+ content: [{ type: "text", text: "shadow_opacity must be 0-100" }],
198
+ };
199
+ }
200
+ // Determine orientations to render
201
+ const ALL_ORIENTATIONS = ["landscape", "portrait", "square"];
202
+ const orientations = explicitOrientation
203
+ ? [explicitOrientation]
204
+ : ALL_ORIENTATIONS;
205
+ const manifestResult = await engine.files.manifest(asset_id);
206
+ if (!manifestResult.ok) {
207
+ return {
208
+ isError: true,
209
+ content: [{ type: "text", text: manifestResult.error.message }],
210
+ };
211
+ }
212
+ const manifest = manifestResult.value;
213
+ const videoFile = findVideoFile(manifest.files);
214
+ if (!videoFile) {
215
+ return {
216
+ isError: true,
217
+ content: [{ type: "text", text: `No video file found in asset ${asset_id}` }],
218
+ };
219
+ }
220
+ const subsEnabled = subtitles_enabled ?? false;
221
+ // Resolve an explicitly passed OSG image up front so a bad reference
222
+ // fails the tool call. Stored per-orientation OSG settings are resolved
223
+ // inside the orientation loop instead (a stale stored id skips the OSG
224
+ // rather than failing the render).
225
+ let osgImagePath = null;
226
+ if (osg_asset_id) {
227
+ const osgGate = await requireArtifactKind(engine, osg_asset_id, ["image"], "osg_asset_id");
228
+ if (osgGate)
229
+ return osgGate;
230
+ const resolved = await resolveOsgImagePath(engine, osg_asset_id);
231
+ if (!resolved.ok) {
232
+ return {
233
+ isError: true,
234
+ content: [{ type: "text", text: resolved.error }],
235
+ };
236
+ }
237
+ osgImagePath = resolved.path;
238
+ }
239
+ const storedOsgPathCache = new Map();
240
+ // A previously failed job leaves the assets row at status='error',
241
+ // which beginAssetWork refuses to lease — and enqueue never resets it.
242
+ // An explicit render is a retry: clear the stale failure first so the
243
+ // new jobs can claim the asset.
244
+ const clearResult = await engine.jobs.failures.clear(asset_id);
245
+ if (!clearResult.ok) {
246
+ logger.warn("generation-error-clear-failed", { assetId: asset_id, error: clearResult.error });
247
+ }
248
+ const assetDir = manifest.path;
249
+ const videoPath = join(assetDir, videoFile.name);
250
+ const jobs = [];
251
+ for (const orientation of orientations) {
252
+ const storedResult = await engine.metadata.artifacts.read(asset_id, `${orientation}.render`);
253
+ const stored = storedResult.ok ? storedResult.value : {};
254
+ const storedMaxCount = stored.keywordGraphicsMaxCount;
255
+ const storedLingerSeconds = stored.keywordGraphicsLingerSeconds;
256
+ const storedKeywords = stored.keywordGraphicsKeywords;
257
+ const keywordGraphicsEnabled = keyword_graphics_enabled
258
+ ?? stored.keywordGraphicsEnabled === true;
259
+ const keywordGraphicsMaxCount = keyword_graphics_max_count
260
+ ?? (typeof storedMaxCount === "number" && Number.isInteger(storedMaxCount) && storedMaxCount >= 1 && storedMaxCount <= 20
261
+ ? storedMaxCount
262
+ : DEFAULT_KEYWORD_GRAPHICS_MAX_COUNT);
263
+ const keywordGraphicsLingerSeconds = keyword_graphics_linger_seconds
264
+ ?? (typeof storedLingerSeconds === "number" && storedLingerSeconds >= 0.8 && storedLingerSeconds <= 5
265
+ ? storedLingerSeconds
266
+ : DEFAULT_KEYWORD_GRAPHICS_LINGER_SECONDS);
267
+ const keywordGraphicsKeywords = keyword_graphics_keywords !== undefined
268
+ ? keyword_graphics_keywords
269
+ : Array.isArray(storedKeywords) && storedKeywords.every((keyword) => typeof keyword === "string")
270
+ ? storedKeywords
271
+ : null;
272
+ // Explicit OSG args win; else fall back to the settings set_video_overlay
273
+ // stored on this orientation (position may differ per orientation — the
274
+ // frame-aware watermark picks a corner for each).
275
+ const storedOsgAssetId = typeof stored.osgAssetId === "string" && stored.osgAssetId.length > 0
276
+ ? stored.osgAssetId
277
+ : null;
278
+ let orientOsgAssetId = osg_asset_id ?? storedOsgAssetId;
279
+ let orientOsgImagePath = osgImagePath;
280
+ if (!osg_asset_id && storedOsgAssetId) {
281
+ if (!storedOsgPathCache.has(storedOsgAssetId)) {
282
+ const resolved = await resolveOsgImagePath(engine, storedOsgAssetId);
283
+ if (!resolved.ok) {
284
+ logger.warn("overlay-skipped", { assetId: storedOsgAssetId, error: resolved.error });
285
+ }
286
+ storedOsgPathCache.set(storedOsgAssetId, resolved.ok ? resolved.path : null);
287
+ }
288
+ orientOsgImagePath = storedOsgPathCache.get(storedOsgAssetId) ?? null;
289
+ if (orientOsgImagePath === null)
290
+ orientOsgAssetId = null;
291
+ }
292
+ const osgPosition = osg_position
293
+ ?? (isOsgPosition(stored.osgPosition) ? stored.osgPosition : "top-right");
294
+ const osgSize = osg_size
295
+ ?? (isOsgSize(stored.osgSize) ? stored.osgSize : "medium");
296
+ const osgScale = osg_scale
297
+ ?? (typeof stored.osgScale === "number" && Number.isFinite(stored.osgScale) ? stored.osgScale : 1);
298
+ const osgOpacity = osg_opacity
299
+ ?? (typeof stored.osgOpacity === "number" && stored.osgOpacity >= 0.05 && stored.osgOpacity <= 1
300
+ ? stored.osgOpacity
301
+ : 1);
302
+ const osgStartSeconds = osg_start_seconds !== undefined
303
+ ? osg_start_seconds
304
+ : typeof stored.osgStartSeconds === "number" ? stored.osgStartSeconds : null;
305
+ const osgEndSeconds = osg_end_seconds !== undefined
306
+ ? osg_end_seconds
307
+ : typeof stored.osgEndSeconds === "number" ? stored.osgEndSeconds : null;
308
+ const osgTailSeconds = typeof stored.osgTailSeconds === "number" ? stored.osgTailSeconds : null;
309
+ const jobId = `render-${asset_id}-${orientation}-${Date.now()}`;
310
+ await queue.enqueue(jobId, {
311
+ type: "render_project",
312
+ bookId: book_id,
313
+ assetId: asset_id,
314
+ orientation,
315
+ assets: [{
316
+ assetId: asset_id,
317
+ assetDir,
318
+ videoPath,
319
+ videoFilename: videoFile.name,
320
+ }],
321
+ subtitlesEnabled: subsEnabled,
322
+ subtitleLanguage: subtitle_language,
323
+ highlightStyle: highlight_style,
324
+ position,
325
+ fontSize: font_size,
326
+ textColor: text_color,
327
+ highlightColor: highlight_color,
328
+ audioFadeIn: audio_fade_in,
329
+ audioFadeOut: audio_fade_out,
330
+ audioVolume: audio_volume,
331
+ osgImagePath: orientOsgImagePath,
332
+ osgAssetId: orientOsgAssetId ?? null,
333
+ osgPosition,
334
+ osgSize,
335
+ osgScale,
336
+ osgOpacity,
337
+ osgStartSeconds,
338
+ osgEndSeconds,
339
+ osgTailSeconds,
340
+ keywordGraphicsEnabled,
341
+ keywordGraphicsMaxCount,
342
+ keywordGraphicsLingerSeconds,
343
+ keywordGraphicsKeywords,
344
+ backgroundMode: background_mode,
345
+ strokeWidth: stroke_width,
346
+ strokeColor: stroke_color,
347
+ shadowBlur: shadow_blur,
348
+ shadowOpacity: shadow_opacity,
349
+ });
350
+ jobs.push({ job_id: jobId, orientation });
351
+ }
352
+ // Persist the requested orientation as the project's timeline render
353
+ // orientation so /final and the viewer agree on what to produce.
354
+ let savedOrientation;
355
+ if (explicitOrientation) {
356
+ try {
357
+ const timeline = readTimeline(engine);
358
+ const updated = await writeTimeline(engine, {
359
+ ...timelineInput(timeline),
360
+ render: explicitOrientation,
361
+ });
362
+ if (!updated.ok)
363
+ throw new Error(updated.error.message);
364
+ savedOrientation = explicitOrientation;
365
+ }
366
+ catch (err) {
367
+ logger.warn("timeline-orientation-failed", { error: err });
368
+ }
369
+ }
370
+ return {
371
+ content: [{
372
+ type: "text",
373
+ text: JSON.stringify({
374
+ asset_id,
375
+ orientations: orientations,
376
+ jobs,
377
+ ...(savedOrientation !== undefined && { timeline_render: savedOrientation }),
378
+ status: "enqueued",
379
+ }),
380
+ }],
381
+ };
382
+ },
383
+ };
384
+ }
@@ -0,0 +1,45 @@
1
+ import { z } from "zod";
2
+ import { extname } from "node:path";
3
+ export function reorderCharacterImagesDef(engine) {
4
+ return {
5
+ name: "reorder_character_images",
6
+ description: "Reorder images in a character folder by renaming them sequentially (source-01, source-02, etc).",
7
+ schema: {
8
+ book_id: z.string().describe("Book id"),
9
+ asset_id: z.string().describe("Character asset ID"),
10
+ ordered_filenames: z.array(z.string()).describe("List of image filenames in the desired new order"),
11
+ },
12
+ handler: async (payload) => {
13
+ const { book_id: _book_id, asset_id, ordered_filenames } = payload;
14
+ // Step 1: Rename all to temporary names to avoid collisions
15
+ const tempNames = [];
16
+ for (let i = 0; i < ordered_filenames.length; i++) {
17
+ const oldName = ordered_filenames[i];
18
+ const ext = extname(oldName).replace(/^\./, "") || "jpg";
19
+ const tempName = `.tmp-reorder-${Date.now()}-${i}.${ext}`;
20
+ const newName = `source-${String(i + 1).padStart(2, '0')}.${ext}`;
21
+ const renameRes = await engine.files.rename(asset_id, oldName, tempName);
22
+ if (!renameRes.ok) {
23
+ return {
24
+ isError: true,
25
+ content: [{ type: "text", text: `Failed to rename ${oldName} to temp name: ${renameRes.error.message}` }],
26
+ };
27
+ }
28
+ tempNames.push({ oldName, tempName, newName });
29
+ }
30
+ // Step 2: Rename from temp names to final sequence names
31
+ for (const mapping of tempNames) {
32
+ const renameRes = await engine.files.rename(asset_id, mapping.tempName, mapping.newName);
33
+ if (!renameRes.ok) {
34
+ return {
35
+ isError: true,
36
+ content: [{ type: "text", text: `Failed to rename temp file to ${mapping.newName}: ${renameRes.error.message}` }],
37
+ };
38
+ }
39
+ }
40
+ return {
41
+ content: [{ type: "text", text: "Successfully reordered character images." }],
42
+ };
43
+ },
44
+ };
45
+ }