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
package/dist/queue.js ADDED
@@ -0,0 +1,810 @@
1
+ import { appendDiagnostic, registerDiagnosticName } from "./diagnostics.js";
2
+ import { createEngine, } from "videobook-engine";
3
+ import { activeBookEngine, withBookContext } from "./book-context.js";
4
+ import { settleFinishedUsage } from "./provider-usage.js";
5
+ import { bookManager } from "./engine.js";
6
+ import { ensureJobHandlersLoaded } from "./jobs/index.js";
7
+ import { runWithJobDeadline } from "./job-deadline.js";
8
+ import { logger } from "./logger.js";
9
+ import { testQueueCleanup } from "./test-queue-cleanup.js";
10
+ import { preserveProviderTask } from "./provider-task-record.js";
11
+ const handlers = new Map();
12
+ const runnerByBook = new Map();
13
+ const startingRunnerByBook = new Map();
14
+ const stoppingRunnerByBook = new Map();
15
+ let stoppingQueue = null;
16
+ const jobIdIndex = new Map();
17
+ const testEngines = new Map();
18
+ let activeTestRoot = null;
19
+ let activeTestBookId = null;
20
+ if (process.env.VITEST) {
21
+ testQueueCleanup.waitForIdle = waitForBookQueuesIdleForTests;
22
+ testQueueCleanup.drain = drainBookQueuesForTests;
23
+ }
24
+ const JOB_INDEX_MAX = 2_000;
25
+ const ASSET_REAPER_INTERVAL_MS = 30_000;
26
+ const DEFAULT_PROVIDER_POLL_INTERVAL_MS = 10_000;
27
+ const SEARCH_REFERENCE_JOB_TYPES = ["prepare_search_reference", "prepare_temporal_search"];
28
+ let providerPollIntervalMs = DEFAULT_PROVIDER_POLL_INTERVAL_MS;
29
+ let providerPollTimeoutOverrideMs;
30
+ let providerPollTickHook = null;
31
+ let providerPollWorkHook = null;
32
+ /** Test-only: shorten poll interval / inject age-out timeout for the driver. */
33
+ export function configureProviderPollDriverForTests(options) {
34
+ if (options.intervalMs !== undefined) {
35
+ providerPollIntervalMs = options.intervalMs;
36
+ }
37
+ if ("timeoutMs" in options) {
38
+ providerPollTimeoutOverrideMs = options.timeoutMs;
39
+ }
40
+ }
41
+ export function resetProviderPollDriverForTests() {
42
+ providerPollIntervalMs = DEFAULT_PROVIDER_POLL_INTERVAL_MS;
43
+ providerPollTimeoutOverrideMs = undefined;
44
+ providerPollTickHook = null;
45
+ providerPollWorkHook = null;
46
+ }
47
+ export function setProviderPollTickHookForTests(hook) {
48
+ providerPollTickHook = hook;
49
+ }
50
+ export function setProviderPollWorkHookForTests(hook) {
51
+ providerPollWorkHook = hook;
52
+ }
53
+ export function isProviderPollDriverActive(bookId) {
54
+ const info = runnerByBook.get(bookId);
55
+ return info?.providerPollTimer != null;
56
+ }
57
+ async function tickProviderPoll(info) {
58
+ if (info.providerPollInFlight || info.pollAbort.signal.aborted)
59
+ return;
60
+ info.providerPollInFlight = true;
61
+ try {
62
+ if (providerPollWorkHook) {
63
+ await providerPollWorkHook(info.pollAbort.signal);
64
+ }
65
+ if (info.pollAbort.signal.aborted)
66
+ return;
67
+ const { reconcileImageSubjectJobFailures } = await import("./jobs/image-subjects.js");
68
+ await reconcileImageSubjectJobFailures(info.engine, info.bookId);
69
+ await settleFinishedUsage(info.engine);
70
+ const pending = info.engine.jobs.pending.findAll();
71
+ if (!pending.ok || pending.value.length === 0)
72
+ return;
73
+ providerPollTickHook?.(info.bookId);
74
+ const { pollPendingProviderTasks } = await import("./tools/reconcile-project-work.js");
75
+ await pollPendingProviderTasks(info.engine, info.bookId, {
76
+ signal: info.pollAbort.signal,
77
+ ...(providerPollTimeoutOverrideMs !== undefined
78
+ ? { timeoutMs: providerPollTimeoutOverrideMs }
79
+ : {}),
80
+ });
81
+ }
82
+ catch (error) {
83
+ if (info.pollAbort.signal.aborted)
84
+ return;
85
+ logger.warn("provider-poll-failed", { bookId: info.bookId, error });
86
+ }
87
+ finally {
88
+ info.providerPollInFlight = false;
89
+ }
90
+ }
91
+ function startProviderPollDriver(info) {
92
+ if (info.providerPollTimer)
93
+ return;
94
+ info.providerPollTimer = setInterval(() => {
95
+ void tickProviderPoll(info);
96
+ }, providerPollIntervalMs);
97
+ }
98
+ function stopProviderPollDriver(info) {
99
+ if (info.providerPollTimer) {
100
+ clearInterval(info.providerPollTimer);
101
+ info.providerPollTimer = null;
102
+ }
103
+ if (!info.pollAbort.signal.aborted)
104
+ info.pollAbort.abort();
105
+ }
106
+ async function awaitProviderPollIdle(info, graceMs = 2_000) {
107
+ const deadline = Date.now() + graceMs;
108
+ while (info.providerPollInFlight && Date.now() < deadline) {
109
+ await new Promise((resolve) => setTimeout(resolve, 10));
110
+ }
111
+ }
112
+ export function registerJobHandler(type, handler) {
113
+ registerDiagnosticName("jobType", type);
114
+ handlers.set(type, handler);
115
+ }
116
+ export function extractAssetId(payload) {
117
+ for (const key of [
118
+ "imgAssetId",
119
+ "vidAssetId",
120
+ "assetId",
121
+ "imageAssetId",
122
+ "audioAssetId",
123
+ "audAssetId",
124
+ ]) {
125
+ const value = payload[key];
126
+ if (typeof value === "string")
127
+ return value;
128
+ }
129
+ return null;
130
+ }
131
+ function extractBookId(payload) {
132
+ const value = payload.bookId;
133
+ if (typeof value !== "string" || value.length === 0) {
134
+ throw new Error("queue payload requires bookId");
135
+ }
136
+ return value;
137
+ }
138
+ function extractType(payload) {
139
+ const value = payload.type;
140
+ if (typeof value !== "string" || value.length === 0) {
141
+ throw new Error("queue payload requires type");
142
+ }
143
+ return value;
144
+ }
145
+ function jobTypeToAssetWorkKind(jobType, payload) {
146
+ if (jobType === "render_project"
147
+ || jobType === "render_timeline"
148
+ || jobType === "render_qr_outro") {
149
+ const orientation = payload.orientation;
150
+ const normalized = orientation === "portrait" || orientation === "landscape" || orientation === "square"
151
+ ? orientation
152
+ : undefined;
153
+ const kind = jobType === "render_timeline" ? "final" : "render";
154
+ return normalized ? { kind, orientation: normalized } : { kind };
155
+ }
156
+ switch (jobType) {
157
+ case "process_video":
158
+ case "process_image":
159
+ case "inject_first_frame":
160
+ case "burn_watermark":
161
+ return { kind: "process" };
162
+ case "analyze_video":
163
+ case "analyze_keyword_placement":
164
+ case "analyze_alive_captions":
165
+ case "suggest_cuts":
166
+ case "suggest_sfx":
167
+ case "analyze_jokes":
168
+ case "detect_scenes":
169
+ return { kind: "analyze" };
170
+ case "transcribe":
171
+ case "transcribe_video_audio":
172
+ return { kind: "transcribe" };
173
+ case "trim_video":
174
+ case "split_video":
175
+ return { kind: "trim" };
176
+ case "crop_video":
177
+ case "crop_image":
178
+ return { kind: "crop" };
179
+ case "edit_cold_open":
180
+ case "splice_video":
181
+ case "smooth_scene_concat":
182
+ return { kind: "splice" };
183
+ case "reverse_video":
184
+ return { kind: "reverse" };
185
+ case "change_video_speed":
186
+ case "timewarp_video":
187
+ case "sync_video_to_audio":
188
+ return { kind: "change_speed" };
189
+ case "replace_video_audio":
190
+ case "clean_video_audio":
191
+ case "remove_sound":
192
+ case "audio_mix":
193
+ return { kind: "replace_audio" };
194
+ case "apply_cuts":
195
+ return { kind: "apply_cuts" };
196
+ case "apply_sfx":
197
+ return { kind: "apply_sfx" };
198
+ case "extract_frame":
199
+ case "extract_notebook_frame":
200
+ case "extract_notebook_audio":
201
+ case "extract_audio":
202
+ return { kind: "extract" };
203
+ case "describe_image":
204
+ return { kind: "describe" };
205
+ case "rewrite_script":
206
+ return { kind: "rewrite_script" };
207
+ case "archive_clip":
208
+ return { kind: "archive" };
209
+ case "restore_archive_clip":
210
+ return { kind: "download" };
211
+ case "download_video":
212
+ return { kind: "download" };
213
+ case "complete_kie_task":
214
+ return { kind: "generate" };
215
+ case "isolate_vocals":
216
+ return { kind: "isolate" };
217
+ case "post_to_x":
218
+ return null;
219
+ default:
220
+ return jobType.startsWith("generate_")
221
+ || jobType.startsWith("extend_")
222
+ || jobType.startsWith("upscale_")
223
+ ? { kind: "generate" }
224
+ : null;
225
+ }
226
+ }
227
+ function providerHandoffKindFor(jobType) {
228
+ if (jobType === "isolate_vocals")
229
+ return "isolate";
230
+ return jobType.startsWith("generate_")
231
+ || jobType.startsWith("extend_")
232
+ || jobType.startsWith("upscale_")
233
+ ? "generate"
234
+ : null;
235
+ }
236
+ export async function resolveBookEngine(bookId) {
237
+ const testEngine = testEngines.get(bookId);
238
+ if (testEngine)
239
+ return testEngine;
240
+ const book = await bookManager.ensure(bookId);
241
+ if (!book.ok)
242
+ throw new Error(book.error.message);
243
+ return book.value.engine;
244
+ }
245
+ export async function listBookEngines() {
246
+ if (activeTestBookId) {
247
+ const engine = testEngines.get(activeTestBookId);
248
+ return engine ? [engine] : [];
249
+ }
250
+ await bookManager.ready();
251
+ return bookManager.list().flatMap((book) => {
252
+ const managed = bookManager.get(book.bookId);
253
+ return managed.ok ? [managed.value.engine] : [];
254
+ });
255
+ }
256
+ function makeWrappedResolver(bookId, engine) {
257
+ return (type) => {
258
+ const handler = handlers.get(type);
259
+ if (!handler)
260
+ return null;
261
+ return async (job, queueSignal) => withBookContext({ bookId, engine, operation: type, ...(job.artifactId ? { artifactId: job.artifactId } : {}) }, () => runWithJobDeadline(type, async (signal) => {
262
+ const jobSignal = queueSignal
263
+ ? AbortSignal.any([signal, queueSignal])
264
+ : signal;
265
+ const lease = { current: null };
266
+ let assetFailureHandled = false;
267
+ let heartbeat = null;
268
+ const context = {
269
+ jobId: job.id,
270
+ signal: jobSignal,
271
+ setLease(next) {
272
+ const current = lease.current;
273
+ if (!current) {
274
+ lease.current = next;
275
+ return;
276
+ }
277
+ if (current.kind === next.kind) {
278
+ const currentId = current.kind === "owner" ? current.ownerId : current.taskId;
279
+ const nextId = next.kind === "owner" ? next.ownerId : next.taskId;
280
+ if (currentId !== nextId)
281
+ throw new Error("Cannot replace an active lease");
282
+ return;
283
+ }
284
+ if (current.kind === "owner" && next.kind === "task") {
285
+ lease.current = next;
286
+ return;
287
+ }
288
+ throw new Error("Invalid lease transition");
289
+ },
290
+ getOwnerId() {
291
+ if (lease.current?.kind !== "owner")
292
+ throw new Error("No active owner lease");
293
+ return lease.current.ownerId;
294
+ },
295
+ markAssetFailureHandled() {
296
+ assetFailureHandled = true;
297
+ },
298
+ };
299
+ const assetId = extractAssetId(job.payload);
300
+ try {
301
+ const providerKind = providerHandoffKindFor(type);
302
+ if (providerKind && assetId) {
303
+ if (type === "generate_image_subject_edit" && job.attempts > 1) {
304
+ await recoverInterruptedSubjectEditArtifact(engine, assetId);
305
+ }
306
+ const claimed = engine.jobs.artifactWork.begin(assetId, {
307
+ kind: providerKind,
308
+ ownerKind: "job",
309
+ durationMs: 15 * 60_000,
310
+ });
311
+ if (!claimed)
312
+ throw new Error(`Could not claim local lease for ${assetId}`);
313
+ context.setLease({ kind: "owner", ownerId: claimed.ownerId });
314
+ heartbeat = setInterval(() => {
315
+ if (lease.current?.kind === "owner") {
316
+ engine.jobs.artifactWork.renew(assetId, claimed.ownerId, 60_000);
317
+ }
318
+ }, 20_000);
319
+ }
320
+ const result = await handler(job.payload, context);
321
+ if (assetId && lease.current?.kind === "task") {
322
+ const pending = engine.jobs.pending.read(assetId);
323
+ if (pending.ok && pending.value) {
324
+ await preserveProviderTask(engine, pending.value).catch((error) => {
325
+ logger.warn("provider-task-record-failed", { bookId, assetId, error });
326
+ });
327
+ }
328
+ }
329
+ if (assetId && lease.current?.kind === "owner") {
330
+ engine.jobs.artifactWork.complete(assetId, lease.current.ownerId);
331
+ }
332
+ return result;
333
+ }
334
+ catch (cause) {
335
+ const detached = isArtifactDetachedJob(type, job.payload);
336
+ if (assetId && !assetFailureHandled && !detached) {
337
+ await dispatchFailure(engine, assetId, lease.current, cause, job);
338
+ }
339
+ else {
340
+ // A job that rolled itself back, or one detached from its
341
+ // artifact, owns the artifact's status — it stays clean rather
342
+ // than showing a spurious error. The attempt still belongs in the
343
+ // append-only log, or a failed-then-restored job would leave no
344
+ // record anywhere that it ran.
345
+ await appendDiagnostic(engine, "job-error", {
346
+ jobType: type,
347
+ operationId: job.operationId,
348
+ error: cause,
349
+ jobId: job.id,
350
+ ...(assetId
351
+ ? { assetId, ...(detached ? { detached: true } : { rolledBack: true }) }
352
+ : {}),
353
+ ...(typeof job.payload.bookId === "string"
354
+ ? { bookId: job.payload.bookId }
355
+ : {}),
356
+ ...(typeof job.payload.notebookId === "string"
357
+ ? { notebookId: job.payload.notebookId }
358
+ : {}),
359
+ ...(typeof job.payload.cellId === "string"
360
+ ? { cellId: job.payload.cellId }
361
+ : {}),
362
+ ...(typeof job.payload.runId === "string"
363
+ ? { runId: job.payload.runId }
364
+ : {}),
365
+ });
366
+ }
367
+ throw cause;
368
+ }
369
+ finally {
370
+ if (heartbeat)
371
+ clearInterval(heartbeat);
372
+ }
373
+ }));
374
+ };
375
+ }
376
+ export async function recoverInterruptedSubjectEditArtifact(engine, artifactId) {
377
+ const view = engine.jobs.artifactWork.read(artifactId);
378
+ if (!view.ok)
379
+ throw new Error(view.error.message);
380
+ if (view.value?.status === "working" && view.value.ownerId) {
381
+ const failed = await engine.jobs.artifactWork.fail(artifactId, view.value.ownerId, { message: "Recovering interrupted image subject edit" });
382
+ if (!failed)
383
+ throw new Error(`Could not release interrupted work for ${artifactId}`);
384
+ }
385
+ const refreshed = engine.jobs.artifactWork.read(artifactId);
386
+ if (!refreshed.ok)
387
+ throw new Error(refreshed.error.message);
388
+ if (refreshed.value?.status !== "error")
389
+ return;
390
+ const cleared = await engine.jobs.failures.clear(artifactId);
391
+ if (!cleared.ok)
392
+ throw new Error(cleared.error.message);
393
+ if (!cleared.value)
394
+ throw new Error(`Could not clear interrupted work for ${artifactId}`);
395
+ }
396
+ async function dispatchFailure(engine, assetId, lease, cause, job) {
397
+ const message = cause instanceof Error ? cause.message : String(cause);
398
+ if (lease?.kind === "owner") {
399
+ await engine.jobs.artifactWork.fail(assetId, lease.ownerId, { message });
400
+ }
401
+ else if (lease?.kind === "task") {
402
+ await engine.jobs.pending.fail(assetId, lease.taskId, { message });
403
+ }
404
+ else {
405
+ await engine.jobs.pending.forceFail(assetId, { message });
406
+ }
407
+ await appendDiagnostic(engine, "job-error", {
408
+ assetId,
409
+ error: cause,
410
+ ...(job ? { jobId: job.id, jobType: job.type, operationId: job.operationId } : {}),
411
+ });
412
+ }
413
+ async function ensureRunner(bookId) {
414
+ await ensureJobHandlersLoaded();
415
+ assertQueueAcceptingWork(bookId);
416
+ const existing = runnerByBook.get(bookId);
417
+ if (existing)
418
+ return existing;
419
+ const starting = startingRunnerByBook.get(bookId);
420
+ if (starting)
421
+ return starting;
422
+ const pending = createBookRunner(bookId);
423
+ startingRunnerByBook.set(bookId, pending);
424
+ try {
425
+ return await pending;
426
+ }
427
+ finally {
428
+ startingRunnerByBook.delete(bookId);
429
+ }
430
+ }
431
+ function assertQueueAcceptingWork(bookId, runner) {
432
+ if (stoppingQueue
433
+ || stoppingRunnerByBook.has(bookId)
434
+ || (runner && runnerByBook.get(bookId)?.runner !== runner)) {
435
+ throw new Error(`Book queue is stopping: ${bookId}`);
436
+ }
437
+ }
438
+ async function createBookRunner(bookId) {
439
+ const engine = await resolveBookEngine(bookId);
440
+ await engine.ready;
441
+ await engine.jobs.queue.reap();
442
+ assertQueueAcceptingWork(bookId);
443
+ const runner = engine.jobs.queue.createRunner({
444
+ concurrency: 5,
445
+ pollIntervalMs: 250,
446
+ preferredJobTypes: SEARCH_REFERENCE_JOB_TYPES,
447
+ resolveHandler: makeWrappedResolver(bookId, engine),
448
+ });
449
+ const searchRunner = engine.jobs.queue.createRunner({
450
+ concurrency: 1,
451
+ pollIntervalMs: 250,
452
+ jobTypes: SEARCH_REFERENCE_JOB_TYPES,
453
+ resolveHandler: makeWrappedResolver(bookId, engine),
454
+ });
455
+ runner.start();
456
+ searchRunner.start();
457
+ const reaper = engine.jobs.startReaper({ intervalMs: ASSET_REAPER_INTERVAL_MS });
458
+ const info = {
459
+ bookId,
460
+ engine,
461
+ runner,
462
+ searchRunner,
463
+ reaper,
464
+ providerPollTimer: null,
465
+ providerPollInFlight: false,
466
+ pollAbort: new AbortController(),
467
+ };
468
+ startProviderPollDriver(info);
469
+ runnerByBook.set(bookId, info);
470
+ return info;
471
+ }
472
+ async function ensureBookReady(payload) {
473
+ const bookId = extractBookId(payload);
474
+ const info = await ensureRunner(bookId);
475
+ return { bookId, engine: info.engine, runner: info.runner };
476
+ }
477
+ /**
478
+ * Jobs that reference an asset without producing or transforming its media.
479
+ * The engine queue marks a job's artifact ready when it completes and errored
480
+ * when it fails, so these stay detached from the artifact row and skip the
481
+ * wrapper's failure dispatch: a post that cannot reach X, or an index run that
482
+ * times out, must never flip a healthy asset into "media failed".
483
+ */
484
+ const ARTIFACT_DETACHED_JOB_TYPES = new Set([
485
+ "post_to_x",
486
+ "post_dropbox",
487
+ "post_icloud",
488
+ "post_prepare_x",
489
+ "post_create",
490
+ "post_update",
491
+ "post_link",
492
+ "post_metrics",
493
+ "post_copy",
494
+ "semantic_index",
495
+ "tag_asset",
496
+ "backfill_asset_tags",
497
+ ...SEARCH_REFERENCE_JOB_TYPES,
498
+ ]);
499
+ function isArtifactDetachedJob(type, payload) {
500
+ return ARTIFACT_DETACHED_JOB_TYPES.has(type)
501
+ || (type === "extract_audio" && payload.destination != null);
502
+ }
503
+ export function enqueueOptions(jobId, payload) {
504
+ const type = extractType(payload);
505
+ const assetId = extractAssetId(payload);
506
+ const classified = assetId ? jobTypeToAssetWorkKind(type, payload) : null;
507
+ const maxAttempts = type === "generate_image_subject_edit"
508
+ ? 2
509
+ : 1;
510
+ return {
511
+ type,
512
+ artifactId: isArtifactDetachedJob(type, payload) ? null : assetId,
513
+ payload,
514
+ dedupeKey: jobId,
515
+ maxAttempts,
516
+ artifactWorkKind: classified?.kind ?? null,
517
+ artifactWorkOrientation: classified?.orientation ?? null,
518
+ };
519
+ }
520
+ export function jobIdForKey(jobKey, bookId) {
521
+ const entry = jobIdIndex.get(jobKey);
522
+ return entry && entry.bookId === bookId ? entry.internalId : null;
523
+ }
524
+ function rememberJob(jobId, entry) {
525
+ jobIdIndex.set(jobId, entry);
526
+ while (jobIdIndex.size > JOB_INDEX_MAX) {
527
+ const oldest = jobIdIndex.keys().next().value;
528
+ if (typeof oldest !== "string")
529
+ return;
530
+ jobIdIndex.delete(oldest);
531
+ }
532
+ }
533
+ function mapState(state) {
534
+ switch (state) {
535
+ case "queued":
536
+ return "queued";
537
+ case "running":
538
+ case "completing":
539
+ return "running";
540
+ case "done":
541
+ return "completed";
542
+ case "failed":
543
+ case "aborted":
544
+ return "failed";
545
+ }
546
+ }
547
+ export const queue = {
548
+ async enqueue(jobId, payload) {
549
+ const { bookId, engine, runner } = await ensureBookReady(payload);
550
+ assertQueueAcceptingWork(bookId, runner);
551
+ const result = engine.jobs.queue.enqueue(enqueueOptions(jobId, payload));
552
+ rememberJob(jobId, { bookId, internalId: result.job.id });
553
+ return result.job;
554
+ },
555
+ async enqueueAndWait(jobId, payload, timeoutMs = 300_000) {
556
+ const { bookId, engine, runner } = await ensureBookReady(payload);
557
+ assertQueueAcceptingWork(bookId, runner);
558
+ const result = engine.jobs.queue.enqueue(enqueueOptions(jobId, payload));
559
+ rememberJob(jobId, { bookId, internalId: result.job.id });
560
+ return await runner.waitFor(result.job.id, timeoutMs);
561
+ },
562
+ async getStatus(jobId) {
563
+ const entry = jobIdIndex.get(jobId);
564
+ if (!entry)
565
+ return null;
566
+ const engine = await resolveBookEngine(entry.bookId);
567
+ const job = engine.jobs.queue.get(entry.internalId);
568
+ if (!job)
569
+ return null;
570
+ const state = mapState(job.state);
571
+ if (state === "completed" || state === "failed")
572
+ jobIdIndex.delete(jobId);
573
+ return {
574
+ state,
575
+ ...(job.error ? { error: { message: job.error.message } } : {}),
576
+ };
577
+ },
578
+ async start() {
579
+ await ensureJobHandlersLoaded();
580
+ await reconcileQueueOnStartup();
581
+ },
582
+ async startBook(bookId) {
583
+ await ensureRunner(bookId);
584
+ },
585
+ async stop() {
586
+ if (stoppingQueue)
587
+ return stoppingQueue;
588
+ const pending = stopQueueRunners();
589
+ stoppingQueue = pending;
590
+ try {
591
+ await pending;
592
+ }
593
+ finally {
594
+ stoppingQueue = null;
595
+ }
596
+ },
597
+ };
598
+ async function stopQueueRunners() {
599
+ await Promise.all([...startingRunnerByBook.values()].map((starting) => starting.catch(() => undefined)));
600
+ await Promise.all(stoppingRunnerByBook.values());
601
+ const infos = [...runnerByBook.values()];
602
+ runnerByBook.clear();
603
+ jobIdIndex.clear();
604
+ for (const info of infos) {
605
+ stopProviderPollDriver(info);
606
+ info.reaper.stop();
607
+ }
608
+ await Promise.all(infos.map((info) => abortActiveJobs(info, "Queue stopped")));
609
+ await Promise.all([
610
+ ...infos.map((info) => info.runner.stop()),
611
+ ...infos.map((info) => info.searchRunner.stop()),
612
+ ...infos.map((info) => awaitProviderPollIdle(info)),
613
+ ]);
614
+ }
615
+ async function reconcileQueueOnStartup() {
616
+ await bookManager.ready();
617
+ await Promise.all(bookManager.list().map(async (book) => {
618
+ const info = await ensureRunner(book.bookId);
619
+ const { getAllToolDefs } = await import("./tools/index.js");
620
+ const { resumeOpenChains } = await import("./chain-executor.js");
621
+ const defs = getAllToolDefs(info.engine);
622
+ resumeOpenChains(info.engine, (name) => defs.find((definition) => definition.name === name));
623
+ }));
624
+ }
625
+ function isClosedDatabaseError(error) {
626
+ return error instanceof Error && /database is closed/i.test(error.message);
627
+ }
628
+ export function listActiveBookJobsForTests() {
629
+ const jobs = [];
630
+ for (const info of runnerByBook.values()) {
631
+ try {
632
+ const active = info.engine.jobs.queue.list({
633
+ states: ["queued", "running", "completing"],
634
+ });
635
+ for (const job of active) {
636
+ jobs.push({
637
+ bookId: info.bookId,
638
+ type: job.type,
639
+ state: job.state,
640
+ });
641
+ }
642
+ }
643
+ catch (error) {
644
+ if (isClosedDatabaseError(error))
645
+ continue;
646
+ throw error;
647
+ }
648
+ }
649
+ return jobs;
650
+ }
651
+ export async function abortActiveBookJobsForTests(reason = "test cleanup") {
652
+ await Promise.all([...runnerByBook.values()].map((info) => abortActiveJobs(info, reason)));
653
+ }
654
+ export async function pauseBookQueueForTests(bookId) {
655
+ if (!testEngines.has(bookId))
656
+ throw new Error("Only test book queues may be paused");
657
+ const info = await ensureRunner(bookId);
658
+ stopProviderPollDriver(info);
659
+ info.reaper.stop();
660
+ await Promise.all([
661
+ info.runner.stop(),
662
+ info.searchRunner.stop(),
663
+ awaitProviderPollIdle(info),
664
+ ]);
665
+ }
666
+ export async function resumeBookQueueForTests(bookId) {
667
+ if (!testEngines.has(bookId))
668
+ throw new Error("Only test book queues may be resumed");
669
+ const info = await ensureRunner(bookId);
670
+ info.runner.start();
671
+ }
672
+ export async function waitForBookQueuesIdleForTests(options = {}) {
673
+ const budgetMs = options.budgetMs ?? 12_000;
674
+ const deadline = Date.now() + budgetMs;
675
+ while (Date.now() < deadline) {
676
+ if (listActiveBookJobsForTests().length === 0)
677
+ return;
678
+ await new Promise((resolve) => setTimeout(resolve, 20));
679
+ }
680
+ const leftover = listActiveBookJobsForTests();
681
+ if (leftover.length === 0)
682
+ return;
683
+ const types = leftover.map((job) => job.type).join(", ");
684
+ throw new Error(`[test drain] ${options.fileLabel ?? "unknown-file"} still running after ${budgetMs}ms: ${types}`);
685
+ }
686
+ export async function drainBookQueuesForTests(options = {}) {
687
+ try {
688
+ await waitForBookQueuesIdleForTests(options);
689
+ }
690
+ finally {
691
+ const bookIds = [...new Set([...runnerByBook.keys(), ...testEngines.keys()])];
692
+ await Promise.all(bookIds.map((bookId) => stopBookQueue(bookId)));
693
+ testEngines.clear();
694
+ activeTestBookId = null;
695
+ activeTestRoot = null;
696
+ }
697
+ }
698
+ async function abortActiveJobs(info, reason) {
699
+ let active;
700
+ try {
701
+ active = info.engine.jobs.queue.list({
702
+ states: ["queued", "running", "completing"],
703
+ });
704
+ }
705
+ catch (error) {
706
+ if (isClosedDatabaseError(error))
707
+ return;
708
+ throw error;
709
+ }
710
+ await Promise.all(active.map(async (job) => {
711
+ try {
712
+ await info.engine.jobs.queue.abort(job.id, reason);
713
+ }
714
+ catch (error) {
715
+ if (!isClosedDatabaseError(error))
716
+ throw error;
717
+ }
718
+ }));
719
+ }
720
+ export async function stopBookQueue(bookId) {
721
+ const stopping = stoppingRunnerByBook.get(bookId);
722
+ if (stopping)
723
+ return stopping;
724
+ const pending = stopBookRunner(bookId);
725
+ stoppingRunnerByBook.set(bookId, pending);
726
+ try {
727
+ await pending;
728
+ }
729
+ finally {
730
+ stoppingRunnerByBook.delete(bookId);
731
+ }
732
+ }
733
+ async function stopBookRunner(bookId) {
734
+ await startingRunnerByBook.get(bookId)?.catch(() => undefined);
735
+ const info = runnerByBook.get(bookId);
736
+ if (!info)
737
+ return;
738
+ runnerByBook.delete(bookId);
739
+ stopProviderPollDriver(info);
740
+ info.reaper.stop();
741
+ await abortActiveJobs(info, "Book deleted");
742
+ await Promise.all([
743
+ info.runner.stop(),
744
+ info.searchRunner.stop(),
745
+ awaitProviderPollIdle(info),
746
+ ]);
747
+ const pending = info.engine.jobs.pending.findAll();
748
+ if (pending.ok) {
749
+ await Promise.all(pending.value.map((task) => (info.engine.jobs.pending.forceFail(task.artifactId, {
750
+ message: "Book deleted",
751
+ failCode: "book_deleted",
752
+ }))));
753
+ }
754
+ }
755
+ export async function waitForJob(jobId, timeoutMs = 300_000) {
756
+ const deadline = Date.now() + timeoutMs;
757
+ let entry = jobIdIndex.get(jobId);
758
+ while (!entry) {
759
+ if (Date.now() >= deadline) {
760
+ throw new Error(`Job ${jobId} timed out after ${timeoutMs}ms`);
761
+ }
762
+ await new Promise((resolve) => setTimeout(resolve, 5));
763
+ entry = jobIdIndex.get(jobId);
764
+ }
765
+ const info = runnerByBook.get(entry.bookId);
766
+ if (info) {
767
+ try {
768
+ await info.runner.waitFor(entry.internalId, Math.max(1, deadline - Date.now()));
769
+ }
770
+ catch (error) {
771
+ const failed = await queue.getStatus(jobId);
772
+ if (failed?.state === "failed") {
773
+ throw new Error(`Job ${jobId} failed: ${failed.error?.message ?? "unknown"}`);
774
+ }
775
+ throw error;
776
+ }
777
+ }
778
+ const status = await queue.getStatus(jobId);
779
+ if (status?.state === "completed")
780
+ return;
781
+ if (status?.state === "failed") {
782
+ throw new Error(`Job ${jobId} failed: ${status.error?.message ?? "unknown"}`);
783
+ }
784
+ throw new Error(`Job ${jobId} timed out after ${timeoutMs}ms`);
785
+ }
786
+ export async function setEngineRoot(rootDir, engine) {
787
+ const previousRoot = activeTestRoot;
788
+ if (previousRoot && previousRoot !== rootDir) {
789
+ if (activeTestBookId) {
790
+ await stopBookQueue(activeTestBookId);
791
+ testEngines.delete(activeTestBookId);
792
+ activeTestBookId = null;
793
+ }
794
+ }
795
+ const next = engine ?? createEngine({ rootDir, initialBookName: "test-book" });
796
+ await next.ready;
797
+ const bookId = next.book.get().bookId;
798
+ testEngines.set(bookId, next);
799
+ activeTestRoot = rootDir;
800
+ activeTestBookId = bookId;
801
+ await ensureRunner(bookId);
802
+ }
803
+ export function getEngineRoot() {
804
+ if (!activeTestRoot)
805
+ return bookManager.rootDir;
806
+ return activeTestRoot;
807
+ }
808
+ export function getActiveEngine() {
809
+ return activeBookEngine();
810
+ }