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,28 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
function error(text) {
|
|
3
|
+
return { isError: true, content: [{ type: "text", text }] };
|
|
4
|
+
}
|
|
5
|
+
export function recoverAssetDef(engine) {
|
|
6
|
+
return {
|
|
7
|
+
name: "recover_asset",
|
|
8
|
+
description: "Clear a failed asset's generation error and keep its media, returning it to ready. Use clean_failed_assets instead to delete failed generations.",
|
|
9
|
+
schema: {
|
|
10
|
+
asset_id: z.string().describe("Asset ID to recover (vid-*, img-*, etc., or @-prefixed)"),
|
|
11
|
+
book_id: z.string().describe("Book id"),
|
|
12
|
+
},
|
|
13
|
+
handler: async (args) => {
|
|
14
|
+
const { asset_id, book_id: _book_id } = args;
|
|
15
|
+
const assetId = asset_id.replace(/^@/, "");
|
|
16
|
+
const cleared = await engine.jobs.failures.clear(assetId);
|
|
17
|
+
if (!cleared.ok) {
|
|
18
|
+
return error(`Failed to clear error for ${assetId}: ${cleared.error.message}`);
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
content: [{
|
|
22
|
+
type: "text",
|
|
23
|
+
text: JSON.stringify({ asset_id: assetId, recovered: cleared.value }),
|
|
24
|
+
}],
|
|
25
|
+
};
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { findAudioFile, findVideoFile, } from "videobook-engine";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { queue } from "../queue.js";
|
|
4
|
+
import { toolError } from "./_shared.js";
|
|
5
|
+
const JOB_TIMEOUT_MS = 30 * 60_000;
|
|
6
|
+
export function removeSoundDef(engine) {
|
|
7
|
+
return {
|
|
8
|
+
name: "remove_sound",
|
|
9
|
+
description: "Silence a described sound (e.g. a horse neigh) in a video or audio asset in place, using ElevenLabs Scribe audio-event timestamps; the picture is unchanged.",
|
|
10
|
+
schema: {
|
|
11
|
+
asset_id: z.string().describe("Artifact id to edit in-place (video or audio)"),
|
|
12
|
+
sound: z.string().min(1).max(200).describe('Description of the sound to remove, e.g. "horse neighing"'),
|
|
13
|
+
book_id: z.string().describe("Book UUID"),
|
|
14
|
+
},
|
|
15
|
+
handler: async (args) => {
|
|
16
|
+
const { asset_id, sound, book_id } = args;
|
|
17
|
+
if (!process.env.ELEVENLABS_API_KEY) {
|
|
18
|
+
return toolError("ELEVENLABS_API_KEY environment variable is not set");
|
|
19
|
+
}
|
|
20
|
+
const assetId = asset_id.replace(/^@/, "");
|
|
21
|
+
const artifact = engine.artifacts.get(assetId);
|
|
22
|
+
if (!artifact.ok)
|
|
23
|
+
return toolError(artifact.error.message);
|
|
24
|
+
const kind = artifact.value.kind === "video"
|
|
25
|
+
? "video"
|
|
26
|
+
: artifact.value.kind === "audio" ? "audio" : null;
|
|
27
|
+
if (!kind) {
|
|
28
|
+
return toolError(`remove_sound only supports video or audio artifacts, got ${artifact.value.kind} artifact ${assetId}`);
|
|
29
|
+
}
|
|
30
|
+
const manifest = await engine.files.manifest(assetId);
|
|
31
|
+
if (!manifest.ok)
|
|
32
|
+
return toolError(manifest.error.message);
|
|
33
|
+
const file = kind === "video"
|
|
34
|
+
? findVideoFile(manifest.value.files)
|
|
35
|
+
: findAudioFile(manifest.value.files, artifact.value.kind);
|
|
36
|
+
if (!file) {
|
|
37
|
+
return toolError(`No ${kind} file found in artifact ${assetId}`);
|
|
38
|
+
}
|
|
39
|
+
const jobId = `remove-sound-${assetId}-${Date.now()}`;
|
|
40
|
+
let result;
|
|
41
|
+
try {
|
|
42
|
+
result = await queue.enqueueAndWait(jobId, {
|
|
43
|
+
type: "remove_sound",
|
|
44
|
+
assetId,
|
|
45
|
+
assetKind: kind,
|
|
46
|
+
assetDir: manifest.value.path,
|
|
47
|
+
filename: file.name,
|
|
48
|
+
sound: sound.trim(),
|
|
49
|
+
bookId: book_id,
|
|
50
|
+
}, JOB_TIMEOUT_MS);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
return toolError(`Sound removal failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
content: [{
|
|
57
|
+
type: "text",
|
|
58
|
+
text: JSON.stringify({
|
|
59
|
+
asset_id: result.asset_id,
|
|
60
|
+
sound: result.sound,
|
|
61
|
+
matched_events: result.matched_events,
|
|
62
|
+
removed_segments: result.removed_segments,
|
|
63
|
+
removed_seconds: result.removed_seconds,
|
|
64
|
+
detected_events: result.detected_events,
|
|
65
|
+
job_id: jobId,
|
|
66
|
+
status: "completed",
|
|
67
|
+
}),
|
|
68
|
+
}],
|
|
69
|
+
};
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { readTimeline, timelineFailure, timelineInput, timelinePayload, writeTimeline, } from "../timeline-helpers.js";
|
|
3
|
+
export function removeTimelineAudioDef(engine) {
|
|
4
|
+
return {
|
|
5
|
+
name: "remove_timeline_audio",
|
|
6
|
+
description: "Remove an audio overlay by its UUIDv7 id",
|
|
7
|
+
schema: {
|
|
8
|
+
book_id: z.string().uuid(),
|
|
9
|
+
audio_id: z.string().uuid(),
|
|
10
|
+
},
|
|
11
|
+
handler: async (args) => {
|
|
12
|
+
const timeline = readTimeline(engine);
|
|
13
|
+
const audioId = String(args.audio_id);
|
|
14
|
+
if (!timeline.audio.some((clip) => clip.id === audioId)) {
|
|
15
|
+
return timelineFailure(`Timeline audio clip not found: ${audioId}`);
|
|
16
|
+
}
|
|
17
|
+
const result = await writeTimeline(engine, {
|
|
18
|
+
...timelineInput(timeline),
|
|
19
|
+
audio: timeline.audio.filter((clip) => clip.id !== audioId).map((clip) => ({
|
|
20
|
+
id: clip.id,
|
|
21
|
+
artifactId: clip.artifactId,
|
|
22
|
+
startFrame: clip.startFrame,
|
|
23
|
+
durationFrames: clip.durationFrames,
|
|
24
|
+
...(clip.volume === undefined ? {} : { volume: clip.volume }),
|
|
25
|
+
...(clip.fadeIn === undefined ? {} : { fadeIn: clip.fadeIn }),
|
|
26
|
+
...(clip.fadeOut === undefined ? {} : { fadeOut: clip.fadeOut }),
|
|
27
|
+
})),
|
|
28
|
+
});
|
|
29
|
+
return result.ok ? {
|
|
30
|
+
content: [{ type: "text", text: JSON.stringify(timelinePayload(result.value)) }],
|
|
31
|
+
} : timelineFailure(result.error.message);
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { slotIndex, readTimeline, timelineFailure, timelineInput, timelineSuccess, writeTimeline, } from "../timeline-helpers.js";
|
|
3
|
+
export function removeTimelineSlotDef(engine) {
|
|
4
|
+
return {
|
|
5
|
+
name: "remove_timeline_slot",
|
|
6
|
+
description: "Remove a typed timeline slot and shift later slots left",
|
|
7
|
+
schema: {
|
|
8
|
+
book_id: z.string().uuid(),
|
|
9
|
+
slot: z.string(),
|
|
10
|
+
},
|
|
11
|
+
handler: async (args) => {
|
|
12
|
+
const index = slotIndex(String(args.slot));
|
|
13
|
+
if (index === null)
|
|
14
|
+
return timelineFailure("Slot must use the form s01, s02, and so on");
|
|
15
|
+
const timeline = readTimeline(engine);
|
|
16
|
+
if (index >= timeline.slots.length) {
|
|
17
|
+
return timelineFailure(`Slot ${String(args.slot)} is outside the current timeline`);
|
|
18
|
+
}
|
|
19
|
+
const removed = timeline.slots[index];
|
|
20
|
+
const result = await writeTimeline(engine, {
|
|
21
|
+
...timelineInput(timeline),
|
|
22
|
+
slots: timeline.slots.filter((_, position) => position !== index).map((slot) => ({
|
|
23
|
+
id: slot.id,
|
|
24
|
+
artifactId: slot.artifactId,
|
|
25
|
+
...(slot.volume === undefined ? {} : { volume: slot.volume }),
|
|
26
|
+
...(slot.audioFadeIn === undefined ? {} : { audioFadeIn: slot.audioFadeIn }),
|
|
27
|
+
...(slot.audioFadeOut === undefined ? {} : { audioFadeOut: slot.audioFadeOut }),
|
|
28
|
+
})),
|
|
29
|
+
});
|
|
30
|
+
if (!result.ok)
|
|
31
|
+
return timelineFailure(result.error.message);
|
|
32
|
+
return {
|
|
33
|
+
content: [{
|
|
34
|
+
type: "text",
|
|
35
|
+
text: JSON.stringify({ ...JSON.parse(timelineSuccess(result.value).content[0].text), removed_artifact_id: removed.artifactId }),
|
|
36
|
+
}],
|
|
37
|
+
};
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { isRenamableKind, buildSuggestionPrompt, parseSuggestionResponse, buildRenameRows, validateEditedRenames, applyableRows, isProcessOrigin, } from "../rename-helpers.js";
|
|
5
|
+
import { INTELLIGENCE_MODEL, buildIntelligenceResponses, firstResponseText, intelligenceRequest, } from "../openai-intelligence.js";
|
|
6
|
+
import { scanNotebookMentions, stripNotebookMentions, } from "videobook-engine/notebook-mentions";
|
|
7
|
+
const schema = {
|
|
8
|
+
book_id: z.string().describe("Book id"),
|
|
9
|
+
dry_run: z
|
|
10
|
+
.boolean()
|
|
11
|
+
.describe("true = preview only (no renames). false = apply the confirmed renames."),
|
|
12
|
+
renames: z
|
|
13
|
+
.array(z.object({
|
|
14
|
+
asset_id: z.string(),
|
|
15
|
+
suggested_name: z.string(),
|
|
16
|
+
}))
|
|
17
|
+
.optional()
|
|
18
|
+
.describe("Edited renames for revalidation (dry_run:true) or confirmed renames to apply (dry_run:false). " +
|
|
19
|
+
"Omit on the first dry run to let GPT-5.6 Sol propose labels."),
|
|
20
|
+
};
|
|
21
|
+
function renameCandidates(assets) {
|
|
22
|
+
return assets.map((a) => ({
|
|
23
|
+
artifactId: a.artifactId,
|
|
24
|
+
...(a.label ? { label: a.label } : {}),
|
|
25
|
+
kind: a.kind,
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
28
|
+
export function renameAllDef(engine) {
|
|
29
|
+
return {
|
|
30
|
+
name: "rename_all",
|
|
31
|
+
description: "Relabel video and image assets in bulk. dry_run proposes descriptive labels from their analysis, transcript and prompts; pass edited renames with dry_run false to apply. Labels are display-only.",
|
|
32
|
+
schema,
|
|
33
|
+
handler: async (args) => {
|
|
34
|
+
const { dry_run, renames } = args;
|
|
35
|
+
const assets = await engine.artifacts.list();
|
|
36
|
+
if (dry_run) {
|
|
37
|
+
return renames && renames.length > 0
|
|
38
|
+
? handleDryRunRevalidation(renames, renameCandidates(assets))
|
|
39
|
+
: handleDryRunSuggest(engine, assets);
|
|
40
|
+
}
|
|
41
|
+
if (!renames || renames.length === 0) {
|
|
42
|
+
return {
|
|
43
|
+
isError: true,
|
|
44
|
+
content: [{
|
|
45
|
+
type: "text",
|
|
46
|
+
text: "Applying requires a non-empty `renames` array. Run a dry run first.",
|
|
47
|
+
}],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return handleApply(engine, renames, renameCandidates(assets));
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
async function handleDryRunSuggest(engine, assets) {
|
|
55
|
+
const apiKey = process.env.OPENAI_API_KEY;
|
|
56
|
+
if (!apiKey) {
|
|
57
|
+
return {
|
|
58
|
+
isError: true,
|
|
59
|
+
content: [{
|
|
60
|
+
type: "text",
|
|
61
|
+
text: "OPENAI_API_KEY environment variable is not set",
|
|
62
|
+
}],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const candidates = assets.filter((a) => isRenamableKind(a.kind));
|
|
66
|
+
if (candidates.length === 0) {
|
|
67
|
+
return {
|
|
68
|
+
content: [{
|
|
69
|
+
type: "text",
|
|
70
|
+
text: JSON.stringify({
|
|
71
|
+
dry_run: true,
|
|
72
|
+
rows: [],
|
|
73
|
+
note: "No video or image assets found.",
|
|
74
|
+
}),
|
|
75
|
+
}],
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const contexts = [];
|
|
79
|
+
for (const a of candidates) {
|
|
80
|
+
const ctx = await loadAssetContext(engine, a);
|
|
81
|
+
contexts.push({
|
|
82
|
+
...(ctx ?? {
|
|
83
|
+
asset_id: a.artifactId,
|
|
84
|
+
type: a.kind,
|
|
85
|
+
context_text: "",
|
|
86
|
+
}),
|
|
87
|
+
...(a.label ? { label: a.label } : {}),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
const previews = await loadPreviewStills(engine, contexts);
|
|
91
|
+
const hasImages = previews.some((p) => p.parts.length > 0);
|
|
92
|
+
const contentParts = buildMultimodalContent(contexts, previews, hasImages);
|
|
93
|
+
const responses = buildIntelligenceResponses(apiKey, { timeout: 120_000 });
|
|
94
|
+
const result = await responses(intelligenceRequest({
|
|
95
|
+
input: [{
|
|
96
|
+
role: "user",
|
|
97
|
+
content: responseContentFromChat(contentParts),
|
|
98
|
+
}],
|
|
99
|
+
text: { format: { type: "json_object" } },
|
|
100
|
+
max_output_tokens: 2048,
|
|
101
|
+
}));
|
|
102
|
+
const suggestions = parseSuggestionResponse(firstResponseText(result));
|
|
103
|
+
const rows = buildRenameRows(contexts, suggestions);
|
|
104
|
+
return {
|
|
105
|
+
content: [{
|
|
106
|
+
type: "text",
|
|
107
|
+
text: JSON.stringify({ dry_run: true, rows, model: INTELLIGENCE_MODEL }),
|
|
108
|
+
}],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
async function handleDryRunRevalidation(renames, candidates) {
|
|
112
|
+
const edits = renames.map((r) => ({
|
|
113
|
+
asset_id: r.asset_id,
|
|
114
|
+
suggested_name: r.suggested_name,
|
|
115
|
+
}));
|
|
116
|
+
const rows = validateEditedRenames(edits, candidates);
|
|
117
|
+
return {
|
|
118
|
+
content: [{
|
|
119
|
+
type: "text",
|
|
120
|
+
text: JSON.stringify({ dry_run: true, rows }),
|
|
121
|
+
}],
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
async function handleApply(engine, renames, candidates) {
|
|
125
|
+
const edits = renames.map((r) => ({
|
|
126
|
+
asset_id: r.asset_id,
|
|
127
|
+
suggested_name: r.suggested_name,
|
|
128
|
+
}));
|
|
129
|
+
const rows = validateEditedRenames(edits, candidates);
|
|
130
|
+
const toApply = applyableRows(rows);
|
|
131
|
+
if (toApply.length === 0) {
|
|
132
|
+
return {
|
|
133
|
+
isError: true,
|
|
134
|
+
content: [{
|
|
135
|
+
type: "text",
|
|
136
|
+
text: "No renamable rows after validation. Resolve conflicts/skips and re-run the dry run.",
|
|
137
|
+
}],
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
const applied = [];
|
|
141
|
+
// Relabeling is display-only: artifact ids are the sole reference currency,
|
|
142
|
+
// so no prompts, timeline slots, or notebook cells need rewriting.
|
|
143
|
+
for (const row of toApply) {
|
|
144
|
+
const result = await engine.artifacts.rename(row.asset_id, row.suggested_name);
|
|
145
|
+
if (!result.ok) {
|
|
146
|
+
applied.push({
|
|
147
|
+
asset_id: row.asset_id,
|
|
148
|
+
label: row.current_name,
|
|
149
|
+
renamed: false,
|
|
150
|
+
error: result.error.message,
|
|
151
|
+
});
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
applied.push({
|
|
155
|
+
asset_id: result.value.artifactId,
|
|
156
|
+
label: result.value.label ?? row.suggested_name,
|
|
157
|
+
renamed: true,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
content: [{
|
|
162
|
+
type: "text",
|
|
163
|
+
text: JSON.stringify({ dry_run: false, applied }),
|
|
164
|
+
}],
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
/** Load the stored context text used to drive GPT's name suggestion.
|
|
168
|
+
* Videos: visual analysis + generation prompt + short transcript.
|
|
169
|
+
* Images: analysis, generation prompt, and parent-video context for
|
|
170
|
+
* extracted frames. Returns null when no useful text is available. */
|
|
171
|
+
async function loadAssetContext(engine, artifact) {
|
|
172
|
+
if (artifact.kind === "video") {
|
|
173
|
+
return loadVideoContext(engine, artifact.artifactId);
|
|
174
|
+
}
|
|
175
|
+
if (artifact.kind === "image") {
|
|
176
|
+
return loadImageContext(engine, artifact.artifactId);
|
|
177
|
+
}
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
function decodeJsonBuffer(value) {
|
|
181
|
+
return typeof value === "string" ? value : new TextDecoder().decode(value);
|
|
182
|
+
}
|
|
183
|
+
/** Pull summary / subjects / setting / tags from `.original.analysis.json`. */
|
|
184
|
+
async function readAnalysisParts(engine, assetId) {
|
|
185
|
+
const parts = [];
|
|
186
|
+
const analysisRead = await engine.files.read(assetId, ".original.analysis.json");
|
|
187
|
+
if (!analysisRead.ok)
|
|
188
|
+
return parts;
|
|
189
|
+
try {
|
|
190
|
+
const analysis = JSON.parse(decodeJsonBuffer(analysisRead.value));
|
|
191
|
+
const summary = typeof analysis.summary === "string" ? analysis.summary : "";
|
|
192
|
+
const subjects = Array.isArray(analysis.subjects) ? analysis.subjects.join(", ") : "";
|
|
193
|
+
const setting = typeof analysis.setting === "string" ? analysis.setting : "";
|
|
194
|
+
const tags = Array.isArray(analysis.tags) ? analysis.tags.join(", ") : "";
|
|
195
|
+
const prompt = typeof analysis.prompt === "string" ? analysis.prompt : "";
|
|
196
|
+
if (summary)
|
|
197
|
+
parts.push(summary);
|
|
198
|
+
if (subjects)
|
|
199
|
+
parts.push(`subjects: ${subjects}`);
|
|
200
|
+
if (setting)
|
|
201
|
+
parts.push(`setting: ${setting}`);
|
|
202
|
+
if (tags)
|
|
203
|
+
parts.push(`tags: ${tags}`);
|
|
204
|
+
// Image describe_image caches a reproduction prompt in analysis.
|
|
205
|
+
if (prompt && !summary)
|
|
206
|
+
parts.push(prompt.slice(0, 400));
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
// ignore parse errors
|
|
210
|
+
}
|
|
211
|
+
return parts;
|
|
212
|
+
}
|
|
213
|
+
async function loadVideoContext(engine, assetId) {
|
|
214
|
+
const parts = [];
|
|
215
|
+
parts.push(...(await readAnalysisParts(engine, assetId)));
|
|
216
|
+
// Generation prompt is often the richest description when analysis is missing.
|
|
217
|
+
const meta = await engine.metadata.artifacts.read(assetId, "original");
|
|
218
|
+
if (meta.ok) {
|
|
219
|
+
const prompt = typeof meta.value.prompt === "string" ? meta.value.prompt.trim() : "";
|
|
220
|
+
if (prompt) {
|
|
221
|
+
// Strip long @refs so the model focuses on the scene description.
|
|
222
|
+
const cleaned = stripNotebookMentions(prompt, scanNotebookMentions(prompt).map((mention) => mention.reference))
|
|
223
|
+
.replace(/\s+/g, " ")
|
|
224
|
+
.trim()
|
|
225
|
+
.slice(0, 500);
|
|
226
|
+
if (cleaned)
|
|
227
|
+
parts.push(`generation prompt: ${cleaned}`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// Transcript is secondary — cap length so it doesn't drown out visuals.
|
|
231
|
+
for (const transFile of [".analysis.gpt4otrans.json", "elevenlabs.json"]) {
|
|
232
|
+
const transRead = await engine.files.read(assetId, transFile);
|
|
233
|
+
if (!transRead.ok)
|
|
234
|
+
continue;
|
|
235
|
+
try {
|
|
236
|
+
const parsed = JSON.parse(decodeJsonBuffer(transRead.value));
|
|
237
|
+
if (parsed.text && parsed.text.trim()) {
|
|
238
|
+
const snippet = parsed.text.trim().replace(/\s+/g, " ").slice(0, 220);
|
|
239
|
+
parts.push(`spoken: ${snippet}`);
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
// ignore
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (parts.length === 0)
|
|
248
|
+
return null;
|
|
249
|
+
return { asset_id: assetId, type: "video", context_text: parts.join("\n") };
|
|
250
|
+
}
|
|
251
|
+
async function loadImageContext(engine, assetId) {
|
|
252
|
+
const parts = [];
|
|
253
|
+
parts.push(...(await readAnalysisParts(engine, assetId)));
|
|
254
|
+
const meta = await engine.metadata.artifacts.read(assetId, "original");
|
|
255
|
+
if (meta.ok) {
|
|
256
|
+
const origin = typeof meta.value.origin === "string" ? meta.value.origin : "";
|
|
257
|
+
const prompt = typeof meta.value.prompt === "string" ? meta.value.prompt.trim() : "";
|
|
258
|
+
const sourceAsset = typeof meta.value.source_asset === "string" ? meta.value.source_asset : "";
|
|
259
|
+
const sourceAssets = Array.isArray(meta.value.source_assets)
|
|
260
|
+
? meta.value.source_assets.filter((s) => typeof s === "string")
|
|
261
|
+
: [];
|
|
262
|
+
// Extracted frames: inherit the parent video's visual analysis, not "last_frame".
|
|
263
|
+
if (sourceAsset && origin && /_frame$/i.test(origin)) {
|
|
264
|
+
const parent = await loadVideoContext(engine, sourceAsset);
|
|
265
|
+
if (parent?.context_text) {
|
|
266
|
+
parts.push(`still from ${sourceAsset}: ${parent.context_text.slice(0, 400)}`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
else if (sourceAssets.length > 0) {
|
|
270
|
+
// Composite / edit images: briefly describe source materials, then edit intent.
|
|
271
|
+
for (const src of sourceAssets.slice(0, 3)) {
|
|
272
|
+
const srcArtifact = engine.artifacts.get(src);
|
|
273
|
+
if (!srcArtifact.ok)
|
|
274
|
+
continue;
|
|
275
|
+
if (srcArtifact.value.kind === "video") {
|
|
276
|
+
const parent = await loadVideoContext(engine, src);
|
|
277
|
+
if (parent?.context_text) {
|
|
278
|
+
parts.push(`source ${src}: ${parent.context_text.slice(0, 200)}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
else if (srcArtifact.value.kind === "image") {
|
|
282
|
+
const srcAnalysis = await readAnalysisParts(engine, src);
|
|
283
|
+
if (srcAnalysis.length > 0) {
|
|
284
|
+
parts.push(`source ${src}: ${srcAnalysis.join(" ").slice(0, 200)}`);
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
const srcMeta = await engine.metadata.artifacts.read(src, "original");
|
|
288
|
+
if (srcMeta.ok) {
|
|
289
|
+
const srcOrigin = typeof srcMeta.value.origin === "string" ? srcMeta.value.origin : "";
|
|
290
|
+
const srcSource = typeof srcMeta.value.source_asset === "string"
|
|
291
|
+
? srcMeta.value.source_asset
|
|
292
|
+
: "";
|
|
293
|
+
if (srcSource && srcOrigin && /_frame$/i.test(srcOrigin)) {
|
|
294
|
+
const parent = await loadVideoContext(engine, srcSource);
|
|
295
|
+
if (parent?.context_text) {
|
|
296
|
+
parts.push(`source ${src}: ${parent.context_text.slice(0, 200)}`);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
if (prompt) {
|
|
304
|
+
parts.push(`edit intent: ${prompt.slice(0, 300)}`);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
else if (prompt) {
|
|
308
|
+
parts.push(prompt.slice(0, 500));
|
|
309
|
+
}
|
|
310
|
+
// Process origins (last_frame, external, …) are not naming signals.
|
|
311
|
+
if (origin && !isProcessOrigin(origin)) {
|
|
312
|
+
parts.push(`origin: ${origin}`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (parts.length === 0)
|
|
316
|
+
return null;
|
|
317
|
+
return { asset_id: assetId, type: "image", context_text: parts.join("\n") };
|
|
318
|
+
}
|
|
319
|
+
const PREVIEW_CANDIDATES = [
|
|
320
|
+
"square.jpg",
|
|
321
|
+
"landscape.jpg",
|
|
322
|
+
"original.jpg",
|
|
323
|
+
"portrait.jpg",
|
|
324
|
+
"original.png",
|
|
325
|
+
];
|
|
326
|
+
/** Prefer first/last original_frames for videos; fall back to poster jpg. */
|
|
327
|
+
async function loadPreviewStills(engine, contexts) {
|
|
328
|
+
const out = [];
|
|
329
|
+
for (const ctx of contexts) {
|
|
330
|
+
const parts = ctx.type === "video"
|
|
331
|
+
? await loadVideoPreviewParts(engine, ctx.asset_id)
|
|
332
|
+
: await loadImagePreviewParts(engine, ctx.asset_id);
|
|
333
|
+
out.push({ asset_id: ctx.asset_id, parts });
|
|
334
|
+
}
|
|
335
|
+
return out;
|
|
336
|
+
}
|
|
337
|
+
async function loadVideoPreviewParts(engine, assetId) {
|
|
338
|
+
const frameParts = await loadEndFrameParts(engine, assetId);
|
|
339
|
+
if (frameParts.length > 0)
|
|
340
|
+
return frameParts;
|
|
341
|
+
return loadImagePreviewParts(engine, assetId);
|
|
342
|
+
}
|
|
343
|
+
/** Read first + last jpg from original_frames via the asset dir on disk.
|
|
344
|
+
* Nested frame paths are not readable through engine.files.read(filename safety). */
|
|
345
|
+
async function loadEndFrameParts(engine, assetId) {
|
|
346
|
+
const manifest = await engine.files.manifest(assetId);
|
|
347
|
+
if (!manifest.ok)
|
|
348
|
+
return [];
|
|
349
|
+
const frames = manifest.value.directories?.original_frames
|
|
350
|
+
?? manifest.value.directories?.landscape_frames
|
|
351
|
+
?? null;
|
|
352
|
+
if (!frames || frames.length === 0)
|
|
353
|
+
return [];
|
|
354
|
+
const jpgs = frames.filter((f) => f.endsWith(".jpg")).sort((a, b) => {
|
|
355
|
+
const ta = parseFloat(a.replace(/\.jpg$/i, "")) || 0;
|
|
356
|
+
const tb = parseFloat(b.replace(/\.jpg$/i, "")) || 0;
|
|
357
|
+
return ta - tb;
|
|
358
|
+
});
|
|
359
|
+
if (jpgs.length === 0)
|
|
360
|
+
return [];
|
|
361
|
+
const first = jpgs[0];
|
|
362
|
+
const last = jpgs[jpgs.length - 1];
|
|
363
|
+
const dirKey = manifest.value.directories?.original_frames
|
|
364
|
+
? "original_frames"
|
|
365
|
+
: "landscape_frames";
|
|
366
|
+
const picks = first === last ? [first] : [first, last];
|
|
367
|
+
const parts = [];
|
|
368
|
+
for (const name of picks) {
|
|
369
|
+
try {
|
|
370
|
+
const buf = await readFile(join(manifest.value.path, dirKey, name));
|
|
371
|
+
parts.push(imagePartFromBuffer(buf, "image/jpeg"));
|
|
372
|
+
}
|
|
373
|
+
catch {
|
|
374
|
+
// skip missing frame
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return parts;
|
|
378
|
+
}
|
|
379
|
+
async function loadImagePreviewParts(engine, assetId) {
|
|
380
|
+
for (const filename of PREVIEW_CANDIDATES) {
|
|
381
|
+
const read = await engine.files.read(assetId, filename);
|
|
382
|
+
if (!read.ok)
|
|
383
|
+
continue;
|
|
384
|
+
const buf = Buffer.isBuffer(read.value)
|
|
385
|
+
? read.value
|
|
386
|
+
: Buffer.from(read.value);
|
|
387
|
+
// Cap very large originals (~1.5MB decoded) so the request stays reasonable.
|
|
388
|
+
if (buf.length > 1_500_000)
|
|
389
|
+
continue;
|
|
390
|
+
const mime = filename.endsWith(".png") ? "image/png" : "image/jpeg";
|
|
391
|
+
return [imagePartFromBuffer(buf, mime)];
|
|
392
|
+
}
|
|
393
|
+
return [];
|
|
394
|
+
}
|
|
395
|
+
function imagePartFromBuffer(buf, mime) {
|
|
396
|
+
return {
|
|
397
|
+
type: "image_url",
|
|
398
|
+
image_url: {
|
|
399
|
+
url: `data:${mime};base64,${buf.toString("base64")}`,
|
|
400
|
+
detail: "low",
|
|
401
|
+
},
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
/** Build multimodal chat content: instruction text + per-asset stills. */
|
|
405
|
+
function buildMultimodalContent(contexts, previews, hasImages) {
|
|
406
|
+
const text = buildSuggestionPrompt(contexts, { hasImages });
|
|
407
|
+
if (!hasImages)
|
|
408
|
+
return text;
|
|
409
|
+
const byId = new Map(previews.map((p) => [p.asset_id, p.parts]));
|
|
410
|
+
const parts = [{ type: "text", text }];
|
|
411
|
+
for (const ctx of contexts) {
|
|
412
|
+
const images = byId.get(ctx.asset_id) ?? [];
|
|
413
|
+
if (images.length === 0)
|
|
414
|
+
continue;
|
|
415
|
+
parts.push({
|
|
416
|
+
type: "text",
|
|
417
|
+
text: `Preview for ${ctx.asset_id}:`,
|
|
418
|
+
});
|
|
419
|
+
parts.push(...images);
|
|
420
|
+
}
|
|
421
|
+
return parts;
|
|
422
|
+
}
|
|
423
|
+
function responseContentFromChat(content) {
|
|
424
|
+
if (typeof content === "string") {
|
|
425
|
+
return [{ type: "input_text", text: content }];
|
|
426
|
+
}
|
|
427
|
+
return content.map((part) => part.type === "text"
|
|
428
|
+
? { type: "input_text", text: part.text }
|
|
429
|
+
: {
|
|
430
|
+
type: "input_image",
|
|
431
|
+
image_url: part.image_url.url,
|
|
432
|
+
detail: part.image_url.detail ?? "auto",
|
|
433
|
+
});
|
|
434
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function renameAssetDef(engine) {
|
|
3
|
+
return {
|
|
4
|
+
name: "rename_asset",
|
|
5
|
+
description: "Set an asset's display label. Labels are display-only — the artifact id and all references stay unchanged.",
|
|
6
|
+
schema: {
|
|
7
|
+
asset_id: z.string().describe("Artifact id of the asset to relabel"),
|
|
8
|
+
new_name: z.string().describe("New display label (free text, e.g. sunset beach)"),
|
|
9
|
+
book_id: z.string().describe("Book id"),
|
|
10
|
+
},
|
|
11
|
+
handler: async (args) => {
|
|
12
|
+
const { asset_id, new_name } = args;
|
|
13
|
+
const result = await engine.artifacts.rename({
|
|
14
|
+
artifact: asset_id,
|
|
15
|
+
label: new_name,
|
|
16
|
+
});
|
|
17
|
+
if (!result.ok) {
|
|
18
|
+
return {
|
|
19
|
+
isError: true,
|
|
20
|
+
content: [{ type: "text", text: result.error.message }],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
content: [{
|
|
25
|
+
type: "text",
|
|
26
|
+
text: JSON.stringify({
|
|
27
|
+
artifact_id: result.value.artifactId,
|
|
28
|
+
label: result.value.label ?? new_name,
|
|
29
|
+
}),
|
|
30
|
+
}],
|
|
31
|
+
};
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|