videobook 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +54 -0
- package/LICENSE +21 -0
- package/README.md +195 -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 +242 -0
- package/dist/cli/config-command.js +103 -0
- package/dist/cli/doctor-command.js +289 -0
- package/dist/cli/entry.js +2 -0
- package/dist/cli/key-report.js +16 -0
- package/dist/cli/main.js +104 -0
- package/dist/cli/mcp-command.js +88 -0
- package/dist/cli/mcp-relay.js +51 -0
- package/dist/cli/open-browser.js +14 -0
- package/dist/cli/run-command.js +231 -0
- package/dist/cli/serve-command.js +62 -0
- package/dist/cli/setup-command.js +60 -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 +447 -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-file.js +91 -0
- package/dist/env-secrets.js +148 -0
- package/dist/ffmpeg-binary.js +12 -0
- package/dist/ffmpeg.js +425 -0
- package/dist/ffprobe-binary.js +77 -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/hydration.js +56 -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 +458 -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 +427 -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 +12 -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/one-password.js +200 -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 +810 -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 +125 -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 +2021 -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/ytdlp-binary.js +65 -0
- package/dist/zod-slots.js +334 -0
- package/package.json +84 -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,16 @@
|
|
|
1
|
+
import { getDefaultSecretStore } from "../env-secrets.js";
|
|
2
|
+
export function onePasswordSummary() {
|
|
3
|
+
const resolution = getDefaultSecretStore().onePasswordResolution();
|
|
4
|
+
if (!resolution?.attempted)
|
|
5
|
+
return null;
|
|
6
|
+
const filled = resolution.fromReferences.length + resolution.fromVault.length;
|
|
7
|
+
const vault = resolution.vault ? ` (vault ${resolution.vault})` : "";
|
|
8
|
+
return `${filled} key(s) from 1Password${vault}`;
|
|
9
|
+
}
|
|
10
|
+
export function onePasswordWarnings() {
|
|
11
|
+
const resolution = getDefaultSecretStore().onePasswordResolution();
|
|
12
|
+
if (!resolution?.attempted)
|
|
13
|
+
return [];
|
|
14
|
+
const failures = resolution.unresolved.map((item) => `could not resolve ${item.key} from ${item.reference}: ${item.reason}`);
|
|
15
|
+
return resolution.error ? [resolution.error, ...failures] : failures;
|
|
16
|
+
}
|
package/dist/cli/main.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
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
|
+
case "setup": {
|
|
50
|
+
const { runSetupCommand } = await import("./setup-command.js");
|
|
51
|
+
return runSetupCommand(parsed.flags);
|
|
52
|
+
}
|
|
53
|
+
case "config": {
|
|
54
|
+
const { runConfigCommand } = await import("./config-command.js");
|
|
55
|
+
return runConfigCommand(parsed);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async function loadDotenv() {
|
|
60
|
+
const { getDefaultSecretStore } = await import("../env-secrets.js");
|
|
61
|
+
getDefaultSecretStore().loadDotenv();
|
|
62
|
+
}
|
|
63
|
+
async function main(argv) {
|
|
64
|
+
const parsed = parseOrReport(argv);
|
|
65
|
+
if (typeof parsed === "number")
|
|
66
|
+
return parsed;
|
|
67
|
+
if (parsed.command === "help") {
|
|
68
|
+
process.stdout.write(helpText());
|
|
69
|
+
return 0;
|
|
70
|
+
}
|
|
71
|
+
if (parsed.command === "version") {
|
|
72
|
+
process.stdout.write(`${versionLine()}\n`);
|
|
73
|
+
return 0;
|
|
74
|
+
}
|
|
75
|
+
applyFlagsToEnv(parsed.flags, process.env, process.cwd());
|
|
76
|
+
if (parsed.command !== "serve")
|
|
77
|
+
redirectConsoleToStderr();
|
|
78
|
+
if (parsed.command !== "setup" && parsed.command !== "config")
|
|
79
|
+
await loadDotenv();
|
|
80
|
+
return runCommand(parsed);
|
|
81
|
+
}
|
|
82
|
+
function flushed(stream) {
|
|
83
|
+
return new Promise((resolveFlush) => stream.write("", () => resolveFlush()));
|
|
84
|
+
}
|
|
85
|
+
function exitOnBrokenPipe(stream) {
|
|
86
|
+
stream.on("error", (error) => {
|
|
87
|
+
if (error.code !== "EPIPE")
|
|
88
|
+
throw error;
|
|
89
|
+
process.exit(0);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
export async function runCli(argv) {
|
|
93
|
+
exitOnBrokenPipe(process.stdout);
|
|
94
|
+
let code;
|
|
95
|
+
try {
|
|
96
|
+
code = await main(argv);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
process.stderr.write(`${error instanceof Error ? error.stack ?? error.message : String(error)}\n`);
|
|
100
|
+
code = 1;
|
|
101
|
+
}
|
|
102
|
+
await Promise.all([flushed(process.stdout), flushed(process.stderr)]);
|
|
103
|
+
process.exit(code);
|
|
104
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { serveStdio } from "@modelcontextprotocol/server/stdio";
|
|
2
|
+
import { resolvePort } from "./args.js";
|
|
3
|
+
import { onePasswordSummary, onePasswordWarnings } from "./key-report.js";
|
|
4
|
+
import { connectUpstream, createRelayServer } from "./mcp-relay.js";
|
|
5
|
+
import { bookManager, scopedEngine } from "../engine.js";
|
|
6
|
+
import { logger } from "../logger.js";
|
|
7
|
+
import { createServer, serverInstructions } from "../server.js";
|
|
8
|
+
import { ServerAlreadyRunningError, inspectServerOwner } from "../server-lock.js";
|
|
9
|
+
import { startServer } from "../serve.js";
|
|
10
|
+
function stdinEnded() {
|
|
11
|
+
return new Promise((resolveEnd) => {
|
|
12
|
+
process.stdin.once("end", () => resolveEnd(0));
|
|
13
|
+
process.stdin.once("close", () => resolveEnd(0));
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
function signalled() {
|
|
17
|
+
return new Promise((resolveSignal) => {
|
|
18
|
+
process.once("SIGINT", () => resolveSignal(0));
|
|
19
|
+
process.once("SIGTERM", () => resolveSignal(0));
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
function reportStdioError(error) {
|
|
23
|
+
process.stderr.write(`videobook mcp: ${error.message}\n`);
|
|
24
|
+
}
|
|
25
|
+
function hydrationFailure(running) {
|
|
26
|
+
return running.ready.then(() => new Promise(() => undefined), (error) => {
|
|
27
|
+
logger.error("startup-failed", { error, port: running.port });
|
|
28
|
+
return 1;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
async function relayTo(owner) {
|
|
32
|
+
const upstream = await connectUpstream(`${owner.url}/mcp`);
|
|
33
|
+
const handle = serveStdio(() => createRelayServer(upstream, serverInstructions(owner.url)), { onerror: reportStdioError });
|
|
34
|
+
process.stderr.write(`videobook mcp: relaying stdio to ${owner.url}/mcp (pid ${owner.pid})\n`);
|
|
35
|
+
const code = await Promise.race([stdinEnded(), signalled()]);
|
|
36
|
+
await handle.close();
|
|
37
|
+
await upstream.close();
|
|
38
|
+
return code;
|
|
39
|
+
}
|
|
40
|
+
async function startOwner(port) {
|
|
41
|
+
try {
|
|
42
|
+
return await startServer({ port, ui: true });
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (error?.code !== "EADDRINUSE" || port === 0)
|
|
46
|
+
throw error;
|
|
47
|
+
process.stderr.write(`videobook mcp: port ${port} is busy; using a free port.\n`);
|
|
48
|
+
return await startServer({ port: 0, ui: true });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
async function serveInProcess(running) {
|
|
52
|
+
const instructions = serverInstructions(running.url);
|
|
53
|
+
const handle = serveStdio(() => createServer(scopedEngine, bookManager, { instructions }), { onerror: reportStdioError });
|
|
54
|
+
process.stderr.write(`videobook mcp: serving stdio; web UI and HTTP MCP at ${running.url}/ for library ${running.libraryDir}\n`);
|
|
55
|
+
const keys = onePasswordSummary();
|
|
56
|
+
if (keys)
|
|
57
|
+
process.stderr.write(`videobook mcp: ${keys}\n`);
|
|
58
|
+
for (const warning of onePasswordWarnings())
|
|
59
|
+
process.stderr.write(`videobook mcp: ${warning}\n`);
|
|
60
|
+
const code = await Promise.race([stdinEnded(), signalled(), hydrationFailure(running)]);
|
|
61
|
+
await handle.close();
|
|
62
|
+
await running.close();
|
|
63
|
+
return code;
|
|
64
|
+
}
|
|
65
|
+
async function ownOrRelay(port) {
|
|
66
|
+
let running;
|
|
67
|
+
try {
|
|
68
|
+
running = await startOwner(port);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
if (error instanceof ServerAlreadyRunningError && error.running.url)
|
|
72
|
+
return relayTo(error.running);
|
|
73
|
+
logger.error("startup-failed", { error, port });
|
|
74
|
+
process.stderr.write(`videobook mcp: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
75
|
+
return 1;
|
|
76
|
+
}
|
|
77
|
+
return serveInProcess(running);
|
|
78
|
+
}
|
|
79
|
+
export async function runMcpCommand() {
|
|
80
|
+
const owner = await inspectServerOwner(bookManager.rootDir);
|
|
81
|
+
if (owner.kind === "running")
|
|
82
|
+
return relayTo(owner.record);
|
|
83
|
+
if (owner.kind === "unresponsive") {
|
|
84
|
+
process.stderr.write(`videobook mcp: videobook (pid ${owner.record.pid}) holds ${bookManager.rootDir} but is not responding. Stop it first.\n`);
|
|
85
|
+
return 1;
|
|
86
|
+
}
|
|
87
|
+
return ownOrRelay(resolvePort(process.env));
|
|
88
|
+
}
|
|
@@ -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,231 @@
|
|
|
1
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
2
|
+
import { Client, StreamableHTTPClientTransport } from "@modelcontextprotocol/client";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { onePasswordWarnings } from "./key-report.js";
|
|
5
|
+
import { gcLibraryCatalogs } from "../catalog-gc.js";
|
|
6
|
+
import { bookManager, scopedEngine } from "../engine.js";
|
|
7
|
+
import { MCP_PROTOCOL_VERSION, SERVER_VERSION } from "../server.js";
|
|
8
|
+
import { inspectServerOwner } from "../server-lock.js";
|
|
9
|
+
import { startServer } from "../serve.js";
|
|
10
|
+
import { getAllToolDefs } from "../tools/index.js";
|
|
11
|
+
const REQUEST_GRACE_MS = 60_000;
|
|
12
|
+
const JOB_POLL_MS = 1_000;
|
|
13
|
+
const FINISHED_JOB_STATES = new Set(["done", "failed", "aborted"]);
|
|
14
|
+
const SETTLED_STATUSES = new Set(["completed", "done"]);
|
|
15
|
+
function newClient() {
|
|
16
|
+
return new Client({ name: "videobook-cli", version: SERVER_VERSION }, {
|
|
17
|
+
capabilities: {},
|
|
18
|
+
supportedProtocolVersions: [MCP_PROTOCOL_VERSION],
|
|
19
|
+
versionNegotiation: { mode: { pin: MCP_PROTOCOL_VERSION } },
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
async function connect(mcpUrl) {
|
|
23
|
+
const client = newClient();
|
|
24
|
+
await client.connect(new StreamableHTTPClientTransport(new URL(mcpUrl)));
|
|
25
|
+
return client;
|
|
26
|
+
}
|
|
27
|
+
function refuseUnresponsive(owner) {
|
|
28
|
+
if (owner.kind !== "unresponsive")
|
|
29
|
+
return;
|
|
30
|
+
throw new Error(`videobook (pid ${owner.record.pid}) holds ${bookManager.rootDir} but is not responding. Stop it first.`);
|
|
31
|
+
}
|
|
32
|
+
async function temporaryOwner() {
|
|
33
|
+
const running = await startServer({ port: 0, ui: false });
|
|
34
|
+
for (const warning of onePasswordWarnings())
|
|
35
|
+
process.stderr.write(`videobook: ${warning}\n`);
|
|
36
|
+
await running.ready;
|
|
37
|
+
const client = await connect(running.mcpUrl);
|
|
38
|
+
return {
|
|
39
|
+
client,
|
|
40
|
+
temporary: true,
|
|
41
|
+
async close() {
|
|
42
|
+
await client.close();
|
|
43
|
+
await running.close();
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
async function openOwnerSession() {
|
|
48
|
+
const owner = await inspectServerOwner(bookManager.rootDir);
|
|
49
|
+
refuseUnresponsive(owner);
|
|
50
|
+
if (owner.kind !== "running")
|
|
51
|
+
return temporaryOwner();
|
|
52
|
+
const client = await connect(`${owner.record.url}/mcp`);
|
|
53
|
+
return { client, temporary: false, close: () => client.close() };
|
|
54
|
+
}
|
|
55
|
+
function textOf(result) {
|
|
56
|
+
return result.content.flatMap((item) => item.type === "text" ? [item.text] : [JSON.stringify(item)]);
|
|
57
|
+
}
|
|
58
|
+
function parseJsonObject(text) {
|
|
59
|
+
if (!text)
|
|
60
|
+
return null;
|
|
61
|
+
try {
|
|
62
|
+
const value = JSON.parse(text);
|
|
63
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function stringField(record, key) {
|
|
70
|
+
const value = record?.[key];
|
|
71
|
+
return typeof value === "string" && value !== "" ? value : null;
|
|
72
|
+
}
|
|
73
|
+
function jobReference(first) {
|
|
74
|
+
const value = first?.job_id;
|
|
75
|
+
if (typeof value === "number" && Number.isInteger(value) && value > 0)
|
|
76
|
+
return { job_ids: [value] };
|
|
77
|
+
if (typeof value === "string" && value !== "")
|
|
78
|
+
return { job_keys: [value] };
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
function parseSnapshot(result) {
|
|
82
|
+
if (result.isError)
|
|
83
|
+
return null;
|
|
84
|
+
const rows = JSON.parse(textOf(result)[0] ?? "[]");
|
|
85
|
+
const job = Array.isArray(rows) ? rows[0] : undefined;
|
|
86
|
+
if (!job || typeof job.state !== "string")
|
|
87
|
+
return null;
|
|
88
|
+
return { state: job.state, error: typeof job.error?.message === "string" ? job.error.message : null };
|
|
89
|
+
}
|
|
90
|
+
async function jobSnapshot(client, bookId, reference) {
|
|
91
|
+
const result = await client.callTool({ name: "list_jobs", arguments: { book_id: bookId, ...reference } });
|
|
92
|
+
return parseSnapshot(result);
|
|
93
|
+
}
|
|
94
|
+
function reportJob(label, job) {
|
|
95
|
+
if (job.state === "done") {
|
|
96
|
+
process.stderr.write(`Job ${label} completed.\n`);
|
|
97
|
+
return 0;
|
|
98
|
+
}
|
|
99
|
+
process.stderr.write(`Job ${label} ${job.state}${job.error ? `: ${job.error}` : ""}\n`);
|
|
100
|
+
return 1;
|
|
101
|
+
}
|
|
102
|
+
async function waitForJob(client, bookId, reference, label, timeoutSeconds) {
|
|
103
|
+
process.stderr.write(`Waiting for job ${label}...\n`);
|
|
104
|
+
const deadline = Date.now() + timeoutSeconds * 1000;
|
|
105
|
+
let job = await jobSnapshot(client, bookId, reference);
|
|
106
|
+
while (job && !FINISHED_JOB_STATES.has(job.state) && Date.now() < deadline) {
|
|
107
|
+
await delay(JOB_POLL_MS);
|
|
108
|
+
job = await jobSnapshot(client, bookId, reference);
|
|
109
|
+
}
|
|
110
|
+
if (!job) {
|
|
111
|
+
process.stderr.write(`Job ${label} is not tracked by the server; check it with list_jobs.\n`);
|
|
112
|
+
return 0;
|
|
113
|
+
}
|
|
114
|
+
if (FINISHED_JOB_STATES.has(job.state))
|
|
115
|
+
return reportJob(label, job);
|
|
116
|
+
process.stderr.write(`Job ${label} did not finish within ${timeoutSeconds} s.\n`);
|
|
117
|
+
return 1;
|
|
118
|
+
}
|
|
119
|
+
async function waitForAsset(client, bookId, assetId, timeoutSeconds) {
|
|
120
|
+
process.stderr.write(`Waiting for asset ${assetId}...\n`);
|
|
121
|
+
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 });
|
|
122
|
+
for (const line of textOf(result))
|
|
123
|
+
process.stdout.write(`${line}\n`);
|
|
124
|
+
return result.isError ? 1 : 0;
|
|
125
|
+
}
|
|
126
|
+
async function waitForOutcome(session, flags, args, first) {
|
|
127
|
+
const reference = jobReference(first);
|
|
128
|
+
const assetId = stringField(first, "asset_id");
|
|
129
|
+
const bookId = typeof args.book_id === "string" ? args.book_id : null;
|
|
130
|
+
const status = stringField(first, "status");
|
|
131
|
+
if (!reference || (status && SETTLED_STATUSES.has(status)))
|
|
132
|
+
return 0;
|
|
133
|
+
if (!flags.wait && !session.temporary)
|
|
134
|
+
return 0;
|
|
135
|
+
if (assetId && bookId)
|
|
136
|
+
return waitForAsset(session.client, bookId, assetId, flags.timeoutSeconds);
|
|
137
|
+
if (!bookId) {
|
|
138
|
+
process.stderr.write(`Job ${String(first?.job_id)} was queued; pass book_id to wait for it.\n`);
|
|
139
|
+
return 0;
|
|
140
|
+
}
|
|
141
|
+
return waitForJob(session.client, bookId, reference, String(first?.job_id), flags.timeoutSeconds);
|
|
142
|
+
}
|
|
143
|
+
async function callOnce(session, flags, tool, args) {
|
|
144
|
+
try {
|
|
145
|
+
return await session.client.callTool({ name: tool, arguments: args }, { timeout: flags.timeoutSeconds * 1000 + REQUEST_GRACE_MS });
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
process.stderr.write(`videobook run: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
export async function runToolCommand(flags, tool, args) {
|
|
153
|
+
const session = await openOwnerSession();
|
|
154
|
+
try {
|
|
155
|
+
const result = await callOnce(session, flags, tool, args);
|
|
156
|
+
if (!result)
|
|
157
|
+
return 1;
|
|
158
|
+
const lines = textOf(result);
|
|
159
|
+
const stream = result.isError ? process.stderr : process.stdout;
|
|
160
|
+
for (const line of lines)
|
|
161
|
+
stream.write(`${line}\n`);
|
|
162
|
+
if (result.isError)
|
|
163
|
+
return 1;
|
|
164
|
+
return await waitForOutcome(session, flags, args, parseJsonObject(lines[0]));
|
|
165
|
+
}
|
|
166
|
+
finally {
|
|
167
|
+
await session.close();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function listingFromJsonSchema(tool) {
|
|
171
|
+
const properties = Object.keys(tool.inputSchema.properties ?? {});
|
|
172
|
+
const required = new Set(tool.inputSchema.required ?? []);
|
|
173
|
+
return {
|
|
174
|
+
name: tool.name,
|
|
175
|
+
...(tool.description ? { description: tool.description } : {}),
|
|
176
|
+
required: properties.filter((key) => required.has(key)),
|
|
177
|
+
optional: properties.filter((key) => !required.has(key)),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
function localListings() {
|
|
181
|
+
return getAllToolDefs(scopedEngine, bookManager).map((def) => {
|
|
182
|
+
const schema = z.object(def.schema).toJSONSchema();
|
|
183
|
+
const required = new Set(schema.required ?? []);
|
|
184
|
+
const keys = Object.keys(def.schema);
|
|
185
|
+
return {
|
|
186
|
+
name: def.name,
|
|
187
|
+
...(def.description ? { description: def.description } : {}),
|
|
188
|
+
required: keys.filter((key) => required.has(key)),
|
|
189
|
+
optional: keys.filter((key) => !required.has(key)),
|
|
190
|
+
};
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
async function ownerListings(owner) {
|
|
194
|
+
if (owner.kind !== "running")
|
|
195
|
+
return null;
|
|
196
|
+
const client = await connect(`${owner.record.url}/mcp`);
|
|
197
|
+
try {
|
|
198
|
+
return (await client.listTools()).tools.map(listingFromJsonSchema);
|
|
199
|
+
}
|
|
200
|
+
finally {
|
|
201
|
+
await client.close();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function renderListing(listing) {
|
|
205
|
+
const params = [...listing.required.map((key) => `<${key}>`), ...listing.optional.map((key) => `[${key}]`)].join(" ");
|
|
206
|
+
const head = params ? `${listing.name} ${params}` : listing.name;
|
|
207
|
+
return listing.description ? `${head}\n ${listing.description}` : head;
|
|
208
|
+
}
|
|
209
|
+
export async function runToolsCommand() {
|
|
210
|
+
const owner = await inspectServerOwner(bookManager.rootDir);
|
|
211
|
+
refuseUnresponsive(owner);
|
|
212
|
+
const listings = await ownerListings(owner) ?? localListings();
|
|
213
|
+
for (const listing of listings)
|
|
214
|
+
process.stdout.write(`${renderListing(listing)}\n`);
|
|
215
|
+
return 0;
|
|
216
|
+
}
|
|
217
|
+
export async function runGcBooksCommand() {
|
|
218
|
+
const owner = await inspectServerOwner(bookManager.rootDir);
|
|
219
|
+
if (owner.kind !== "none") {
|
|
220
|
+
process.stderr.write(`videobook is running at ${owner.record.url} (pid ${owner.record.pid}); stop it before collecting catalog garbage.\n`);
|
|
221
|
+
return 1;
|
|
222
|
+
}
|
|
223
|
+
try {
|
|
224
|
+
const reports = await gcLibraryCatalogs(bookManager);
|
|
225
|
+
process.stdout.write(`${JSON.stringify({ catalogs: reports }, null, 2)}\n`);
|
|
226
|
+
return 0;
|
|
227
|
+
}
|
|
228
|
+
finally {
|
|
229
|
+
await bookManager.close();
|
|
230
|
+
}
|
|
231
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { resolvePort } from "./args.js";
|
|
2
|
+
import { onePasswordSummary, onePasswordWarnings } from "./key-report.js";
|
|
3
|
+
import { openInBrowser } from "./open-browser.js";
|
|
4
|
+
import { logger } from "../logger.js";
|
|
5
|
+
import { SERVER_VERSION } from "../server.js";
|
|
6
|
+
import { ServerAlreadyRunningError } from "../server-lock.js";
|
|
7
|
+
import { installSignalHandlers, startServer } from "../serve.js";
|
|
8
|
+
function uiLine(running) {
|
|
9
|
+
if (running.uiStatus === "served")
|
|
10
|
+
return `${running.url}/`;
|
|
11
|
+
if (running.uiStatus === "disabled")
|
|
12
|
+
return "off (--headless)";
|
|
13
|
+
return `not built at ${running.uiDir}; serving MCP and API only`;
|
|
14
|
+
}
|
|
15
|
+
function printBanner(running) {
|
|
16
|
+
const keys = onePasswordSummary();
|
|
17
|
+
process.stdout.write([
|
|
18
|
+
`videobook ${SERVER_VERSION}`,
|
|
19
|
+
`Library ${running.libraryDir}`,
|
|
20
|
+
`UI ${uiLine(running)}`,
|
|
21
|
+
`MCP ${running.mcpUrl}`,
|
|
22
|
+
...(keys ? [`Keys ${keys}`] : []),
|
|
23
|
+
"Agents claude mcp add videobook -- videobook mcp",
|
|
24
|
+
"",
|
|
25
|
+
].join("\n"));
|
|
26
|
+
for (const warning of onePasswordWarnings())
|
|
27
|
+
process.stderr.write(`videobook: ${warning}\n`);
|
|
28
|
+
}
|
|
29
|
+
function reportStartupFailure(error, port) {
|
|
30
|
+
if (error instanceof ServerAlreadyRunningError) {
|
|
31
|
+
process.stderr.write(`${error.message}\n`);
|
|
32
|
+
return 1;
|
|
33
|
+
}
|
|
34
|
+
logger.error("startup-failed", { error, port });
|
|
35
|
+
if (error?.code === "EADDRINUSE") {
|
|
36
|
+
process.stderr.write(`Port ${port} is already in use by another process; pass --port <n>.\n`);
|
|
37
|
+
}
|
|
38
|
+
return 1;
|
|
39
|
+
}
|
|
40
|
+
function hydrationFailure(running) {
|
|
41
|
+
return running.ready.then(() => new Promise(() => undefined), async (error) => {
|
|
42
|
+
logger.error("startup-failed", { error, port: running.port });
|
|
43
|
+
await running.close().catch(() => undefined);
|
|
44
|
+
return 1;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
export async function runServeCommand(flags) {
|
|
48
|
+
const port = resolvePort(process.env);
|
|
49
|
+
const starting = startServer({ port, ui: flags.ui });
|
|
50
|
+
const stopped = new Promise((resolveStop) => installSignalHandlers(starting, resolveStop));
|
|
51
|
+
let running;
|
|
52
|
+
try {
|
|
53
|
+
running = await starting;
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
return reportStartupFailure(error, port);
|
|
57
|
+
}
|
|
58
|
+
printBanner(running);
|
|
59
|
+
if (flags.open && running.uiStatus === "served")
|
|
60
|
+
openInBrowser(`${running.url}/`);
|
|
61
|
+
return Promise.race([stopped, hydrationFailure(running)]);
|
|
62
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { readEnvValues, updateEnvFile } from "../env-file.js";
|
|
2
|
+
import { MANAGED_ENV_KEYS, getDefaultSecretStore } from "../env-secrets.js";
|
|
3
|
+
import { OP_TOKEN_SETTING, OP_VAULT_SETTING, listVaultItemTitles, opErrorMessage, resolveServiceToken, } from "../one-password.js";
|
|
4
|
+
const AMBIENT_TOKEN_VARIABLE = "OP_SERVICE_ACCOUNT_TOKEN";
|
|
5
|
+
function savedSetting(name, file) {
|
|
6
|
+
return process.env[name]?.trim() || file[name]?.trim() || null;
|
|
7
|
+
}
|
|
8
|
+
function chosenVault(flags, file) {
|
|
9
|
+
return flags.vault?.trim() || savedSetting(OP_VAULT_SETTING, file);
|
|
10
|
+
}
|
|
11
|
+
function chosenToken(flags, file) {
|
|
12
|
+
const explicit = flags.token?.trim() || savedSetting(OP_TOKEN_SETTING, file);
|
|
13
|
+
if (explicit)
|
|
14
|
+
return explicit;
|
|
15
|
+
return process.env[AMBIENT_TOKEN_VARIABLE]?.trim() ? `env:${AMBIENT_TOKEN_VARIABLE}` : null;
|
|
16
|
+
}
|
|
17
|
+
function describeToken(token) {
|
|
18
|
+
if (!token)
|
|
19
|
+
return "op's own sign-in";
|
|
20
|
+
const resolved = resolveServiceToken(token, process.env);
|
|
21
|
+
if (resolved.source === "literal")
|
|
22
|
+
return "a literal token (stored in the file; prefer env:VAR)";
|
|
23
|
+
return resolved.value ? token : `${token}, which is not set in this shell`;
|
|
24
|
+
}
|
|
25
|
+
async function removeSettings(envPath) {
|
|
26
|
+
await updateEnvFile(envPath, { [OP_VAULT_SETTING]: null, [OP_TOKEN_SETTING]: null });
|
|
27
|
+
process.stdout.write(`Removed ${OP_VAULT_SETTING} and ${OP_TOKEN_SETTING} from ${envPath}\n`);
|
|
28
|
+
return 0;
|
|
29
|
+
}
|
|
30
|
+
function verifyVault(vault, token) {
|
|
31
|
+
const value = token ? resolveServiceToken(token, process.env).value : null;
|
|
32
|
+
try {
|
|
33
|
+
const titles = listVaultItemTitles(vault, process.env, value);
|
|
34
|
+
const found = MANAGED_ENV_KEYS.filter((key) => titles.has(key));
|
|
35
|
+
const missing = MANAGED_ENV_KEYS.filter((key) => !titles.has(key));
|
|
36
|
+
process.stdout.write(`1Password vault ${vault}: ${found.length} of ${MANAGED_ENV_KEYS.length} videobook keys found\n`);
|
|
37
|
+
if (missing.length > 0)
|
|
38
|
+
process.stdout.write(`Not in the vault: ${missing.join(", ")}\n`);
|
|
39
|
+
return 0;
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
process.stderr.write(`Could not list 1Password vault ${vault}: ${opErrorMessage(error, value)}\n`);
|
|
43
|
+
return 1;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export async function runSetupCommand(flags) {
|
|
47
|
+
const envPath = getDefaultSecretStore().envPath();
|
|
48
|
+
if (flags.remove)
|
|
49
|
+
return removeSettings(envPath);
|
|
50
|
+
const file = readEnvValues(envPath);
|
|
51
|
+
const vault = chosenVault(flags, file);
|
|
52
|
+
if (!vault) {
|
|
53
|
+
process.stderr.write("setup 1password needs --op-vault <vault>, the vault whose items are named after videobook's keys\n");
|
|
54
|
+
return 2;
|
|
55
|
+
}
|
|
56
|
+
const token = chosenToken(flags, file);
|
|
57
|
+
await updateEnvFile(envPath, { [OP_VAULT_SETTING]: vault, [OP_TOKEN_SETTING]: token });
|
|
58
|
+
process.stdout.write(`Saved ${OP_VAULT_SETTING}=${vault} and the token setting (${describeToken(token)}) to ${envPath}\n`);
|
|
59
|
+
return flags.verify ? verifyVault(vault, token) : 0;
|
|
60
|
+
}
|
|
@@ -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
|
+
}
|