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,523 @@
1
+ import { createHash } from "node:crypto";
2
+ import sharp from "sharp";
3
+ import { findPrimaryMediaFile } from "videobook-engine";
4
+ import { z } from "zod";
5
+ export const IMAGE_SUBJECT_MAP_VERSION = 1;
6
+ const IMAGE_SUBJECT_MAP_METADATA_KEY = "image-subject-map.v1";
7
+ const imageSubjectMapWriteChains = new Map();
8
+ const normalizedBoxSchema = z.object({
9
+ x: z.number().min(0).max(1),
10
+ y: z.number().min(0).max(1),
11
+ width: z.number().positive().max(1),
12
+ height: z.number().positive().max(1),
13
+ }).strict().refine((box) => box.x + box.width <= 1.001 && box.y + box.height <= 1.001, { message: "subject box extends beyond the image" });
14
+ const subjectSchema = z.object({
15
+ id: z.string().min(1),
16
+ label: z.string().min(1),
17
+ description: z.string(),
18
+ color: z.string().regex(/^#[0-9a-f]{6}$/i),
19
+ normalizedBox: normalizedBoxSchema,
20
+ layerFile: z.string().min(1),
21
+ thumbnailFile: z.string().min(1),
22
+ zIndex: z.number().int(),
23
+ exactEdit: z.boolean(),
24
+ maskIndex: z.number().int().min(0).optional(),
25
+ }).strict();
26
+ const subjectsSchema = z.array(subjectSchema).superRefine((subjects, context) => {
27
+ const ids = new Set();
28
+ const zIndexes = new Set();
29
+ for (const [index, subject] of subjects.entries()) {
30
+ if (ids.has(subject.id)) {
31
+ context.addIssue({
32
+ code: "custom",
33
+ path: [index, "id"],
34
+ message: `duplicate subject id ${subject.id}`,
35
+ });
36
+ }
37
+ if (zIndexes.has(subject.zIndex)) {
38
+ context.addIssue({
39
+ code: "custom",
40
+ path: [index, "zIndex"],
41
+ message: `duplicate subject z-index ${subject.zIndex}`,
42
+ });
43
+ }
44
+ ids.add(subject.id);
45
+ zIndexes.add(subject.zIndex);
46
+ }
47
+ });
48
+ const recordBaseSchema = z.object({
49
+ version: z.literal(IMAGE_SUBJECT_MAP_VERSION),
50
+ artifactId: z.string().min(1),
51
+ objectHash: z.string().min(1),
52
+ width: z.number().int().positive(),
53
+ height: z.number().int().positive(),
54
+ jobId: z.string().min(1),
55
+ }).strict();
56
+ const analyzingRecordSchema = recordBaseSchema.extend({
57
+ status: z.literal("analyzing"),
58
+ startedAt: z.string().min(1),
59
+ taskId: z.string().min(1).optional(),
60
+ });
61
+ const failedRecordSchema = recordBaseSchema.extend({
62
+ status: z.literal("failed"),
63
+ error: z.string().min(1),
64
+ failedAt: z.string().min(1),
65
+ taskId: z.string().min(1).optional(),
66
+ });
67
+ const readyRecordSchema = recordBaseSchema.extend({
68
+ status: z.literal("ready"),
69
+ provider: z.literal("kie"),
70
+ model: z.literal("seedream/5-pro-layer-decomposition"),
71
+ analysisId: z.string().min(1),
72
+ analyzedAt: z.string().min(1),
73
+ taskId: z.string().min(1),
74
+ grokSourceTaskId: z.string().min(1).optional(),
75
+ baseFile: z.string().min(1),
76
+ subjects: subjectsSchema,
77
+ });
78
+ export const imageSubjectMapRecordSchema = z.discriminatedUnion("status", [
79
+ analyzingRecordSchema,
80
+ failedRecordSchema,
81
+ readyRecordSchema,
82
+ ]);
83
+ export function grokMaskIndex(index) {
84
+ return Number.isInteger(index) && index >= 0 ? index : undefined;
85
+ }
86
+ const SUBJECT_BOX_IOU_THRESHOLD = 0.25;
87
+ function boxIou(left, right) {
88
+ const x1 = Math.max(left.x, right.x);
89
+ const y1 = Math.max(left.y, right.y);
90
+ const x2 = Math.min(left.x + left.width, right.x + right.width);
91
+ const y2 = Math.min(left.y + left.height, right.y + right.height);
92
+ const overlap = Math.max(0, x2 - x1) * Math.max(0, y2 - y1);
93
+ const union = left.width * left.height + right.width * right.height - overlap;
94
+ return union > 0 ? overlap / union : 0;
95
+ }
96
+ export function parseGrokSegmentMapResult(resultJson) {
97
+ let parsed;
98
+ try {
99
+ parsed = JSON.parse(resultJson);
100
+ }
101
+ catch {
102
+ throw new Error("Grok segment map returned invalid result JSON");
103
+ }
104
+ const root = parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)
105
+ ? parsed
106
+ : null;
107
+ const nested = root && root.resultObject !== null && typeof root.resultObject === "object"
108
+ && !Array.isArray(root.resultObject)
109
+ ? root.resultObject
110
+ : null;
111
+ const raw = [root?.segments, nested?.segments, root?.result, nested?.result]
112
+ .find((value) => Array.isArray(value));
113
+ if (!Array.isArray(raw))
114
+ return [];
115
+ return raw.flatMap((entry) => {
116
+ if (entry === null || typeof entry !== "object" || Array.isArray(entry))
117
+ return [];
118
+ const item = entry;
119
+ const index = Number(item.index);
120
+ if (!Number.isInteger(index))
121
+ return [];
122
+ const name = typeof item.name === "string" && item.name.trim() ? item.name.trim() : undefined;
123
+ const maskUrl = typeof item.maskUrl === "string" && item.maskUrl
124
+ ? item.maskUrl
125
+ : typeof item.mask_url === "string" && item.mask_url
126
+ ? item.mask_url
127
+ : undefined;
128
+ const boxSource = item.normalizedBox ?? item.normalized_box ?? item.box;
129
+ const box = normalizedBoxSchema.safeParse(boxSource);
130
+ return [{
131
+ index,
132
+ ...(name ? { name } : {}),
133
+ ...(maskUrl ? { maskUrl } : {}),
134
+ ...(box.success ? { normalizedBox: box.data } : {}),
135
+ }];
136
+ });
137
+ }
138
+ export function applyGrokSegmentsToSubjects(subjects, segments) {
139
+ const usable = segments.filter((segment) => grokMaskIndex(segment.index) !== undefined);
140
+ const claimed = new Set();
141
+ return subjects.map((subject) => {
142
+ const byName = usable.find((segment) => !claimed.has(segment.index)
143
+ && segment.name
144
+ && segment.name.toLowerCase() === subject.label.toLowerCase());
145
+ const boxMatches = usable.flatMap((segment) => {
146
+ if (claimed.has(segment.index) || !segment.normalizedBox)
147
+ return [];
148
+ const iou = boxIou(subject.normalizedBox, segment.normalizedBox);
149
+ return iou >= SUBJECT_BOX_IOU_THRESHOLD ? [{ segment, iou }] : [];
150
+ }).sort((left, right) => right.iou - left.iou);
151
+ const match = byName ?? boxMatches[0]?.segment;
152
+ if (!match) {
153
+ const { maskIndex: _dropped, ...rest } = subject;
154
+ return { ...rest, exactEdit: false };
155
+ }
156
+ claimed.add(match.index);
157
+ const maskIndex = grokMaskIndex(match.index);
158
+ if (maskIndex === undefined) {
159
+ const { maskIndex: _dropped, ...rest } = subject;
160
+ return { ...rest, exactEdit: false };
161
+ }
162
+ return { ...subject, exactEdit: true, maskIndex };
163
+ });
164
+ }
165
+ export function exactEditMaskIndexes(subjects, subjectIds) {
166
+ const selected = subjects.filter((subject) => subjectIds.includes(subject.id));
167
+ if (selected.length === 0)
168
+ return null;
169
+ const indexes = [];
170
+ for (const subject of selected) {
171
+ if (!subject.exactEdit || subject.maskIndex === undefined)
172
+ return null;
173
+ const index = grokMaskIndex(subject.maskIndex);
174
+ if (index === undefined)
175
+ return null;
176
+ indexes.push(index);
177
+ }
178
+ return [...new Set(indexes)].sort((left, right) => left - right);
179
+ }
180
+ const SUBJECT_COLORS = [
181
+ "#ef4444",
182
+ "#3b82f6",
183
+ "#22c55e",
184
+ "#f59e0b",
185
+ "#a855f7",
186
+ "#06b6d4",
187
+ "#ec4899",
188
+ "#84cc16",
189
+ "#f97316",
190
+ "#6366f1",
191
+ "#14b8a6",
192
+ "#eab308",
193
+ ];
194
+ function imageFileUrl(bookId, artifactId, filename) {
195
+ const encodedFilename = filename
196
+ .split("/")
197
+ .map((segment) => encodeURIComponent(segment))
198
+ .join("/");
199
+ return `/files/${encodeURIComponent(bookId)}/${encodeURIComponent(artifactId)}/${encodedFilename}`;
200
+ }
201
+ function positiveDimension(value) {
202
+ const number = Number(value);
203
+ return Number.isInteger(number) && number > 0 ? number : null;
204
+ }
205
+ export async function resolveImageSubjectSource(engine, artifactId) {
206
+ const artifact = engine.artifacts.get(artifactId);
207
+ if (!artifact.ok)
208
+ throw new Error(artifact.error.message);
209
+ if (artifact.value.kind !== "image") {
210
+ throw new Error(`Image subject analysis requires an image artifact, got ${artifact.value.kind}`);
211
+ }
212
+ const manifest = await engine.files.manifest(artifactId);
213
+ if (!manifest.ok)
214
+ throw new Error(manifest.error.message);
215
+ const file = findPrimaryMediaFile(manifest.value.files, artifact.value.kind);
216
+ if (!file)
217
+ throw new Error(`No primary image file found for ${artifactId}`);
218
+ const metadata = await engine.metadata.artifacts.read(artifactId, "original");
219
+ let width = metadata.ok ? positiveDimension(metadata.value?.width) : null;
220
+ let height = metadata.ok ? positiveDimension(metadata.value?.height) : null;
221
+ if (!width || !height) {
222
+ const read = await engine.files.read(artifactId, file.name);
223
+ if (!read.ok)
224
+ throw new Error(read.error.message);
225
+ const probed = await sharp(read.value).metadata();
226
+ width = positiveDimension(probed.width);
227
+ height = positiveDimension(probed.height);
228
+ }
229
+ if (!width || !height) {
230
+ throw new Error(`Could not determine dimensions for ${artifactId}`);
231
+ }
232
+ return {
233
+ artifactId,
234
+ file,
235
+ objectHash: file.objectHash,
236
+ width,
237
+ height,
238
+ };
239
+ }
240
+ export async function readImageSubjectMapRecord(engine, artifactId) {
241
+ const result = await engine.metadata.artifacts.read(artifactId, IMAGE_SUBJECT_MAP_METADATA_KEY);
242
+ if (!result.ok)
243
+ return null;
244
+ const parsed = imageSubjectMapRecordSchema.safeParse(result.value);
245
+ return parsed.success ? parsed.data : null;
246
+ }
247
+ async function serializeImageSubjectMapWrite(artifactId, operation) {
248
+ const previous = imageSubjectMapWriteChains.get(artifactId) ?? Promise.resolve();
249
+ const result = previous.catch(() => undefined).then(operation);
250
+ const tail = result.then(() => undefined, () => undefined);
251
+ imageSubjectMapWriteChains.set(artifactId, tail);
252
+ return await result.finally(() => {
253
+ if (imageSubjectMapWriteChains.get(artifactId) === tail) {
254
+ imageSubjectMapWriteChains.delete(artifactId);
255
+ }
256
+ });
257
+ }
258
+ async function writeImageSubjectMapRecordUnlocked(engine, record) {
259
+ const parsed = imageSubjectMapRecordSchema.parse(record);
260
+ const result = await engine.metadata.artifacts.write(record.artifactId, IMAGE_SUBJECT_MAP_METADATA_KEY, parsed);
261
+ if (!result.ok)
262
+ throw new Error(result.error.message);
263
+ }
264
+ export async function writeImageSubjectMapRecord(engine, record) {
265
+ await serializeImageSubjectMapWrite(record.artifactId, async () => {
266
+ await writeImageSubjectMapRecordUnlocked(engine, record);
267
+ });
268
+ }
269
+ export async function writeCurrentImageSubjectAnalysisRecord(engine, record) {
270
+ return await serializeImageSubjectMapWrite(record.artifactId, async () => {
271
+ const current = await readImageSubjectMapRecord(engine, record.artifactId);
272
+ if (current?.status !== "analyzing"
273
+ || current.objectHash !== record.objectHash
274
+ || current.jobId !== record.jobId) {
275
+ return false;
276
+ }
277
+ await writeImageSubjectMapRecordUnlocked(engine, record);
278
+ return true;
279
+ });
280
+ }
281
+ export function subjectAnalysisProviderFailureMessage(message, code) {
282
+ const detail = message?.trim() || code?.trim() || "Unknown provider error";
283
+ const codeDetail = code?.trim() && code.trim() !== detail
284
+ ? ` (${code.trim()})`
285
+ : "";
286
+ const normalized = `${detail}${codeDetail}`.replace(/[.\s]+$/u, "");
287
+ if (normalized.startsWith("Subject analysis provider failed:")
288
+ && normalized.includes("Retry in a moment")) {
289
+ return `${normalized}.`;
290
+ }
291
+ return `Subject analysis provider failed: ${normalized}. Retry in a moment.`;
292
+ }
293
+ export function imageSubjectMapResponse(bookId, source, record) {
294
+ if (!record || record.objectHash !== source.objectHash) {
295
+ return {
296
+ status: "idle",
297
+ artifact_id: source.artifactId,
298
+ object_hash: source.objectHash,
299
+ width: source.width,
300
+ height: source.height,
301
+ base_url: null,
302
+ error: null,
303
+ job_id: null,
304
+ subjects: [],
305
+ };
306
+ }
307
+ if (record.status !== "ready") {
308
+ return {
309
+ status: record.status,
310
+ artifact_id: source.artifactId,
311
+ object_hash: source.objectHash,
312
+ width: source.width,
313
+ height: source.height,
314
+ base_url: null,
315
+ error: record.status === "failed" ? record.error : null,
316
+ job_id: record.jobId,
317
+ ...(record.status === "analyzing"
318
+ ? { started_at: record.startedAt }
319
+ : {}),
320
+ subjects: [],
321
+ };
322
+ }
323
+ return {
324
+ status: "ready",
325
+ artifact_id: source.artifactId,
326
+ object_hash: source.objectHash,
327
+ width: record.width,
328
+ height: record.height,
329
+ base_url: imageFileUrl(bookId, source.artifactId, record.baseFile),
330
+ error: null,
331
+ job_id: record.jobId,
332
+ subjects: record.subjects.map((subject) => ({
333
+ id: subject.id,
334
+ label: subject.label,
335
+ description: subject.description,
336
+ color: subject.color,
337
+ normalized_box: subject.normalizedBox,
338
+ layer_url: imageFileUrl(bookId, source.artifactId, subject.layerFile),
339
+ thumbnail_url: imageFileUrl(bookId, source.artifactId, subject.thumbnailFile),
340
+ z_index: subject.zIndex,
341
+ capabilities: {
342
+ remove: true,
343
+ exact_edit: subject.exactEdit,
344
+ },
345
+ })),
346
+ };
347
+ }
348
+ function clamp(value, min, max) {
349
+ return Math.min(max, Math.max(min, value));
350
+ }
351
+ function pixelBox(box, width, height) {
352
+ const left = clamp(Math.round(box.x * width), 0, width - 1);
353
+ const top = clamp(Math.round(box.y * height), 0, height - 1);
354
+ const boxWidth = clamp(Math.round(box.width * width), 1, width - left);
355
+ const boxHeight = clamp(Math.round(box.height * height), 1, height - top);
356
+ return { left, top, width: boxWidth, height: boxHeight };
357
+ }
358
+ async function fullCanvasLayer(input, sourceWidth, sourceHeight, box) {
359
+ const metadata = await sharp(input).metadata();
360
+ if (metadata.width === sourceWidth && metadata.height === sourceHeight) {
361
+ return sharp(input).ensureAlpha().png().toBuffer();
362
+ }
363
+ if (box) {
364
+ const placement = pixelBox(box, sourceWidth, sourceHeight);
365
+ const resized = await sharp(input)
366
+ .ensureAlpha()
367
+ .resize(placement.width, placement.height, { fit: "fill" })
368
+ .png()
369
+ .toBuffer();
370
+ return sharp({
371
+ create: {
372
+ width: sourceWidth,
373
+ height: sourceHeight,
374
+ channels: 4,
375
+ background: { r: 0, g: 0, b: 0, alpha: 0 },
376
+ },
377
+ }).composite([{ input: resized, left: placement.left, top: placement.top }])
378
+ .png()
379
+ .toBuffer();
380
+ }
381
+ throw new Error(`Seedream layer dimensions ${metadata.width ?? 0}x${metadata.height ?? 0} do not match ${sourceWidth}x${sourceHeight} and have no valid bounding box`);
382
+ }
383
+ async function alphaBoundingBox(input) {
384
+ const { data, info } = await sharp(input)
385
+ .ensureAlpha()
386
+ .raw()
387
+ .toBuffer({ resolveWithObject: true });
388
+ let minX = info.width;
389
+ let minY = info.height;
390
+ let maxX = -1;
391
+ let maxY = -1;
392
+ for (let y = 0; y < info.height; y += 1) {
393
+ for (let x = 0; x < info.width; x += 1) {
394
+ const alpha = data[(y * info.width + x) * info.channels + 3];
395
+ if (alpha === undefined || alpha <= 4)
396
+ continue;
397
+ minX = Math.min(minX, x);
398
+ minY = Math.min(minY, y);
399
+ maxX = Math.max(maxX, x);
400
+ maxY = Math.max(maxY, y);
401
+ }
402
+ }
403
+ if (maxX < minX || maxY < minY) {
404
+ throw new Error("Seedream returned an all-transparent subject layer");
405
+ }
406
+ return {
407
+ x: minX / info.width,
408
+ y: minY / info.height,
409
+ width: (maxX - minX + 1) / info.width,
410
+ height: (maxY - minY + 1) / info.height,
411
+ };
412
+ }
413
+ function subjectId(layer, zIndex) {
414
+ const hash = createHash("sha256")
415
+ .update(layer)
416
+ .update(":")
417
+ .update(String(zIndex))
418
+ .digest("hex")
419
+ .slice(0, 16);
420
+ return `subject-${hash}`;
421
+ }
422
+ async function subjectThumbnail(layer, box, width, height) {
423
+ const region = pixelBox(box, width, height);
424
+ return sharp(layer)
425
+ .extract(region)
426
+ .resize(192, 192, {
427
+ fit: "contain",
428
+ background: { r: 0, g: 0, b: 0, alpha: 0 },
429
+ })
430
+ .png()
431
+ .toBuffer();
432
+ }
433
+ export async function persistSeedreamSubjectLayers(engine, source, input) {
434
+ const analysisId = input.taskId.replace(/[^a-zA-Z0-9_-]/g, "-");
435
+ const root = `.image-subjects/${source.objectHash}/${analysisId}`;
436
+ const baseResponse = await fetch(input.baseUrl, { signal: input.signal });
437
+ if (!baseResponse.ok) {
438
+ throw new Error(`Failed to download Seedream base: ${baseResponse.status} ${baseResponse.statusText}`);
439
+ }
440
+ const base = await sharp(Buffer.from(await baseResponse.arrayBuffer()))
441
+ .resize(source.width, source.height, { fit: "fill" })
442
+ .png()
443
+ .toBuffer();
444
+ const baseFile = `${root}/base.png`;
445
+ const baseWrite = await engine.files.write(source.artifactId, baseFile, base);
446
+ if (!baseWrite.ok)
447
+ throw new Error(baseWrite.error.message);
448
+ const ordered = [...input.layers].sort((left, right) => left.zIndex - right.zIndex);
449
+ const subjects = [];
450
+ for (const [index, layer] of ordered.entries()) {
451
+ const response = await fetch(layer.url, { signal: input.signal });
452
+ if (!response.ok) {
453
+ throw new Error(`Failed to download Seedream layer: ${response.status} ${response.statusText}`);
454
+ }
455
+ const canvas = await fullCanvasLayer(Buffer.from(await response.arrayBuffer()), source.width, source.height, layer.normalizedBox);
456
+ const normalizedBox = await alphaBoundingBox(canvas);
457
+ const id = subjectId(canvas, layer.zIndex);
458
+ const layerFile = `${root}/layer-${index}-${id}.png`;
459
+ const thumbnailFile = `${root}/thumbnail-${index}-${id}.png`;
460
+ const thumbnail = await subjectThumbnail(canvas, normalizedBox, source.width, source.height);
461
+ const [layerWrite, thumbnailWrite] = await Promise.all([
462
+ engine.files.write(source.artifactId, layerFile, canvas),
463
+ engine.files.write(source.artifactId, thumbnailFile, thumbnail),
464
+ ]);
465
+ if (!layerWrite.ok)
466
+ throw new Error(layerWrite.error.message);
467
+ if (!thumbnailWrite.ok)
468
+ throw new Error(thumbnailWrite.error.message);
469
+ subjects.push({
470
+ id,
471
+ label: layer.name?.trim() || `Subject ${index + 1}`,
472
+ description: layer.description?.trim() || "",
473
+ color: SUBJECT_COLORS[index % SUBJECT_COLORS.length],
474
+ normalizedBox,
475
+ layerFile,
476
+ thumbnailFile,
477
+ zIndex: layer.zIndex,
478
+ exactEdit: false,
479
+ });
480
+ }
481
+ return readyRecordSchema.parse({
482
+ version: IMAGE_SUBJECT_MAP_VERSION,
483
+ status: "ready",
484
+ artifactId: source.artifactId,
485
+ objectHash: source.objectHash,
486
+ width: source.width,
487
+ height: source.height,
488
+ jobId: input.jobId,
489
+ provider: "kie",
490
+ model: "seedream/5-pro-layer-decomposition",
491
+ analysisId,
492
+ analyzedAt: new Date().toISOString(),
493
+ taskId: input.taskId,
494
+ baseFile,
495
+ subjects,
496
+ });
497
+ }
498
+ export async function composeImageSubjectRemoval(engine, sourceArtifactId, record, removedSubjectIds) {
499
+ const selected = new Set(removedSubjectIds);
500
+ const known = new Set(record.subjects.map((subject) => subject.id));
501
+ const missing = [...selected].filter((id) => !known.has(id));
502
+ if (missing.length > 0) {
503
+ throw new Error(`Unknown subject ids: ${missing.join(", ")}`);
504
+ }
505
+ const baseRead = await engine.files.read(sourceArtifactId, record.baseFile);
506
+ if (!baseRead.ok)
507
+ throw new Error(baseRead.error.message);
508
+ const retained = record.subjects
509
+ .filter((subject) => !selected.has(subject.id))
510
+ .sort((left, right) => left.zIndex - right.zIndex);
511
+ const layers = [];
512
+ for (const subject of retained) {
513
+ const read = await engine.files.read(sourceArtifactId, subject.layerFile);
514
+ if (!read.ok)
515
+ throw new Error(read.error.message);
516
+ layers.push(Buffer.from(read.value));
517
+ }
518
+ return sharp(baseRead.value)
519
+ .resize(record.width, record.height, { fit: "fill" })
520
+ .composite(layers.map((layer) => ({ input: layer, left: 0, top: 0 })))
521
+ .png()
522
+ .toBuffer();
523
+ }
package/dist/image.js ADDED
@@ -0,0 +1,46 @@
1
+ import sharp from "sharp";
2
+ export async function decodeHeicToPng(buffer) {
3
+ const { default: decode } = await import("heic-decode");
4
+ const { width, height, data } = await decode({ buffer });
5
+ return sharp(Buffer.from(data), { raw: { width, height, channels: 4 } })
6
+ .png()
7
+ .toBuffer();
8
+ }
9
+ export async function probeImage(buffer) {
10
+ const metadata = await sharp(buffer).metadata();
11
+ const width = metadata.width ?? 0;
12
+ const height = metadata.height ?? 0;
13
+ return { width, height };
14
+ }
15
+ export async function cropToFill(buffer, targetW, targetH) {
16
+ return sharp(buffer)
17
+ .resize(targetW, targetH, { fit: "cover", position: "centre" })
18
+ .jpeg({ quality: 95 })
19
+ .toBuffer();
20
+ }
21
+ /** Extract a normalized (0..1) region and resize to target dimensions as JPEG. */
22
+ export async function cropRegion(buffer, rect, targetW, targetH) {
23
+ const meta = await sharp(buffer).metadata();
24
+ const srcW = meta.width ?? 0;
25
+ const srcH = meta.height ?? 0;
26
+ if (!srcW || !srcH) {
27
+ throw new Error("Could not probe image dimensions");
28
+ }
29
+ let width = Math.max(1, Math.round(rect.w * srcW));
30
+ let height = Math.max(1, Math.round(rect.h * srcH));
31
+ if (width > srcW)
32
+ width = srcW;
33
+ if (height > srcH)
34
+ height = srcH;
35
+ let left = Math.max(0, Math.round(rect.x * srcW));
36
+ let top = Math.max(0, Math.round(rect.y * srcH));
37
+ if (left + width > srcW)
38
+ left = Math.max(0, srcW - width);
39
+ if (top + height > srcH)
40
+ top = Math.max(0, srcH - height);
41
+ return sharp(buffer)
42
+ .extract({ left, top, width, height })
43
+ .resize(targetW, targetH, { fit: "fill" })
44
+ .jpeg({ quality: 95 })
45
+ .toBuffer();
46
+ }
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ import "./load-env.js";
2
+ import { logger } from "./logger.js";
3
+ import { installSignalHandlers, startServer } from "./serve.js";
4
+ const port = Number(process.env.PORT ?? 3001);
5
+ const starting = startServer({ port, ui: true });
6
+ installSignalHandlers(starting);
7
+ starting
8
+ .then((running) => running.ready)
9
+ .catch((error) => {
10
+ logger.error("startup-failed", { error, port });
11
+ process.exit(1);
12
+ });
@@ -0,0 +1,60 @@
1
+ const JOB_CANCELLATION_GRACE_MS = 10_000;
2
+ const JOB_TIMEOUTS_MS = new Map([
3
+ ["repair_search_indexes", 15 * 60_000],
4
+ ["render_project", 30 * 60_000],
5
+ ["render_timeline", 30 * 60_000],
6
+ ["render_qr_outro", 15 * 60_000],
7
+ ["analyze_keyword_placement", 5 * 60_000],
8
+ ["analyze_alive_captions", 10 * 60_000],
9
+ ["osg_placement", 5 * 60_000],
10
+ ["analyze_image_subjects", 30 * 60_000],
11
+ ["generate_image_subject_edit", 30 * 60_000],
12
+ ["burn_watermark", 15 * 60_000],
13
+ // Whole-job backstop above the 30 min /synth fetch timeout, so the client's
14
+ // more specific error wins when ace-server hangs; covers synth-lock wait,
15
+ // first-request model load, and the ffmpeg passes on a 240 s CPU-only run.
16
+ ["generate_acestep_cover", 45 * 60_000],
17
+ ]);
18
+ export class JobDeadlineError extends Error {
19
+ jobType;
20
+ timeoutMs;
21
+ constructor(jobType, timeoutMs) {
22
+ super(`Job ${jobType} timed out after ${timeoutMs}ms`);
23
+ this.name = "JobDeadlineError";
24
+ this.jobType = jobType;
25
+ this.timeoutMs = timeoutMs;
26
+ }
27
+ }
28
+ function getJobTimeoutMs(jobType) {
29
+ return JOB_TIMEOUTS_MS.get(jobType) ?? null;
30
+ }
31
+ export async function runWithJobDeadline(jobType, run, timeoutMs = getJobTimeoutMs(jobType), cancellationGraceMs = JOB_CANCELLATION_GRACE_MS) {
32
+ const controller = new AbortController();
33
+ const work = Promise.resolve().then(() => run(controller.signal));
34
+ if (timeoutMs === null)
35
+ return work;
36
+ let deadlineTimer;
37
+ const settled = work.then((value) => ({ state: "resolved", value }), (error) => ({ state: "rejected", error }));
38
+ const deadline = new Promise((resolve) => {
39
+ deadlineTimer = setTimeout(() => resolve({ state: "deadline" }), timeoutMs);
40
+ deadlineTimer.unref?.();
41
+ });
42
+ const first = await Promise.race([settled, deadline]);
43
+ if (deadlineTimer !== undefined)
44
+ clearTimeout(deadlineTimer);
45
+ if (first.state === "resolved")
46
+ return first.value;
47
+ if (first.state === "rejected")
48
+ throw first.error;
49
+ const timeoutError = new JobDeadlineError(jobType, timeoutMs);
50
+ controller.abort(timeoutError);
51
+ let graceTimer;
52
+ const grace = new Promise((resolve) => {
53
+ graceTimer = setTimeout(resolve, cancellationGraceMs);
54
+ graceTimer.unref?.();
55
+ });
56
+ await Promise.race([settled.then(() => undefined), grace]);
57
+ if (graceTimer !== undefined)
58
+ clearTimeout(graceTimer);
59
+ throw timeoutError;
60
+ }
@@ -0,0 +1,30 @@
1
+ import { createAlibaba } from "../usage-providers.js";
2
+ import { uploadFile } from "@apicity/alibaba";
3
+ import { prepareAssetForUpload, uploadBytesToFreeHost } from "../upload.js";
4
+ import { getApiKey } from "../api-key.js";
5
+ import { logger } from "../logger.js";
6
+ // Upload an asset to DashScope's OSS bucket via @apicity/alibaba's uploadFile
7
+ // helper. Falls back to the free-host rotation if the policy fetch or OSS
8
+ // POST fails.
9
+ export async function uploadAssetToAlibaba(engine, assetId, filename, model) {
10
+ const { data, hashedFilename, contentType } = await prepareAssetForUpload(engine, assetId, filename);
11
+ const apiKey = getApiKey("DASHSCOPE_API_KEY");
12
+ if (apiKey) {
13
+ try {
14
+ const provider = createAlibaba({ apiKey, timeout: 60_000 });
15
+ return await uploadFile(provider, {
16
+ model,
17
+ data,
18
+ filename: hashedFilename,
19
+ contentType,
20
+ });
21
+ }
22
+ catch (err) {
23
+ logger.warn("alibaba-upload-fallback", { error: err });
24
+ }
25
+ }
26
+ else {
27
+ logger.warn("alibaba-upload-unconfigured");
28
+ }
29
+ return uploadBytesToFreeHost(data, hashedFilename);
30
+ }