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,116 @@
1
+ import { z } from "zod";
2
+ const ADVERTISED_SCHEMAS = new WeakMap();
3
+ const SUBSCHEMA_MAPS = new Set([
4
+ "properties",
5
+ "patternProperties",
6
+ "$defs",
7
+ "definitions",
8
+ "dependentSchemas",
9
+ ]);
10
+ const SUBSCHEMA_LISTS = new Set(["anyOf", "oneOf", "allOf", "prefixItems"]);
11
+ const SUBSCHEMAS = new Set([
12
+ "items",
13
+ "additionalProperties",
14
+ "propertyNames",
15
+ "not",
16
+ "contains",
17
+ "if",
18
+ "then",
19
+ "else",
20
+ "unevaluatedItems",
21
+ "unevaluatedProperties",
22
+ ]);
23
+ function isSchemaObject(value) {
24
+ return typeof value === "object" && value !== null && !Array.isArray(value);
25
+ }
26
+ function isPlainStringSchema(value) {
27
+ return isSchemaObject(value)
28
+ && Object.keys(value).length === 1
29
+ && value.type === "string";
30
+ }
31
+ function isRedundantKeyword(key, value, schema) {
32
+ switch (key) {
33
+ case "$schema":
34
+ return true;
35
+ case "pattern":
36
+ return typeof schema.format === "string";
37
+ case "propertyNames":
38
+ return isPlainStringSchema(value);
39
+ case "maximum":
40
+ return value === Number.MAX_SAFE_INTEGER;
41
+ case "minimum":
42
+ return value === Number.MIN_SAFE_INTEGER;
43
+ default:
44
+ return false;
45
+ }
46
+ }
47
+ function compactSubschemaMap(value) {
48
+ if (!isSchemaObject(value))
49
+ return value;
50
+ return Object.fromEntries(Object.entries(value).map(([name, child]) => [name, compactNode(child)]));
51
+ }
52
+ function compactKeyword(key, value) {
53
+ if (SUBSCHEMA_MAPS.has(key))
54
+ return compactSubschemaMap(value);
55
+ if (SUBSCHEMA_LISTS.has(key) && Array.isArray(value))
56
+ return value.map(compactNode);
57
+ if (!SUBSCHEMAS.has(key))
58
+ return value;
59
+ return Array.isArray(value) ? value.map(compactNode) : compactNode(value);
60
+ }
61
+ function compactNode(value) {
62
+ if (!isSchemaObject(value))
63
+ return value;
64
+ return Object.fromEntries(Object.entries(value)
65
+ .filter(([key, child]) => !isRedundantKeyword(key, child, value))
66
+ .map(([key, child]) => [key, compactKeyword(key, child)]));
67
+ }
68
+ export function advertisedAs(schema, advertised) {
69
+ ADVERTISED_SCHEMAS.set(schema, advertised);
70
+ return schema;
71
+ }
72
+ const advertise = ({ zodSchema, jsonSchema }) => {
73
+ const advertised = ADVERTISED_SCHEMAS.get(zodSchema);
74
+ if (!advertised)
75
+ return;
76
+ const target = jsonSchema;
77
+ for (const key of Object.keys(target))
78
+ delete target[key];
79
+ Object.assign(target, advertised);
80
+ };
81
+ export function compactJsonSchema(schema) {
82
+ return compactNode(schema);
83
+ }
84
+ export function toolInputJsonSchema(shape, unrepresentable = "throw") {
85
+ return compactJsonSchema(z.toJSONSchema(z.object(shape), {
86
+ io: "input",
87
+ unrepresentable,
88
+ override: advertise,
89
+ }));
90
+ }
91
+ export function toolInputSchema(shape) {
92
+ const standard = z.object(shape)["~standard"];
93
+ const advertisedInputs = new Map();
94
+ return {
95
+ "~standard": {
96
+ version: standard.version,
97
+ vendor: standard.vendor,
98
+ validate: standard.validate,
99
+ jsonSchema: {
100
+ input: (options) => {
101
+ const cached = options.libraryOptions ? undefined : advertisedInputs.get(options.target);
102
+ if (cached)
103
+ return cached;
104
+ const advertised = compactJsonSchema(standard.jsonSchema.input({
105
+ ...options,
106
+ libraryOptions: { ...options.libraryOptions, override: advertise },
107
+ }));
108
+ if (!options.libraryOptions)
109
+ advertisedInputs.set(options.target, advertised);
110
+ return advertised;
111
+ },
112
+ output: (options) => compactJsonSchema(standard.jsonSchema.output(options)),
113
+ },
114
+ },
115
+ };
116
+ }
@@ -0,0 +1,90 @@
1
+ import { z } from "zod";
2
+ import { join } from "node:path";
3
+ import { queue } from "../queue.js";
4
+ import { findAudioFile, findVideoFile } from "videobook-engine";
5
+ function error(text) {
6
+ return { isError: true, content: [{ type: "text", text }] };
7
+ }
8
+ export function transcribeDef(engine) {
9
+ return {
10
+ name: "transcribe",
11
+ description: "Transcribe a video or audio asset: word-timed ElevenLabs speech-to-text when available, otherwise OpenAI speech-to-text.",
12
+ schema: {
13
+ asset_id: z.string().describe("Video or audio artifact id"),
14
+ book_id: z.string().describe("Book UUID"),
15
+ language_code: z.string().optional().describe("Language code (e.g. 'en'). Auto-detected if omitted."),
16
+ tag_audio_events: z.boolean().optional().default(true).describe("Tag audio events like laughter, music, etc."),
17
+ diarize: z.boolean().optional().default(true).describe("Enable speaker diarization"),
18
+ wait: z.boolean().optional().default(false).describe("Wait for the transcription job to finish before returning (default: return as soon as the job is enqueued)"),
19
+ },
20
+ handler: async (args) => {
21
+ const { asset_id, book_id, language_code, tag_audio_events, diarize, wait } = args;
22
+ const hasElevenLabsKey = Boolean(process.env.ELEVENLABS_API_KEY);
23
+ const hasOpenAiKey = Boolean(process.env.OPENAI_API_KEY);
24
+ if (!hasElevenLabsKey && !hasOpenAiKey) {
25
+ return error("Speech-to-text needs ELEVENLABS_API_KEY or OPENAI_API_KEY — neither is set");
26
+ }
27
+ const artifactResult = engine.artifacts.get(asset_id);
28
+ if (!artifactResult.ok)
29
+ return error(artifactResult.error.message);
30
+ const manifestResult = await engine.files.manifest(asset_id);
31
+ if (!manifestResult.ok)
32
+ return error(manifestResult.error.message);
33
+ const manifest = manifestResult.value;
34
+ const audioFile = findAudioFile(manifest.files, artifactResult.value.kind);
35
+ // ElevenLabs needs a standalone audio track; without one (or without its
36
+ // key) OpenAI speech-to-text reads the video file directly.
37
+ const mediaFile = audioFile ?? findVideoFile(manifest.files);
38
+ if (!mediaFile) {
39
+ return error(`No audio file found in artifact ${asset_id}`);
40
+ }
41
+ const useElevenLabs = hasElevenLabsKey && audioFile !== null;
42
+ if (!useElevenLabs && !hasOpenAiKey) {
43
+ return error(`No audio file found in artifact ${asset_id}`);
44
+ }
45
+ const provider = useElevenLabs ? "elevenlabs" : "openai";
46
+ const jobId = `transcribe-${asset_id}-${Date.now()}`;
47
+ const jobPayload = (useElevenLabs
48
+ ? {
49
+ type: "transcribe",
50
+ audioPath: join(manifest.path, mediaFile.name),
51
+ audioFilename: mediaFile.name,
52
+ tagAudioEvents: tag_audio_events,
53
+ diarize,
54
+ ...(language_code ? { languageCode: language_code } : {}),
55
+ assetId: asset_id,
56
+ assetDir: manifest.path,
57
+ bookId: book_id,
58
+ }
59
+ : {
60
+ type: "transcribe_video_audio",
61
+ videoPath: join(manifest.path, mediaFile.name),
62
+ sourceFilename: mediaFile.name,
63
+ assetId: asset_id,
64
+ assetDir: manifest.path,
65
+ bookId: book_id,
66
+ });
67
+ if (wait) {
68
+ try {
69
+ await queue.enqueueAndWait(jobId, jobPayload);
70
+ }
71
+ catch (err) {
72
+ return error(`Transcription failed: ${err instanceof Error ? err.message : String(err)}`);
73
+ }
74
+ return {
75
+ content: [{
76
+ type: "text",
77
+ text: JSON.stringify({ asset_id, job_id: jobId, provider, status: "completed" }),
78
+ }],
79
+ };
80
+ }
81
+ await queue.enqueue(jobId, jobPayload);
82
+ return {
83
+ content: [{
84
+ type: "text",
85
+ text: JSON.stringify({ asset_id, job_id: jobId, provider, status: "enqueued" }),
86
+ }],
87
+ };
88
+ },
89
+ };
90
+ }
@@ -0,0 +1,59 @@
1
+ import { z } from "zod";
2
+ import { queue } from "../queue.js";
3
+ import { requireArtifactKind } from "./_shared.js";
4
+ export function translateSubtitlesDef(engine) {
5
+ return {
6
+ name: "translate_subtitles",
7
+ description: "Translate an asset's transcript subtitles into another language for renders with that subtitle_language; cached per language until the asset is re-transcribed.",
8
+ schema: {
9
+ asset_id: z.string().describe("Video or audio artifact id"),
10
+ book_id: z.string().describe("Book id"),
11
+ language: z
12
+ .string()
13
+ .regex(/^[a-z]{2}$/)
14
+ .describe("Target language as a two-letter ISO 639-1 code (e.g. 'en')"),
15
+ },
16
+ handler: async (args) => {
17
+ const { asset_id, book_id, language } = args;
18
+ const kindGate = await requireArtifactKind(engine, asset_id, ["video", "audio"], "translate_subtitles");
19
+ if (kindGate)
20
+ return kindGate;
21
+ if (!process.env.OPENAI_API_KEY) {
22
+ return {
23
+ isError: true,
24
+ content: [{ type: "text", text: "OPENAI_API_KEY environment variable is not set" }],
25
+ };
26
+ }
27
+ const manifestResult = await engine.files.manifest(asset_id);
28
+ if (!manifestResult.ok) {
29
+ return {
30
+ isError: true,
31
+ content: [{ type: "text", text: manifestResult.error.message }],
32
+ };
33
+ }
34
+ const hasTranscript = manifestResult.value.files.some((f) => f.name === "elevenlabs.json");
35
+ if (!hasTranscript) {
36
+ return {
37
+ isError: true,
38
+ content: [{
39
+ type: "text",
40
+ text: `No elevenlabs.json in asset ${asset_id}. Transcribe the asset first.`,
41
+ }],
42
+ };
43
+ }
44
+ const jobId = `translate-${asset_id}-${language}-${Date.now()}`;
45
+ await queue.enqueue(jobId, {
46
+ type: "translate_transcript",
47
+ assetId: asset_id,
48
+ bookId: book_id,
49
+ targetLanguage: language,
50
+ });
51
+ return {
52
+ content: [{
53
+ type: "text",
54
+ text: JSON.stringify({ asset_id, language, job_id: jobId, status: "enqueued" }),
55
+ }],
56
+ };
57
+ },
58
+ };
59
+ }
@@ -0,0 +1,139 @@
1
+ import { z } from "zod";
2
+ import { join, extname } from "node:path";
3
+ import { queue } from "../queue.js";
4
+ import { findAudioFile } from "videobook-engine";
5
+ import { probeVideo } from "../ffmpeg.js";
6
+ import { validateAudioTrimRange } from "../audio-trim-range.js";
7
+ function validateTrimParams(trimStart, trimEnd, cutStart, cutEnd) {
8
+ const hasKeep = trimStart !== undefined || trimEnd !== undefined;
9
+ const hasCut = cutStart !== undefined || cutEnd !== undefined;
10
+ if (hasKeep && hasCut) {
11
+ return { error: "Cannot specify both trim (keep) and cut (remove) parameters. Use one mode at a time." };
12
+ }
13
+ if (!hasKeep && !hasCut) {
14
+ return { error: "Must specify trim_start/trim_end (keep range) or cut_start/cut_end (remove range)." };
15
+ }
16
+ if (hasCut) {
17
+ if (cutStart === undefined || cutEnd === undefined) {
18
+ return { error: "Both cut_start and cut_end are required for cut mode." };
19
+ }
20
+ if (cutStart >= cutEnd) {
21
+ return { error: "cut_start must be less than cut_end." };
22
+ }
23
+ if (cutStart < 0 || cutEnd < 0) {
24
+ return { error: "cut_start and cut_end must be >= 0." };
25
+ }
26
+ return { mode: "cut", cutStart, cutEnd };
27
+ }
28
+ if (trimStart !== undefined && trimStart < 0) {
29
+ return { error: "trim_start must be >= 0." };
30
+ }
31
+ if (trimEnd !== undefined && trimEnd < 0) {
32
+ return { error: "trim_end must be >= 0." };
33
+ }
34
+ if (trimStart !== undefined && trimEnd !== undefined && trimStart >= trimEnd) {
35
+ return { error: "trim_start must be less than trim_end." };
36
+ }
37
+ return { mode: "keep", trimStart, trimEnd };
38
+ }
39
+ export function trimAudioDef(engine) {
40
+ return {
41
+ name: "trim_audio",
42
+ description: "Trim an audio asset: keep a time range (trim_start/trim_end) or cut out a section (cut_start/cut_end). Use trim_video for video assets so picture and sound remain synchronized. Operates in-place with git versioning.",
43
+ schema: {
44
+ asset_id: z.string().describe("Audio artifact id"),
45
+ book_id: z.string().describe("Book UUID"),
46
+ trim_start: z.number().optional().describe("Start of range to keep (seconds)"),
47
+ trim_end: z.number().optional().describe("End of range to keep (seconds)"),
48
+ cut_start: z.number().optional().describe("Start of chunk to remove (seconds)"),
49
+ cut_end: z.number().optional().describe("End of chunk to remove (seconds)"),
50
+ },
51
+ handler: async (args) => {
52
+ const { asset_id, book_id, trim_start, trim_end, cut_start, cut_end } = args;
53
+ const validated = validateTrimParams(trim_start, trim_end, cut_start, cut_end);
54
+ if ("error" in validated) {
55
+ return {
56
+ isError: true,
57
+ content: [{ type: "text", text: validated.error }],
58
+ };
59
+ }
60
+ const assetId = asset_id.replace(/^@/, "");
61
+ const artifact = engine.artifacts.get(assetId);
62
+ if (!artifact.ok) {
63
+ return {
64
+ isError: true,
65
+ content: [{ type: "text", text: artifact.error.message }],
66
+ };
67
+ }
68
+ if (artifact.value.kind !== "audio") {
69
+ return {
70
+ isError: true,
71
+ content: [{ type: "text", text: "trim_audio requires an audio artifact. Use trim_video for video artifacts." }],
72
+ };
73
+ }
74
+ const manifestResult = await engine.files.manifest(assetId);
75
+ if (!manifestResult.ok) {
76
+ return {
77
+ isError: true,
78
+ content: [{ type: "text", text: manifestResult.error.message }],
79
+ };
80
+ }
81
+ const manifest = manifestResult.value;
82
+ const files = manifest.files || [];
83
+ const mediaFile = findAudioFile(files, artifact.value.kind);
84
+ if (!mediaFile) {
85
+ return {
86
+ isError: true,
87
+ content: [{ type: "text", text: `No audio file found in artifact ${assetId}` }],
88
+ };
89
+ }
90
+ const assetDir = manifest.path;
91
+ const mediaPath = join(assetDir, mediaFile.name);
92
+ try {
93
+ const source = await probeVideo(mediaPath);
94
+ if (!source.hasAudio)
95
+ throw new Error("Audio file has no usable audio stream");
96
+ validateAudioTrimRange(source.audioDuration ?? source.duration, validated.mode, trim_start, trim_end, cut_start, cut_end);
97
+ }
98
+ catch (error) {
99
+ return { isError: true, content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }] };
100
+ }
101
+ const ext = extname(mediaFile.name);
102
+ const jobId = `trim-audio-${assetId}-${Date.now()}`;
103
+ const tempPath = join(assetDir, `${jobId}${ext}`);
104
+ const jobPayload = {
105
+ type: "trim_audio",
106
+ inputPath: mediaPath,
107
+ outputPath: tempPath,
108
+ mode: validated.mode,
109
+ assetId,
110
+ assetDir,
111
+ bookId: book_id,
112
+ commitParams: { trim_start, trim_end, cut_start, cut_end, mode: validated.mode },
113
+ };
114
+ if (validated.mode === "keep") {
115
+ jobPayload.trimStart = validated.trimStart;
116
+ jobPayload.trimEnd = validated.trimEnd;
117
+ }
118
+ else {
119
+ jobPayload.cutStart = validated.cutStart;
120
+ jobPayload.cutEnd = validated.cutEnd;
121
+ }
122
+ try {
123
+ await queue.enqueueAndWait(jobId, jobPayload);
124
+ }
125
+ catch (err) {
126
+ return {
127
+ isError: true,
128
+ content: [{ type: "text", text: `Trim audio failed: ${err instanceof Error ? err.message : String(err)}` }],
129
+ };
130
+ }
131
+ return {
132
+ content: [{
133
+ type: "text",
134
+ text: JSON.stringify({ asset_id: assetId, job_id: jobId, status: "completed" }),
135
+ }],
136
+ };
137
+ },
138
+ };
139
+ }
@@ -0,0 +1,168 @@
1
+ import { findVideoFile } from "videobook-engine";
2
+ import { z } from "zod";
3
+ import { join } from "node:path";
4
+ import { queue } from "../queue.js";
5
+ import { resolveVideoAssetReference } from "./video-source-reference.js";
6
+ import { parseVideoTimestamp, videoTimestampSchema } from "./video-time-input.js";
7
+ import { waitForAssetReadyDef } from "./wait-for-asset-ready.js";
8
+ function validateTrimParams(trimStart, trimEnd, cutStart, cutEnd) {
9
+ const hasKeep = trimStart !== undefined || trimEnd !== undefined;
10
+ const hasCut = cutStart !== undefined || cutEnd !== undefined;
11
+ if (hasKeep && hasCut) {
12
+ return { error: "Cannot specify both trim (keep) and cut (remove) parameters. Use one mode at a time." };
13
+ }
14
+ if (!hasKeep && !hasCut) {
15
+ return { error: "Must specify trim_start/trim_end (keep range) or cut_start/cut_end (remove range)." };
16
+ }
17
+ if (hasCut) {
18
+ if (cutStart === undefined || cutEnd === undefined) {
19
+ return { error: "Both cut_start and cut_end are required for cut mode." };
20
+ }
21
+ if (cutStart >= cutEnd) {
22
+ return { error: "cut_start must be less than cut_end." };
23
+ }
24
+ if (cutStart < 0 || cutEnd < 0) {
25
+ return { error: "cut_start and cut_end must be >= 0." };
26
+ }
27
+ return { mode: "cut", cutStart, cutEnd };
28
+ }
29
+ if (trimStart !== undefined && trimStart < 0) {
30
+ return { error: "trim_start must be >= 0." };
31
+ }
32
+ if (trimEnd !== undefined && trimEnd < 0) {
33
+ return { error: "trim_end must be >= 0." };
34
+ }
35
+ if (trimStart !== undefined && trimEnd !== undefined && trimStart >= trimEnd) {
36
+ return { error: "trim_start must be less than trim_end." };
37
+ }
38
+ return { mode: "keep", trimStart, trimEnd };
39
+ }
40
+ export function trimVideoDef(engine) {
41
+ return {
42
+ name: "trim_video",
43
+ description: "Change an existing video in place by keeping a range or removing a middle section. Use extract_video_clip for a new clip that keeps the source. Times accept seconds, 15s or 1:30.",
44
+ annotations: {
45
+ title: "Trim video in place",
46
+ readOnlyHint: false,
47
+ destructiveHint: true,
48
+ idempotentHint: false,
49
+ openWorldHint: false,
50
+ },
51
+ schema: {
52
+ asset_id: z.string().describe("Source video asset ID or notebook reference such as @a1"),
53
+ book_id: z.string().describe("Book id"),
54
+ notebook_id: z.string().uuid().optional().describe("Notebook UUID when asset_id is a grid reference and the book has multiple matching notebooks"),
55
+ trim_start: videoTimestampSchema.optional().describe("Start of range to keep"),
56
+ trim_end: videoTimestampSchema.optional().describe("End of range to keep"),
57
+ cut_start: videoTimestampSchema.optional().describe("Start of section to remove"),
58
+ cut_end: videoTimestampSchema.optional().describe("End of section to remove"),
59
+ },
60
+ handler: async (args) => {
61
+ const { asset_id, book_id, notebook_id, trim_start, trim_end, cut_start, cut_end } = args;
62
+ let assetId;
63
+ let parsedTrimStart;
64
+ let parsedTrimEnd;
65
+ let parsedCutStart;
66
+ let parsedCutEnd;
67
+ try {
68
+ assetId = resolveVideoAssetReference(engine, asset_id, notebook_id);
69
+ parsedTrimStart = parseVideoTimestamp(trim_start, "trim_start");
70
+ parsedTrimEnd = parseVideoTimestamp(trim_end, "trim_end");
71
+ parsedCutStart = parseVideoTimestamp(cut_start, "cut_start");
72
+ parsedCutEnd = parseVideoTimestamp(cut_end, "cut_end");
73
+ }
74
+ catch (err) {
75
+ return {
76
+ isError: true,
77
+ content: [{ type: "text", text: err instanceof Error ? err.message : String(err) }],
78
+ };
79
+ }
80
+ const validated = validateTrimParams(parsedTrimStart, parsedTrimEnd, parsedCutStart, parsedCutEnd);
81
+ if ("error" in validated) {
82
+ return {
83
+ isError: true,
84
+ content: [{ type: "text", text: validated.error }],
85
+ };
86
+ }
87
+ const manifestResult = await engine.files.manifest(assetId);
88
+ if (!manifestResult.ok) {
89
+ return {
90
+ isError: true,
91
+ content: [{ type: "text", text: manifestResult.error.message }],
92
+ };
93
+ }
94
+ const manifest = manifestResult.value;
95
+ const videoFile = findVideoFile(manifest.files);
96
+ if (!videoFile) {
97
+ return {
98
+ isError: true,
99
+ content: [{ type: "text", text: `No video file found in asset ${assetId}` }],
100
+ };
101
+ }
102
+ const assetDir = manifest.path;
103
+ const videoPath = join(assetDir, videoFile.name);
104
+ const tempPath = join(assetDir, "original_trimmed.mp4");
105
+ const jobId = `trim-${assetId}-${Date.now()}`;
106
+ const jobPayload = {
107
+ type: "trim_video",
108
+ inputPath: videoPath,
109
+ outputPath: tempPath,
110
+ mode: validated.mode,
111
+ assetId,
112
+ assetDir,
113
+ bookId: book_id,
114
+ commitParams: {
115
+ trim_start: parsedTrimStart,
116
+ trim_end: parsedTrimEnd,
117
+ cut_start: parsedCutStart,
118
+ cut_end: parsedCutEnd,
119
+ mode: validated.mode,
120
+ },
121
+ };
122
+ if (validated.mode === "keep") {
123
+ jobPayload.trimStart = validated.trimStart;
124
+ jobPayload.trimEnd = validated.trimEnd;
125
+ }
126
+ else {
127
+ jobPayload.cutStart = validated.cutStart;
128
+ jobPayload.cutEnd = validated.cutEnd;
129
+ }
130
+ try {
131
+ await queue.enqueueAndWait(jobId, jobPayload);
132
+ const readiness = await waitForAssetReadyDef(engine).handler({
133
+ asset_id: assetId,
134
+ book_id,
135
+ timeout_seconds: 300,
136
+ });
137
+ if (readiness.isError) {
138
+ const text = readiness.content.find((entry) => entry.type === "text")?.text;
139
+ let detail = text ?? "Post-trim processing failed";
140
+ if (text) {
141
+ try {
142
+ const parsed = JSON.parse(text);
143
+ detail = typeof parsed.error === "string"
144
+ ? parsed.error
145
+ : `Post-trim processing ${String(parsed.status ?? "failed")}`;
146
+ }
147
+ catch {
148
+ detail = text;
149
+ }
150
+ }
151
+ throw new Error(detail);
152
+ }
153
+ }
154
+ catch (err) {
155
+ return {
156
+ isError: true,
157
+ content: [{ type: "text", text: `Trim failed: ${err instanceof Error ? err.message : String(err)}` }],
158
+ };
159
+ }
160
+ return {
161
+ content: [{
162
+ type: "text",
163
+ text: JSON.stringify({ asset_id: assetId, job_id: jobId, status: "completed" }),
164
+ }],
165
+ };
166
+ },
167
+ };
168
+ }
@@ -0,0 +1,25 @@
1
+ import { z } from "zod";
2
+ export function updateScriptDef(engine) {
3
+ return {
4
+ name: "update_script",
5
+ description: "Update a script asset's markdown content (original.md).",
6
+ schema: {
7
+ asset_id: z.string().describe("Asset ID of the script"),
8
+ content: z.string().describe("New markdown content"),
9
+ book_id: z.string().describe("Book id"),
10
+ },
11
+ handler: async (args) => {
12
+ const { asset_id, content, book_id: _book_id } = args;
13
+ const result = await engine.files.write(asset_id, "original.md", content);
14
+ if (!result.ok) {
15
+ return {
16
+ isError: true,
17
+ content: [{ type: "text", text: result.error.message }],
18
+ };
19
+ }
20
+ return {
21
+ content: [{ type: "text", text: JSON.stringify({ ok: true, path: result.value }) }],
22
+ };
23
+ },
24
+ };
25
+ }
@@ -0,0 +1,40 @@
1
+ import { z } from "zod";
2
+ import { readTimeline, timelineFailure, timelineInput, timelinePayload, writeTimeline, } from "../timeline-helpers.js";
3
+ export function updateTimelineAudioDef(engine) {
4
+ return {
5
+ name: "update_timeline_audio",
6
+ description: "Update an audio overlay's volume or fades",
7
+ schema: {
8
+ book_id: z.string().uuid(),
9
+ audio_id: z.string().uuid(),
10
+ volume: z.number().min(0).max(100).optional(),
11
+ fade_in: z.number().min(0).optional(),
12
+ fade_out: z.number().min(0).optional(),
13
+ },
14
+ handler: async (args) => {
15
+ if (args.volume === undefined && args.fade_in === undefined && args.fade_out === undefined) {
16
+ return timelineFailure("Provide volume, fade_in, or fade_out");
17
+ }
18
+ const timeline = readTimeline(engine);
19
+ const audioId = String(args.audio_id);
20
+ if (!timeline.audio.some((clip) => clip.id === audioId)) {
21
+ return timelineFailure(`Timeline audio clip not found: ${audioId}`);
22
+ }
23
+ const result = await writeTimeline(engine, {
24
+ ...timelineInput(timeline),
25
+ audio: timeline.audio.map((clip) => ({
26
+ id: clip.id,
27
+ artifactId: clip.artifactId,
28
+ startFrame: clip.startFrame,
29
+ durationFrames: clip.durationFrames,
30
+ ...(clip.id === audioId && args.volume !== undefined ? { volume: args.volume } : clip.volume === undefined ? {} : { volume: clip.volume }),
31
+ ...(clip.id === audioId && args.fade_in !== undefined ? { fadeIn: args.fade_in } : clip.fadeIn === undefined ? {} : { fadeIn: clip.fadeIn }),
32
+ ...(clip.id === audioId && args.fade_out !== undefined ? { fadeOut: args.fade_out } : clip.fadeOut === undefined ? {} : { fadeOut: clip.fadeOut }),
33
+ })),
34
+ });
35
+ return result.ok ? {
36
+ content: [{ type: "text", text: JSON.stringify(timelinePayload(result.value)) }],
37
+ } : timelineFailure(result.error.message);
38
+ },
39
+ };
40
+ }
@@ -0,0 +1,41 @@
1
+ import { z } from "zod";
2
+ import { uploadLocalBytes } from "../local-upload.js";
3
+ function isBase64Upload(value) {
4
+ const normalized = value.replace(/\s/gu, "");
5
+ const match = /^([A-Za-z0-9+/_-]*)(={0,2})$/u.exec(normalized);
6
+ return match !== null
7
+ && match[1].length % 4 !== 1
8
+ && (match[2].length === 0 || normalized.length % 4 === 0);
9
+ }
10
+ export function uploadFileDef(engine) {
11
+ return {
12
+ name: "upload_file",
13
+ description: "Upload a media or markdown file into a Videobook project as an asset",
14
+ schema: {
15
+ book_id: z.string().describe("Book id to upload into"),
16
+ filename: z.string().describe("Original filename with extension"),
17
+ data: z.string().refine(isBase64Upload, "File contents must be valid base64").describe("Base64-encoded file contents"),
18
+ },
19
+ handler: async (args) => {
20
+ const { book_id, filename, data } = args;
21
+ const result = await uploadLocalBytes(engine, {
22
+ bookId: book_id,
23
+ filename,
24
+ data: Buffer.from(data, "base64"),
25
+ waitForProcessing: true,
26
+ });
27
+ if (!result.ok) {
28
+ return {
29
+ isError: true,
30
+ content: [{ type: "text", text: result.message }],
31
+ };
32
+ }
33
+ return {
34
+ content: [{
35
+ type: "text",
36
+ text: JSON.stringify(result.value),
37
+ }],
38
+ };
39
+ },
40
+ };
41
+ }