videobook 0.0.0-stage → 0.1.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.
- package/CHANGELOG.md +18 -0
- package/LICENSE +21 -0
- package/README.md +133 -2
- package/bin/videobook.js +7 -0
- package/dist/acestep/client.js +203 -0
- package/dist/acestep/config.js +62 -0
- package/dist/acestep/errors.js +8 -0
- package/dist/acestep/process.js +175 -0
- package/dist/alive-captions/analyze.js +117 -0
- package/dist/alive-captions/audio.js +179 -0
- package/dist/alive-captions/frames.js +59 -0
- package/dist/alive-captions/model.js +37 -0
- package/dist/alive-captions/schema.js +49 -0
- package/dist/alive-captions/track.js +9 -0
- package/dist/api-key.js +33 -0
- package/dist/app-state.js +56 -0
- package/dist/app.js +257 -0
- package/dist/archive-b2.js +332 -0
- package/dist/archive-store.js +169 -0
- package/dist/artifact-label.js +36 -0
- package/dist/artifact-refs.js +49 -0
- package/dist/asset-tag-extraction.js +169 -0
- package/dist/asset-tags.js +228 -0
- package/dist/asset-usage.js +23 -0
- package/dist/audio-cue.js +71 -0
- package/dist/audio-encoding.js +32 -0
- package/dist/audio-sync-request.js +1 -0
- package/dist/audio-sync.js +223 -0
- package/dist/audio-trim-range.js +31 -0
- package/dist/book-context.js +31 -0
- package/dist/book-manager.js +374 -0
- package/dist/catalog-gc.js +26 -0
- package/dist/chain-executor.js +815 -0
- package/dist/chain-generation.js +115 -0
- package/dist/chain-plan.js +746 -0
- package/dist/chain-step-review.js +60 -0
- package/dist/character-name.js +25 -0
- package/dist/chat-generation-routing.js +73 -0
- package/dist/chat-generation-settings.js +78 -0
- package/dist/chat-models.js +44 -0
- package/dist/chat-prompt-rewrite.js +67 -0
- package/dist/chat-router.js +628 -0
- package/dist/chat-view-actions.js +49 -0
- package/dist/cli/args.js +192 -0
- package/dist/cli/doctor-command.js +255 -0
- package/dist/cli/entry.js +2 -0
- package/dist/cli/main.js +91 -0
- package/dist/cli/mcp-command.js +82 -0
- package/dist/cli/mcp-relay.js +51 -0
- package/dist/cli/open-browser.js +14 -0
- package/dist/cli/run-command.js +187 -0
- package/dist/cli/serve-command.js +57 -0
- package/dist/cli/stdio-guard.js +8 -0
- package/dist/cold-open-model.js +7 -0
- package/dist/cold-open.js +79 -0
- package/dist/command-reference.js +95 -0
- package/dist/content-store.js +74 -0
- package/dist/cost.js +8 -0
- package/dist/crop-geometry.js +23 -0
- package/dist/dancing-workflow.js +446 -0
- package/dist/data-home.js +44 -0
- package/dist/diagnostics.js +109 -0
- package/dist/dialog-quality.js +34 -0
- package/dist/downloads-dest.js +56 -0
- package/dist/editing-moments.js +194 -0
- package/dist/elevenlabs-audio-extension.js +66 -0
- package/dist/engine.js +54 -0
- package/dist/env-secrets.js +184 -0
- package/dist/ffmpeg-binary.js +12 -0
- package/dist/ffmpeg.js +425 -0
- package/dist/ffprobe-binary.js +55 -0
- package/dist/forget-asset.js +216 -0
- package/dist/frame-utils.js +27 -0
- package/dist/generation-credit-error.js +11 -0
- package/dist/generation-input-mentions.js +117 -0
- package/dist/generation-input-policy.js +68 -0
- package/dist/generation-reference-mentions.js +97 -0
- package/dist/goal-characters.js +49 -0
- package/dist/goal-plan-v3.js +273 -0
- package/dist/goal-plan.js +220 -0
- package/dist/goal-planner-prompt.js +28 -0
- package/dist/goal-prompt-budget.js +4 -0
- package/dist/goal-reference-images.js +25 -0
- package/dist/goal-state.js +261 -0
- package/dist/goal-stills.js +153 -0
- package/dist/goal-store.js +144 -0
- package/dist/goal-styles.js +144 -0
- package/dist/goal-v3-runtime.js +157 -0
- package/dist/guides/character_schema_guide.md +70 -0
- package/dist/guides/gpt_image_2_5_prompting_guide.md +21 -0
- package/dist/guides/grok_imagine_prompting_guide.md +72 -0
- package/dist/guides/image_reproduction_guide.md +84 -0
- package/dist/guides/kling_3_0_prompting_guide.md +136 -0
- package/dist/guides/nano_banana_pro_prompting_guide.md +28 -0
- package/dist/guides/seedance_2_prompting_guide.md +172 -0
- package/dist/http-access.js +49 -0
- package/dist/http-byte-range.js +31 -0
- package/dist/image-compress.js +77 -0
- package/dist/image-edit-default.js +1 -0
- package/dist/image-edit-models.js +41 -0
- package/dist/image-edit-settings.js +11 -0
- package/dist/image-region-edit.js +32 -0
- package/dist/image-subject-map.js +523 -0
- package/dist/image.js +46 -0
- package/dist/index.js +12 -0
- package/dist/job-deadline.js +60 -0
- package/dist/jobs/_alibaba-upload.js +30 -0
- package/dist/jobs/_audio-chunks.js +31 -0
- package/dist/jobs/_download-into-asset.js +15 -0
- package/dist/jobs/_fal-shared.js +22 -0
- package/dist/jobs/_fal-upload.js +35 -0
- package/dist/jobs/_image-edit-adapter.js +213 -0
- package/dist/jobs/_kie-poll.js +119 -0
- package/dist/jobs/_kie-upload.js +32 -0
- package/dist/jobs/_lease.js +163 -0
- package/dist/jobs/_persist-output.js +19 -0
- package/dist/jobs/_storage-retry.js +21 -0
- package/dist/jobs/_transcribe.js +245 -0
- package/dist/jobs/_translate-transcript.js +270 -0
- package/dist/jobs/analyze-alive-captions.js +10 -0
- package/dist/jobs/analyze-editing-moments.js +104 -0
- package/dist/jobs/analyze-jokes.js +143 -0
- package/dist/jobs/analyze-keyword-placement.js +155 -0
- package/dist/jobs/analyze-music.js +14 -0
- package/dist/jobs/analyze-video.js +282 -0
- package/dist/jobs/apply-cuts.js +83 -0
- package/dist/jobs/apply-sfx.js +107 -0
- package/dist/jobs/archive-clip.js +457 -0
- package/dist/jobs/assemble-goal.js +98 -0
- package/dist/jobs/audio-boost.js +71 -0
- package/dist/jobs/audio-mix.js +161 -0
- package/dist/jobs/backfill-asset-tags.js +144 -0
- package/dist/jobs/burn-watermark.js +316 -0
- package/dist/jobs/change-video-speed.js +176 -0
- package/dist/jobs/clean-video-audio.js +207 -0
- package/dist/jobs/cold-open.js +166 -0
- package/dist/jobs/combine-goal.js +108 -0
- package/dist/jobs/complete-provider-task.js +185 -0
- package/dist/jobs/copy-library-asset.js +33 -0
- package/dist/jobs/cost-estimate-assessment.js +113 -0
- package/dist/jobs/crop-image.js +47 -0
- package/dist/jobs/crop-video.js +71 -0
- package/dist/jobs/describe-image.js +181 -0
- package/dist/jobs/detect-scenes.js +33 -0
- package/dist/jobs/doctor-fix.js +100 -0
- package/dist/jobs/download-video.js +426 -0
- package/dist/jobs/enqueue-search-index.js +24 -0
- package/dist/jobs/even-split.js +122 -0
- package/dist/jobs/export-sequence.js +365 -0
- package/dist/jobs/extend-audio.js +229 -0
- package/dist/jobs/extract-audio.js +72 -0
- package/dist/jobs/extract-frame.js +58 -0
- package/dist/jobs/extract-notebook-audio.js +82 -0
- package/dist/jobs/extract-notebook-frame.js +93 -0
- package/dist/jobs/extract-video-clip.js +8 -0
- package/dist/jobs/extract-video-poster.js +55 -0
- package/dist/jobs/extract-waveform.js +25 -0
- package/dist/jobs/finish-goal.js +255 -0
- package/dist/jobs/forget-asset.js +11 -0
- package/dist/jobs/generate-acestep-cover.js +219 -0
- package/dist/jobs/generate-media.js +70 -0
- package/dist/jobs/image-subjects.js +1059 -0
- package/dist/jobs/index.js +18 -0
- package/dist/jobs/inject-first-frame.js +175 -0
- package/dist/jobs/isolate-vocals.js +69 -0
- package/dist/jobs/kie-paygate.js +177 -0
- package/dist/jobs/label-music-sections.js +5 -0
- package/dist/jobs/osg-placement.js +135 -0
- package/dist/jobs/pad-video.js +72 -0
- package/dist/jobs/plan-goal.js +97 -0
- package/dist/jobs/post-record.js +21 -0
- package/dist/jobs/post-to-x.js +21 -0
- package/dist/jobs/prepare-search-reference.js +190 -0
- package/dist/jobs/prepare-temporal-search.js +16 -0
- package/dist/jobs/process-audio.js +67 -0
- package/dist/jobs/process-image.js +58 -0
- package/dist/jobs/process-upload.js +166 -0
- package/dist/jobs/process-video.js +438 -0
- package/dist/jobs/register-all.js +161 -0
- package/dist/jobs/relayout-notebook.js +78 -0
- package/dist/jobs/remove-sound.js +299 -0
- package/dist/jobs/render-project.js +380 -0
- package/dist/jobs/render-qr-outro.js +138 -0
- package/dist/jobs/render-timeline.js +283 -0
- package/dist/jobs/repair-search-indexes.js +42 -0
- package/dist/jobs/replace-video-audio.js +64 -0
- package/dist/jobs/restore-archive-clip.js +53 -0
- package/dist/jobs/reverse-video.js +53 -0
- package/dist/jobs/rewrite-script.js +173 -0
- package/dist/jobs/run-notebook.js +3335 -0
- package/dist/jobs/semantic-index.js +797 -0
- package/dist/jobs/smooth-scene-concat.js +257 -0
- package/dist/jobs/splice-video.js +280 -0
- package/dist/jobs/split-video.js +127 -0
- package/dist/jobs/suggest-cuts.js +149 -0
- package/dist/jobs/suggest-sfx.js +135 -0
- package/dist/jobs/sync-video-to-audio.js +329 -0
- package/dist/jobs/tag-asset.js +266 -0
- package/dist/jobs/task-failure.js +16 -0
- package/dist/jobs/timewarp-video.js +152 -0
- package/dist/jobs/transcribe-video-audio.js +117 -0
- package/dist/jobs/transcribe.js +35 -0
- package/dist/jobs/translate-transcript.js +55 -0
- package/dist/jobs/trim-audio.js +133 -0
- package/dist/jobs/trim-video.js +291 -0
- package/dist/jobs/xai-paygate.js +26 -0
- package/dist/keyword-placement-cache.js +74 -0
- package/dist/lazy-render.js +142 -0
- package/dist/library-browse.js +78 -0
- package/dist/lightning-defaults.js +206 -0
- package/dist/load-env.js +9 -0
- package/dist/local-search-models.js +206 -0
- package/dist/local-upload.js +318 -0
- package/dist/logger.js +69 -0
- package/dist/media/adapters/alibaba.js +20 -0
- package/dist/media/adapters/elevenlabs.js +58 -0
- package/dist/media/adapters/fal.js +45 -0
- package/dist/media/adapters/index.js +26 -0
- package/dist/media/adapters/kie.js +120 -0
- package/dist/media/adapters/pending.js +21 -0
- package/dist/media/adapters/types.js +5 -0
- package/dist/media/adapters/xai.js +18 -0
- package/dist/media/catalog.js +236 -0
- package/dist/media/complete.js +191 -0
- package/dist/media/cost.js +45 -0
- package/dist/media/descriptor.js +157 -0
- package/dist/media/endpoints.js +11 -0
- package/dist/media/frame-options.js +18 -0
- package/dist/media/insert-video-timing.js +17 -0
- package/dist/media/json-schema.js +75 -0
- package/dist/media/legacy-task-types.js +129 -0
- package/dist/media/listing.js +24 -0
- package/dist/media/model-id.js +14 -0
- package/dist/media/preview.js +86 -0
- package/dist/media/prompt-dialects.js +65 -0
- package/dist/media/ref-fields.js +106 -0
- package/dist/media/refs.js +131 -0
- package/dist/media/request.js +188 -0
- package/dist/media/types.js +6 -0
- package/dist/media/wire.js +161 -0
- package/dist/media-library.js +535 -0
- package/dist/media-output-path.js +33 -0
- package/dist/multyculty-workflow.js +757 -0
- package/dist/notebook-action-catalog.js +608 -0
- package/dist/notebook-cell-outputs.js +726 -0
- package/dist/notebook-credit-retry.js +45 -0
- package/dist/notebook-fixtures.js +8 -0
- package/dist/notebook-generation-contract.js +77 -0
- package/dist/notebook-generation-controls.js +145 -0
- package/dist/notebook-goal-layout.js +40 -0
- package/dist/notebook-graph.js +37 -0
- package/dist/notebook-image-edit.js +84 -0
- package/dist/notebook-initial-generation-prompt.js +222 -0
- package/dist/notebook-live-analysis.js +941 -0
- package/dist/notebook-media-ops.js +579 -0
- package/dist/notebook-mutations.js +20 -0
- package/dist/notebook-placement.js +41 -0
- package/dist/notebook-provider-failure.js +87 -0
- package/dist/notebook-published-post.js +26 -0
- package/dist/notebook-reference-resolver.js +227 -0
- package/dist/notebook-relayout.js +270 -0
- package/dist/notebook-run-plan.js +519 -0
- package/dist/notebook-shots.js +162 -0
- package/dist/notebook-workflow.js +856 -0
- package/dist/openai-intelligence.js +99 -0
- package/dist/original-media-file.js +12 -0
- package/dist/osg-assets.js +36 -0
- package/dist/osg-placement-cache.js +64 -0
- package/dist/osg-settings.js +27 -0
- package/dist/paid-tools.js +22 -0
- package/dist/platform-guidance.js +26 -0
- package/dist/posts/dropbox-auth.js +91 -0
- package/dist/posts/dropbox-upload.js +132 -0
- package/dist/posts/icloud-drive.js +134 -0
- package/dist/posts/icloud-status.js +64 -0
- package/dist/posts/post-contract.js +2 -0
- package/dist/posts/post-copy-format.js +43 -0
- package/dist/posts/post-copy.js +193 -0
- package/dist/posts/post-folder.js +18 -0
- package/dist/posts/post-metrics.js +119 -0
- package/dist/posts/post-store.js +340 -0
- package/dist/posts/x-post-target.js +43 -0
- package/dist/preview-payload.js +90 -0
- package/dist/prompt-surfaces.js +22 -0
- package/dist/provider-prompt-transform.js +77 -0
- package/dist/provider-status-link.js +21 -0
- package/dist/provider-task-record.js +78 -0
- package/dist/provider-task-status.js +47 -0
- package/dist/provider-usage.js +294 -0
- package/dist/queue.js +806 -0
- package/dist/reference-video-duration.js +17 -0
- package/dist/remotion/BlurFillVideo.js +51 -0
- package/dist/remotion/KeywordGraphicsOverlay.js +68 -0
- package/dist/remotion/OsgOverlay.js +30 -0
- package/dist/remotion/QrOutroComposition.js +47 -0
- package/dist/remotion/RenderComposition.js +11 -0
- package/dist/remotion/browser.js +59 -0
- package/dist/remotion/constants.js +35 -0
- package/dist/remotion/index.js +66 -0
- package/dist/remotion/keyword-graphics.js +151 -0
- package/dist/remotion/keyword-placement.js +182 -0
- package/dist/remotion/osg-spec.js +198 -0
- package/dist/remotion/public/qr-outro-impact.wav +0 -0
- package/dist/remotion/qr-outro-audio.js +1 -0
- package/dist/remotion/qr-outro-spec.js +211 -0
- package/dist/remotion/shared-bundle.js +37 -0
- package/dist/remotion/subtitles/AliveCaptions.js +83 -0
- package/dist/remotion/subtitles/Highlight.js +58 -0
- package/dist/remotion/subtitles/Impact.js +67 -0
- package/dist/remotion/subtitles/SubtitleOverlay.js +9 -0
- package/dist/remotion/subtitles/alive-layout.js +321 -0
- package/dist/remotion/subtitles/alive-types.js +17 -0
- package/dist/remotion/subtitles/chunker.js +68 -0
- package/dist/remotion/subtitles/segments.js +42 -0
- package/dist/remotion/subtitles/styles.js +4 -0
- package/dist/remotion/subtitles/types.js +1 -0
- package/dist/remotion/util.js +18 -0
- package/dist/remotion-cancellation.js +15 -0
- package/dist/rename-helpers.js +204 -0
- package/dist/rhythm/analyze.js +128 -0
- package/dist/rhythm/contract.js +14 -0
- package/dist/rhythm/draft-model.js +19 -0
- package/dist/rhythm/energy.js +133 -0
- package/dist/rhythm/fft.js +44 -0
- package/dist/rhythm/model.js +71 -0
- package/dist/rhythm/music-analysis.js +124 -0
- package/dist/rhythm/music-draft.js +261 -0
- package/dist/rhythm/onset.js +101 -0
- package/dist/rhythm/run-worker.js +51 -0
- package/dist/rhythm/score.js +47 -0
- package/dist/rhythm/section-labels.js +168 -0
- package/dist/rhythm/sequence-rhythm.js +67 -0
- package/dist/rhythm/synthesize.js +103 -0
- package/dist/rhythm/timing.js +98 -0
- package/dist/rhythm/track.js +320 -0
- package/dist/rhythm/visual-fit.js +4 -0
- package/dist/rhythm/worker.js +25 -0
- package/dist/scene-detect.js +200 -0
- package/dist/scene-transition.js +12 -0
- package/dist/scheduled-posts.js +200 -0
- package/dist/search-index-coverage.js +13 -0
- package/dist/search-index-media.js +71 -0
- package/dist/search-reference-store.js +60 -0
- package/dist/search-scoring.js +17 -0
- package/dist/serve.js +150 -0
- package/dist/server-lock.js +143 -0
- package/dist/server.js +42 -0
- package/dist/srt.js +16 -0
- package/dist/tag-backfill-state.js +121 -0
- package/dist/tag-settings.js +30 -0
- package/dist/tag-vocabulary.js +237 -0
- package/dist/test-queue-cleanup.js +1 -0
- package/dist/time-parse.js +50 -0
- package/dist/timeline-helpers.js +554 -0
- package/dist/timewarp.js +87 -0
- package/dist/tools/_image-inputs.js +9 -0
- package/dist/tools/_media-refs.js +84 -0
- package/dist/tools/_shared.js +59 -0
- package/dist/tools/_suggest-shared.js +279 -0
- package/dist/tools/add-timeline-audio.js +73 -0
- package/dist/tools/alive-captions.js +157 -0
- package/dist/tools/analyze-jokes.js +56 -0
- package/dist/tools/analyze-video.js +66 -0
- package/dist/tools/animate-keywords.js +217 -0
- package/dist/tools/api-status.js +42 -0
- package/dist/tools/application-commands.js +34 -0
- package/dist/tools/apply-cuts.js +112 -0
- package/dist/tools/apply-sfx.js +152 -0
- package/dist/tools/apply-watermark.js +670 -0
- package/dist/tools/archive-clip.js +74 -0
- package/dist/tools/asset-history.js +61 -0
- package/dist/tools/asset-manifest.js +24 -0
- package/dist/tools/asset-tags.js +151 -0
- package/dist/tools/assign-slot.js +97 -0
- package/dist/tools/audio-boost.js +107 -0
- package/dist/tools/audio-mix.js +121 -0
- package/dist/tools/book-usage.js +16 -0
- package/dist/tools/chain.js +136 -0
- package/dist/tools/change-video-speed.js +105 -0
- package/dist/tools/chat-history.js +5 -0
- package/dist/tools/chat.js +848 -0
- package/dist/tools/clean-video-audio.js +97 -0
- package/dist/tools/clear-failed-tasks.js +66 -0
- package/dist/tools/cold-open.js +114 -0
- package/dist/tools/command-plan.js +1680 -0
- package/dist/tools/commands.js +1666 -0
- package/dist/tools/concat-images.js +236 -0
- package/dist/tools/connect-service.js +55 -0
- package/dist/tools/copy-file-to-clipboard.js +58 -0
- package/dist/tools/create-character.js +114 -0
- package/dist/tools/create-qart.js +253 -0
- package/dist/tools/create-qr-outro.js +149 -0
- package/dist/tools/create-qrbtf.js +215 -0
- package/dist/tools/create-watermark.js +97 -0
- package/dist/tools/crop-asset.js +18 -0
- package/dist/tools/crop-image.js +120 -0
- package/dist/tools/crop-video.js +97 -0
- package/dist/tools/delete-asset.js +104 -0
- package/dist/tools/describe-image.js +69 -0
- package/dist/tools/detect-scenes.js +44 -0
- package/dist/tools/doctor.js +126 -0
- package/dist/tools/download-video.js +114 -0
- package/dist/tools/duplicate-asset.js +184 -0
- package/dist/tools/edit-timeline.js +59 -0
- package/dist/tools/editing-moments.js +105 -0
- package/dist/tools/estimate-cost.js +171 -0
- package/dist/tools/even-split.js +295 -0
- package/dist/tools/export-asset-file.js +22 -0
- package/dist/tools/export-asset.js +140 -0
- package/dist/tools/export-sequence.js +250 -0
- package/dist/tools/export-to-downloads.js +54 -0
- package/dist/tools/export-transcript-srt.js +79 -0
- package/dist/tools/extend-audio.js +100 -0
- package/dist/tools/extract-audio.js +174 -0
- package/dist/tools/extract-frame.js +108 -0
- package/dist/tools/extract-notebook-audio.js +173 -0
- package/dist/tools/extract-notebook-frame.js +160 -0
- package/dist/tools/extract-video-clip.js +101 -0
- package/dist/tools/generate-media.js +157 -0
- package/dist/tools/generation-payload.js +48 -0
- package/dist/tools/get-timeline.js +12 -0
- package/dist/tools/get-watermark-config.js +38 -0
- package/dist/tools/goal.js +644 -0
- package/dist/tools/image-subjects.js +711 -0
- package/dist/tools/index.js +323 -0
- package/dist/tools/inject-first-frame.js +154 -0
- package/dist/tools/invert-image.js +70 -0
- package/dist/tools/isolate-vocals.js +66 -0
- package/dist/tools/list-assets.js +98 -0
- package/dist/tools/list-cell-generations.js +225 -0
- package/dist/tools/list-elevenlabs-voices.js +163 -0
- package/dist/tools/log-wrapper.js +29 -0
- package/dist/tools/make-cover-song.js +95 -0
- package/dist/tools/mark-asset-seen.js +23 -0
- package/dist/tools/media-library.js +245 -0
- package/dist/tools/move-timeline-audio.js +35 -0
- package/dist/tools/music-analysis.js +60 -0
- package/dist/tools/music-draft.js +29 -0
- package/dist/tools/pad-video.js +87 -0
- package/dist/tools/post-record.js +83 -0
- package/dist/tools/post.js +168 -0
- package/dist/tools/prompt-history.js +164 -0
- package/dist/tools/provider-key-status.js +248 -0
- package/dist/tools/qrbtf-render.js +849 -0
- package/dist/tools/read-asset-metadata.js +26 -0
- package/dist/tools/read-file-at-commit.js +29 -0
- package/dist/tools/read-transcript.js +41 -0
- package/dist/tools/reconcile-project-work.js +1452 -0
- package/dist/tools/recover-asset.js +28 -0
- package/dist/tools/remove-sound.js +72 -0
- package/dist/tools/remove-timeline-audio.js +34 -0
- package/dist/tools/remove-timeline-slot.js +40 -0
- package/dist/tools/rename-all.js +434 -0
- package/dist/tools/rename-asset.js +34 -0
- package/dist/tools/render-settings.js +47 -0
- package/dist/tools/render-timeline.js +120 -0
- package/dist/tools/render.js +384 -0
- package/dist/tools/reorder-character-images.js +45 -0
- package/dist/tools/replace-video-audio.js +133 -0
- package/dist/tools/restore-archive-clips.js +209 -0
- package/dist/tools/restore-asset-version.js +37 -0
- package/dist/tools/retry-generation.js +54 -0
- package/dist/tools/reverse-video.js +74 -0
- package/dist/tools/rewrite-prompt.js +388 -0
- package/dist/tools/rewrite-script.js +67 -0
- package/dist/tools/schema-from-apicity.js +66 -0
- package/dist/tools/search-archive.js +150 -0
- package/dist/tools/semantic-search.js +1214 -0
- package/dist/tools/sequence-rhythm.js +43 -0
- package/dist/tools/set-character-metadata.js +84 -0
- package/dist/tools/set-env-secret.js +40 -0
- package/dist/tools/set-osg.js +143 -0
- package/dist/tools/show-folder.js +37 -0
- package/dist/tools/smooth-scene-concat.js +122 -0
- package/dist/tools/splice-video.js +149 -0
- package/dist/tools/split-video.js +202 -0
- package/dist/tools/suggest-cuts.js +45 -0
- package/dist/tools/suggest-sfx.js +43 -0
- package/dist/tools/sync-video-to-audio.js +115 -0
- package/dist/tools/tag-backfill.js +177 -0
- package/dist/tools/timewarp-video.js +85 -0
- package/dist/tools/tool-def.js +1 -0
- package/dist/tools/tool-json-schema.js +116 -0
- package/dist/tools/transcribe.js +90 -0
- package/dist/tools/translate-subtitles.js +59 -0
- package/dist/tools/trim-audio.js +139 -0
- package/dist/tools/trim-video.js +168 -0
- package/dist/tools/update-script.js +25 -0
- package/dist/tools/update-timeline-audio.js +40 -0
- package/dist/tools/upload-file.js +41 -0
- package/dist/tools/verify-x-mentions.js +141 -0
- package/dist/tools/video-editing-command-contract.js +291 -0
- package/dist/tools/video-source-reference.js +30 -0
- package/dist/tools/video-time-input.js +18 -0
- package/dist/tools/videobook-domain.js +2006 -0
- package/dist/tools/wait-for-asset-ready.js +143 -0
- package/dist/tools/workspace-actions.js +54 -0
- package/dist/transcript-projection.js +202 -0
- package/dist/transcript-srt.js +33 -0
- package/dist/upload.js +76 -0
- package/dist/usage-providers.js +25 -0
- package/dist/video-analysis-projection.js +46 -0
- package/dist/video-poster.js +33 -0
- package/dist/video-proxy.js +99 -0
- package/dist/vision-regions.js +111 -0
- package/dist/voice-actions.js +90 -0
- package/dist/voice-audio.js +55 -0
- package/dist/voice-config.js +122 -0
- package/dist/voice-continuation.js +54 -0
- package/dist/voice-contract.js +1 -0
- package/dist/voice-http.js +145 -0
- package/dist/voice-reply-progress.js +55 -0
- package/dist/voice-session.js +992 -0
- package/dist/watermark-badge.js +259 -0
- package/dist/watermark-config.js +111 -0
- package/dist/watermark-handle-position.js +55 -0
- package/dist/watermark-platform.js +15 -0
- package/dist/watermark-timing.js +16 -0
- package/dist/workspace-action-catalog.js +315 -0
- package/dist/workspace-action-contract.js +21 -0
- package/dist/workspace-action-groups.js +98 -0
- package/dist/workspace-action-surfaces.js +7 -0
- package/dist/x/account.js +88 -0
- package/dist/x/authenticated-handle.js +30 -0
- package/dist/x/oauth-flow.js +191 -0
- package/dist/x/post-contract.js +1 -0
- package/dist/x/post-metadata.js +44 -0
- package/dist/x/post-to-x.js +456 -0
- package/dist/x/published-post-cell.js +90 -0
- package/dist/x/x-tokens.js +69 -0
- package/dist/zod-slots.js +334 -0
- package/package.json +92 -4
- package/ui/assets/alive-captions-BWb5nLKj.css +1 -0
- package/ui/assets/alive-captions-DPEr1nKM.js +1 -0
- package/ui/assets/confirm-bodies-D8ouHXKy.js +1 -0
- package/ui/assets/confirm-bodies-DrMhaXC6.css +1 -0
- package/ui/assets/fullscreen-B7nw4YbE.js +1 -0
- package/ui/assets/goal-ui-DK-xTCK0.js +2 -0
- package/ui/assets/goal-ui-DXdUM2KF.css +1 -0
- package/ui/assets/index-DzPAnCUj.css +1 -0
- package/ui/assets/index-kS1905s5.js +37 -0
- package/ui/assets/inspector-MlzQbTVo.js +1 -0
- package/ui/assets/jobs-ui-CoinOWx0.js +1 -0
- package/ui/assets/music-video-Cbe80kmQ.js +1 -0
- package/ui/assets/music-video-DPaiKKEq.css +1 -0
- package/ui/assets/ui-Oirr9VOf.css +1 -0
- package/ui/assets/ui-xo_Y1yQg.js +2 -0
- package/ui/assets/vendor-D2rfiZ5j.css +1 -0
- package/ui/assets/vendor-DfTCWgKm.js +19 -0
- package/ui/assets/vendor-flow-BXbeWxW7.js +7 -0
- package/ui/assets/vendor-flow-BnuhLJ6X.css +1 -0
- package/ui/assets/vendor-icons-CGqhCIXV.js +1 -0
- package/ui/assets/vendor-mcp-BTlEJcHV.js +67 -0
- package/ui/assets/vendor-react-Dj7D1fD4.js +9 -0
- package/ui/assets/vendor-remotion-CH1HXrlc.js +95 -0
- package/ui/assets/videobook-Qlp9HQFu.js +20 -0
- package/ui/favicon.svg +8 -0
- package/ui/index.html +126 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Client, ProtocolError, StreamableHTTPClientTransport } from "@modelcontextprotocol/client";
|
|
2
|
+
import { McpServer, fromJsonSchema, } from "@modelcontextprotocol/server";
|
|
3
|
+
import { MCP_PROTOCOL_VERSION, SERVER_VERSION } from "../server.js";
|
|
4
|
+
const RELAYED_CALL_TIMEOUT_MS = 30 * 60_000;
|
|
5
|
+
async function listAllTools(client) {
|
|
6
|
+
const tools = [];
|
|
7
|
+
let cursor;
|
|
8
|
+
do {
|
|
9
|
+
const page = await client.listTools(cursor === undefined ? undefined : { cursor });
|
|
10
|
+
tools.push(...page.tools);
|
|
11
|
+
cursor = page.nextCursor;
|
|
12
|
+
} while (cursor !== undefined);
|
|
13
|
+
return tools;
|
|
14
|
+
}
|
|
15
|
+
export async function connectUpstream(mcpUrl) {
|
|
16
|
+
const client = new Client({ name: "videobook-mcp-relay", version: SERVER_VERSION }, {
|
|
17
|
+
capabilities: {},
|
|
18
|
+
supportedProtocolVersions: [MCP_PROTOCOL_VERSION],
|
|
19
|
+
versionNegotiation: { mode: { pin: MCP_PROTOCOL_VERSION } },
|
|
20
|
+
});
|
|
21
|
+
await client.connect(new StreamableHTTPClientTransport(new URL(mcpUrl)));
|
|
22
|
+
return { url: mcpUrl, client, tools: await listAllTools(client), close: () => client.close() };
|
|
23
|
+
}
|
|
24
|
+
function relayFailure(url, error) {
|
|
25
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
26
|
+
const text = error instanceof ProtocolError
|
|
27
|
+
? reason
|
|
28
|
+
: `The videobook server at ${url} did not answer (${reason}). Restart it or restart videobook mcp.`;
|
|
29
|
+
return { isError: true, content: [{ type: "text", text }] };
|
|
30
|
+
}
|
|
31
|
+
function registerRelayedTool(server, upstream, tool) {
|
|
32
|
+
server.registerTool(tool.name, {
|
|
33
|
+
...(tool.title ? { title: tool.title } : {}),
|
|
34
|
+
...(tool.description ? { description: tool.description } : {}),
|
|
35
|
+
...(tool.annotations ? { annotations: tool.annotations } : {}),
|
|
36
|
+
inputSchema: fromJsonSchema(tool.inputSchema),
|
|
37
|
+
}, async (args, ctx) => {
|
|
38
|
+
try {
|
|
39
|
+
return await upstream.client.callTool({ name: tool.name, arguments: args }, { timeout: RELAYED_CALL_TIMEOUT_MS, signal: ctx.mcpReq.signal });
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
return relayFailure(upstream.url, error);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
export function createRelayServer(upstream, instructions) {
|
|
47
|
+
const server = new McpServer({ name: "videobook", version: SERVER_VERSION }, { instructions });
|
|
48
|
+
for (const tool of upstream.tools)
|
|
49
|
+
registerRelayedTool(server, upstream, tool);
|
|
50
|
+
return server;
|
|
51
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
function browserCommand(url, platform) {
|
|
3
|
+
if (platform === "darwin")
|
|
4
|
+
return { command: "open", args: [url] };
|
|
5
|
+
if (platform === "win32")
|
|
6
|
+
return { command: "cmd", args: ["/c", "start", "", url] };
|
|
7
|
+
return { command: "xdg-open", args: [url] };
|
|
8
|
+
}
|
|
9
|
+
export function openInBrowser(url, platform = process.platform) {
|
|
10
|
+
const { command, args } = browserCommand(url, platform);
|
|
11
|
+
const child = spawn(command, args, { detached: true, stdio: "ignore" });
|
|
12
|
+
child.on("error", () => undefined);
|
|
13
|
+
child.unref();
|
|
14
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { Client, StreamableHTTPClientTransport } from "@modelcontextprotocol/client";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { gcLibraryCatalogs } from "../catalog-gc.js";
|
|
4
|
+
import { bookManager, scopedEngine } from "../engine.js";
|
|
5
|
+
import { waitForJob } from "../queue.js";
|
|
6
|
+
import { MCP_PROTOCOL_VERSION, SERVER_VERSION } from "../server.js";
|
|
7
|
+
import { inspectServerOwner } from "../server-lock.js";
|
|
8
|
+
import { startServer } from "../serve.js";
|
|
9
|
+
import { getAllToolDefs } from "../tools/index.js";
|
|
10
|
+
const REQUEST_GRACE_MS = 60_000;
|
|
11
|
+
function newClient() {
|
|
12
|
+
return new Client({ name: "videobook-cli", version: SERVER_VERSION }, {
|
|
13
|
+
capabilities: {},
|
|
14
|
+
supportedProtocolVersions: [MCP_PROTOCOL_VERSION],
|
|
15
|
+
versionNegotiation: { mode: { pin: MCP_PROTOCOL_VERSION } },
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
async function connect(mcpUrl) {
|
|
19
|
+
const client = newClient();
|
|
20
|
+
await client.connect(new StreamableHTTPClientTransport(new URL(mcpUrl)));
|
|
21
|
+
return client;
|
|
22
|
+
}
|
|
23
|
+
function refuseUnresponsive(owner) {
|
|
24
|
+
if (owner.kind !== "unresponsive")
|
|
25
|
+
return;
|
|
26
|
+
throw new Error(`videobook (pid ${owner.record.pid}) holds ${bookManager.rootDir} but is not responding. Stop it first.`);
|
|
27
|
+
}
|
|
28
|
+
async function temporaryOwner() {
|
|
29
|
+
const running = await startServer({ port: 0, ui: false });
|
|
30
|
+
await running.ready;
|
|
31
|
+
const client = await connect(running.mcpUrl);
|
|
32
|
+
return {
|
|
33
|
+
client,
|
|
34
|
+
temporary: true,
|
|
35
|
+
async close() {
|
|
36
|
+
await client.close();
|
|
37
|
+
await running.close();
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
async function openOwnerSession() {
|
|
42
|
+
const owner = await inspectServerOwner(bookManager.rootDir);
|
|
43
|
+
refuseUnresponsive(owner);
|
|
44
|
+
if (owner.kind !== "running")
|
|
45
|
+
return temporaryOwner();
|
|
46
|
+
const client = await connect(`${owner.record.url}/mcp`);
|
|
47
|
+
return { client, temporary: false, close: () => client.close() };
|
|
48
|
+
}
|
|
49
|
+
function textOf(result) {
|
|
50
|
+
return result.content.flatMap((item) => item.type === "text" ? [item.text] : [JSON.stringify(item)]);
|
|
51
|
+
}
|
|
52
|
+
function parseJsonObject(text) {
|
|
53
|
+
if (!text)
|
|
54
|
+
return null;
|
|
55
|
+
try {
|
|
56
|
+
const value = JSON.parse(text);
|
|
57
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function stringField(record, key) {
|
|
64
|
+
const value = record?.[key];
|
|
65
|
+
return typeof value === "string" && value !== "" ? value : null;
|
|
66
|
+
}
|
|
67
|
+
async function waitInProcess(jobId, timeoutSeconds) {
|
|
68
|
+
process.stderr.write(`Waiting for job ${jobId}...\n`);
|
|
69
|
+
try {
|
|
70
|
+
await waitForJob(jobId, timeoutSeconds * 1000);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
74
|
+
return 1;
|
|
75
|
+
}
|
|
76
|
+
process.stderr.write(`Job ${jobId} completed.\n`);
|
|
77
|
+
return 0;
|
|
78
|
+
}
|
|
79
|
+
async function waitForAsset(client, bookId, assetId, timeoutSeconds) {
|
|
80
|
+
process.stderr.write(`Waiting for asset ${assetId}...\n`);
|
|
81
|
+
const result = await client.callTool({ name: "wait_for_asset_ready", arguments: { book_id: bookId, asset_id: assetId, timeout_seconds: timeoutSeconds } }, { timeout: timeoutSeconds * 1000 + REQUEST_GRACE_MS });
|
|
82
|
+
for (const line of textOf(result))
|
|
83
|
+
process.stdout.write(`${line}\n`);
|
|
84
|
+
return result.isError ? 1 : 0;
|
|
85
|
+
}
|
|
86
|
+
async function waitForOutcome(session, flags, args, first) {
|
|
87
|
+
const jobId = stringField(first, "job_id");
|
|
88
|
+
const assetId = stringField(first, "asset_id");
|
|
89
|
+
const bookId = typeof args.book_id === "string" ? args.book_id : null;
|
|
90
|
+
if (session.temporary && jobId)
|
|
91
|
+
return waitInProcess(jobId, flags.timeoutSeconds);
|
|
92
|
+
if (!flags.wait || !jobId)
|
|
93
|
+
return 0;
|
|
94
|
+
if (assetId && bookId)
|
|
95
|
+
return waitForAsset(session.client, bookId, assetId, flags.timeoutSeconds);
|
|
96
|
+
process.stderr.write(`Job ${jobId} is queued on the running server; follow it with list_jobs.\n`);
|
|
97
|
+
return 0;
|
|
98
|
+
}
|
|
99
|
+
async function callOnce(session, flags, tool, args) {
|
|
100
|
+
try {
|
|
101
|
+
return await session.client.callTool({ name: tool, arguments: args }, { timeout: flags.timeoutSeconds * 1000 + REQUEST_GRACE_MS });
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
process.stderr.write(`videobook run: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
export async function runToolCommand(flags, tool, args) {
|
|
109
|
+
const session = await openOwnerSession();
|
|
110
|
+
try {
|
|
111
|
+
const result = await callOnce(session, flags, tool, args);
|
|
112
|
+
if (!result)
|
|
113
|
+
return 1;
|
|
114
|
+
const lines = textOf(result);
|
|
115
|
+
const stream = result.isError ? process.stderr : process.stdout;
|
|
116
|
+
for (const line of lines)
|
|
117
|
+
stream.write(`${line}\n`);
|
|
118
|
+
if (result.isError)
|
|
119
|
+
return 1;
|
|
120
|
+
return await waitForOutcome(session, flags, args, parseJsonObject(lines[0]));
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
await session.close();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function listingFromJsonSchema(tool) {
|
|
127
|
+
const properties = Object.keys(tool.inputSchema.properties ?? {});
|
|
128
|
+
const required = new Set(tool.inputSchema.required ?? []);
|
|
129
|
+
return {
|
|
130
|
+
name: tool.name,
|
|
131
|
+
...(tool.description ? { description: tool.description } : {}),
|
|
132
|
+
required: properties.filter((key) => required.has(key)),
|
|
133
|
+
optional: properties.filter((key) => !required.has(key)),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function localListings() {
|
|
137
|
+
return getAllToolDefs(scopedEngine, bookManager).map((def) => {
|
|
138
|
+
const schema = z.object(def.schema).toJSONSchema();
|
|
139
|
+
const required = new Set(schema.required ?? []);
|
|
140
|
+
const keys = Object.keys(def.schema);
|
|
141
|
+
return {
|
|
142
|
+
name: def.name,
|
|
143
|
+
...(def.description ? { description: def.description } : {}),
|
|
144
|
+
required: keys.filter((key) => required.has(key)),
|
|
145
|
+
optional: keys.filter((key) => !required.has(key)),
|
|
146
|
+
};
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
async function ownerListings(owner) {
|
|
150
|
+
if (owner.kind !== "running")
|
|
151
|
+
return null;
|
|
152
|
+
const client = await connect(`${owner.record.url}/mcp`);
|
|
153
|
+
try {
|
|
154
|
+
return (await client.listTools()).tools.map(listingFromJsonSchema);
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
await client.close();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function renderListing(listing) {
|
|
161
|
+
const params = [...listing.required.map((key) => `<${key}>`), ...listing.optional.map((key) => `[${key}]`)].join(" ");
|
|
162
|
+
const head = params ? `${listing.name} ${params}` : listing.name;
|
|
163
|
+
return listing.description ? `${head}\n ${listing.description}` : head;
|
|
164
|
+
}
|
|
165
|
+
export async function runToolsCommand() {
|
|
166
|
+
const owner = await inspectServerOwner(bookManager.rootDir);
|
|
167
|
+
refuseUnresponsive(owner);
|
|
168
|
+
const listings = await ownerListings(owner) ?? localListings();
|
|
169
|
+
for (const listing of listings)
|
|
170
|
+
process.stdout.write(`${renderListing(listing)}\n`);
|
|
171
|
+
return 0;
|
|
172
|
+
}
|
|
173
|
+
export async function runGcBooksCommand() {
|
|
174
|
+
const owner = await inspectServerOwner(bookManager.rootDir);
|
|
175
|
+
if (owner.kind !== "none") {
|
|
176
|
+
process.stderr.write(`videobook is running at ${owner.record.url} (pid ${owner.record.pid}); stop it before collecting catalog garbage.\n`);
|
|
177
|
+
return 1;
|
|
178
|
+
}
|
|
179
|
+
try {
|
|
180
|
+
const reports = await gcLibraryCatalogs(bookManager);
|
|
181
|
+
process.stdout.write(`${JSON.stringify({ catalogs: reports }, null, 2)}\n`);
|
|
182
|
+
return 0;
|
|
183
|
+
}
|
|
184
|
+
finally {
|
|
185
|
+
await bookManager.close();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { resolvePort } from "./args.js";
|
|
2
|
+
import { openInBrowser } from "./open-browser.js";
|
|
3
|
+
import { logger } from "../logger.js";
|
|
4
|
+
import { SERVER_VERSION } from "../server.js";
|
|
5
|
+
import { ServerAlreadyRunningError } from "../server-lock.js";
|
|
6
|
+
import { installSignalHandlers, startServer } from "../serve.js";
|
|
7
|
+
function uiLine(running) {
|
|
8
|
+
if (running.uiStatus === "served")
|
|
9
|
+
return `${running.url}/`;
|
|
10
|
+
if (running.uiStatus === "disabled")
|
|
11
|
+
return "off (--headless)";
|
|
12
|
+
return `not built at ${running.uiDir}; serving MCP and API only`;
|
|
13
|
+
}
|
|
14
|
+
function printBanner(running) {
|
|
15
|
+
process.stdout.write([
|
|
16
|
+
`videobook ${SERVER_VERSION}`,
|
|
17
|
+
`Library ${running.libraryDir}`,
|
|
18
|
+
`UI ${uiLine(running)}`,
|
|
19
|
+
`MCP ${running.mcpUrl}`,
|
|
20
|
+
"Agents claude mcp add videobook -- videobook mcp",
|
|
21
|
+
"",
|
|
22
|
+
].join("\n"));
|
|
23
|
+
}
|
|
24
|
+
function reportStartupFailure(error, port) {
|
|
25
|
+
if (error instanceof ServerAlreadyRunningError) {
|
|
26
|
+
process.stderr.write(`${error.message}\n`);
|
|
27
|
+
return 1;
|
|
28
|
+
}
|
|
29
|
+
logger.error("startup-failed", { error, port });
|
|
30
|
+
if (error?.code === "EADDRINUSE") {
|
|
31
|
+
process.stderr.write(`Port ${port} is already in use by another process; pass --port <n>.\n`);
|
|
32
|
+
}
|
|
33
|
+
return 1;
|
|
34
|
+
}
|
|
35
|
+
function hydrationFailure(running) {
|
|
36
|
+
return running.ready.then(() => new Promise(() => undefined), async (error) => {
|
|
37
|
+
logger.error("startup-failed", { error, port: running.port });
|
|
38
|
+
await running.close().catch(() => undefined);
|
|
39
|
+
return 1;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
export async function runServeCommand(flags) {
|
|
43
|
+
const port = resolvePort(process.env);
|
|
44
|
+
const starting = startServer({ port, ui: flags.ui });
|
|
45
|
+
const stopped = new Promise((resolveStop) => installSignalHandlers(starting, resolveStop));
|
|
46
|
+
let running;
|
|
47
|
+
try {
|
|
48
|
+
running = await starting;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
return reportStartupFailure(error, port);
|
|
52
|
+
}
|
|
53
|
+
printBanner(running);
|
|
54
|
+
if (flags.open && running.uiStatus === "served")
|
|
55
|
+
openInBrowser(`${running.url}/`);
|
|
56
|
+
return Promise.race([stopped, hydrationFailure(running)]);
|
|
57
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export function coldOpenRangeError(start, end, duration) {
|
|
2
|
+
if (![start, end, duration].every(Number.isFinite) || duration < 0.5)
|
|
3
|
+
return "Choose a playable video at least 0.5 seconds long.";
|
|
4
|
+
if (start < 0 || end > duration || end - start < 0.5 - 0.000001)
|
|
5
|
+
return "Choose a highlight at least 0.5 seconds long within the video.";
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { findPrimaryMediaFile } from "videobook-engine";
|
|
4
|
+
import { notebookGridTag, notebookOccupiedSlots, NOTEBOOK_GRID_ROW_COUNT, NOTEBOOK_GRID_COLUMN_COUNT } from "videobook-engine/notebook-grid";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { INTELLIGENCE_MODEL } from "./openai-intelligence.js";
|
|
7
|
+
import { coldOpenRangeError } from "./cold-open-model.js";
|
|
8
|
+
const coldOpenSuggestionSchema = z.object({
|
|
9
|
+
sourceArtifactId: z.string().uuid(),
|
|
10
|
+
sourceObjectHash: z.string().min(1),
|
|
11
|
+
durationSeconds: z.number().finite().min(0.5),
|
|
12
|
+
fps: z.number().finite().positive(),
|
|
13
|
+
startSeconds: z.number().finite().nonnegative(),
|
|
14
|
+
endSeconds: z.number().finite().positive(),
|
|
15
|
+
reason: z.string().min(1),
|
|
16
|
+
model: z.string().min(1),
|
|
17
|
+
});
|
|
18
|
+
export async function coldOpenSource(engine, artifactId, expectedHash) {
|
|
19
|
+
const artifact = engine.artifacts.get(artifactId);
|
|
20
|
+
if (!artifact.ok)
|
|
21
|
+
throw new Error(artifact.error.message);
|
|
22
|
+
if (!["video", "final"].includes(artifact.value.kind))
|
|
23
|
+
throw new Error("Choose a video for the cold open.");
|
|
24
|
+
const manifest = await engine.files.manifest(artifactId, { includeDotfiles: true });
|
|
25
|
+
if (!manifest.ok)
|
|
26
|
+
throw new Error(manifest.error.message);
|
|
27
|
+
const file = findPrimaryMediaFile(manifest.value.files, artifact.value.kind);
|
|
28
|
+
if (!file)
|
|
29
|
+
throw new Error("The source video is missing.");
|
|
30
|
+
if (expectedHash && file.objectHash !== expectedHash)
|
|
31
|
+
throw new Error("The source video changed. Open a new cold-open review.");
|
|
32
|
+
const streams = engine.streams.list(artifactId);
|
|
33
|
+
if (!streams.ok)
|
|
34
|
+
throw new Error(streams.error.message);
|
|
35
|
+
const video = streams.value.find(stream => stream.kind === "video" && stream.sourcePath === file.name && stream.objectHash === file.objectHash);
|
|
36
|
+
if (!video)
|
|
37
|
+
throw new Error("Wait for the video to finish processing before making a cold open.");
|
|
38
|
+
const durationSeconds = video.durationTicks * video.timeBase.numerator / video.timeBase.denominator;
|
|
39
|
+
const metadata = await engine.metadata.artifacts.read(artifactId, "original");
|
|
40
|
+
const fps = metadata.ok && metadata.value.fps && metadata.value.fps > 0 ? metadata.value.fps : 30;
|
|
41
|
+
const error = coldOpenRangeError(0, durationSeconds, durationSeconds);
|
|
42
|
+
if (error)
|
|
43
|
+
throw new Error(error);
|
|
44
|
+
return { artifact: artifact.value, file, manifest: manifest.value, path: join(manifest.value.path, file.name), durationSeconds, fps };
|
|
45
|
+
}
|
|
46
|
+
export function coldOpenCacheKey(source) {
|
|
47
|
+
const inputs = source.manifest.files.filter(file => ["elevenlabs.json", ".analysis.gpt4otrans.json"].includes(file.name))
|
|
48
|
+
.map(file => [file.name, file.objectHash]).sort();
|
|
49
|
+
return `cold-open.suggestion.${createHash("sha256").update(JSON.stringify([1, INTELLIGENCE_MODEL, source.artifact.artifactId, source.file.objectHash, inputs])).digest("hex")}`;
|
|
50
|
+
}
|
|
51
|
+
export function readColdOpenSuggestion(engine, key) {
|
|
52
|
+
const parsed = coldOpenSuggestionSchema.safeParse(engine.settings.get(key));
|
|
53
|
+
if (!parsed.success || coldOpenRangeError(parsed.data.startSeconds, parsed.data.endSeconds, parsed.data.durationSeconds))
|
|
54
|
+
return null;
|
|
55
|
+
return parsed.data;
|
|
56
|
+
}
|
|
57
|
+
export function coldOpenDestination(notebook, sourceArtifactId) {
|
|
58
|
+
const occupied = new Set(notebookOccupiedSlots(notebook.cells).map(notebookGridTag));
|
|
59
|
+
const source = notebook.cells.find(cell => cell.outputArtifactId === sourceArtifactId);
|
|
60
|
+
const below = source ? { row: source.slot.row + 1, column: source.slot.column } : null;
|
|
61
|
+
if (below && below.row < NOTEBOOK_GRID_ROW_COUNT && !occupied.has(notebookGridTag(below)))
|
|
62
|
+
return below;
|
|
63
|
+
for (let row = 0; row < NOTEBOOK_GRID_ROW_COUNT; row += 1) {
|
|
64
|
+
for (let column = 0; column < NOTEBOOK_GRID_COLUMN_COUNT; column += 1) {
|
|
65
|
+
const slot = { row, column };
|
|
66
|
+
if (!occupied.has(notebookGridTag(slot)))
|
|
67
|
+
return slot;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
throw new Error("The notebook needs one empty cell for the cold-open copy.");
|
|
71
|
+
}
|
|
72
|
+
export function checkColdOpenNotebook(engine, notebookId, sourceArtifactId) {
|
|
73
|
+
if (!notebookId)
|
|
74
|
+
return;
|
|
75
|
+
const notebook = engine.notebooks.read(notebookId);
|
|
76
|
+
if (!notebook.ok)
|
|
77
|
+
throw new Error(notebook.error.message);
|
|
78
|
+
coldOpenDestination(notebook.value, sourceArtifactId);
|
|
79
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { listModels } from "./media/listing.js";
|
|
2
|
+
import { buildWorkspaceActionCatalog } from "./workspace-action-catalog.js";
|
|
3
|
+
import { WORKSPACE_ACTION_SURFACES } from "./workspace-action-surfaces.js";
|
|
4
|
+
function oneLine(value) {
|
|
5
|
+
return value.replace(/\s+/g, " ").trim();
|
|
6
|
+
}
|
|
7
|
+
function escapeTable(value) {
|
|
8
|
+
return oneLine(value).replaceAll("|", "\\|");
|
|
9
|
+
}
|
|
10
|
+
export function renderCommandReference(tools, commands) {
|
|
11
|
+
const toolLines = [...tools]
|
|
12
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
13
|
+
.map((tool) => `- \`${tool.name}\` — ${oneLine(tool.description)}`);
|
|
14
|
+
const catalog = buildWorkspaceActionCatalog(tools, commands);
|
|
15
|
+
const commandLines = [...catalog.actions]
|
|
16
|
+
.sort((a, b) => a.command.localeCompare(b.command))
|
|
17
|
+
.map((command) => {
|
|
18
|
+
const aliases = command.aliases ?? [];
|
|
19
|
+
const aliasText = aliases.length > 0 ? ` (aliases: ${aliases.map((alias) => `\`${alias}\``).join(", ")})` : "";
|
|
20
|
+
const target = command.handler.kind === "tool"
|
|
21
|
+
? `; MCP: \`${command.handler.tool}\``
|
|
22
|
+
: command.handler.kind === "ui" ? `; UI: \`${command.handler.action}\`` : "; preview or workspace operation";
|
|
23
|
+
return `- \`${command.command}\`${aliasText} — ${oneLine(command.description)}${target}`;
|
|
24
|
+
});
|
|
25
|
+
return `# Command Reference
|
|
26
|
+
|
|
27
|
+
Every current MCP endpoint and discoverable action is listed once. Use the Actions button on any screen, press Ctrl+Shift+K / Cmd+Shift+K, or type a slash in chat to browse the complete catalog. Search includes aliases and descriptions. A command with missing arguments opens its form; media, notebook, entity, track and clip selectors provide targets. Advanced provider alternatives retain shared input fields and show a cost review before generation.
|
|
28
|
+
|
|
29
|
+
Media commands \`/export\`, \`/delete\`, \`/reverse\` and \`/trim\` target media. Sequence operations use \`/export-sequence\`, \`/clip-delete\`, \`/clip-reverse\`, \`/clip-trim\`, and \`/clip-audio\`; clip timing uses frames and media trimming uses seconds. \`/clip-audio\` edits gain, mute, and fades on an audio-track clip. Explicit legacy clip IDs and \`/export @sequence-main\` remain accepted. Use grid addresses such as \`/crop @a1\`, \`/trim @a1 0 2\` or \`/insert @a1 0 60\`.
|
|
30
|
+
|
|
31
|
+
Image, video and audio generation runs through \`generate_image\`, \`generate_video\` and \`generate_audio\` with a model id from \`list_generation_models\`; see [generation-models.md](generation-models.md). Slash aliases match their canonical action in search. The legacy \`/skeleton\` shortcut opens a reviewed AI pose-guide request. Import owns upload transport and duplicate-byte checks; it uses a native file picker. Set \`VIDEOBOOK_DOWNLOADS_DIR\` to override the server's default Downloads directory.
|
|
32
|
+
|
|
33
|
+
Routine pointer actions return focus to chat. Editing fields, open dialogs, keyboard navigation and native playback retain focus. Closing a dialog restores its opener when appropriate; delayed work cannot take focus from a newer edit.
|
|
34
|
+
|
|
35
|
+
## MCP endpoints (${tools.length})
|
|
36
|
+
|
|
37
|
+
${toolLines.join("\n")}
|
|
38
|
+
|
|
39
|
+
## Slash actions (${catalog.actions.length}; ${commands.length} dedicated command routes)
|
|
40
|
+
|
|
41
|
+
${commandLines.join("\n")}
|
|
42
|
+
- \`/<project-name>\` — Switch to that project; generated dynamically for each project whose name does not conflict with a command or alias.
|
|
43
|
+
`;
|
|
44
|
+
}
|
|
45
|
+
export function renderWorkspaceActionCoverage(catalog) {
|
|
46
|
+
const rows = [...catalog.actions].sort((left, right) => left.id.localeCompare(right.id)).map((action) => {
|
|
47
|
+
const target = action.target.argument ? `${action.target.kind}${action.target.multiple ? " selection" : ""}: ${action.target.argument}` : action.target.kind;
|
|
48
|
+
const handler = action.handler.kind === "tool" ? action.handler.tool : action.handler.kind === "ui" ? action.handler.action : action.handler.command;
|
|
49
|
+
return `| \`${action.id}\` | \`${action.command}\` | ${action.aliases.join(", ")} | ${action.scope} | ${escapeTable(target)} | \`${handler}\` | ${action.surfaces.join(", ")} | ${action.focus} | ${action.confirmation} |`;
|
|
50
|
+
});
|
|
51
|
+
const surfaces = Object.entries(WORKSPACE_ACTION_SURFACES).map(([name, entry]) => `| ${name} | [${entry.component}](../${entry.component}) | ${entry.trigger} |`);
|
|
52
|
+
const workflows = catalog.tools.filter((entry) => entry.exposure === "workflow").map((entry) => `| \`${entry.tool}\` | \`${entry.actionId}\` | ${escapeTable(entry.reason)} |`);
|
|
53
|
+
return `# Workspace action coverage
|
|
54
|
+
|
|
55
|
+
Generated by \`npm run docs:commands\` from the registered tools, command definitions and surface contracts. ${catalog.tools.length} active endpoints map to ${catalog.actions.length} visible actions or their owning workflows. Unknown endpoints, handlers, aliases and missing declared surfaces fail the catalog contract.
|
|
56
|
+
|
|
57
|
+
The browser regression suite checks catalog equality, complete slash traversal, typed form discovery, named target selection, menu and slash parity, review and cancellation, real media mutations and focus. Provider generation, posting and OS clipboard integration require separate execution validation; opening a form never starts a paid job or publishes content.
|
|
58
|
+
|
|
59
|
+
## Surface entry points
|
|
60
|
+
|
|
61
|
+
| Surface | Component | Entry point |
|
|
62
|
+
| --- | --- | --- |
|
|
63
|
+
${surfaces.join("\n")}
|
|
64
|
+
|
|
65
|
+
## Actions
|
|
66
|
+
|
|
67
|
+
| Stable ID | Command | Aliases | Scope | Target | Handler | Surfaces | Focus | Review |
|
|
68
|
+
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
|
69
|
+
${rows.join("\n")}
|
|
70
|
+
|
|
71
|
+
## Transport and workflow ownership
|
|
72
|
+
|
|
73
|
+
| Endpoint | Visible owner | Reason |
|
|
74
|
+
| --- | --- | --- |
|
|
75
|
+
${workflows.join("\n")}
|
|
76
|
+
`;
|
|
77
|
+
}
|
|
78
|
+
export function renderGenerationModels() {
|
|
79
|
+
const models = listModels({ includeHidden: true });
|
|
80
|
+
const rows = models.map((model) => {
|
|
81
|
+
const refs = model.slots.map((slot) => `${slot.role} ${slot.min}-${slot.max ?? "n"}`).join(", ") || "none";
|
|
82
|
+
const modes = model.modes.length > 1 ? model.modes.map((mode) => mode.id).join(", ") : "";
|
|
83
|
+
return `| \`${escapeTable(model.id)}\` | ${escapeTable(model.title)} | ${model.media} | ${model.entry.operations.join(", ")} | ${escapeTable(refs)} | ${modes} | ${model.entry.hidden ? "hidden" : "visible"} |`;
|
|
84
|
+
});
|
|
85
|
+
return `# Generation Models
|
|
86
|
+
|
|
87
|
+
Generated by \`npm run docs:commands\` from the media catalog (\`server/src/media/catalog.ts\`). Call \`generate_image\`, \`generate_video\` or \`generate_audio\` with one of these model ids; \`list_generation_models\` returns the same list with every option, enum, range and default read from the apicity schemas.
|
|
88
|
+
|
|
89
|
+
- Models: ${models.length}
|
|
90
|
+
|
|
91
|
+
| Model | Title | Media | Operations | Refs | Modes | Picker |
|
|
92
|
+
| --- | --- | --- | --- | --- | --- | --- |
|
|
93
|
+
${rows.join("\n")}
|
|
94
|
+
`;
|
|
95
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { B2Error, createB2 } from "@apicity/b2";
|
|
3
|
+
function createB2ContentStore(config) {
|
|
4
|
+
const provider = createB2({
|
|
5
|
+
accessKeyId: config.accessKeyId,
|
|
6
|
+
secretAccessKey: config.secretAccessKey,
|
|
7
|
+
region: config.region,
|
|
8
|
+
...(config.endpoint ? { endpoint: config.endpoint } : {}),
|
|
9
|
+
});
|
|
10
|
+
return {
|
|
11
|
+
head: async (key) => {
|
|
12
|
+
try {
|
|
13
|
+
const result = await provider.objects.head({
|
|
14
|
+
bucket: config.bucket,
|
|
15
|
+
key,
|
|
16
|
+
});
|
|
17
|
+
return {
|
|
18
|
+
exists: true,
|
|
19
|
+
...(result.contentLength !== undefined
|
|
20
|
+
? { size: result.contentLength }
|
|
21
|
+
: {}),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
if (error instanceof B2Error && error.status === 404) {
|
|
26
|
+
return { exists: false };
|
|
27
|
+
}
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
uploadFile: async (key, sourcePath) => {
|
|
32
|
+
await provider.objects.put({
|
|
33
|
+
bucket: config.bucket,
|
|
34
|
+
key,
|
|
35
|
+
body: await readFile(sourcePath),
|
|
36
|
+
contentType: "application/octet-stream",
|
|
37
|
+
});
|
|
38
|
+
},
|
|
39
|
+
downloadFile: async (key, destinationPath) => {
|
|
40
|
+
const result = await provider.objects.get({
|
|
41
|
+
bucket: config.bucket,
|
|
42
|
+
key,
|
|
43
|
+
});
|
|
44
|
+
await writeFile(destinationPath, new Uint8Array(result.body));
|
|
45
|
+
},
|
|
46
|
+
delete: async (key) => {
|
|
47
|
+
await provider.objects.del({
|
|
48
|
+
bucket: config.bucket,
|
|
49
|
+
key,
|
|
50
|
+
});
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export function b2ContentStoreConfigFromEnvironment() {
|
|
55
|
+
const accessKeyId = process.env.B2_ACCESS_KEY_ID;
|
|
56
|
+
const secretAccessKey = process.env.B2_SECRET_ACCESS_KEY;
|
|
57
|
+
const region = process.env.B2_REGION;
|
|
58
|
+
const bucket = process.env.B2_BUCKET;
|
|
59
|
+
if (!accessKeyId || !secretAccessKey || !region || !bucket)
|
|
60
|
+
return undefined;
|
|
61
|
+
return {
|
|
62
|
+
accessKeyId,
|
|
63
|
+
secretAccessKey,
|
|
64
|
+
region,
|
|
65
|
+
bucket,
|
|
66
|
+
...(process.env.B2_ENDPOINT
|
|
67
|
+
? { endpoint: process.env.B2_ENDPOINT }
|
|
68
|
+
: {}),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export function contentStoreFromEnvironment() {
|
|
72
|
+
const config = b2ContentStoreConfigFromEnvironment();
|
|
73
|
+
return config ? createB2ContentStore(config) : undefined;
|
|
74
|
+
}
|
package/dist/cost.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const CANONICAL_DIMS = {
|
|
2
|
+
"16:9": [1920, 1080],
|
|
3
|
+
"9:16": [1080, 1920],
|
|
4
|
+
"1:1": [1080, 1080],
|
|
5
|
+
};
|
|
6
|
+
export function cropGeometry(rect, srcW, srcH, ratio, alignment) {
|
|
7
|
+
if (!Number.isFinite(srcW) || !Number.isFinite(srcH) || srcW < alignment || srcH < alignment) {
|
|
8
|
+
throw new Error("Could not probe usable source dimensions");
|
|
9
|
+
}
|
|
10
|
+
const cropW = Math.min(Math.floor(srcW / alignment) * alignment, Math.max(alignment, Math.round(rect.w * srcW / alignment) * alignment));
|
|
11
|
+
const cropH = Math.min(Math.floor(srcH / alignment) * alignment, Math.max(alignment, Math.round(rect.h * srcH / alignment) * alignment));
|
|
12
|
+
const roundOrigin = alignment === 2 ? Math.floor : Math.round;
|
|
13
|
+
const cropX = Math.min(srcW - cropW, Math.max(0, roundOrigin(rect.x * srcW / alignment) * alignment));
|
|
14
|
+
const cropY = Math.min(srcH - cropH, Math.max(0, roundOrigin(rect.y * srcH / alignment) * alignment));
|
|
15
|
+
const [outW, outH] = CANONICAL_DIMS[ratio];
|
|
16
|
+
const cropAspect = cropW / cropH;
|
|
17
|
+
const targetAspect = outW / outH;
|
|
18
|
+
const aspectError = Math.abs(cropAspect - targetAspect) / targetAspect;
|
|
19
|
+
if (aspectError > 0.02) {
|
|
20
|
+
throw new Error(`Crop rectangle aspect ${cropAspect.toFixed(4)} (${cropW}x${cropH}) does not match target ratio ${ratio} (${targetAspect.toFixed(4)}); error ${(aspectError * 100).toFixed(1)}% > 2%`);
|
|
21
|
+
}
|
|
22
|
+
return { cropX, cropY, cropW, cropH, outW, outH };
|
|
23
|
+
}
|