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,49 @@
|
|
|
1
|
+
import { resolveNotebookCellArtifactId } from "videobook-engine";
|
|
2
|
+
import { notebookGridTag } from "videobook-engine/notebook-grid";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { resolveNotebookArtifactReference } from "./notebook-reference-resolver.js";
|
|
5
|
+
const SHOW_MEDIA_FULLSCREEN = "show_media_fullscreen";
|
|
6
|
+
const FULLSCREEN_MEDIA_KINDS = ["image", "video", "final"];
|
|
7
|
+
const CHAT_VIEW_ACTIONS = [
|
|
8
|
+
{
|
|
9
|
+
name: SHOW_MEDIA_FULLSCREEN,
|
|
10
|
+
description: "Show, open, view or enlarge one notebook cell's image or video in the fullscreen viewer. Changes nothing in the book",
|
|
11
|
+
schema: {
|
|
12
|
+
asset_id: z.string().min(1).describe("Artifact ID or grid address of the cell's image or video"),
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
];
|
|
16
|
+
function resolveMediaFullscreen(engine, notebook, reference) {
|
|
17
|
+
if (!notebook)
|
|
18
|
+
return { ok: false, message: "Open a notebook before showing media fullscreen" };
|
|
19
|
+
const resolved = resolveNotebookArtifactReference({ engine, documents: [notebook], reference });
|
|
20
|
+
if ("code" in resolved)
|
|
21
|
+
return { ok: false, message: resolved.message };
|
|
22
|
+
const cell = notebook.cells.find((candidate) => resolved.cellId
|
|
23
|
+
? candidate.id === resolved.cellId
|
|
24
|
+
: resolveNotebookCellArtifactId(notebook, candidate) === resolved.artifactId);
|
|
25
|
+
if (!cell)
|
|
26
|
+
return { ok: false, message: `No cell in this notebook holds ${resolved.mention}` };
|
|
27
|
+
const artifact = engine.artifacts.get(resolved.artifactId);
|
|
28
|
+
if (!artifact.ok)
|
|
29
|
+
return { ok: false, message: artifact.error.message };
|
|
30
|
+
if (!FULLSCREEN_MEDIA_KINDS.includes(artifact.value.kind)) {
|
|
31
|
+
return {
|
|
32
|
+
ok: false,
|
|
33
|
+
message: `${notebookGridTag(cell.slot)} holds ${artifact.value.kind}; fullscreen shows images and video`,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return { ok: true, params: { asset_id: resolved.artifactId, cell_id: cell.id } };
|
|
37
|
+
}
|
|
38
|
+
/** Validate a routed view action and pin it to the cell the app should open. */
|
|
39
|
+
function resolveChatViewAction(engine, notebook, tool, params) {
|
|
40
|
+
const action = CHAT_VIEW_ACTIONS.find((candidate) => candidate.name === tool);
|
|
41
|
+
if (!action)
|
|
42
|
+
return { ok: false, message: `Unknown view action: ${tool}` };
|
|
43
|
+
const parsed = z.object(action.schema).safeParse(params);
|
|
44
|
+
if (!parsed.success) {
|
|
45
|
+
return { ok: false, message: `Invalid parameters for ${tool}: ${z.prettifyError(parsed.error)}` };
|
|
46
|
+
}
|
|
47
|
+
return resolveMediaFullscreen(engine, notebook, String(parsed.data.asset_id));
|
|
48
|
+
}
|
|
49
|
+
export { CHAT_VIEW_ACTIONS, resolveChatViewAction };
|
package/dist/cli/args.js
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { parseArgs } from "node:util";
|
|
3
|
+
export class CliUsageError extends Error {
|
|
4
|
+
constructor(message) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "CliUsageError";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
const LOG_LEVELS = new Set(["silent", "error", "warn", "info", "debug"]);
|
|
10
|
+
const COMMANDS = new Set(["serve", "mcp", "run", "tools", "gc-books", "doctor", "version", "help"]);
|
|
11
|
+
const DEFAULT_RUN_TIMEOUT_SECONDS = 600;
|
|
12
|
+
const GLOBAL_OPTIONS = {
|
|
13
|
+
port: { type: "string" },
|
|
14
|
+
library: { type: "string" },
|
|
15
|
+
home: { type: "string" },
|
|
16
|
+
"log-level": { type: "string" },
|
|
17
|
+
help: { type: "boolean", short: "h" },
|
|
18
|
+
version: { type: "boolean", short: "v" },
|
|
19
|
+
};
|
|
20
|
+
const COMMAND_OPTIONS = {
|
|
21
|
+
serve: { headless: { type: "boolean" }, "no-ui": { type: "boolean" }, open: { type: "boolean" } },
|
|
22
|
+
mcp: {},
|
|
23
|
+
run: { wait: { type: "boolean" }, "no-wait": { type: "boolean" }, timeout: { type: "string" } },
|
|
24
|
+
tools: {},
|
|
25
|
+
"gc-books": {},
|
|
26
|
+
doctor: { json: { type: "boolean" } },
|
|
27
|
+
};
|
|
28
|
+
function splitCommand(argv) {
|
|
29
|
+
const [first, ...rest] = argv;
|
|
30
|
+
if (first === undefined || first.startsWith("-"))
|
|
31
|
+
return { command: "serve", rest: argv };
|
|
32
|
+
if (!COMMANDS.has(first))
|
|
33
|
+
throw new CliUsageError(`Unknown command "${first}".`);
|
|
34
|
+
return { command: first, rest };
|
|
35
|
+
}
|
|
36
|
+
function parsePort(raw) {
|
|
37
|
+
if (typeof raw !== "string")
|
|
38
|
+
return undefined;
|
|
39
|
+
const port = Number(raw);
|
|
40
|
+
if (!/^\d+$/u.test(raw) || port > 65535)
|
|
41
|
+
throw new CliUsageError(`--port must be an integer from 0 to 65535, got "${raw}".`);
|
|
42
|
+
return port;
|
|
43
|
+
}
|
|
44
|
+
function parseLogLevel(raw) {
|
|
45
|
+
if (typeof raw !== "string")
|
|
46
|
+
return undefined;
|
|
47
|
+
if (!LOG_LEVELS.has(raw))
|
|
48
|
+
throw new CliUsageError(`--log-level must be one of silent, error, warn, info, debug; got "${raw}".`);
|
|
49
|
+
return raw;
|
|
50
|
+
}
|
|
51
|
+
function parseTimeout(raw) {
|
|
52
|
+
if (typeof raw !== "string")
|
|
53
|
+
return DEFAULT_RUN_TIMEOUT_SECONDS;
|
|
54
|
+
const seconds = Number(raw);
|
|
55
|
+
if (!Number.isFinite(seconds) || seconds <= 0)
|
|
56
|
+
throw new CliUsageError(`--timeout must be a positive number of seconds, got "${raw}".`);
|
|
57
|
+
return seconds;
|
|
58
|
+
}
|
|
59
|
+
function stringValue(raw) {
|
|
60
|
+
return typeof raw === "string" && raw.trim() !== "" ? raw : undefined;
|
|
61
|
+
}
|
|
62
|
+
function globalFlags(values) {
|
|
63
|
+
const flags = {};
|
|
64
|
+
const port = parsePort(values.port);
|
|
65
|
+
const library = stringValue(values.library);
|
|
66
|
+
const home = stringValue(values.home);
|
|
67
|
+
const logLevel = parseLogLevel(values["log-level"]);
|
|
68
|
+
if (port !== undefined)
|
|
69
|
+
flags.port = port;
|
|
70
|
+
if (library !== undefined)
|
|
71
|
+
flags.library = library;
|
|
72
|
+
if (home !== undefined)
|
|
73
|
+
flags.home = home;
|
|
74
|
+
if (logLevel !== undefined)
|
|
75
|
+
flags.logLevel = logLevel;
|
|
76
|
+
return flags;
|
|
77
|
+
}
|
|
78
|
+
function parseValue(raw) {
|
|
79
|
+
try {
|
|
80
|
+
return JSON.parse(raw);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return raw;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export function parseToolArgs(pairs) {
|
|
87
|
+
const args = {};
|
|
88
|
+
for (const pair of pairs) {
|
|
89
|
+
const eq = pair.indexOf("=");
|
|
90
|
+
if (eq <= 0)
|
|
91
|
+
throw new CliUsageError(`Invalid tool argument "${pair}". Use key=value.`);
|
|
92
|
+
args[pair.slice(0, eq)] = parseValue(pair.slice(eq + 1));
|
|
93
|
+
}
|
|
94
|
+
return args;
|
|
95
|
+
}
|
|
96
|
+
function parseCommandArgs(command, rest) {
|
|
97
|
+
try {
|
|
98
|
+
const parsed = parseArgs({
|
|
99
|
+
args: rest,
|
|
100
|
+
options: { ...GLOBAL_OPTIONS, ...COMMAND_OPTIONS[command] },
|
|
101
|
+
allowPositionals: command === "run",
|
|
102
|
+
strict: true,
|
|
103
|
+
});
|
|
104
|
+
return { values: parsed.values, positionals: parsed.positionals };
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
throw new CliUsageError(error instanceof Error ? error.message : String(error));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function buildCommand(command, values, positionals) {
|
|
111
|
+
const flags = globalFlags(values);
|
|
112
|
+
switch (command) {
|
|
113
|
+
case "serve":
|
|
114
|
+
return { command, flags: { ...flags, ui: values.headless !== true && values["no-ui"] !== true, open: values.open === true } };
|
|
115
|
+
case "run": {
|
|
116
|
+
const [tool, ...pairs] = positionals;
|
|
117
|
+
if (!tool)
|
|
118
|
+
throw new CliUsageError("run needs a tool name, for example: videobook run list_books");
|
|
119
|
+
return { command, tool, toolArgs: parseToolArgs(pairs), flags: { ...flags, wait: values["no-wait"] !== true, timeoutSeconds: parseTimeout(values.timeout) } };
|
|
120
|
+
}
|
|
121
|
+
case "doctor":
|
|
122
|
+
return { command, flags: { ...flags, json: values.json === true } };
|
|
123
|
+
default:
|
|
124
|
+
return { command, flags };
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
export function parseCli(argv) {
|
|
128
|
+
const { command, rest } = splitCommand(argv);
|
|
129
|
+
if (command === "help")
|
|
130
|
+
return { command: "help" };
|
|
131
|
+
if (command === "version")
|
|
132
|
+
return { command: "version" };
|
|
133
|
+
const name = command;
|
|
134
|
+
const { values, positionals } = parseCommandArgs(name, rest);
|
|
135
|
+
if (values.help === true)
|
|
136
|
+
return { command: "help" };
|
|
137
|
+
if (values.version === true)
|
|
138
|
+
return { command: "version" };
|
|
139
|
+
return buildCommand(name, values, positionals);
|
|
140
|
+
}
|
|
141
|
+
const HOME_DERIVED_ENV = ["VIDEOBOOK_LIBRARY_DIR", "VIDEOBOOK_APP_STATE_PATH", "VIDEOBOOK_ENV_PATH"];
|
|
142
|
+
function applyHome(home, env, cwd) {
|
|
143
|
+
env.VIDEOBOOK_HOME = resolve(cwd, home);
|
|
144
|
+
for (const name of HOME_DERIVED_ENV)
|
|
145
|
+
delete env[name];
|
|
146
|
+
}
|
|
147
|
+
export function applyFlagsToEnv(flags, env, cwd) {
|
|
148
|
+
if (flags.home !== undefined)
|
|
149
|
+
applyHome(flags.home, env, cwd);
|
|
150
|
+
if (flags.library !== undefined)
|
|
151
|
+
env.VIDEOBOOK_LIBRARY_DIR = resolve(cwd, flags.library);
|
|
152
|
+
if (flags.port !== undefined)
|
|
153
|
+
env.PORT = String(flags.port);
|
|
154
|
+
if (flags.logLevel !== undefined)
|
|
155
|
+
env.VIDEOBOOK_LOG_LEVEL = flags.logLevel;
|
|
156
|
+
}
|
|
157
|
+
export function resolvePort(env) {
|
|
158
|
+
const raw = env.PORT?.trim();
|
|
159
|
+
const port = raw ? Number(raw) : 3001;
|
|
160
|
+
return Number.isInteger(port) && port >= 0 && port <= 65535 ? port : 3001;
|
|
161
|
+
}
|
|
162
|
+
export function helpText() {
|
|
163
|
+
return [
|
|
164
|
+
"Usage: videobook [command] [options]",
|
|
165
|
+
"",
|
|
166
|
+
"Commands:",
|
|
167
|
+
" serve Run the MCP server with the web UI (default)",
|
|
168
|
+
" mcp Serve MCP over stdio for agent hosts such as Claude Code",
|
|
169
|
+
" run <tool> [k=v] Call one MCP tool and print its result",
|
|
170
|
+
" tools List the MCP tools and their parameters",
|
|
171
|
+
" gc-books Reclaim catalog storage in every book",
|
|
172
|
+
" doctor Check Node, media binaries, native modules and keys",
|
|
173
|
+
" version Print versions",
|
|
174
|
+
" help Print this help",
|
|
175
|
+
"",
|
|
176
|
+
"Options:",
|
|
177
|
+
" --port <n> HTTP port on 127.0.0.1 (default 3001, or PORT)",
|
|
178
|
+
" --library <dir> Book library directory (default <data home>/books)",
|
|
179
|
+
" --home <dir> Data home (default ~/.videobook, or VIDEOBOOK_HOME)",
|
|
180
|
+
" --log-level <l> silent, error, warn, info or debug",
|
|
181
|
+
" --headless serve: MCP and API only, no web UI (alias --no-ui)",
|
|
182
|
+
" --open serve: open the web UI in a browser",
|
|
183
|
+
" --no-wait run: do not wait for queued jobs",
|
|
184
|
+
" --timeout <s> run: seconds to wait for jobs (default 600)",
|
|
185
|
+
" --json doctor: print machine-readable results",
|
|
186
|
+
" -v, --version Print versions",
|
|
187
|
+
" -h, --help Print this help",
|
|
188
|
+
"",
|
|
189
|
+
"Agent hosts: claude mcp add videobook -- videobook mcp",
|
|
190
|
+
"",
|
|
191
|
+
].join("\n");
|
|
192
|
+
}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { access, constants, readdir } from "node:fs/promises";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
import { SCHEMA_VERSION } from "videobook-engine/catalog-metadata";
|
|
7
|
+
import { resolvePort } from "./args.js";
|
|
8
|
+
import { dataLayout } from "../data-home.js";
|
|
9
|
+
import { bookManager } from "../engine.js";
|
|
10
|
+
import { MANAGED_ENV_KEYS, getDefaultSecretStore, isSecretEnvKey, maskEnvValue } from "../env-secrets.js";
|
|
11
|
+
import { FFMPEG } from "../ffmpeg-binary.js";
|
|
12
|
+
import { FFPROBE } from "../ffprobe-binary.js";
|
|
13
|
+
import { inspectServerOwner } from "../server-lock.js";
|
|
14
|
+
import { remotionBrowserDir, remotionCacheRoot } from "../remotion/browser.js";
|
|
15
|
+
const execFileAsync = promisify(execFile);
|
|
16
|
+
const require = createRequire(import.meta.url);
|
|
17
|
+
const UUID_V7 = /^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u;
|
|
18
|
+
const ALLOW_SCRIPTS = "ffmpeg-static,ytdlp-nodejs,@dolthub/doltlite,usearch,onnxruntime-node,esbuild";
|
|
19
|
+
const INSTALL_REMEDY = `Reinstall with install scripts allowed: npm install -g videobook --allow-scripts=${ALLOW_SCRIPTS}`;
|
|
20
|
+
function check(fields) {
|
|
21
|
+
return fields;
|
|
22
|
+
}
|
|
23
|
+
function message(error) {
|
|
24
|
+
return error instanceof Error ? error.message.split("\n")[0] ?? error.message : String(error);
|
|
25
|
+
}
|
|
26
|
+
function nodeCheck() {
|
|
27
|
+
const engines = require("../../package.json").engines?.node ?? ">=22";
|
|
28
|
+
const minimum = Number(/(\d+)/u.exec(engines)?.[1] ?? 22);
|
|
29
|
+
const major = Number(process.versions.node.split(".")[0]);
|
|
30
|
+
return check({
|
|
31
|
+
id: "node",
|
|
32
|
+
label: "Node.js",
|
|
33
|
+
status: major >= minimum ? "ok" : "fail",
|
|
34
|
+
detail: `${process.version} (needs ${engines})`,
|
|
35
|
+
...(major >= minimum ? {} : { remedy: `Install Node.js ${minimum} or newer.` }),
|
|
36
|
+
required: true,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
async function nearestExisting(path) {
|
|
40
|
+
let current = path;
|
|
41
|
+
for (;;) {
|
|
42
|
+
try {
|
|
43
|
+
await access(current);
|
|
44
|
+
return current;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
const parent = dirname(current);
|
|
48
|
+
if (parent === current)
|
|
49
|
+
return current;
|
|
50
|
+
current = parent;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async function dataHomeCheck() {
|
|
55
|
+
const layout = dataLayout();
|
|
56
|
+
const kind = layout.kind === "checkout" ? "source checkout" : process.env.VIDEOBOOK_HOME?.trim() ? "VIDEOBOOK_HOME" : "default";
|
|
57
|
+
const target = await nearestExisting(layout.root);
|
|
58
|
+
try {
|
|
59
|
+
await access(target, constants.W_OK);
|
|
60
|
+
return check({ id: "data-home", label: "Data home", status: "ok", detail: `${layout.root} (${kind}); .env ${layout.envPath}`, required: true });
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return check({
|
|
64
|
+
id: "data-home",
|
|
65
|
+
label: "Data home",
|
|
66
|
+
status: "fail",
|
|
67
|
+
detail: `${layout.root} is not writable (${kind})`,
|
|
68
|
+
remedy: "Set VIDEOBOOK_HOME or pass --home to a writable directory.",
|
|
69
|
+
required: true,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async function libraryCheck() {
|
|
74
|
+
const libraryDir = bookManager.rootDir;
|
|
75
|
+
try {
|
|
76
|
+
const books = (await readdir(libraryDir, { withFileTypes: true })).filter((entry) => entry.isDirectory() && UUID_V7.test(entry.name));
|
|
77
|
+
return check({ id: "library", label: "Library", status: "ok", detail: `${libraryDir}: ${books.length} book(s), schema ${SCHEMA_VERSION}`, required: false });
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return check({ id: "library", label: "Library", status: "ok", detail: `${libraryDir} is created on first start (schema ${SCHEMA_VERSION})`, required: false });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function bundledPath(moduleName) {
|
|
84
|
+
try {
|
|
85
|
+
const value = require(moduleName);
|
|
86
|
+
return typeof value === "string" ? value : value?.path ?? null;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async function binaryCheck(id, label, binary, bundled, envName) {
|
|
93
|
+
const origin = binary === bundled ? "bundled" : binary.includes("/") ? envName : "from PATH";
|
|
94
|
+
try {
|
|
95
|
+
const { stdout } = await execFileAsync(binary, ["-version"], { timeout: 15_000 });
|
|
96
|
+
return check({ id, label, status: "ok", detail: `${stdout.split("\n")[0]} (${origin}: ${binary})`, required: true });
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
return check({
|
|
100
|
+
id,
|
|
101
|
+
label,
|
|
102
|
+
status: "fail",
|
|
103
|
+
detail: `${binary} did not run: ${message(error)}`,
|
|
104
|
+
remedy: `${INSTALL_REMEDY}, install ${label} on PATH, or set ${envName}.`,
|
|
105
|
+
required: true,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function ytdlpCheck() {
|
|
110
|
+
try {
|
|
111
|
+
const { helpers } = require("ytdlp-nodejs");
|
|
112
|
+
const binary = helpers.findYtdlpBinary();
|
|
113
|
+
if (binary)
|
|
114
|
+
return check({ id: "yt-dlp", label: "yt-dlp", status: "ok", detail: binary, required: false });
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
return check({ id: "yt-dlp", label: "yt-dlp", status: "warn", detail: message(error), remedy: INSTALL_REMEDY, required: false });
|
|
118
|
+
}
|
|
119
|
+
return check({
|
|
120
|
+
id: "yt-dlp",
|
|
121
|
+
label: "yt-dlp",
|
|
122
|
+
status: "warn",
|
|
123
|
+
detail: "not downloaded; download_video and archive_clip from URLs will fail",
|
|
124
|
+
remedy: INSTALL_REMEDY,
|
|
125
|
+
required: false,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
function sharedObjects(pattern) {
|
|
129
|
+
const report = process.report.getReport();
|
|
130
|
+
return (report.sharedObjects ?? []).filter((name) => pattern.test(name));
|
|
131
|
+
}
|
|
132
|
+
function engineRequire() {
|
|
133
|
+
return createRequire(require.resolve("videobook-engine/package.json"));
|
|
134
|
+
}
|
|
135
|
+
function nativeCoreCheck() {
|
|
136
|
+
try {
|
|
137
|
+
const { DatabaseSync } = engineRequire()("@dolthub/doltlite");
|
|
138
|
+
const database = new DatabaseSync(":memory:");
|
|
139
|
+
const dolt = database.doltVersion();
|
|
140
|
+
database.close();
|
|
141
|
+
const sharp = require("sharp");
|
|
142
|
+
const copies = sharedObjects(/sharp[^/\\]*\.node$/u).length;
|
|
143
|
+
return check({
|
|
144
|
+
id: "native-runtime",
|
|
145
|
+
label: "Native modules",
|
|
146
|
+
status: copies > 1 ? "fail" : "ok",
|
|
147
|
+
detail: `DoltLite ${dolt}; Sharp ${sharp.versions.sharp ?? "?"} / libvips ${sharp.versions.vips ?? "?"}${copies > 1 ? `; ${copies} Sharp copies loaded` : ""}`,
|
|
148
|
+
...(copies > 1 ? { remedy: "Reinstall videobook so npm dedupes Sharp to one copy." } : {}),
|
|
149
|
+
required: true,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
return check({ id: "native-runtime", label: "Native modules", status: "fail", detail: message(error), remedy: INSTALL_REMEDY, required: true });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function searchRuntimeCheck() {
|
|
157
|
+
const fromEngine = engineRequire();
|
|
158
|
+
try {
|
|
159
|
+
fromEngine("onnxruntime-node");
|
|
160
|
+
fromEngine("usearch");
|
|
161
|
+
return check({ id: "search-runtime", label: "Semantic search", status: "ok", detail: "ONNX Runtime and USearch load", required: false });
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
return check({
|
|
165
|
+
id: "search-runtime",
|
|
166
|
+
label: "Semantic search",
|
|
167
|
+
status: "warn",
|
|
168
|
+
detail: `unavailable on ${process.platform}-${process.arch}: ${message(error)}`,
|
|
169
|
+
required: false,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
async function renderBrowserCheck() {
|
|
174
|
+
const root = remotionCacheRoot();
|
|
175
|
+
if (!root) {
|
|
176
|
+
return check({ id: "render-browser", label: "Render browser", status: "ok", detail: "managed by Remotion in this source checkout", required: false });
|
|
177
|
+
}
|
|
178
|
+
const directory = remotionBrowserDir(root);
|
|
179
|
+
try {
|
|
180
|
+
await access(join(directory, "chrome-headless-shell"));
|
|
181
|
+
return check({ id: "render-browser", label: "Render browser", status: "ok", detail: `Chrome Headless Shell in ${directory}`, required: false });
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
return check({
|
|
185
|
+
id: "render-browser",
|
|
186
|
+
label: "Render browser",
|
|
187
|
+
status: "warn",
|
|
188
|
+
detail: `not downloaded yet; the first render downloads Chrome Headless Shell into ${directory}`,
|
|
189
|
+
required: false,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function providerKeysCheck() {
|
|
194
|
+
const store = getDefaultSecretStore();
|
|
195
|
+
const configured = MANAGED_ENV_KEYS.filter((key) => key.endsWith("_API_KEY") && store.getSource(key) !== "missing");
|
|
196
|
+
if (configured.length === 0) {
|
|
197
|
+
return check({
|
|
198
|
+
id: "provider-keys",
|
|
199
|
+
label: "Provider keys",
|
|
200
|
+
status: "warn",
|
|
201
|
+
detail: "none configured; generation and transcription tools will fail",
|
|
202
|
+
remedy: `Add keys such as OPENAI_API_KEY to ${dataLayout().envPath} or the environment.`,
|
|
203
|
+
required: false,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
const shown = configured.map((key) => {
|
|
207
|
+
const value = store.getValue(key) ?? "";
|
|
208
|
+
return `${key}=${isSecretEnvKey(key) ? maskEnvValue(value) : value} (${store.getSource(key)})`;
|
|
209
|
+
});
|
|
210
|
+
return check({ id: "provider-keys", label: "Provider keys", status: "ok", detail: shown.join(", "), required: false });
|
|
211
|
+
}
|
|
212
|
+
async function ownerCheck(port) {
|
|
213
|
+
const owner = await inspectServerOwner(bookManager.rootDir);
|
|
214
|
+
if (owner.kind === "running") {
|
|
215
|
+
return check({ id: "owner", label: "Server", status: "ok", detail: `running at ${owner.record.url} (pid ${owner.record.pid})`, required: false });
|
|
216
|
+
}
|
|
217
|
+
if (owner.kind === "unresponsive") {
|
|
218
|
+
return check({ id: "owner", label: "Server", status: "warn", detail: `pid ${owner.record.pid} holds the library but does not answer at ${owner.record.url}`, required: false });
|
|
219
|
+
}
|
|
220
|
+
return check({ id: "owner", label: "Server", status: "ok", detail: `not running (videobook serve listens on 127.0.0.1:${port})`, required: false });
|
|
221
|
+
}
|
|
222
|
+
async function collectDoctorChecks() {
|
|
223
|
+
return [
|
|
224
|
+
nodeCheck(),
|
|
225
|
+
await dataHomeCheck(),
|
|
226
|
+
await libraryCheck(),
|
|
227
|
+
await binaryCheck("ffmpeg", "ffmpeg", FFMPEG, bundledPath("ffmpeg-static"), "VIDEOBOOK_FFMPEG_PATH"),
|
|
228
|
+
await binaryCheck("ffprobe", "ffprobe", FFPROBE, bundledPath("ffprobe-static"), "VIDEOBOOK_FFPROBE_PATH"),
|
|
229
|
+
ytdlpCheck(),
|
|
230
|
+
nativeCoreCheck(),
|
|
231
|
+
searchRuntimeCheck(),
|
|
232
|
+
await renderBrowserCheck(),
|
|
233
|
+
providerKeysCheck(),
|
|
234
|
+
await ownerCheck(resolvePort(process.env)),
|
|
235
|
+
];
|
|
236
|
+
}
|
|
237
|
+
function statusLabel(status) {
|
|
238
|
+
return status === "ok" ? "ok " : status === "warn" ? "warn" : "FAIL";
|
|
239
|
+
}
|
|
240
|
+
function renderText(checks) {
|
|
241
|
+
const lines = checks.flatMap((item) => [
|
|
242
|
+
`${statusLabel(item.status)} ${item.label.padEnd(16)} ${item.detail}`,
|
|
243
|
+
...(item.remedy && item.status !== "ok" ? [` ${"".padEnd(16)} fix: ${item.remedy}`] : []),
|
|
244
|
+
]);
|
|
245
|
+
return `${lines.join("\n")}\n`;
|
|
246
|
+
}
|
|
247
|
+
function doctorExitCode(checks) {
|
|
248
|
+
return checks.some((item) => item.required && item.status === "fail") ? 1 : 0;
|
|
249
|
+
}
|
|
250
|
+
export async function runDoctorCommand(flags) {
|
|
251
|
+
const checks = await collectDoctorChecks();
|
|
252
|
+
const code = doctorExitCode(checks);
|
|
253
|
+
process.stdout.write(flags.json ? `${JSON.stringify({ ok: code === 0, checks }, null, 2)}\n` : renderText(checks));
|
|
254
|
+
return code;
|
|
255
|
+
}
|
package/dist/cli/main.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { SCHEMA_VERSION } from "videobook-engine/catalog-metadata";
|
|
3
|
+
import { redirectConsoleToStderr } from "./stdio-guard.js";
|
|
4
|
+
import { CliUsageError, applyFlagsToEnv, helpText, parseCli, } from "./args.js";
|
|
5
|
+
const MCP_PROTOCOL_VERSION = "2026-07-28";
|
|
6
|
+
function versionLine() {
|
|
7
|
+
const require = createRequire(import.meta.url);
|
|
8
|
+
const own = require("../../package.json");
|
|
9
|
+
const engine = require("videobook-engine/package.json");
|
|
10
|
+
return `videobook ${own.version} (engine ${engine.version}, schema ${SCHEMA_VERSION}, mcp ${MCP_PROTOCOL_VERSION})`;
|
|
11
|
+
}
|
|
12
|
+
function parseOrReport(argv) {
|
|
13
|
+
try {
|
|
14
|
+
return parseCli(argv);
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
if (!(error instanceof CliUsageError))
|
|
18
|
+
throw error;
|
|
19
|
+
process.stderr.write(`${error.message}\n\n${helpText()}`);
|
|
20
|
+
return 2;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
async function runCommand(parsed) {
|
|
24
|
+
switch (parsed.command) {
|
|
25
|
+
case "serve": {
|
|
26
|
+
const { runServeCommand } = await import("./serve-command.js");
|
|
27
|
+
return runServeCommand(parsed.flags);
|
|
28
|
+
}
|
|
29
|
+
case "mcp": {
|
|
30
|
+
const { runMcpCommand } = await import("./mcp-command.js");
|
|
31
|
+
return runMcpCommand();
|
|
32
|
+
}
|
|
33
|
+
case "run": {
|
|
34
|
+
const { runToolCommand } = await import("./run-command.js");
|
|
35
|
+
return runToolCommand(parsed.flags, parsed.tool, parsed.toolArgs);
|
|
36
|
+
}
|
|
37
|
+
case "tools": {
|
|
38
|
+
const { runToolsCommand } = await import("./run-command.js");
|
|
39
|
+
return runToolsCommand();
|
|
40
|
+
}
|
|
41
|
+
case "gc-books": {
|
|
42
|
+
const { runGcBooksCommand } = await import("./run-command.js");
|
|
43
|
+
return runGcBooksCommand();
|
|
44
|
+
}
|
|
45
|
+
case "doctor": {
|
|
46
|
+
const { runDoctorCommand } = await import("./doctor-command.js");
|
|
47
|
+
return runDoctorCommand(parsed.flags);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
async function main(argv) {
|
|
52
|
+
const parsed = parseOrReport(argv);
|
|
53
|
+
if (typeof parsed === "number")
|
|
54
|
+
return parsed;
|
|
55
|
+
if (parsed.command === "help") {
|
|
56
|
+
process.stdout.write(helpText());
|
|
57
|
+
return 0;
|
|
58
|
+
}
|
|
59
|
+
if (parsed.command === "version") {
|
|
60
|
+
process.stdout.write(`${versionLine()}\n`);
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
applyFlagsToEnv(parsed.flags, process.env, process.cwd());
|
|
64
|
+
if (parsed.command !== "serve")
|
|
65
|
+
redirectConsoleToStderr();
|
|
66
|
+
await import("../load-env.js");
|
|
67
|
+
return runCommand(parsed);
|
|
68
|
+
}
|
|
69
|
+
function flushed(stream) {
|
|
70
|
+
return new Promise((resolveFlush) => stream.write("", () => resolveFlush()));
|
|
71
|
+
}
|
|
72
|
+
function exitOnBrokenPipe(stream) {
|
|
73
|
+
stream.on("error", (error) => {
|
|
74
|
+
if (error.code !== "EPIPE")
|
|
75
|
+
throw error;
|
|
76
|
+
process.exit(0);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
export async function runCli(argv) {
|
|
80
|
+
exitOnBrokenPipe(process.stdout);
|
|
81
|
+
let code;
|
|
82
|
+
try {
|
|
83
|
+
code = await main(argv);
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
process.stderr.write(`${error instanceof Error ? error.stack ?? error.message : String(error)}\n`);
|
|
87
|
+
code = 1;
|
|
88
|
+
}
|
|
89
|
+
await Promise.all([flushed(process.stdout), flushed(process.stderr)]);
|
|
90
|
+
process.exit(code);
|
|
91
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { serveStdio } from "@modelcontextprotocol/server/stdio";
|
|
2
|
+
import { resolvePort } from "./args.js";
|
|
3
|
+
import { connectUpstream, createRelayServer } from "./mcp-relay.js";
|
|
4
|
+
import { bookManager, scopedEngine } from "../engine.js";
|
|
5
|
+
import { logger } from "../logger.js";
|
|
6
|
+
import { createServer, serverInstructions } from "../server.js";
|
|
7
|
+
import { ServerAlreadyRunningError, inspectServerOwner } from "../server-lock.js";
|
|
8
|
+
import { startServer } from "../serve.js";
|
|
9
|
+
function stdinEnded() {
|
|
10
|
+
return new Promise((resolveEnd) => {
|
|
11
|
+
process.stdin.once("end", () => resolveEnd(0));
|
|
12
|
+
process.stdin.once("close", () => resolveEnd(0));
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
function signalled() {
|
|
16
|
+
return new Promise((resolveSignal) => {
|
|
17
|
+
process.once("SIGINT", () => resolveSignal(0));
|
|
18
|
+
process.once("SIGTERM", () => resolveSignal(0));
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function reportStdioError(error) {
|
|
22
|
+
process.stderr.write(`videobook mcp: ${error.message}\n`);
|
|
23
|
+
}
|
|
24
|
+
function hydrationFailure(running) {
|
|
25
|
+
return running.ready.then(() => new Promise(() => undefined), (error) => {
|
|
26
|
+
logger.error("startup-failed", { error, port: running.port });
|
|
27
|
+
return 1;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
async function relayTo(owner) {
|
|
31
|
+
const upstream = await connectUpstream(`${owner.url}/mcp`);
|
|
32
|
+
const handle = serveStdio(() => createRelayServer(upstream, serverInstructions(owner.url)), { onerror: reportStdioError });
|
|
33
|
+
process.stderr.write(`videobook mcp: relaying stdio to ${owner.url}/mcp (pid ${owner.pid})\n`);
|
|
34
|
+
const code = await Promise.race([stdinEnded(), signalled()]);
|
|
35
|
+
await handle.close();
|
|
36
|
+
await upstream.close();
|
|
37
|
+
return code;
|
|
38
|
+
}
|
|
39
|
+
async function startOwner(port) {
|
|
40
|
+
try {
|
|
41
|
+
return await startServer({ port, ui: true });
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
if (error?.code !== "EADDRINUSE" || port === 0)
|
|
45
|
+
throw error;
|
|
46
|
+
process.stderr.write(`videobook mcp: port ${port} is busy; using a free port.\n`);
|
|
47
|
+
return await startServer({ port: 0, ui: true });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async function serveInProcess(running) {
|
|
51
|
+
const instructions = serverInstructions(running.url);
|
|
52
|
+
const handle = serveStdio(() => createServer(scopedEngine, bookManager, { instructions }), { onerror: reportStdioError });
|
|
53
|
+
process.stderr.write(`videobook mcp: serving stdio; web UI and HTTP MCP at ${running.url}/ for library ${running.libraryDir}\n`);
|
|
54
|
+
const code = await Promise.race([stdinEnded(), signalled(), hydrationFailure(running)]);
|
|
55
|
+
await handle.close();
|
|
56
|
+
await running.close();
|
|
57
|
+
return code;
|
|
58
|
+
}
|
|
59
|
+
async function ownOrRelay(port) {
|
|
60
|
+
let running;
|
|
61
|
+
try {
|
|
62
|
+
running = await startOwner(port);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
if (error instanceof ServerAlreadyRunningError && error.running.url)
|
|
66
|
+
return relayTo(error.running);
|
|
67
|
+
logger.error("startup-failed", { error, port });
|
|
68
|
+
process.stderr.write(`videobook mcp: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
69
|
+
return 1;
|
|
70
|
+
}
|
|
71
|
+
return serveInProcess(running);
|
|
72
|
+
}
|
|
73
|
+
export async function runMcpCommand() {
|
|
74
|
+
const owner = await inspectServerOwner(bookManager.rootDir);
|
|
75
|
+
if (owner.kind === "running")
|
|
76
|
+
return relayTo(owner.record);
|
|
77
|
+
if (owner.kind === "unresponsive") {
|
|
78
|
+
process.stderr.write(`videobook mcp: videobook (pid ${owner.record.pid}) holds ${bookManager.rootDir} but is not responding. Stop it first.\n`);
|
|
79
|
+
return 1;
|
|
80
|
+
}
|
|
81
|
+
return ownOrRelay(resolvePort(process.env));
|
|
82
|
+
}
|