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,253 @@
1
+ import { z } from "zod";
2
+ import QRCode from "qrcode";
3
+ import sharp from "sharp";
4
+ import { findImageFile } from "./_image-inputs.js";
5
+ import { derivedLabel } from "../artifact-label.js";
6
+ const DEFAULT_QART_SIZE = 2520;
7
+ export const QART_QUIET_MODULES = 4;
8
+ const QART_MIN_MODULE_PX = 3;
9
+ export const QART_MIN_VERSION = 20;
10
+ const QART_IMAGE_STRENGTH = 0.46;
11
+ export const QART_DARK_MAX = Math.round(255 * QART_IMAGE_STRENGTH);
12
+ export const QART_LIGHT_MIN = Math.round(255 * (1 - QART_IMAGE_STRENGTH));
13
+ const LANDSCAPE_WIDTH = 1920;
14
+ const LANDSCAPE_HEIGHT = 1080;
15
+ const PORTRAIT_WIDTH = 1080;
16
+ const PORTRAIT_HEIGHT = 1920;
17
+ const SQUARE_SIZE = 1080;
18
+ function error(text) {
19
+ return { isError: true, content: [{ type: "text", text }] };
20
+ }
21
+ function createQr(value) {
22
+ const automatic = QRCode.create(value, { errorCorrectionLevel: "H" });
23
+ const version = Math.max(QART_MIN_VERSION, automatic.version);
24
+ if (version === automatic.version)
25
+ return automatic;
26
+ return QRCode.create(value, { errorCorrectionLevel: "H", version });
27
+ }
28
+ async function sampleCover(coverBuffer, moduleCount) {
29
+ const sampled = await sharp(coverBuffer)
30
+ .rotate()
31
+ .sharpen({ sigma: 1.2 })
32
+ .modulate({ saturation: 1.25 })
33
+ .resize(moduleCount, moduleCount, { fit: "cover", position: "centre" })
34
+ .removeAlpha()
35
+ .raw()
36
+ .toBuffer({ resolveWithObject: true });
37
+ return { data: sampled.data, channels: sampled.info.channels };
38
+ }
39
+ function luma(r, g, b) {
40
+ return Math.round(0.2126 * r + 0.7152 * g + 0.0722 * b);
41
+ }
42
+ function moduleColor(qr, sampled, row, col, style) {
43
+ const dark = qr.modules.get(row, col) === 1;
44
+ if (qr.modules.isReserved(row, col)) {
45
+ const channel = dark ? 0 : 255;
46
+ return [channel, channel, channel];
47
+ }
48
+ const sampleIndex = (row * qr.modules.size + col) * sampled.channels;
49
+ const r = sampled.data[sampleIndex] ?? 255;
50
+ const g = sampled.data[sampleIndex + 1] ?? 255;
51
+ const b = sampled.data[sampleIndex + 2] ?? 255;
52
+ let source;
53
+ if (style === "monochrome" || style === "inverted") {
54
+ const l = luma(r, g, b);
55
+ source = style === "inverted" ? [255 - l, 255 - l, 255 - l] : [l, l, l];
56
+ }
57
+ else {
58
+ source = [r, g, b];
59
+ }
60
+ const channels = source.map((channel) => dark
61
+ ? Math.round(channel * QART_IMAGE_STRENGTH)
62
+ : Math.round(255 - (255 - channel) * QART_IMAGE_STRENGTH));
63
+ return [channels[0], channels[1], channels[2]];
64
+ }
65
+ async function renderQart(qr, sampled, style, width, height) {
66
+ const moduleCount = qr.modules.size;
67
+ const totalModules = moduleCount + QART_QUIET_MODULES * 2;
68
+ const modulePx = Math.floor(Math.min(width, height) / totalModules);
69
+ if (modulePx < QART_MIN_MODULE_PX) {
70
+ const minimum = totalModules * QART_MIN_MODULE_PX;
71
+ throw new Error(`size is too small for QR version ${qr.version}; minimum is ${minimum}px`);
72
+ }
73
+ const gridSize = totalModules * modulePx;
74
+ const offsetX = Math.floor((width - gridSize) / 2);
75
+ const offsetY = Math.floor((height - gridSize) / 2);
76
+ const output = Buffer.alloc(width * height * 3, 255);
77
+ for (let row = 0; row < moduleCount; row++) {
78
+ for (let col = 0; col < moduleCount; col++) {
79
+ const color = moduleColor(qr, sampled, row, col, style);
80
+ const originX = offsetX + (col + QART_QUIET_MODULES) * modulePx;
81
+ const originY = offsetY + (row + QART_QUIET_MODULES) * modulePx;
82
+ for (let dy = 0; dy < modulePx; dy++) {
83
+ for (let dx = 0; dx < modulePx; dx++) {
84
+ const outputIndex = ((originY + dy) * width + originX + dx) * 3;
85
+ output[outputIndex] = color[0];
86
+ output[outputIndex + 1] = color[1];
87
+ output[outputIndex + 2] = color[2];
88
+ }
89
+ }
90
+ }
91
+ }
92
+ const buffer = await sharp(output, { raw: { width, height, channels: 3 } }).png().toBuffer();
93
+ return { buffer, width, height, modulePx, moduleCount, qrVersion: qr.version };
94
+ }
95
+ async function toJpeg(buffer) {
96
+ return sharp(buffer)
97
+ .jpeg({ quality: 98, chromaSubsampling: "4:4:4" })
98
+ .toBuffer();
99
+ }
100
+ async function buildStyleFiles(qr, sampled, style, size) {
101
+ const [original, landscapePng, portraitPng, squarePng] = await Promise.all([
102
+ renderQart(qr, sampled, style, size, size),
103
+ renderQart(qr, sampled, style, LANDSCAPE_WIDTH, LANDSCAPE_HEIGHT),
104
+ renderQart(qr, sampled, style, PORTRAIT_WIDTH, PORTRAIT_HEIGHT),
105
+ renderQart(qr, sampled, style, SQUARE_SIZE, SQUARE_SIZE),
106
+ ]);
107
+ const [landscape, portrait, square] = await Promise.all([
108
+ toJpeg(landscapePng.buffer),
109
+ toJpeg(portraitPng.buffer),
110
+ toJpeg(squarePng.buffer),
111
+ ]);
112
+ return {
113
+ original: original.buffer,
114
+ landscape,
115
+ portrait,
116
+ square,
117
+ modulePx: original.modulePx,
118
+ };
119
+ }
120
+ export async function buildQartPng(value, coverBuffer, options) {
121
+ const qr = createQr(value);
122
+ const sampled = await sampleCover(coverBuffer, qr.modules.size);
123
+ return renderQart(qr, sampled, options.style, options.size, options.size);
124
+ }
125
+ async function readCoverImage(engine, imageRef) {
126
+ const assetId = imageRef.replace(/^@/, "");
127
+ const artifact = engine.artifacts.get(assetId);
128
+ if (!artifact.ok)
129
+ return { ok: false, error: artifact.error.message };
130
+ if (artifact.value.kind !== "image") {
131
+ return { ok: false, error: `image_ref must be an image artifact, got ${artifact.value.kind}: ${assetId}` };
132
+ }
133
+ const manifestResult = await engine.files.manifest(assetId);
134
+ if (!manifestResult.ok)
135
+ return { ok: false, error: manifestResult.error.message };
136
+ const imageFile = findImageFile(manifestResult.value.files);
137
+ if (!imageFile)
138
+ return { ok: false, error: `No image file found in asset ${assetId}` };
139
+ const readResult = await engine.files.read(assetId, imageFile.name);
140
+ if (!readResult.ok)
141
+ return { ok: false, error: readResult.error.message };
142
+ const buffer = Buffer.isBuffer(readResult.value)
143
+ ? readResult.value
144
+ : Buffer.from(readResult.value);
145
+ return { ok: true, assetId, buffer };
146
+ }
147
+ async function persistQartAsset(engine, assetName, files, metadata) {
148
+ const created = await engine.artifacts.create("img", assetName);
149
+ if (!created.ok)
150
+ return { ok: false, error: `Failed to create image asset: ${created.error.message}` };
151
+ const assetId = created.value.artifactId;
152
+ const writes = await Promise.all([
153
+ engine.files.write(assetId, "original.png", files.original),
154
+ engine.files.write(assetId, "landscape.jpg", files.landscape),
155
+ engine.files.write(assetId, "portrait.jpg", files.portrait),
156
+ engine.files.write(assetId, "square.jpg", files.square),
157
+ ]);
158
+ const writeFailure = writes.find((result) => !result.ok);
159
+ if (writeFailure && !writeFailure.ok) {
160
+ await engine.artifacts.delete(assetId).catch(() => undefined);
161
+ return { ok: false, error: writeFailure.error.message };
162
+ }
163
+ const metadataResult = await engine.metadata.artifacts.write(assetId, "original", metadata);
164
+ if (!metadataResult.ok) {
165
+ await engine.artifacts.delete(assetId).catch(() => undefined);
166
+ return { ok: false, error: metadataResult.error.message };
167
+ }
168
+ return { ok: true, assetId };
169
+ }
170
+ export function createQartDef(engine) {
171
+ return {
172
+ name: "create_qart",
173
+ description: "Create scan-safe color and monochrome QR mosaics from a source image artifact. The image is sampled per QR module with protected QR structures and white quiet zones.",
174
+ schema: {
175
+ book_id: z.string().describe("Book id"),
176
+ image_ref: z.string().describe("Source image artifact id"),
177
+ value: z.string().min(1).describe("Text or URL encoded in the QR code"),
178
+ size: z
179
+ .number()
180
+ .int()
181
+ .min(256)
182
+ .max(4096)
183
+ .optional()
184
+ .describe(`Output edge length in pixels (default ${DEFAULT_QART_SIZE})`),
185
+ style: z
186
+ .enum(["color", "monochrome", "inverted"])
187
+ .optional()
188
+ .describe("Style of the QArt output. 'color', 'monochrome', or 'inverted' (default)"),
189
+ name: z.string().optional().describe("Optional base name for the generated assets"),
190
+ },
191
+ handler: async (args) => {
192
+ const { image_ref, value, size, style, name, } = args;
193
+ const resolvedValue = value.trim();
194
+ if (!resolvedValue)
195
+ return error("value must be a non-empty string");
196
+ const resolvedSize = size ?? DEFAULT_QART_SIZE;
197
+ const stylesToBuild = style ? [style] : ["inverted", "monochrome"];
198
+ const cover = await readCoverImage(engine, image_ref);
199
+ if (!cover.ok)
200
+ return error(cover.error);
201
+ const qr = createQr(resolvedValue);
202
+ const sampled = await sampleCover(cover.buffer, qr.modules.size);
203
+ const stem = name ?? cover.assetId;
204
+ const createdAt = new Date().toISOString();
205
+ const baseMeta = {
206
+ origin: "create_qart",
207
+ qart_style: "module_mosaic",
208
+ created_at: createdAt,
209
+ value: resolvedValue,
210
+ source_image: cover.assetId,
211
+ width: resolvedSize,
212
+ height: resolvedSize,
213
+ qr_version: qr.version,
214
+ error_correction: "H",
215
+ quiet_zone_modules: QART_QUIET_MODULES,
216
+ image_strength: QART_IMAGE_STRENGTH,
217
+ sample_grid: `${qr.modules.size}x${qr.modules.size}`,
218
+ };
219
+ const resultIds = {};
220
+ for (const s of stylesToBuild) {
221
+ let buildFiles;
222
+ try {
223
+ buildFiles = await buildStyleFiles(qr, sampled, s, resolvedSize);
224
+ }
225
+ catch (err) {
226
+ return error(`Failed to build QR mosaic: ${err instanceof Error ? err.message : String(err)}`);
227
+ }
228
+ const resultAsset = await persistQartAsset(engine, derivedLabel(`qart ${s}`, stem), buildFiles, { ...baseMeta, color_mode: s, module_pixels: buildFiles.modulePx });
229
+ if (!resultAsset.ok)
230
+ return error(resultAsset.error);
231
+ if (style) {
232
+ resultIds.asset_id = resultAsset.assetId;
233
+ resultIds.style = s;
234
+ }
235
+ else {
236
+ resultIds[`${s}_asset_id`] = resultAsset.assetId;
237
+ }
238
+ }
239
+ return {
240
+ content: [{
241
+ type: "text",
242
+ text: JSON.stringify({
243
+ ...resultIds,
244
+ value: resolvedValue,
245
+ size: resolvedSize,
246
+ source_image: cover.assetId,
247
+ qr_version: qr.version,
248
+ }),
249
+ }],
250
+ };
251
+ },
252
+ };
253
+ }
@@ -0,0 +1,149 @@
1
+ import { findAudioFile } from "videobook-engine";
2
+ import { z } from "zod";
3
+ import { queue } from "../queue.js";
4
+ import { buildOutroSpec, orientationForAspectRatio, } from "../remotion/qr-outro-spec.js";
5
+ import { findImageFile } from "./_image-inputs.js";
6
+ function toolError(message) {
7
+ return {
8
+ isError: true,
9
+ content: [{ type: "text", text: message }],
10
+ };
11
+ }
12
+ function normalizeAssetId(raw) {
13
+ return raw.trim().replace(/^@/, "");
14
+ }
15
+ async function resolveImageRef(engine, raw, field) {
16
+ const assetId = normalizeAssetId(raw);
17
+ const artifact = engine.artifacts.get(assetId);
18
+ if (!artifact.ok)
19
+ return { ok: false, error: artifact.error.message };
20
+ if (artifact.value.kind !== "image") {
21
+ return { ok: false, error: `${field} must be an image artifact, got ${artifact.value.kind}: ${assetId}` };
22
+ }
23
+ const manifest = await engine.files.manifest(assetId);
24
+ if (!manifest.ok)
25
+ return { ok: false, error: manifest.error.message };
26
+ const file = findImageFile(manifest.value.files);
27
+ if (!file)
28
+ return { ok: false, error: `No image file found in asset ${assetId}` };
29
+ return { ok: true, value: { assetId, filename: file.name } };
30
+ }
31
+ async function resolveAudioRef(engine, raw) {
32
+ const assetId = normalizeAssetId(raw);
33
+ const artifact = engine.artifacts.get(assetId);
34
+ if (!artifact.ok)
35
+ return { ok: false, error: artifact.error.message };
36
+ if (artifact.value.kind !== "audio") {
37
+ return {
38
+ ok: false,
39
+ error: `sound_effect_ref must be an audio artifact, got ${artifact.value.kind}: ${assetId}`,
40
+ };
41
+ }
42
+ const manifest = await engine.files.manifest(assetId);
43
+ if (!manifest.ok)
44
+ return { ok: false, error: manifest.error.message };
45
+ const file = findAudioFile(manifest.value.files, "audio");
46
+ if (!file)
47
+ return { ok: false, error: `No audio file found in asset ${assetId}` };
48
+ return { ok: true, value: { assetId, filename: file.name } };
49
+ }
50
+ export function createQrOutroDef(engine) {
51
+ return {
52
+ name: "create_qr_outro",
53
+ description: "Build a standalone animated outro from 1-4 QR image assets with configurable duration and optional impact audio.",
54
+ schema: {
55
+ book_id: z.string().describe("Book id"),
56
+ qr_image_refs: z
57
+ .array(z.string())
58
+ .min(1)
59
+ .max(4)
60
+ .describe("One to four QR code image artifact ids"),
61
+ sound_effect_ref: z
62
+ .string()
63
+ .optional()
64
+ .describe("Optional impact sound override (audio artifact id); defaults to the bundled sound"),
65
+ aspect_ratio: z
66
+ .enum(["16:9", "9:16", "1:1"])
67
+ .optional()
68
+ .describe("Output aspect ratio (default: 16:9)"),
69
+ duration_seconds: z
70
+ .number()
71
+ .min(3)
72
+ .max(60)
73
+ .optional()
74
+ .describe("Output duration in seconds from 3 to 60; defaults to the animation minimum"),
75
+ include_audio: z
76
+ .boolean()
77
+ .optional()
78
+ .describe("Include impact audio (default: true); set false for a silent output"),
79
+ },
80
+ handler: async (args) => {
81
+ const { book_id, qr_image_refs, sound_effect_ref, aspect_ratio = "16:9", duration_seconds, include_audio = true, } = args;
82
+ if (!include_audio && sound_effect_ref) {
83
+ return toolError("sound_effect_ref cannot be used when include_audio is false");
84
+ }
85
+ const qrImages = [];
86
+ for (const raw of qr_image_refs) {
87
+ const resolved = await resolveImageRef(engine, raw, "qr_image_refs entry");
88
+ if (!resolved.ok)
89
+ return toolError(resolved.error);
90
+ qrImages.push(resolved.value);
91
+ }
92
+ let soundEffect = null;
93
+ if (include_audio && sound_effect_ref) {
94
+ const resolved = await resolveAudioRef(engine, sound_effect_ref);
95
+ if (!resolved.ok)
96
+ return toolError(resolved.error);
97
+ soundEffect = resolved.value;
98
+ }
99
+ const spec = buildOutroSpec(aspect_ratio, qrImages.length, duration_seconds);
100
+ const created = await engine.artifacts.create("vid", `qr-outro-${qrImages[0].assetId}`);
101
+ if (!created.ok) {
102
+ return toolError(`Failed to create output asset: ${created.error.message}`);
103
+ }
104
+ const assetId = created.value.artifactId;
105
+ const jobId = `render-qr-outro-${assetId}-${Date.now()}`;
106
+ try {
107
+ await queue.enqueue(jobId, {
108
+ type: "render_qr_outro",
109
+ bookId: book_id,
110
+ assetId,
111
+ orientation: orientationForAspectRatio(aspect_ratio),
112
+ qrImages,
113
+ soundEffect,
114
+ aspectRatio: aspect_ratio,
115
+ durationSeconds: spec.totalDurationSeconds,
116
+ includeAudio: include_audio,
117
+ });
118
+ }
119
+ catch (error) {
120
+ const message = error instanceof Error ? error.message : String(error);
121
+ const deleted = await engine.artifacts.delete(assetId);
122
+ if (!deleted.ok) {
123
+ await engine.jobs.pending.forceFail(assetId, {
124
+ message: `Enqueue failed and rollback failed: ${deleted.error.message}`,
125
+ });
126
+ }
127
+ return toolError(`Failed to enqueue QR outro: ${message}`);
128
+ }
129
+ return {
130
+ content: [{
131
+ type: "text",
132
+ text: JSON.stringify({
133
+ asset_id: assetId,
134
+ job_id: jobId,
135
+ status: "enqueued",
136
+ aspect_ratio,
137
+ duration_seconds: spec.totalDurationSeconds,
138
+ qr_image_refs: qrImages.map((image) => image.assetId),
139
+ include_audio,
140
+ sound_effect_source: include_audio
141
+ ? soundEffect ? "asset" : "bundled_default"
142
+ : "none",
143
+ ...(soundEffect ? { sound_effect_ref: soundEffect.assetId } : {}),
144
+ }),
145
+ }],
146
+ };
147
+ },
148
+ };
149
+ }
@@ -0,0 +1,215 @@
1
+ import { z } from "zod";
2
+ import sharp from "sharp";
3
+ import { derivedLabel } from "../artifact-label.js";
4
+ import { buildQrbtfSvg, wrapQrbtfSvg, } from "./qrbtf-render.js";
5
+ export const DEFAULT_QRBTF_SIZE = 1024;
6
+ const LANDSCAPE_WIDTH = 1920;
7
+ const LANDSCAPE_HEIGHT = 1080;
8
+ const PORTRAIT_WIDTH = 1080;
9
+ const PORTRAIT_HEIGHT = 1920;
10
+ const SQUARE_SIZE = 1080;
11
+ function error(text) {
12
+ return { isError: true, content: [{ type: "text", text }] };
13
+ }
14
+ async function toJpeg(buffer) {
15
+ return sharp(buffer).jpeg({ quality: 98, chromaSubsampling: "4:4:4" }).toBuffer();
16
+ }
17
+ async function renderAtSize(content, viewBox, bgColor, width, height) {
18
+ const svg = wrapQrbtfSvg({ content, viewBox, bgColor }, width, height);
19
+ return sharp(Buffer.from(svg)).png().toBuffer();
20
+ }
21
+ async function renderVariant(content, viewBox, bgColor, width, height) {
22
+ const minDim = Math.min(width, height);
23
+ const qrPng = await renderAtSize(content, viewBox, bgColor, minDim, minDim);
24
+ const left = Math.floor((width - minDim) / 2);
25
+ const top = Math.floor((height - minDim) / 2);
26
+ if (left === 0 && top === 0)
27
+ return toJpeg(qrPng);
28
+ return sharp(qrPng)
29
+ .extend({
30
+ left,
31
+ top,
32
+ right: width - minDim - left,
33
+ bottom: height - minDim - top,
34
+ background: bgColor,
35
+ })
36
+ .jpeg({ quality: 98, chromaSubsampling: "4:4:4" })
37
+ .toBuffer();
38
+ }
39
+ async function buildQrbtfFiles(content, viewBox, bgColor, size) {
40
+ const [original, landscape, portrait, square] = await Promise.all([
41
+ renderAtSize(content, viewBox, bgColor, size, size),
42
+ renderVariant(content, viewBox, bgColor, LANDSCAPE_WIDTH, LANDSCAPE_HEIGHT),
43
+ renderVariant(content, viewBox, bgColor, PORTRAIT_WIDTH, PORTRAIT_HEIGHT),
44
+ renderVariant(content, viewBox, bgColor, SQUARE_SIZE, SQUARE_SIZE),
45
+ ]);
46
+ return {
47
+ original,
48
+ landscape,
49
+ portrait,
50
+ square,
51
+ };
52
+ }
53
+ export async function buildQrbtfPng(value, style, params, size) {
54
+ const r = buildQrbtfSvg(value, style, params);
55
+ const buffer = await renderAtSize(r.content, r.viewBox, r.bgColor, size, size);
56
+ return {
57
+ buffer,
58
+ width: size,
59
+ height: size,
60
+ qrVersion: r.version,
61
+ moduleCount: r.moduleCount,
62
+ };
63
+ }
64
+ async function persistQrbtfAsset(engine, assetName, files, metadata) {
65
+ const created = await engine.artifacts.create("img", assetName);
66
+ if (!created.ok)
67
+ return { ok: false, error: `Failed to create image asset: ${created.error.message}` };
68
+ const assetId = created.value.artifactId;
69
+ const writes = await Promise.all([
70
+ engine.files.write(assetId, "original.png", files.original),
71
+ engine.files.write(assetId, "landscape.jpg", files.landscape),
72
+ engine.files.write(assetId, "portrait.jpg", files.portrait),
73
+ engine.files.write(assetId, "square.jpg", files.square),
74
+ ]);
75
+ const writeFailure = writes.find((result) => !result.ok);
76
+ if (writeFailure && !writeFailure.ok) {
77
+ await engine.artifacts.delete(assetId).catch(() => undefined);
78
+ return { ok: false, error: writeFailure.error.message };
79
+ }
80
+ const metadataResult = await engine.metadata.artifacts.write(assetId, "original", metadata);
81
+ if (!metadataResult.ok) {
82
+ await engine.artifacts.delete(assetId).catch(() => undefined);
83
+ return { ok: false, error: metadataResult.error.message };
84
+ }
85
+ return { ok: true, assetId };
86
+ }
87
+ export function createQrbtfDef(engine) {
88
+ return {
89
+ name: "create_qrbtf",
90
+ description: "Generate parametric QR codes in artistic styles (normal, line, dsj, bubble, func) from the qrbtf library. Renders SVG and rasterizes to image assets. Black background with bright modules by default.",
91
+ schema: {
92
+ book_id: z.string().describe("Book id"),
93
+ value: z.string().min(1).describe("Text or URL encoded in the QR code"),
94
+ style: z
95
+ .enum(["normal", "line", "dsj", "bubble", "func"])
96
+ .optional()
97
+ .describe("QR style (default: normal)"),
98
+ point_type: z
99
+ .enum(["rect", "round", "rand"])
100
+ .optional()
101
+ .describe("Data point shape for normal/func styles"),
102
+ pos_type: z
103
+ .enum(["rect", "round", "planet", "roundRect"])
104
+ .optional()
105
+ .describe("Position detection pattern shape"),
106
+ direction: z
107
+ .enum(["left-right", "up-down", "h-v", "loop", "topLeft-bottomRight", "topRight-bottomLeft", "cross"])
108
+ .optional()
109
+ .describe("Line direction for line style"),
110
+ func_type: z
111
+ .enum(["A", "B"])
112
+ .optional()
113
+ .describe("Function type for func style"),
114
+ other_color: z
115
+ .string()
116
+ .optional()
117
+ .describe("Data module color (hex, e.g. #FFFFFF)"),
118
+ other_color_2: z
119
+ .string()
120
+ .optional()
121
+ .describe("Secondary color for func style (hex)"),
122
+ pos_color: z
123
+ .string()
124
+ .optional()
125
+ .describe("Position pattern color (hex)"),
126
+ bg_color: z
127
+ .string()
128
+ .optional()
129
+ .describe("Background / quiet-zone color (hex, default #000000)"),
130
+ size: z
131
+ .number()
132
+ .int()
133
+ .min(256)
134
+ .max(4096)
135
+ .optional()
136
+ .describe(`Output edge length in pixels (default ${DEFAULT_QRBTF_SIZE})`),
137
+ name: z.string().optional().describe("Optional base name for the generated asset"),
138
+ },
139
+ handler: async (args) => {
140
+ const { value, style, point_type, pos_type, direction, func_type, other_color, other_color_2, pos_color, bg_color, size, name, } = args;
141
+ const resolvedValue = value.trim();
142
+ if (!resolvedValue)
143
+ return error("value must be a non-empty string");
144
+ const resolvedStyle = style ?? "normal";
145
+ const resolvedSize = size ?? DEFAULT_QRBTF_SIZE;
146
+ const params = {};
147
+ if (point_type)
148
+ params.pointType = point_type;
149
+ if (pos_type)
150
+ params.posType = pos_type;
151
+ if (direction)
152
+ params.direction = direction;
153
+ if (func_type)
154
+ params.funcType = func_type;
155
+ if (other_color)
156
+ params.otherColor = other_color;
157
+ if (other_color_2)
158
+ params.otherColor2 = other_color_2;
159
+ if (pos_color)
160
+ params.posColor = pos_color;
161
+ if (bg_color)
162
+ params.bgColor = bg_color;
163
+ const r = buildQrbtfSvg(resolvedValue, resolvedStyle, params);
164
+ let files;
165
+ try {
166
+ files = await buildQrbtfFiles(r.content, r.viewBox, r.bgColor, resolvedSize);
167
+ }
168
+ catch (err) {
169
+ return error(`Failed to render QR: ${err instanceof Error ? err.message : String(err)}`);
170
+ }
171
+ const stem = name ?? resolvedStyle;
172
+ const createdAt = new Date().toISOString();
173
+ const metadata = {
174
+ origin: "create_qrbtf",
175
+ qrbtf_style: resolvedStyle,
176
+ created_at: createdAt,
177
+ value: resolvedValue,
178
+ width: resolvedSize,
179
+ height: resolvedSize,
180
+ qr_version: r.version,
181
+ error_correction: "H",
182
+ bg_color: r.bgColor,
183
+ module_count: r.moduleCount,
184
+ };
185
+ if (other_color)
186
+ metadata.other_color = other_color;
187
+ if (pos_color)
188
+ metadata.pos_color = pos_color;
189
+ if (point_type)
190
+ metadata.point_type = point_type;
191
+ if (pos_type)
192
+ metadata.pos_type = pos_type;
193
+ if (direction)
194
+ metadata.direction = direction;
195
+ if (func_type)
196
+ metadata.func_type = func_type;
197
+ const resultAsset = await persistQrbtfAsset(engine, derivedLabel("qrbtf", stem), files, metadata);
198
+ if (!resultAsset.ok)
199
+ return error(resultAsset.error);
200
+ return {
201
+ content: [{
202
+ type: "text",
203
+ text: JSON.stringify({
204
+ asset_id: resultAsset.assetId,
205
+ value: resolvedValue,
206
+ style: resolvedStyle,
207
+ size: resolvedSize,
208
+ qr_version: r.version,
209
+ bg_color: r.bgColor,
210
+ }),
211
+ }],
212
+ };
213
+ },
214
+ };
215
+ }