videobook 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/LICENSE +21 -0
- package/README.md +133 -2
- package/bin/videobook.js +7 -0
- package/dist/acestep/client.js +203 -0
- package/dist/acestep/config.js +62 -0
- package/dist/acestep/errors.js +8 -0
- package/dist/acestep/process.js +175 -0
- package/dist/alive-captions/analyze.js +117 -0
- package/dist/alive-captions/audio.js +179 -0
- package/dist/alive-captions/frames.js +59 -0
- package/dist/alive-captions/model.js +37 -0
- package/dist/alive-captions/schema.js +49 -0
- package/dist/alive-captions/track.js +9 -0
- package/dist/api-key.js +33 -0
- package/dist/app-state.js +56 -0
- package/dist/app.js +257 -0
- package/dist/archive-b2.js +332 -0
- package/dist/archive-store.js +169 -0
- package/dist/artifact-label.js +36 -0
- package/dist/artifact-refs.js +49 -0
- package/dist/asset-tag-extraction.js +169 -0
- package/dist/asset-tags.js +228 -0
- package/dist/asset-usage.js +23 -0
- package/dist/audio-cue.js +71 -0
- package/dist/audio-encoding.js +32 -0
- package/dist/audio-sync-request.js +1 -0
- package/dist/audio-sync.js +223 -0
- package/dist/audio-trim-range.js +31 -0
- package/dist/book-context.js +31 -0
- package/dist/book-manager.js +374 -0
- package/dist/catalog-gc.js +26 -0
- package/dist/chain-executor.js +815 -0
- package/dist/chain-generation.js +115 -0
- package/dist/chain-plan.js +746 -0
- package/dist/chain-step-review.js +60 -0
- package/dist/character-name.js +25 -0
- package/dist/chat-generation-routing.js +73 -0
- package/dist/chat-generation-settings.js +78 -0
- package/dist/chat-models.js +44 -0
- package/dist/chat-prompt-rewrite.js +67 -0
- package/dist/chat-router.js +628 -0
- package/dist/chat-view-actions.js +49 -0
- package/dist/cli/args.js +192 -0
- package/dist/cli/doctor-command.js +255 -0
- package/dist/cli/entry.js +2 -0
- package/dist/cli/main.js +91 -0
- package/dist/cli/mcp-command.js +82 -0
- package/dist/cli/mcp-relay.js +51 -0
- package/dist/cli/open-browser.js +14 -0
- package/dist/cli/run-command.js +187 -0
- package/dist/cli/serve-command.js +57 -0
- package/dist/cli/stdio-guard.js +8 -0
- package/dist/cold-open-model.js +7 -0
- package/dist/cold-open.js +79 -0
- package/dist/command-reference.js +95 -0
- package/dist/content-store.js +74 -0
- package/dist/cost.js +8 -0
- package/dist/crop-geometry.js +23 -0
- package/dist/dancing-workflow.js +446 -0
- package/dist/data-home.js +44 -0
- package/dist/diagnostics.js +109 -0
- package/dist/dialog-quality.js +34 -0
- package/dist/downloads-dest.js +56 -0
- package/dist/editing-moments.js +194 -0
- package/dist/elevenlabs-audio-extension.js +66 -0
- package/dist/engine.js +54 -0
- package/dist/env-secrets.js +184 -0
- package/dist/ffmpeg-binary.js +12 -0
- package/dist/ffmpeg.js +425 -0
- package/dist/ffprobe-binary.js +55 -0
- package/dist/forget-asset.js +216 -0
- package/dist/frame-utils.js +27 -0
- package/dist/generation-credit-error.js +11 -0
- package/dist/generation-input-mentions.js +117 -0
- package/dist/generation-input-policy.js +68 -0
- package/dist/generation-reference-mentions.js +97 -0
- package/dist/goal-characters.js +49 -0
- package/dist/goal-plan-v3.js +273 -0
- package/dist/goal-plan.js +220 -0
- package/dist/goal-planner-prompt.js +28 -0
- package/dist/goal-prompt-budget.js +4 -0
- package/dist/goal-reference-images.js +25 -0
- package/dist/goal-state.js +261 -0
- package/dist/goal-stills.js +153 -0
- package/dist/goal-store.js +144 -0
- package/dist/goal-styles.js +144 -0
- package/dist/goal-v3-runtime.js +157 -0
- package/dist/guides/character_schema_guide.md +70 -0
- package/dist/guides/gpt_image_2_5_prompting_guide.md +21 -0
- package/dist/guides/grok_imagine_prompting_guide.md +72 -0
- package/dist/guides/image_reproduction_guide.md +84 -0
- package/dist/guides/kling_3_0_prompting_guide.md +136 -0
- package/dist/guides/nano_banana_pro_prompting_guide.md +28 -0
- package/dist/guides/seedance_2_prompting_guide.md +172 -0
- package/dist/http-access.js +49 -0
- package/dist/http-byte-range.js +31 -0
- package/dist/image-compress.js +77 -0
- package/dist/image-edit-default.js +1 -0
- package/dist/image-edit-models.js +41 -0
- package/dist/image-edit-settings.js +11 -0
- package/dist/image-region-edit.js +32 -0
- package/dist/image-subject-map.js +523 -0
- package/dist/image.js +46 -0
- package/dist/index.js +12 -0
- package/dist/job-deadline.js +60 -0
- package/dist/jobs/_alibaba-upload.js +30 -0
- package/dist/jobs/_audio-chunks.js +31 -0
- package/dist/jobs/_download-into-asset.js +15 -0
- package/dist/jobs/_fal-shared.js +22 -0
- package/dist/jobs/_fal-upload.js +35 -0
- package/dist/jobs/_image-edit-adapter.js +213 -0
- package/dist/jobs/_kie-poll.js +119 -0
- package/dist/jobs/_kie-upload.js +32 -0
- package/dist/jobs/_lease.js +163 -0
- package/dist/jobs/_persist-output.js +19 -0
- package/dist/jobs/_storage-retry.js +21 -0
- package/dist/jobs/_transcribe.js +245 -0
- package/dist/jobs/_translate-transcript.js +270 -0
- package/dist/jobs/analyze-alive-captions.js +10 -0
- package/dist/jobs/analyze-editing-moments.js +104 -0
- package/dist/jobs/analyze-jokes.js +143 -0
- package/dist/jobs/analyze-keyword-placement.js +155 -0
- package/dist/jobs/analyze-music.js +14 -0
- package/dist/jobs/analyze-video.js +282 -0
- package/dist/jobs/apply-cuts.js +83 -0
- package/dist/jobs/apply-sfx.js +107 -0
- package/dist/jobs/archive-clip.js +457 -0
- package/dist/jobs/assemble-goal.js +98 -0
- package/dist/jobs/audio-boost.js +71 -0
- package/dist/jobs/audio-mix.js +161 -0
- package/dist/jobs/backfill-asset-tags.js +144 -0
- package/dist/jobs/burn-watermark.js +316 -0
- package/dist/jobs/change-video-speed.js +176 -0
- package/dist/jobs/clean-video-audio.js +207 -0
- package/dist/jobs/cold-open.js +166 -0
- package/dist/jobs/combine-goal.js +108 -0
- package/dist/jobs/complete-provider-task.js +185 -0
- package/dist/jobs/copy-library-asset.js +33 -0
- package/dist/jobs/cost-estimate-assessment.js +113 -0
- package/dist/jobs/crop-image.js +47 -0
- package/dist/jobs/crop-video.js +71 -0
- package/dist/jobs/describe-image.js +181 -0
- package/dist/jobs/detect-scenes.js +33 -0
- package/dist/jobs/doctor-fix.js +100 -0
- package/dist/jobs/download-video.js +426 -0
- package/dist/jobs/enqueue-search-index.js +24 -0
- package/dist/jobs/even-split.js +122 -0
- package/dist/jobs/export-sequence.js +365 -0
- package/dist/jobs/extend-audio.js +229 -0
- package/dist/jobs/extract-audio.js +72 -0
- package/dist/jobs/extract-frame.js +58 -0
- package/dist/jobs/extract-notebook-audio.js +82 -0
- package/dist/jobs/extract-notebook-frame.js +93 -0
- package/dist/jobs/extract-video-clip.js +8 -0
- package/dist/jobs/extract-video-poster.js +55 -0
- package/dist/jobs/extract-waveform.js +25 -0
- package/dist/jobs/finish-goal.js +255 -0
- package/dist/jobs/forget-asset.js +11 -0
- package/dist/jobs/generate-acestep-cover.js +219 -0
- package/dist/jobs/generate-media.js +70 -0
- package/dist/jobs/image-subjects.js +1059 -0
- package/dist/jobs/index.js +18 -0
- package/dist/jobs/inject-first-frame.js +175 -0
- package/dist/jobs/isolate-vocals.js +69 -0
- package/dist/jobs/kie-paygate.js +177 -0
- package/dist/jobs/label-music-sections.js +5 -0
- package/dist/jobs/osg-placement.js +135 -0
- package/dist/jobs/pad-video.js +72 -0
- package/dist/jobs/plan-goal.js +97 -0
- package/dist/jobs/post-record.js +21 -0
- package/dist/jobs/post-to-x.js +21 -0
- package/dist/jobs/prepare-search-reference.js +190 -0
- package/dist/jobs/prepare-temporal-search.js +16 -0
- package/dist/jobs/process-audio.js +67 -0
- package/dist/jobs/process-image.js +58 -0
- package/dist/jobs/process-upload.js +166 -0
- package/dist/jobs/process-video.js +438 -0
- package/dist/jobs/register-all.js +161 -0
- package/dist/jobs/relayout-notebook.js +78 -0
- package/dist/jobs/remove-sound.js +299 -0
- package/dist/jobs/render-project.js +380 -0
- package/dist/jobs/render-qr-outro.js +138 -0
- package/dist/jobs/render-timeline.js +283 -0
- package/dist/jobs/repair-search-indexes.js +42 -0
- package/dist/jobs/replace-video-audio.js +64 -0
- package/dist/jobs/restore-archive-clip.js +53 -0
- package/dist/jobs/reverse-video.js +53 -0
- package/dist/jobs/rewrite-script.js +173 -0
- package/dist/jobs/run-notebook.js +3335 -0
- package/dist/jobs/semantic-index.js +797 -0
- package/dist/jobs/smooth-scene-concat.js +257 -0
- package/dist/jobs/splice-video.js +280 -0
- package/dist/jobs/split-video.js +127 -0
- package/dist/jobs/suggest-cuts.js +149 -0
- package/dist/jobs/suggest-sfx.js +135 -0
- package/dist/jobs/sync-video-to-audio.js +329 -0
- package/dist/jobs/tag-asset.js +266 -0
- package/dist/jobs/task-failure.js +16 -0
- package/dist/jobs/timewarp-video.js +152 -0
- package/dist/jobs/transcribe-video-audio.js +117 -0
- package/dist/jobs/transcribe.js +35 -0
- package/dist/jobs/translate-transcript.js +55 -0
- package/dist/jobs/trim-audio.js +133 -0
- package/dist/jobs/trim-video.js +291 -0
- package/dist/jobs/xai-paygate.js +26 -0
- package/dist/keyword-placement-cache.js +74 -0
- package/dist/lazy-render.js +142 -0
- package/dist/library-browse.js +78 -0
- package/dist/lightning-defaults.js +206 -0
- package/dist/load-env.js +9 -0
- package/dist/local-search-models.js +206 -0
- package/dist/local-upload.js +318 -0
- package/dist/logger.js +69 -0
- package/dist/media/adapters/alibaba.js +20 -0
- package/dist/media/adapters/elevenlabs.js +58 -0
- package/dist/media/adapters/fal.js +45 -0
- package/dist/media/adapters/index.js +26 -0
- package/dist/media/adapters/kie.js +120 -0
- package/dist/media/adapters/pending.js +21 -0
- package/dist/media/adapters/types.js +5 -0
- package/dist/media/adapters/xai.js +18 -0
- package/dist/media/catalog.js +236 -0
- package/dist/media/complete.js +191 -0
- package/dist/media/cost.js +45 -0
- package/dist/media/descriptor.js +157 -0
- package/dist/media/endpoints.js +11 -0
- package/dist/media/frame-options.js +18 -0
- package/dist/media/insert-video-timing.js +17 -0
- package/dist/media/json-schema.js +75 -0
- package/dist/media/legacy-task-types.js +129 -0
- package/dist/media/listing.js +24 -0
- package/dist/media/model-id.js +14 -0
- package/dist/media/preview.js +86 -0
- package/dist/media/prompt-dialects.js +65 -0
- package/dist/media/ref-fields.js +106 -0
- package/dist/media/refs.js +131 -0
- package/dist/media/request.js +188 -0
- package/dist/media/types.js +6 -0
- package/dist/media/wire.js +161 -0
- package/dist/media-library.js +535 -0
- package/dist/media-output-path.js +33 -0
- package/dist/multyculty-workflow.js +757 -0
- package/dist/notebook-action-catalog.js +608 -0
- package/dist/notebook-cell-outputs.js +726 -0
- package/dist/notebook-credit-retry.js +45 -0
- package/dist/notebook-fixtures.js +8 -0
- package/dist/notebook-generation-contract.js +77 -0
- package/dist/notebook-generation-controls.js +145 -0
- package/dist/notebook-goal-layout.js +40 -0
- package/dist/notebook-graph.js +37 -0
- package/dist/notebook-image-edit.js +84 -0
- package/dist/notebook-initial-generation-prompt.js +222 -0
- package/dist/notebook-live-analysis.js +941 -0
- package/dist/notebook-media-ops.js +579 -0
- package/dist/notebook-mutations.js +20 -0
- package/dist/notebook-placement.js +41 -0
- package/dist/notebook-provider-failure.js +87 -0
- package/dist/notebook-published-post.js +26 -0
- package/dist/notebook-reference-resolver.js +227 -0
- package/dist/notebook-relayout.js +270 -0
- package/dist/notebook-run-plan.js +519 -0
- package/dist/notebook-shots.js +162 -0
- package/dist/notebook-workflow.js +856 -0
- package/dist/openai-intelligence.js +99 -0
- package/dist/original-media-file.js +12 -0
- package/dist/osg-assets.js +36 -0
- package/dist/osg-placement-cache.js +64 -0
- package/dist/osg-settings.js +27 -0
- package/dist/paid-tools.js +22 -0
- package/dist/platform-guidance.js +26 -0
- package/dist/posts/dropbox-auth.js +91 -0
- package/dist/posts/dropbox-upload.js +132 -0
- package/dist/posts/icloud-drive.js +134 -0
- package/dist/posts/icloud-status.js +64 -0
- package/dist/posts/post-contract.js +2 -0
- package/dist/posts/post-copy-format.js +43 -0
- package/dist/posts/post-copy.js +193 -0
- package/dist/posts/post-folder.js +18 -0
- package/dist/posts/post-metrics.js +119 -0
- package/dist/posts/post-store.js +340 -0
- package/dist/posts/x-post-target.js +43 -0
- package/dist/preview-payload.js +90 -0
- package/dist/prompt-surfaces.js +22 -0
- package/dist/provider-prompt-transform.js +77 -0
- package/dist/provider-status-link.js +21 -0
- package/dist/provider-task-record.js +78 -0
- package/dist/provider-task-status.js +47 -0
- package/dist/provider-usage.js +294 -0
- package/dist/queue.js +806 -0
- package/dist/reference-video-duration.js +17 -0
- package/dist/remotion/BlurFillVideo.js +51 -0
- package/dist/remotion/KeywordGraphicsOverlay.js +68 -0
- package/dist/remotion/OsgOverlay.js +30 -0
- package/dist/remotion/QrOutroComposition.js +47 -0
- package/dist/remotion/RenderComposition.js +11 -0
- package/dist/remotion/browser.js +59 -0
- package/dist/remotion/constants.js +35 -0
- package/dist/remotion/index.js +66 -0
- package/dist/remotion/keyword-graphics.js +151 -0
- package/dist/remotion/keyword-placement.js +182 -0
- package/dist/remotion/osg-spec.js +198 -0
- package/dist/remotion/public/qr-outro-impact.wav +0 -0
- package/dist/remotion/qr-outro-audio.js +1 -0
- package/dist/remotion/qr-outro-spec.js +211 -0
- package/dist/remotion/shared-bundle.js +37 -0
- package/dist/remotion/subtitles/AliveCaptions.js +83 -0
- package/dist/remotion/subtitles/Highlight.js +58 -0
- package/dist/remotion/subtitles/Impact.js +67 -0
- package/dist/remotion/subtitles/SubtitleOverlay.js +9 -0
- package/dist/remotion/subtitles/alive-layout.js +321 -0
- package/dist/remotion/subtitles/alive-types.js +17 -0
- package/dist/remotion/subtitles/chunker.js +68 -0
- package/dist/remotion/subtitles/segments.js +42 -0
- package/dist/remotion/subtitles/styles.js +4 -0
- package/dist/remotion/subtitles/types.js +1 -0
- package/dist/remotion/util.js +18 -0
- package/dist/remotion-cancellation.js +15 -0
- package/dist/rename-helpers.js +204 -0
- package/dist/rhythm/analyze.js +128 -0
- package/dist/rhythm/contract.js +14 -0
- package/dist/rhythm/draft-model.js +19 -0
- package/dist/rhythm/energy.js +133 -0
- package/dist/rhythm/fft.js +44 -0
- package/dist/rhythm/model.js +71 -0
- package/dist/rhythm/music-analysis.js +124 -0
- package/dist/rhythm/music-draft.js +261 -0
- package/dist/rhythm/onset.js +101 -0
- package/dist/rhythm/run-worker.js +51 -0
- package/dist/rhythm/score.js +47 -0
- package/dist/rhythm/section-labels.js +168 -0
- package/dist/rhythm/sequence-rhythm.js +67 -0
- package/dist/rhythm/synthesize.js +103 -0
- package/dist/rhythm/timing.js +98 -0
- package/dist/rhythm/track.js +320 -0
- package/dist/rhythm/visual-fit.js +4 -0
- package/dist/rhythm/worker.js +25 -0
- package/dist/scene-detect.js +200 -0
- package/dist/scene-transition.js +12 -0
- package/dist/scheduled-posts.js +200 -0
- package/dist/search-index-coverage.js +13 -0
- package/dist/search-index-media.js +71 -0
- package/dist/search-reference-store.js +60 -0
- package/dist/search-scoring.js +17 -0
- package/dist/serve.js +150 -0
- package/dist/server-lock.js +143 -0
- package/dist/server.js +42 -0
- package/dist/srt.js +16 -0
- package/dist/tag-backfill-state.js +121 -0
- package/dist/tag-settings.js +30 -0
- package/dist/tag-vocabulary.js +237 -0
- package/dist/test-queue-cleanup.js +1 -0
- package/dist/time-parse.js +50 -0
- package/dist/timeline-helpers.js +554 -0
- package/dist/timewarp.js +87 -0
- package/dist/tools/_image-inputs.js +9 -0
- package/dist/tools/_media-refs.js +84 -0
- package/dist/tools/_shared.js +59 -0
- package/dist/tools/_suggest-shared.js +279 -0
- package/dist/tools/add-timeline-audio.js +73 -0
- package/dist/tools/alive-captions.js +157 -0
- package/dist/tools/analyze-jokes.js +56 -0
- package/dist/tools/analyze-video.js +66 -0
- package/dist/tools/animate-keywords.js +217 -0
- package/dist/tools/api-status.js +42 -0
- package/dist/tools/application-commands.js +34 -0
- package/dist/tools/apply-cuts.js +112 -0
- package/dist/tools/apply-sfx.js +152 -0
- package/dist/tools/apply-watermark.js +670 -0
- package/dist/tools/archive-clip.js +74 -0
- package/dist/tools/asset-history.js +61 -0
- package/dist/tools/asset-manifest.js +24 -0
- package/dist/tools/asset-tags.js +151 -0
- package/dist/tools/assign-slot.js +97 -0
- package/dist/tools/audio-boost.js +107 -0
- package/dist/tools/audio-mix.js +121 -0
- package/dist/tools/book-usage.js +16 -0
- package/dist/tools/chain.js +136 -0
- package/dist/tools/change-video-speed.js +105 -0
- package/dist/tools/chat-history.js +5 -0
- package/dist/tools/chat.js +848 -0
- package/dist/tools/clean-video-audio.js +97 -0
- package/dist/tools/clear-failed-tasks.js +66 -0
- package/dist/tools/cold-open.js +114 -0
- package/dist/tools/command-plan.js +1680 -0
- package/dist/tools/commands.js +1666 -0
- package/dist/tools/concat-images.js +236 -0
- package/dist/tools/connect-service.js +55 -0
- package/dist/tools/copy-file-to-clipboard.js +58 -0
- package/dist/tools/create-character.js +114 -0
- package/dist/tools/create-qart.js +253 -0
- package/dist/tools/create-qr-outro.js +149 -0
- package/dist/tools/create-qrbtf.js +215 -0
- package/dist/tools/create-watermark.js +97 -0
- package/dist/tools/crop-asset.js +18 -0
- package/dist/tools/crop-image.js +120 -0
- package/dist/tools/crop-video.js +97 -0
- package/dist/tools/delete-asset.js +104 -0
- package/dist/tools/describe-image.js +69 -0
- package/dist/tools/detect-scenes.js +44 -0
- package/dist/tools/doctor.js +126 -0
- package/dist/tools/download-video.js +114 -0
- package/dist/tools/duplicate-asset.js +184 -0
- package/dist/tools/edit-timeline.js +59 -0
- package/dist/tools/editing-moments.js +105 -0
- package/dist/tools/estimate-cost.js +171 -0
- package/dist/tools/even-split.js +295 -0
- package/dist/tools/export-asset-file.js +22 -0
- package/dist/tools/export-asset.js +140 -0
- package/dist/tools/export-sequence.js +250 -0
- package/dist/tools/export-to-downloads.js +54 -0
- package/dist/tools/export-transcript-srt.js +79 -0
- package/dist/tools/extend-audio.js +100 -0
- package/dist/tools/extract-audio.js +174 -0
- package/dist/tools/extract-frame.js +108 -0
- package/dist/tools/extract-notebook-audio.js +173 -0
- package/dist/tools/extract-notebook-frame.js +160 -0
- package/dist/tools/extract-video-clip.js +101 -0
- package/dist/tools/generate-media.js +157 -0
- package/dist/tools/generation-payload.js +48 -0
- package/dist/tools/get-timeline.js +12 -0
- package/dist/tools/get-watermark-config.js +38 -0
- package/dist/tools/goal.js +644 -0
- package/dist/tools/image-subjects.js +711 -0
- package/dist/tools/index.js +323 -0
- package/dist/tools/inject-first-frame.js +154 -0
- package/dist/tools/invert-image.js +70 -0
- package/dist/tools/isolate-vocals.js +66 -0
- package/dist/tools/list-assets.js +98 -0
- package/dist/tools/list-cell-generations.js +225 -0
- package/dist/tools/list-elevenlabs-voices.js +163 -0
- package/dist/tools/log-wrapper.js +29 -0
- package/dist/tools/make-cover-song.js +95 -0
- package/dist/tools/mark-asset-seen.js +23 -0
- package/dist/tools/media-library.js +245 -0
- package/dist/tools/move-timeline-audio.js +35 -0
- package/dist/tools/music-analysis.js +60 -0
- package/dist/tools/music-draft.js +29 -0
- package/dist/tools/pad-video.js +87 -0
- package/dist/tools/post-record.js +83 -0
- package/dist/tools/post.js +168 -0
- package/dist/tools/prompt-history.js +164 -0
- package/dist/tools/provider-key-status.js +248 -0
- package/dist/tools/qrbtf-render.js +849 -0
- package/dist/tools/read-asset-metadata.js +26 -0
- package/dist/tools/read-file-at-commit.js +29 -0
- package/dist/tools/read-transcript.js +41 -0
- package/dist/tools/reconcile-project-work.js +1452 -0
- package/dist/tools/recover-asset.js +28 -0
- package/dist/tools/remove-sound.js +72 -0
- package/dist/tools/remove-timeline-audio.js +34 -0
- package/dist/tools/remove-timeline-slot.js +40 -0
- package/dist/tools/rename-all.js +434 -0
- package/dist/tools/rename-asset.js +34 -0
- package/dist/tools/render-settings.js +47 -0
- package/dist/tools/render-timeline.js +120 -0
- package/dist/tools/render.js +384 -0
- package/dist/tools/reorder-character-images.js +45 -0
- package/dist/tools/replace-video-audio.js +133 -0
- package/dist/tools/restore-archive-clips.js +209 -0
- package/dist/tools/restore-asset-version.js +37 -0
- package/dist/tools/retry-generation.js +54 -0
- package/dist/tools/reverse-video.js +74 -0
- package/dist/tools/rewrite-prompt.js +388 -0
- package/dist/tools/rewrite-script.js +67 -0
- package/dist/tools/schema-from-apicity.js +66 -0
- package/dist/tools/search-archive.js +150 -0
- package/dist/tools/semantic-search.js +1214 -0
- package/dist/tools/sequence-rhythm.js +43 -0
- package/dist/tools/set-character-metadata.js +84 -0
- package/dist/tools/set-env-secret.js +40 -0
- package/dist/tools/set-osg.js +143 -0
- package/dist/tools/show-folder.js +37 -0
- package/dist/tools/smooth-scene-concat.js +122 -0
- package/dist/tools/splice-video.js +149 -0
- package/dist/tools/split-video.js +202 -0
- package/dist/tools/suggest-cuts.js +45 -0
- package/dist/tools/suggest-sfx.js +43 -0
- package/dist/tools/sync-video-to-audio.js +115 -0
- package/dist/tools/tag-backfill.js +177 -0
- package/dist/tools/timewarp-video.js +85 -0
- package/dist/tools/tool-def.js +1 -0
- package/dist/tools/tool-json-schema.js +116 -0
- package/dist/tools/transcribe.js +90 -0
- package/dist/tools/translate-subtitles.js +59 -0
- package/dist/tools/trim-audio.js +139 -0
- package/dist/tools/trim-video.js +168 -0
- package/dist/tools/update-script.js +25 -0
- package/dist/tools/update-timeline-audio.js +40 -0
- package/dist/tools/upload-file.js +41 -0
- package/dist/tools/verify-x-mentions.js +141 -0
- package/dist/tools/video-editing-command-contract.js +291 -0
- package/dist/tools/video-source-reference.js +30 -0
- package/dist/tools/video-time-input.js +18 -0
- package/dist/tools/videobook-domain.js +2006 -0
- package/dist/tools/wait-for-asset-ready.js +143 -0
- package/dist/tools/workspace-actions.js +54 -0
- package/dist/transcript-projection.js +202 -0
- package/dist/transcript-srt.js +33 -0
- package/dist/upload.js +76 -0
- package/dist/usage-providers.js +25 -0
- package/dist/video-analysis-projection.js +46 -0
- package/dist/video-poster.js +33 -0
- package/dist/video-proxy.js +99 -0
- package/dist/vision-regions.js +111 -0
- package/dist/voice-actions.js +90 -0
- package/dist/voice-audio.js +55 -0
- package/dist/voice-config.js +122 -0
- package/dist/voice-continuation.js +54 -0
- package/dist/voice-contract.js +1 -0
- package/dist/voice-http.js +145 -0
- package/dist/voice-reply-progress.js +55 -0
- package/dist/voice-session.js +992 -0
- package/dist/watermark-badge.js +259 -0
- package/dist/watermark-config.js +111 -0
- package/dist/watermark-handle-position.js +55 -0
- package/dist/watermark-platform.js +15 -0
- package/dist/watermark-timing.js +16 -0
- package/dist/workspace-action-catalog.js +315 -0
- package/dist/workspace-action-contract.js +21 -0
- package/dist/workspace-action-groups.js +98 -0
- package/dist/workspace-action-surfaces.js +7 -0
- package/dist/x/account.js +88 -0
- package/dist/x/authenticated-handle.js +30 -0
- package/dist/x/oauth-flow.js +191 -0
- package/dist/x/post-contract.js +1 -0
- package/dist/x/post-metadata.js +44 -0
- package/dist/x/post-to-x.js +456 -0
- package/dist/x/published-post-cell.js +90 -0
- package/dist/x/x-tokens.js +69 -0
- package/dist/zod-slots.js +334 -0
- package/package.json +92 -4
- package/ui/assets/alive-captions-BWb5nLKj.css +1 -0
- package/ui/assets/alive-captions-DPEr1nKM.js +1 -0
- package/ui/assets/confirm-bodies-D8ouHXKy.js +1 -0
- package/ui/assets/confirm-bodies-DrMhaXC6.css +1 -0
- package/ui/assets/fullscreen-B7nw4YbE.js +1 -0
- package/ui/assets/goal-ui-DK-xTCK0.js +2 -0
- package/ui/assets/goal-ui-DXdUM2KF.css +1 -0
- package/ui/assets/index-DzPAnCUj.css +1 -0
- package/ui/assets/index-kS1905s5.js +37 -0
- package/ui/assets/inspector-MlzQbTVo.js +1 -0
- package/ui/assets/jobs-ui-CoinOWx0.js +1 -0
- package/ui/assets/music-video-Cbe80kmQ.js +1 -0
- package/ui/assets/music-video-DPaiKKEq.css +1 -0
- package/ui/assets/ui-Oirr9VOf.css +1 -0
- package/ui/assets/ui-xo_Y1yQg.js +2 -0
- package/ui/assets/vendor-D2rfiZ5j.css +1 -0
- package/ui/assets/vendor-DfTCWgKm.js +19 -0
- package/ui/assets/vendor-flow-BXbeWxW7.js +7 -0
- package/ui/assets/vendor-flow-BnuhLJ6X.css +1 -0
- package/ui/assets/vendor-icons-CGqhCIXV.js +1 -0
- package/ui/assets/vendor-mcp-BTlEJcHV.js +67 -0
- package/ui/assets/vendor-react-Dj7D1fD4.js +9 -0
- package/ui/assets/vendor-remotion-CH1HXrlc.js +95 -0
- package/ui/assets/videobook-Qlp9HQFu.js +20 -0
- package/ui/favicon.svg +8 -0
- package/ui/index.html +126 -0
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { extname } from "node:path";
|
|
3
|
+
import { v7 as uuidv7 } from "uuid";
|
|
4
|
+
import { findPrimaryMediaFile } from "videobook-engine";
|
|
5
|
+
import { nextHorizontalSlotFrom, notebookGridTag, notebookOccupiedSlots } from "videobook-engine/notebook-grid";
|
|
6
|
+
import { mutateNotebook, withNotebookMutation } from "../notebook-mutations.js";
|
|
7
|
+
import { slotRight } from "../notebook-placement.js";
|
|
8
|
+
const key = (id) => `application.post.v1.${id}`;
|
|
9
|
+
export function readPostRuntime(engine, id) {
|
|
10
|
+
return engine.settings.get(key(id)) ?? { status: "draft", metrics: [] };
|
|
11
|
+
}
|
|
12
|
+
export function patchPostRuntime(engine, id, patch) {
|
|
13
|
+
const next = { ...readPostRuntime(engine, id), ...patch };
|
|
14
|
+
const ids = engine.settings.get("application.posts.index.v1") ?? [];
|
|
15
|
+
if (!ids.includes(id)) {
|
|
16
|
+
const indexed = engine.settings.set("application.posts.index.v1", [...ids, id]);
|
|
17
|
+
if (!indexed.ok)
|
|
18
|
+
throw new Error(indexed.error.message);
|
|
19
|
+
}
|
|
20
|
+
const written = engine.settings.set(key(id), next);
|
|
21
|
+
if (!written.ok)
|
|
22
|
+
throw new Error(written.error.message);
|
|
23
|
+
return next;
|
|
24
|
+
}
|
|
25
|
+
function postDefinition(cell) {
|
|
26
|
+
const value = cell.inputs?.post;
|
|
27
|
+
if (!value || value.version !== 1)
|
|
28
|
+
throw new Error("Post needs migration before editing");
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
export function notebookPosts(engine, notebook) {
|
|
32
|
+
return notebook.cells.filter((cell) => cell.type === "post" && cell.inputs?.post).map((cell) => {
|
|
33
|
+
const definition = postDefinition(cell);
|
|
34
|
+
const runtime = readPostRuntime(engine, cell.id);
|
|
35
|
+
return {
|
|
36
|
+
id: cell.id, notebookId: notebook.id, address: notebookGridTag(cell.slot), label: cell.label ?? "Post",
|
|
37
|
+
definition: runtime.publication ? { ...runtime.publication, notes: definition.notes } : definition, runtime,
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
export function listPosts(engine) {
|
|
42
|
+
return engine.notebooks.list().flatMap((item) => {
|
|
43
|
+
const read = engine.notebooks.read(item.id);
|
|
44
|
+
return read.ok ? notebookPosts(engine, read.value) : [];
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
export function getPost(engine, id) {
|
|
48
|
+
const post = listPosts(engine).find((item) => item.id === id);
|
|
49
|
+
if (!post)
|
|
50
|
+
throw new Error(`Post cell ${id} not found`);
|
|
51
|
+
return post;
|
|
52
|
+
}
|
|
53
|
+
function captureLineage(notebook, source) {
|
|
54
|
+
const seen = new Set();
|
|
55
|
+
const visit = (cell) => {
|
|
56
|
+
if (seen.has(cell.id))
|
|
57
|
+
return;
|
|
58
|
+
seen.add(cell.id);
|
|
59
|
+
for (const edge of notebook.edges.filter((item) => item.target === cell.id)) {
|
|
60
|
+
const parent = notebook.cells.find((item) => item.id === edge.source);
|
|
61
|
+
if (parent)
|
|
62
|
+
visit(parent);
|
|
63
|
+
}
|
|
64
|
+
for (const name of ["producerCellId", "sourceCellId"]) {
|
|
65
|
+
const parent = notebook.cells.find((item) => item.id === cell.inputs?.[name]);
|
|
66
|
+
if (parent)
|
|
67
|
+
visit(parent);
|
|
68
|
+
}
|
|
69
|
+
if (typeof cell.inputs?.goalId === "string") {
|
|
70
|
+
const goal = notebook.cells.find((item) => item.type === "goal" && item.inputs?.goalId === cell.inputs?.goalId);
|
|
71
|
+
if (goal)
|
|
72
|
+
visit(goal);
|
|
73
|
+
}
|
|
74
|
+
const artifactIds = ["sourceArtifactId", "source_asset_id", "first_asset_id", "second_asset_id", "additional_asset_ids", "sourceArtifactIds", "image_asset_ids"].flatMap((name) => {
|
|
75
|
+
const value = cell.inputs?.[name];
|
|
76
|
+
return typeof value === "string" ? [value] : Array.isArray(value) ? value.filter((id) => typeof id === "string") : [];
|
|
77
|
+
});
|
|
78
|
+
for (const artifactId of artifactIds) {
|
|
79
|
+
const parent = notebook.cells.find((item) => item.outputArtifactId === artifactId && item.type !== "post");
|
|
80
|
+
if (parent)
|
|
81
|
+
visit(parent);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
visit(source);
|
|
85
|
+
return notebook.cells.filter((cell) => seen.has(cell.id)).map((cell) => ({
|
|
86
|
+
cellId: cell.id, type: cell.type, label: cell.label, artifactId: cell.outputArtifactId,
|
|
87
|
+
prompt: cell.prompt, inputs: cell.inputs,
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
async function freezeSource(engine, source) {
|
|
91
|
+
const artifactId = source.outputArtifactId;
|
|
92
|
+
if (!artifactId)
|
|
93
|
+
throw new Error("Choose a finished image or video cell");
|
|
94
|
+
const artifact = engine.artifacts.get(artifactId);
|
|
95
|
+
if (!artifact.ok || !["video", "image", "final"].includes(artifact.value.kind))
|
|
96
|
+
throw new Error("A Post needs an image or video source");
|
|
97
|
+
const revision = engine.head;
|
|
98
|
+
const manifest = await engine.files.manifest(artifactId);
|
|
99
|
+
if (!manifest.ok)
|
|
100
|
+
throw new Error(manifest.error.message);
|
|
101
|
+
const file = findPrimaryMediaFile(manifest.value.files, artifact.value.kind);
|
|
102
|
+
if (!file)
|
|
103
|
+
throw new Error("Source has no finished media file");
|
|
104
|
+
const bytes = await engine.files.readAtRevision(artifactId, file.name, revision);
|
|
105
|
+
if (!bytes.ok)
|
|
106
|
+
throw new Error(bytes.error.message);
|
|
107
|
+
const snapshot = await engine.artifacts.create(artifact.value.kind === "final" ? "video" : artifact.value.kind, `Post source · ${source.label ?? artifact.value.label ?? "clip"}`);
|
|
108
|
+
if (!snapshot.ok)
|
|
109
|
+
throw new Error(snapshot.error.message);
|
|
110
|
+
const snapshotArtifactId = snapshot.value.artifactId;
|
|
111
|
+
const filename = `original${extname(file.name)}`;
|
|
112
|
+
const written = await engine.files.write(snapshotArtifactId, filename, bytes.value);
|
|
113
|
+
if (!written.ok)
|
|
114
|
+
throw new Error(written.error.message);
|
|
115
|
+
const tagged = await engine.metadata.artifacts.write(snapshotArtifactId, "publication", { role: "source-snapshot", sourceArtifactId: artifactId, sourceRevision: revision });
|
|
116
|
+
if (!tagged.ok)
|
|
117
|
+
throw new Error(tagged.error.message);
|
|
118
|
+
return { cellId: source.id, artifactId, revision, objectHash: createHash("sha256").update(bytes.value).digest("hex"), filename, sourceFilename: file.name, snapshotArtifactId };
|
|
119
|
+
}
|
|
120
|
+
export async function createPost(engine, args) {
|
|
121
|
+
let createdId = "";
|
|
122
|
+
await withNotebookMutation(args.notebookId, async () => {
|
|
123
|
+
const read = engine.notebooks.read(args.notebookId);
|
|
124
|
+
if (!read.ok)
|
|
125
|
+
throw new Error(read.error.message);
|
|
126
|
+
const notebook = read.value;
|
|
127
|
+
const source = args.sourceCellId ? notebook.cells.find((cell) => cell.id === args.sourceCellId && cell.type !== "post") : undefined;
|
|
128
|
+
if (args.sourceCellId && !source)
|
|
129
|
+
throw new Error("Source cell not found");
|
|
130
|
+
const frozen = source ? await freezeSource(engine, source) : null;
|
|
131
|
+
const platform = args.platform ?? "tiktok";
|
|
132
|
+
const definition = {
|
|
133
|
+
version: 1, platform, caption: args.caption ?? "", credits: args.credits ?? "", account: args.account ?? "", notes: args.notes ?? "",
|
|
134
|
+
...(platform === "tiktok" ? { delivery: args.delivery ?? "icloud" } : {}),
|
|
135
|
+
createdAt: new Date().toISOString(), source: frozen, lineage: source ? captureLineage(notebook, source) : [],
|
|
136
|
+
};
|
|
137
|
+
const cell = engine.notebooks.createCell({
|
|
138
|
+
type: "post", label: `Post · ${definition.platform}`,
|
|
139
|
+
slot: nextHorizontalSlotFrom(source ? slotRight(source.slot) : { row: 0, column: 0 }, notebookOccupiedSlots(notebook.cells)),
|
|
140
|
+
inputs: { post: definition },
|
|
141
|
+
...(frozen?.snapshotArtifactId ? { outputArtifactId: frozen.snapshotArtifactId, references: [{
|
|
142
|
+
id: uuidv7(), kind: "artifact", targetId: frozen.artifactId,
|
|
143
|
+
snapshot: { objectHash: frozen.objectHash, revision: frozen.revision }, ordinal: 0,
|
|
144
|
+
}] } : {}),
|
|
145
|
+
});
|
|
146
|
+
createdId = cell.id;
|
|
147
|
+
const result = await engine.notebooks.write({ ...notebook, cells: [...notebook.cells, cell], edges: source ? [...notebook.edges, engine.notebooks.createEdge({ source: source.id, target: cell.id, targetInput: "post" })] : notebook.edges }, { operation: "create_post" });
|
|
148
|
+
if (!result.ok)
|
|
149
|
+
throw new Error(result.error.message);
|
|
150
|
+
});
|
|
151
|
+
return getPost(engine, createdId);
|
|
152
|
+
}
|
|
153
|
+
export async function updatePost(engine, id, patch) {
|
|
154
|
+
const post = getPost(engine, id);
|
|
155
|
+
const locked = Boolean(post.runtime.outbound || post.runtime.url || post.runtime.status === "scheduled");
|
|
156
|
+
if (locked && Object.keys(patch).some((name) => name !== "notes"))
|
|
157
|
+
throw new Error("This publication is frozen. Create a new Post for a different clip, caption or platform.");
|
|
158
|
+
let frozen = post.definition.source;
|
|
159
|
+
let lineage = post.definition.lineage;
|
|
160
|
+
if (patch.sourceCellId) {
|
|
161
|
+
if (frozen)
|
|
162
|
+
throw new Error("The source version is frozen. Create a new Post for another clip.");
|
|
163
|
+
const read = engine.notebooks.read(post.notebookId);
|
|
164
|
+
if (!read.ok)
|
|
165
|
+
throw new Error(read.error.message);
|
|
166
|
+
const source = read.value.cells.find((cell) => cell.id === patch.sourceCellId && cell.type !== "post");
|
|
167
|
+
if (!source)
|
|
168
|
+
throw new Error("Source cell not found");
|
|
169
|
+
frozen = await freezeSource(engine, source);
|
|
170
|
+
lineage = captureLineage(read.value, source);
|
|
171
|
+
}
|
|
172
|
+
const { sourceCellId: _source, ...fields } = patch;
|
|
173
|
+
const written = await mutateNotebook(engine, post.notebookId, "update_post", (document) => ({
|
|
174
|
+
...document,
|
|
175
|
+
cells: document.cells.map((cell) => cell.id !== id ? cell : {
|
|
176
|
+
...cell, label: `Post · ${fields.platform ?? post.definition.platform}`,
|
|
177
|
+
inputs: { post: { ...postDefinition(cell), ...fields, source: frozen, lineage } },
|
|
178
|
+
...(frozen?.snapshotArtifactId ? { outputArtifactId: frozen.snapshotArtifactId } : {}),
|
|
179
|
+
}),
|
|
180
|
+
edges: patch.sourceCellId ? [...document.edges, engine.notebooks.createEdge({ source: patch.sourceCellId, target: id, targetInput: "post" })] : document.edges,
|
|
181
|
+
}));
|
|
182
|
+
if (!written.ok)
|
|
183
|
+
throw new Error(written.error.message);
|
|
184
|
+
return getPost(engine, id);
|
|
185
|
+
}
|
|
186
|
+
export async function freezePostOutbound(engine, id, media) {
|
|
187
|
+
const post = getPost(engine, id);
|
|
188
|
+
if (post.runtime.outbound)
|
|
189
|
+
return post.runtime.outbound;
|
|
190
|
+
const source = post.definition.source;
|
|
191
|
+
if (!source?.snapshotArtifactId || !source.filename)
|
|
192
|
+
throw new Error("Historical media version is unknown. Create a new Post to upload a clip.");
|
|
193
|
+
const artifact = engine.artifacts.get(source.snapshotArtifactId);
|
|
194
|
+
if (!artifact.ok)
|
|
195
|
+
throw new Error(artifact.error.message);
|
|
196
|
+
const created = await engine.artifacts.create(artifact.value.kind, `Publication · ${post.address}`);
|
|
197
|
+
if (!created.ok)
|
|
198
|
+
throw new Error(created.error.message);
|
|
199
|
+
const artifactId = created.value.artifactId;
|
|
200
|
+
const filename = media?.filename ?? source.filename;
|
|
201
|
+
const copied = media ? await engine.files.write(artifactId, filename, media.bytes) : await engine.files.copy(source.snapshotArtifactId, source.filename, artifactId, filename);
|
|
202
|
+
if (!copied.ok)
|
|
203
|
+
throw new Error(copied.error.message);
|
|
204
|
+
const caption = [post.definition.caption, post.definition.credits].filter(Boolean).join("\n\n");
|
|
205
|
+
const written = await engine.files.write(artifactId, "caption.txt", caption);
|
|
206
|
+
if (!written.ok)
|
|
207
|
+
throw new Error(written.error.message);
|
|
208
|
+
const tagged = await engine.metadata.artifacts.write(artifactId, "publication", { role: "outbound", postId: id });
|
|
209
|
+
if (!tagged.ok)
|
|
210
|
+
throw new Error(tagged.error.message);
|
|
211
|
+
const result = await mutateNotebook(engine, post.notebookId, "freeze_post", (document) => ({ ...document, cells: document.cells.map((cell) => cell.id === id ? { ...cell, outputArtifactId: artifactId } : cell) }));
|
|
212
|
+
if (!result.ok)
|
|
213
|
+
throw new Error(result.error.message);
|
|
214
|
+
const manifest = await engine.files.manifest(artifactId);
|
|
215
|
+
if (!manifest.ok)
|
|
216
|
+
throw new Error(manifest.error.message);
|
|
217
|
+
const outbound = { artifactId, filename, caption, frozenAt: new Date().toISOString(), revision: engine.head, objectHash: manifest.value.files.find(file => file.name === filename).objectHash };
|
|
218
|
+
patchPostRuntime(engine, id, { outbound, publication: post.definition });
|
|
219
|
+
return outbound;
|
|
220
|
+
}
|
|
221
|
+
function normalizePostUrl(value, platform) {
|
|
222
|
+
const url = new URL(value.trim());
|
|
223
|
+
if (url.protocol !== "https:" || url.username || url.password)
|
|
224
|
+
throw new Error("Use a public HTTPS post link");
|
|
225
|
+
const host = url.hostname.toLowerCase().replace(/^www\./, "");
|
|
226
|
+
const allowed = { x: ["x.com", "twitter.com"], instagram: ["instagram.com"], tiktok: ["tiktok.com", "vm.tiktok.com", "vt.tiktok.com"], youtube: ["youtube.com", "youtu.be"], other: [] };
|
|
227
|
+
if (platform !== "other" && !allowed[platform].includes(host))
|
|
228
|
+
throw new Error(`The URL does not match ${platform}`);
|
|
229
|
+
if (platform === "x") {
|
|
230
|
+
const id = url.pathname.match(/\/status\/(\d+)/)?.[1];
|
|
231
|
+
if (!id)
|
|
232
|
+
throw new Error("Use an X post URL containing /status/<id>");
|
|
233
|
+
return `https://x.com/i/web/status/${id}`;
|
|
234
|
+
}
|
|
235
|
+
url.hostname = host;
|
|
236
|
+
url.hash = "";
|
|
237
|
+
for (const name of [...url.searchParams.keys()])
|
|
238
|
+
if (name !== "v")
|
|
239
|
+
url.searchParams.delete(name);
|
|
240
|
+
return url.toString().replace(/\/$/, "");
|
|
241
|
+
}
|
|
242
|
+
export async function linkPost(engine, id, url, postedAt) {
|
|
243
|
+
const post = getPost(engine, id);
|
|
244
|
+
if (!post.definition.source)
|
|
245
|
+
throw new Error("Choose the source clip before linking a publication");
|
|
246
|
+
const normalized = normalizePostUrl(url, post.definition.platform);
|
|
247
|
+
const time = new Date(postedAt).getTime();
|
|
248
|
+
if (!Number.isFinite(time) || time > Date.now() + 60_000)
|
|
249
|
+
throw new Error("Enter the actual publication time");
|
|
250
|
+
const duplicate = () => (engine.settings.get("application.posts.index.v1") ?? []).some((otherId) => otherId !== id && readPostRuntime(engine, otherId).url === normalized);
|
|
251
|
+
if (duplicate())
|
|
252
|
+
throw new Error("That publication is already tracked by another Post cell");
|
|
253
|
+
if (post.runtime.url && post.runtime.url !== normalized)
|
|
254
|
+
throw new Error("This Post already represents a publication. Create a new Post for a repost.");
|
|
255
|
+
if (post.runtime.url)
|
|
256
|
+
return post;
|
|
257
|
+
await freezePostOutbound(engine, id);
|
|
258
|
+
if (duplicate())
|
|
259
|
+
throw new Error("That publication is already tracked by another Post cell");
|
|
260
|
+
patchPostRuntime(engine, id, { url: normalized, postedAt: new Date(time).toISOString(), status: "posted", error: null, nextMetricAt: post.definition.platform === "x" ? new Date().toISOString() : null, metricsPaused: false });
|
|
261
|
+
return getPost(engine, id);
|
|
262
|
+
}
|
|
263
|
+
function legacyXReceipt(inputs) {
|
|
264
|
+
const replies = (Array.isArray(inputs.replies) ? inputs.replies : []).flatMap((value) => {
|
|
265
|
+
if (!value || typeof value !== "object")
|
|
266
|
+
return [];
|
|
267
|
+
const reply = value;
|
|
268
|
+
const id = reply.tweet_id ?? reply.tweetId;
|
|
269
|
+
const url = reply.tweet_url ?? reply.tweetUrl;
|
|
270
|
+
if (typeof id !== "string" || typeof url !== "string")
|
|
271
|
+
return [];
|
|
272
|
+
return [{ tweet_id: id, tweet_url: url, media_ids: Array.isArray(reply.media_ids) ? reply.media_ids.filter((id) => typeof id === "string") : [], text: typeof reply.text === "string" ? reply.text : null, posted_at: typeof reply.posted_at === "string" ? reply.posted_at : "" }];
|
|
273
|
+
});
|
|
274
|
+
const error = inputs.followupError && typeof inputs.followupError === "object" ? inputs.followupError : null;
|
|
275
|
+
return { tweetId: String(inputs.tweetId ?? ""), mediaId: "", replies, followupError: error ? { reply_number: Number(error.reply_number ?? error.replyNumber), message: String(error.message ?? "Reply failed") } : null };
|
|
276
|
+
}
|
|
277
|
+
function migrateLegacyNotebook(engine, notebook) {
|
|
278
|
+
const legacy = notebook.cells.filter(cell => cell.inputs?.sourceOperation === "post_to_x" && !cell.inputs.post);
|
|
279
|
+
const ids = new Set(legacy.map(cell => cell.id));
|
|
280
|
+
const cells = [...notebook.cells];
|
|
281
|
+
const parents = new Map();
|
|
282
|
+
for (const cell of legacy) {
|
|
283
|
+
let source = cells.find(item => item.id === cell.inputs?.sourceCellId && !ids.has(item.id))
|
|
284
|
+
?? cells.find(item => item.outputArtifactId === cell.outputArtifactId && !ids.has(item.id) && ["image", "video", "generate_image", "generate_video", "concat", "splice", "split_video"].includes(item.type));
|
|
285
|
+
if (!source && notebook.edges.some(edge => edge.source === cell.id)) {
|
|
286
|
+
source = engine.notebooks.createCell({ type: cell.type === "image" ? "image" : "video", label: "Historical post source", outputArtifactId: cell.outputArtifactId, slot: nextHorizontalSlotFrom(slotRight(cell.slot), notebookOccupiedSlots(cells)) });
|
|
287
|
+
cells.push(source);
|
|
288
|
+
}
|
|
289
|
+
if (source)
|
|
290
|
+
parents.set(cell.id, source);
|
|
291
|
+
}
|
|
292
|
+
const edges = notebook.edges.map(edge => ids.has(edge.source) ? { ...edge, source: parents.get(edge.source).id } : edge);
|
|
293
|
+
for (const cell of legacy) {
|
|
294
|
+
const source = parents.get(cell.id);
|
|
295
|
+
if (source && !edges.some(edge => edge.target === cell.id))
|
|
296
|
+
edges.push(engine.notebooks.createEdge({ source: source.id, target: cell.id, targetInput: "post" }));
|
|
297
|
+
}
|
|
298
|
+
return { ...notebook, edges, cells: cells.map(cell => {
|
|
299
|
+
if (!ids.has(cell.id))
|
|
300
|
+
return cell;
|
|
301
|
+
const source = parents.get(cell.id);
|
|
302
|
+
const definition = {
|
|
303
|
+
version: 1, platform: "x", account: "", caption: "", credits: "", notes: "Historical media version is unknown.",
|
|
304
|
+
createdAt: String(cell.inputs?.postedAt ?? new Date().toISOString()),
|
|
305
|
+
source: { cellId: source?.id ?? String(cell.inputs?.sourceCellId ?? ""), artifactId: cell.outputArtifactId ?? "", revision: null, objectHash: null, filename: null, snapshotArtifactId: null },
|
|
306
|
+
lineage: source ? captureLineage({ ...notebook, cells }, source) : [],
|
|
307
|
+
};
|
|
308
|
+
return { ...cell, type: "post", inputs: { post: definition } };
|
|
309
|
+
}) };
|
|
310
|
+
}
|
|
311
|
+
export async function migrateLegacyPosts(engine) {
|
|
312
|
+
for (const item of engine.notebooks.list()) {
|
|
313
|
+
const read = engine.notebooks.read(item.id);
|
|
314
|
+
if (!read.ok)
|
|
315
|
+
continue;
|
|
316
|
+
const legacy = read.value.cells.filter((cell) => cell.inputs?.sourceOperation === "post_to_x" && !cell.inputs.post);
|
|
317
|
+
if (!legacy.length)
|
|
318
|
+
continue;
|
|
319
|
+
for (const cell of legacy) {
|
|
320
|
+
const inputs = cell.inputs ?? {};
|
|
321
|
+
if (readPostRuntime(engine, cell.id).url)
|
|
322
|
+
continue;
|
|
323
|
+
patchPostRuntime(engine, cell.id, {
|
|
324
|
+
status: inputs.status === "partially_posted" ? "partially_posted" : "posted",
|
|
325
|
+
url: typeof inputs.tweetUrl === "string" ? inputs.tweetUrl : undefined,
|
|
326
|
+
postedAt: typeof inputs.postedAt === "string" ? inputs.postedAt : undefined,
|
|
327
|
+
x: legacyXReceipt(inputs),
|
|
328
|
+
nextMetricAt: new Date().toISOString(),
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
const result = await mutateNotebook(engine, item.id, "migrate_post_cells", (notebook) => migrateLegacyNotebook(engine, notebook));
|
|
332
|
+
if (!result.ok)
|
|
333
|
+
throw new Error(result.error.message);
|
|
334
|
+
for (const cell of legacy) {
|
|
335
|
+
const post = getPost(engine, cell.id);
|
|
336
|
+
if (!post.runtime.publication)
|
|
337
|
+
patchPostRuntime(engine, cell.id, { publication: post.definition });
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { createPost, getPost, updatePost } from "./post-store.js";
|
|
2
|
+
export async function ensureXPost(engine, assetId, text, postCellId) {
|
|
3
|
+
if (postCellId) {
|
|
4
|
+
let post = getPost(engine, postCellId);
|
|
5
|
+
if (post.definition.platform !== "x")
|
|
6
|
+
throw new Error("This Post targets another platform");
|
|
7
|
+
if (!post.definition.source?.snapshotArtifactId)
|
|
8
|
+
throw new Error("Choose a finished source clip first");
|
|
9
|
+
if (assetId && ![post.definition.source.artifactId, post.definition.source.snapshotArtifactId, post.runtime.outbound?.artifactId].includes(assetId))
|
|
10
|
+
throw new Error("Asset does not match the Post source");
|
|
11
|
+
if (text !== undefined && text !== post.definition.caption && text !== post.runtime.xRequest?.text)
|
|
12
|
+
post = await updatePost(engine, postCellId, { caption: text });
|
|
13
|
+
return post;
|
|
14
|
+
}
|
|
15
|
+
if (!assetId)
|
|
16
|
+
throw new Error("Provide asset_id or post_cell_id");
|
|
17
|
+
let source;
|
|
18
|
+
let notebookId;
|
|
19
|
+
for (const item of engine.notebooks.list()) {
|
|
20
|
+
const read = engine.notebooks.read(item.id);
|
|
21
|
+
if (!read.ok)
|
|
22
|
+
continue;
|
|
23
|
+
source = read.value.cells.find((cell) => cell.outputArtifactId === assetId && cell.type !== "post" && cell.inputs?.sourceOperation !== "post_to_x");
|
|
24
|
+
if (source) {
|
|
25
|
+
notebookId = item.id;
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (!source || !notebookId) {
|
|
30
|
+
const artifact = engine.artifacts.get(assetId);
|
|
31
|
+
if (!artifact.ok || !["image", "video", "final"].includes(artifact.value.kind))
|
|
32
|
+
throw new Error("Post source must be an image or video");
|
|
33
|
+
const created = await engine.notebooks.create("Publications");
|
|
34
|
+
if (!created.ok)
|
|
35
|
+
throw new Error(created.error.message);
|
|
36
|
+
notebookId = created.value.id;
|
|
37
|
+
source = engine.notebooks.createCell({ type: artifact.value.kind === "image" ? "image" : "video", slot: { row: 0, column: 0 }, outputArtifactId: assetId, label: artifact.value.label });
|
|
38
|
+
const written = await engine.notebooks.write({ ...created.value, cells: [source], edges: [] }, { operation: "create_post_source" });
|
|
39
|
+
if (!written.ok)
|
|
40
|
+
throw new Error(written.error.message);
|
|
41
|
+
}
|
|
42
|
+
return createPost(engine, { notebookId, sourceCellId: source.id, platform: "x", caption: text });
|
|
43
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { isUuidV7 } from "videobook-engine";
|
|
2
|
+
import { isNotebookGridAddress, normalizeNotebookReference } from "videobook-engine/notebook-mentions";
|
|
3
|
+
export const PREVIEW_PLACEHOLDER_HOST = "url.resolved.later.for";
|
|
4
|
+
const PREVIEW_PLACEHOLDER_ORIGIN = `http://${PREVIEW_PLACEHOLDER_HOST}`;
|
|
5
|
+
const URL_FIELD_KEYS = new Set([
|
|
6
|
+
"image_urls",
|
|
7
|
+
"image_url",
|
|
8
|
+
"reference_image",
|
|
9
|
+
"reference_image_urls",
|
|
10
|
+
"reference_images",
|
|
11
|
+
"reference_video_urls",
|
|
12
|
+
"reference_audio_urls",
|
|
13
|
+
"video_url",
|
|
14
|
+
"video_urls",
|
|
15
|
+
"first_frame_url",
|
|
16
|
+
"last_frame_url",
|
|
17
|
+
"first_frame",
|
|
18
|
+
"last_frame",
|
|
19
|
+
"audio_url",
|
|
20
|
+
"audio_urls",
|
|
21
|
+
"character_image_urls",
|
|
22
|
+
"motion_video_urls",
|
|
23
|
+
"driving_audio_url",
|
|
24
|
+
"start_image",
|
|
25
|
+
"end_image",
|
|
26
|
+
]);
|
|
27
|
+
export function previewPlaceholderUrl(reference) {
|
|
28
|
+
const bare = normalizeNotebookReference(reference);
|
|
29
|
+
return `${PREVIEW_PLACEHOLDER_ORIGIN}/${encodeURIComponent(bare)}`;
|
|
30
|
+
}
|
|
31
|
+
export function isPreviewPlaceholderUrl(value) {
|
|
32
|
+
try {
|
|
33
|
+
return new URL(value).host === PREVIEW_PLACEHOLDER_HOST;
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function isUrlishKey(key) {
|
|
40
|
+
if (URL_FIELD_KEYS.has(key))
|
|
41
|
+
return true;
|
|
42
|
+
if (key.endsWith("_url") || key.endsWith("_urls"))
|
|
43
|
+
return true;
|
|
44
|
+
return key === "image"
|
|
45
|
+
|| key === "images"
|
|
46
|
+
|| key === "video"
|
|
47
|
+
|| key === "videos"
|
|
48
|
+
|| key === "audio"
|
|
49
|
+
|| key === "audios";
|
|
50
|
+
}
|
|
51
|
+
// Pre-dispatch payloads reference media as artifact ids (the internal
|
|
52
|
+
// token) or, when still unresolved, grid addresses. Either form becomes a
|
|
53
|
+
// placeholder URL in previews; real URLs are substituted only at dispatch.
|
|
54
|
+
function localReference(value) {
|
|
55
|
+
if (/^https?:\/\//iu.test(value))
|
|
56
|
+
return null;
|
|
57
|
+
const reference = normalizeNotebookReference(value);
|
|
58
|
+
return isUuidV7(reference) || isNotebookGridAddress(reference)
|
|
59
|
+
? reference
|
|
60
|
+
: null;
|
|
61
|
+
}
|
|
62
|
+
function transformValue(value, key, path, placeholders) {
|
|
63
|
+
if (typeof value === "string") {
|
|
64
|
+
if (!isUrlishKey(key))
|
|
65
|
+
return value;
|
|
66
|
+
const reference = localReference(value);
|
|
67
|
+
if (!reference)
|
|
68
|
+
return value;
|
|
69
|
+
const url = previewPlaceholderUrl(reference);
|
|
70
|
+
placeholders.push({ path, reference: `@${reference}`, url });
|
|
71
|
+
return url;
|
|
72
|
+
}
|
|
73
|
+
if (Array.isArray(value)) {
|
|
74
|
+
return value.map((item, index) => transformValue(item, key, `${path}[${index}]`, placeholders));
|
|
75
|
+
}
|
|
76
|
+
if (!value || typeof value !== "object")
|
|
77
|
+
return value;
|
|
78
|
+
return Object.fromEntries(Object.entries(value).map(([childKey, childValue]) => [
|
|
79
|
+
childKey,
|
|
80
|
+
transformValue(childValue, childKey, path ? `${path}.${childKey}` : childKey, placeholders),
|
|
81
|
+
]));
|
|
82
|
+
}
|
|
83
|
+
export function applyPreviewPlaceholderUrls(payload) {
|
|
84
|
+
const placeholders = [];
|
|
85
|
+
const transformed = transformValue(payload, "", "", placeholders);
|
|
86
|
+
return {
|
|
87
|
+
payload: transformed,
|
|
88
|
+
placeholders,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const PROMPT_SURFACES = [
|
|
2
|
+
"chat",
|
|
3
|
+
"image_rewrite",
|
|
4
|
+
"video_rewrite",
|
|
5
|
+
"script_rewrite",
|
|
6
|
+
"post",
|
|
7
|
+
"image_generate",
|
|
8
|
+
"video_generate",
|
|
9
|
+
"audio_generate",
|
|
10
|
+
];
|
|
11
|
+
export function isPromptSurface(value) {
|
|
12
|
+
return (typeof value === "string" &&
|
|
13
|
+
PROMPT_SURFACES.includes(value));
|
|
14
|
+
}
|
|
15
|
+
const GENERATION_PROMPT_SURFACES = {
|
|
16
|
+
generate_image: "image_generate",
|
|
17
|
+
generate_video: "video_generate",
|
|
18
|
+
generate_audio: "audio_generate",
|
|
19
|
+
};
|
|
20
|
+
export function generationPromptSurface(cellType) {
|
|
21
|
+
return GENERATION_PROMPT_SURFACES[cellType] ?? null;
|
|
22
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { scanNotebookMentions } from "videobook-engine/notebook-mentions";
|
|
2
|
+
import { PREVIEW_PLACEHOLDER_HOST } from "./preview-payload.js";
|
|
3
|
+
// An @<artifact-id> token is the internal reference currency between the
|
|
4
|
+
// grid resolver and the per-tool dialect rewrite; one surviving into a
|
|
5
|
+
// provider payload means the rewrite missed it.
|
|
6
|
+
const ARTIFACT_ID_MENTION_PATTERN = /@[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}(?![\w-])/giu;
|
|
7
|
+
export class ProviderMentionError extends Error {
|
|
8
|
+
tokens;
|
|
9
|
+
toolName;
|
|
10
|
+
constructor(toolName, tokens) {
|
|
11
|
+
super(`Unresolved provider mention${tokens.length === 1 ? "" : "s"} `
|
|
12
|
+
+ `${tokens.join(", ")} in ${toolName}`);
|
|
13
|
+
this.name = "ProviderMentionError";
|
|
14
|
+
this.tokens = tokens;
|
|
15
|
+
this.toolName = toolName;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
class ProviderPlaceholderError extends Error {
|
|
19
|
+
paths;
|
|
20
|
+
toolName;
|
|
21
|
+
constructor(toolName, paths) {
|
|
22
|
+
super(`Preview placeholder URL reached wire payload for ${toolName}: ${paths.join(", ")}`);
|
|
23
|
+
this.name = "ProviderPlaceholderError";
|
|
24
|
+
this.paths = paths;
|
|
25
|
+
this.toolName = toolName;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export function unresolvedProviderMentions(prompt) {
|
|
29
|
+
const leaks = [
|
|
30
|
+
...scanNotebookMentions(prompt).map((mention) => mention.raw),
|
|
31
|
+
...[...prompt.matchAll(ARTIFACT_ID_MENTION_PATTERN)].map((match) => match[0]),
|
|
32
|
+
];
|
|
33
|
+
return [...new Set(leaks)];
|
|
34
|
+
}
|
|
35
|
+
export function assertNoUnresolvedMentions(prompt, options) {
|
|
36
|
+
const tokens = unresolvedProviderMentions(prompt);
|
|
37
|
+
if (tokens.length > 0)
|
|
38
|
+
throw new ProviderMentionError(options.toolName, tokens);
|
|
39
|
+
}
|
|
40
|
+
function visitProviderPayload(value, path, key, leaks, placeholders) {
|
|
41
|
+
if (typeof value === "string") {
|
|
42
|
+
if (value.includes(PREVIEW_PLACEHOLDER_HOST))
|
|
43
|
+
placeholders.push(path);
|
|
44
|
+
if (key.toLowerCase().includes("prompt") || key === "text") {
|
|
45
|
+
for (const token of unresolvedProviderMentions(value)) {
|
|
46
|
+
leaks.push({ path, token });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (Array.isArray(value)) {
|
|
52
|
+
value.forEach((item, index) => {
|
|
53
|
+
visitProviderPayload(item, `${path}[${index}]`, key, leaks, placeholders);
|
|
54
|
+
});
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (!value || typeof value !== "object")
|
|
58
|
+
return;
|
|
59
|
+
for (const [childKey, childValue] of Object.entries(value)) {
|
|
60
|
+
visitProviderPayload(childValue, path ? `${path}.${childKey}` : childKey, childKey, leaks, placeholders);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function inspectProviderPayload(value) {
|
|
64
|
+
const promptLeaks = [];
|
|
65
|
+
const placeholderPaths = [];
|
|
66
|
+
visitProviderPayload(value, "", "", promptLeaks, placeholderPaths);
|
|
67
|
+
return { promptLeaks, placeholderPaths };
|
|
68
|
+
}
|
|
69
|
+
export function assertProviderPayload(value, options) {
|
|
70
|
+
const inspection = inspectProviderPayload(value);
|
|
71
|
+
const tokens = [...new Set(inspection.promptLeaks.map((leak) => leak.token))];
|
|
72
|
+
if (tokens.length > 0)
|
|
73
|
+
throw new ProviderMentionError(options.toolName, tokens);
|
|
74
|
+
if (options.wire && inspection.placeholderPaths.length > 0) {
|
|
75
|
+
throw new ProviderPlaceholderError(options.toolName, inspection.placeholderPaths);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function providerStatusUrl(provider, model) {
|
|
2
|
+
if (provider === "kie")
|
|
3
|
+
return "https://kie.ai/logs";
|
|
4
|
+
if (provider !== "fal" || !model || !/^[a-z0-9_-]+(?:\/[a-z0-9_.-]+)+$/iu.test(model))
|
|
5
|
+
return null;
|
|
6
|
+
if (model.split("/").some((part) => part === "." || part === ".."))
|
|
7
|
+
return null;
|
|
8
|
+
return `https://fal.ai/models/${model.split("/").map(encodeURIComponent).join("/")}/requests`;
|
|
9
|
+
}
|
|
10
|
+
export function parseProviderTaskStatus(value) {
|
|
11
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
12
|
+
return null;
|
|
13
|
+
const record = value;
|
|
14
|
+
if (record.provider !== "kie" && record.provider !== "fal")
|
|
15
|
+
return null;
|
|
16
|
+
return {
|
|
17
|
+
provider: record.provider,
|
|
18
|
+
model: typeof record.model === "string" ? record.model : null,
|
|
19
|
+
taskId: typeof record.taskId === "string" ? record.taskId : null,
|
|
20
|
+
};
|
|
21
|
+
}
|