@hraness/slopcamera 3.2.4
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/LICENSE +21 -0
- package/NOTICE.md +60 -0
- package/PRIVACY.md +46 -0
- package/README.md +311 -0
- package/SECURITY.md +21 -0
- package/apps/desktop/README.md +966 -0
- package/apps/desktop/analysis/protocol.ts +223 -0
- package/apps/desktop/application/capability-binding.ts +594 -0
- package/apps/desktop/application/context.ts +97 -0
- package/apps/desktop/application/creative-iteration.ts +1385 -0
- package/apps/desktop/application/default-registry.ts +146 -0
- package/apps/desktop/application/directing-contract.ts +218 -0
- package/apps/desktop/application/directing-plan.ts +178 -0
- package/apps/desktop/application/errors.ts +18 -0
- package/apps/desktop/application/gateway-port.ts +867 -0
- package/apps/desktop/application/html-overlay-browser-runtime.ts +687 -0
- package/apps/desktop/application/html-overlay-integrity.ts +337 -0
- package/apps/desktop/application/html-overlay-renderer.ts +45 -0
- package/apps/desktop/application/index.ts +13 -0
- package/apps/desktop/application/operation-completion-checkpoint.ts +333 -0
- package/apps/desktop/application/operation-effects.ts +94 -0
- package/apps/desktop/application/operation.ts +224 -0
- package/apps/desktop/application/operations/analysis/faces.ts +244 -0
- package/apps/desktop/application/operations/analysis/music.ts +248 -0
- package/apps/desktop/application/operations/analysis/project-auto-zooms.ts +302 -0
- package/apps/desktop/application/operations/analysis/project-inactivity.ts +381 -0
- package/apps/desktop/application/operations/analysis/scenes.ts +272 -0
- package/apps/desktop/application/operations/analysis/shared.ts +202 -0
- package/apps/desktop/application/operations/derive/edit-batch.ts +967 -0
- package/apps/desktop/application/operations/derive/follow-faces.ts +454 -0
- package/apps/desktop/application/operations/edit/bind-candidate-revision.ts +91 -0
- package/apps/desktop/application/operations/edit/create-candidate-revision.ts +377 -0
- package/apps/desktop/application/operations/edit/create-revision.ts +255 -0
- package/apps/desktop/application/operations/edit/freeze-revision.ts +182 -0
- package/apps/desktop/application/operations/gateway/image.ts +41 -0
- package/apps/desktop/application/operations/gateway/index.ts +17 -0
- package/apps/desktop/application/operations/gateway/shared.ts +122 -0
- package/apps/desktop/application/operations/gateway/speech.ts +41 -0
- package/apps/desktop/application/operations/gateway/transcription.ts +42 -0
- package/apps/desktop/application/operations/gateway/video.ts +41 -0
- package/apps/desktop/application/operations/index.ts +31 -0
- package/apps/desktop/application/operations/iteration/create-candidate.ts +240 -0
- package/apps/desktop/application/operations/iteration/create-matrix.ts +117 -0
- package/apps/desktop/application/operations/iteration/select.ts +155 -0
- package/apps/desktop/application/operations/iteration/shared.ts +250 -0
- package/apps/desktop/application/operations/media/audio-effects.ts +376 -0
- package/apps/desktop/application/operations/media/binding.ts +79 -0
- package/apps/desktop/application/operations/media/capabilities.ts +112 -0
- package/apps/desktop/application/operations/media/color-grade.ts +343 -0
- package/apps/desktop/application/operations/media/html-overlay.ts +802 -0
- package/apps/desktop/application/operations/media/index.ts +8 -0
- package/apps/desktop/application/operations/media/ingest.ts +303 -0
- package/apps/desktop/application/operations/media/overlay.ts +729 -0
- package/apps/desktop/application/operations/media/shared.ts +845 -0
- package/apps/desktop/application/operations/media/transform-platform.ts +120 -0
- package/apps/desktop/application/operations/project/commit-edits.ts +2054 -0
- package/apps/desktop/application/operations/project/promote-selection.ts +495 -0
- package/apps/desktop/application/operations/project/snapshot.ts +95 -0
- package/apps/desktop/application/operations/recording/index.ts +5 -0
- package/apps/desktop/application/operations/recording/pause.ts +34 -0
- package/apps/desktop/application/operations/recording/resume.ts +34 -0
- package/apps/desktop/application/operations/recording/shared.ts +199 -0
- package/apps/desktop/application/operations/recording/start.ts +169 -0
- package/apps/desktop/application/operations/recording/stop.ts +34 -0
- package/apps/desktop/application/operations/render/bind-candidate-output.ts +324 -0
- package/apps/desktop/application/operations/render/materialize-selection.ts +322 -0
- package/apps/desktop/application/operations/render/project-caption-plan.ts +287 -0
- package/apps/desktop/application/operations/render/project-plan.ts +939 -0
- package/apps/desktop/application/operations/render/project-spatial-receipt.ts +70 -0
- package/apps/desktop/application/operations/render/project.ts +1922 -0
- package/apps/desktop/application/operations/shared.ts +67 -0
- package/apps/desktop/application/operations/slopcamera-portable.ts +1188 -0
- package/apps/desktop/application/operations/slopcamera-visuals.ts +784 -0
- package/apps/desktop/application/operations/spatial-project.ts +209 -0
- package/apps/desktop/application/operations/spatial-render.ts +415 -0
- package/apps/desktop/application/operations/spatial-scene.ts +97 -0
- package/apps/desktop/application/operations/studio.ts +143 -0
- package/apps/desktop/application/output-publication-lease.ts +163 -0
- package/apps/desktop/application/project-publication-lease.ts +233 -0
- package/apps/desktop/application/project-store.ts +196 -0
- package/apps/desktop/application/receipts.ts +533 -0
- package/apps/desktop/application/registry.ts +182 -0
- package/apps/desktop/application/spatial-assets.ts +675 -0
- package/apps/desktop/application/spatial-compositor-cadence.ts +23 -0
- package/apps/desktop/application/spatial-project-authority.ts +35 -0
- package/apps/desktop/application/spatial-project-lease.ts +126 -0
- package/apps/desktop/application/spatial-project-render-preparation.ts +225 -0
- package/apps/desktop/application/spatial-project-store.ts +286 -0
- package/apps/desktop/application/spatial-render-projection.ts +201 -0
- package/apps/desktop/application/spatial-render.ts +677 -0
- package/apps/desktop/application/spatial-spz.ts +44 -0
- package/apps/desktop/application/spatial-world-import.ts +135 -0
- package/apps/desktop/application/spatial-world-provenance.ts +46 -0
- package/apps/desktop/application/studio-port.ts +58 -0
- package/apps/desktop/application/verified-receipt-reconciliation.ts +1574 -0
- package/apps/desktop/capture/protocol.ts +689 -0
- package/apps/desktop/cli/analyzer.ts +406 -0
- package/apps/desktop/cli/args.ts +3181 -0
- package/apps/desktop/cli/asset-ingest.ts +747 -0
- package/apps/desktop/cli/atomic-render-effects.ts +210 -0
- package/apps/desktop/cli/atomic-render-platform.ts +205 -0
- package/apps/desktop/cli/atomic-render.ts +50 -0
- package/apps/desktop/cli/audio-analysis.ts +252 -0
- package/apps/desktop/cli/bounded-map.ts +39 -0
- package/apps/desktop/cli/bundle-service.ts +256 -0
- package/apps/desktop/cli/capabilities.ts +212 -0
- package/apps/desktop/cli/capture-bundle.ts +1117 -0
- package/apps/desktop/cli/command-host-resources.ts +251 -0
- package/apps/desktop/cli/commands.ts +6836 -0
- package/apps/desktop/cli/directing-blob.ts +291 -0
- package/apps/desktop/cli/directing-media.ts +517 -0
- package/apps/desktop/cli/directing-quote.ts +89 -0
- package/apps/desktop/cli/directing-service.ts +288 -0
- package/apps/desktop/cli/emoji-assets.ts +538 -0
- package/apps/desktop/cli/errors.ts +29 -0
- package/apps/desktop/cli/face-analysis-service.ts +940 -0
- package/apps/desktop/cli/filter-script.ts +76 -0
- package/apps/desktop/cli/gateway-application-port.ts +1142 -0
- package/apps/desktop/cli/gateway-credential.ts +81 -0
- package/apps/desktop/cli/gateway-media-artifacts.ts +723 -0
- package/apps/desktop/cli/gateway-media-catalog.ts +922 -0
- package/apps/desktop/cli/gateway-media-service.ts +3106 -0
- package/apps/desktop/cli/gateway-media-signature.ts +122 -0
- package/apps/desktop/cli/gateway-provider-options.ts +562 -0
- package/apps/desktop/cli/gateway-scene-provider.ts +391 -0
- package/apps/desktop/cli/help.ts +501 -0
- package/apps/desktop/cli/html-overlay-gpu.ts +97 -0
- package/apps/desktop/cli/html-overlay-renderer.ts +2478 -0
- package/apps/desktop/cli/io.ts +290 -0
- package/apps/desktop/cli/main.ts +123 -0
- package/apps/desktop/cli/media-effects-service.ts +885 -0
- package/apps/desktop/cli/media-ingest-model.ts +364 -0
- package/apps/desktop/cli/media-ingest-platform.ts +227 -0
- package/apps/desktop/cli/media-ingest-program.ts +200 -0
- package/apps/desktop/cli/media-ingest.ts +30 -0
- package/apps/desktop/cli/music-analysis-service.ts +434 -0
- package/apps/desktop/cli/mutation-lock.ts +530 -0
- package/apps/desktop/cli/native-assets.d.ts +12 -0
- package/apps/desktop/cli/native-media-runtime.macos.ts +7 -0
- package/apps/desktop/cli/overlay-playback.ts +177 -0
- package/apps/desktop/cli/paths.ts +265 -0
- package/apps/desktop/cli/portable-surface.ts +242 -0
- package/apps/desktop/cli/project-camera-receipt.ts +112 -0
- package/apps/desktop/cli/project-face-camera.ts +394 -0
- package/apps/desktop/cli/project-inactivity-service.ts +552 -0
- package/apps/desktop/cli/project-media-integrity.ts +121 -0
- package/apps/desktop/cli/project-renderer.ts +1526 -0
- package/apps/desktop/cli/project-service.ts +519 -0
- package/apps/desktop/cli/project-state-transaction.ts +520 -0
- package/apps/desktop/cli/query-limits.ts +1 -0
- package/apps/desktop/cli/recording-controller.ts +1567 -0
- package/apps/desktop/cli/recording-daemon.ts +931 -0
- package/apps/desktop/cli/recording-ref.ts +56 -0
- package/apps/desktop/cli/renamed-environment.ts +6 -0
- package/apps/desktop/cli/renderer.ts +1439 -0
- package/apps/desktop/cli/run-cli-test-helper.ts +60 -0
- package/apps/desktop/cli/scene-analysis-service.ts +879 -0
- package/apps/desktop/cli/spatial-compositor-verifier.ts +213 -0
- package/apps/desktop/cli/spatial-project-service.ts +90 -0
- package/apps/desktop/cli/spatial-scene-service.ts +105 -0
- package/apps/desktop/cli/spatial-world-service.ts +39 -0
- package/apps/desktop/cli/speech-analysis-service.ts +682 -0
- package/apps/desktop/cli/studio-assemble.ts +58 -0
- package/apps/desktop/cli/studio-command.ts +60 -0
- package/apps/desktop/cli/studio-custody.ts +60 -0
- package/apps/desktop/cli/studio-encode.ts +216 -0
- package/apps/desktop/cli/studio-exr.ts +52 -0
- package/apps/desktop/cli/studio-files.ts +179 -0
- package/apps/desktop/cli/studio-output-validation.ts +162 -0
- package/apps/desktop/cli/studio-process.ts +145 -0
- package/apps/desktop/cli/studio-runtime.ts +77 -0
- package/apps/desktop/cli/studio-scaffold.ts +70 -0
- package/apps/desktop/cli/studio-service.ts +301 -0
- package/apps/desktop/cli/studio-spatial-asset.ts +248 -0
- package/apps/desktop/cli/studio-template-names.ts +3 -0
- package/apps/desktop/cli/studio-workflow.ts +19 -0
- package/apps/desktop/cli/time.ts +25 -0
- package/apps/desktop/cli/workflow-code.ts +584 -0
- package/apps/desktop/cli/workflow-runs.ts +576 -0
- package/apps/desktop/code/advanced.ts +139 -0
- package/apps/desktop/code/application-node-planner.ts +1288 -0
- package/apps/desktop/code/compiler.ts +329 -0
- package/apps/desktop/code/contracts.ts +13 -0
- package/apps/desktop/code/creative-recipes.ts +94 -0
- package/apps/desktop/code/define-workflow.ts +115 -0
- package/apps/desktop/code/editing.ts +284 -0
- package/apps/desktop/code/file-candidate-provenance.ts +420 -0
- package/apps/desktop/code/file-candidate.ts +69 -0
- package/apps/desktop/code/graph-builder.ts +10 -0
- package/apps/desktop/code/host-resource-policy.ts +55 -0
- package/apps/desktop/code/plan-contracts.ts +159 -0
- package/apps/desktop/code/planning.ts +361 -0
- package/apps/desktop/code/public.ts +134 -0
- package/apps/desktop/code/reference-projection.ts +62 -0
- package/apps/desktop/code/run-contracts.ts +466 -0
- package/apps/desktop/code/run-store.ts +2478 -0
- package/apps/desktop/code/runtime-identity.ts +328 -0
- package/apps/desktop/code/scheduler.ts +3067 -0
- package/apps/desktop/code/semantic-builder.ts +1436 -0
- package/apps/desktop/code/source-bundle.ts +1641 -0
- package/apps/desktop/code/source-typecheck.ts +211 -0
- package/apps/desktop/code/testing.ts +79 -0
- package/apps/desktop/code/worker-client.ts +1675 -0
- package/apps/desktop/code/worker-effects.ts +141 -0
- package/apps/desktop/code/worker-entry.ts +435 -0
- package/apps/desktop/code/worker-lease-guardian.ts +179 -0
- package/apps/desktop/code/worker-process-identity.ts +484 -0
- package/apps/desktop/code/worker-protocol.ts +211 -0
- package/apps/desktop/code/workflow-effects.ts +132 -0
- package/apps/desktop/contracts/analysis.ts +949 -0
- package/apps/desktop/contracts/edit.ts +576 -0
- package/apps/desktop/contracts/index.ts +9 -0
- package/apps/desktop/contracts/media-effects.ts +139 -0
- package/apps/desktop/contracts/project-render.ts +644 -0
- package/apps/desktop/contracts/project.ts +740 -0
- package/apps/desktop/contracts/recording.ts +1434 -0
- package/apps/desktop/contracts/render.ts +334 -0
- package/apps/desktop/contracts/runtime.ts +155 -0
- package/apps/desktop/contracts/spatial-compositor.ts +58 -0
- package/apps/desktop/contracts/spatial-project.ts +159 -0
- package/apps/desktop/contracts/spatial-world.ts +48 -0
- package/apps/desktop/contracts/video-effects.ts +120 -0
- package/apps/desktop/core/alignment-apply.ts +400 -0
- package/apps/desktop/core/audio-alignment.ts +466 -0
- package/apps/desktop/core/auto-zoom.ts +209 -0
- package/apps/desktop/core/canonical-json.ts +14 -0
- package/apps/desktop/core/emoji.ts +210 -0
- package/apps/desktop/core/events.ts +409 -0
- package/apps/desktop/core/face-framing.ts +533 -0
- package/apps/desktop/core/face-tracks.ts +326 -0
- package/apps/desktop/core/inactivity.ts +137 -0
- package/apps/desktop/core/index.ts +30 -0
- package/apps/desktop/core/intervals.ts +123 -0
- package/apps/desktop/core/montage.ts +236 -0
- package/apps/desktop/core/music-analysis.ts +518 -0
- package/apps/desktop/core/plan.ts +244 -0
- package/apps/desktop/core/project-auto-zoom.ts +75 -0
- package/apps/desktop/core/project-camera-spatial.ts +676 -0
- package/apps/desktop/core/project-camera.ts +403 -0
- package/apps/desktop/core/project-captions.ts +888 -0
- package/apps/desktop/core/project-inactivity.ts +125 -0
- package/apps/desktop/core/project-metadata.ts +927 -0
- package/apps/desktop/core/project-plan.ts +375 -0
- package/apps/desktop/core/project-render-plan.ts +303 -0
- package/apps/desktop/core/project-time.ts +469 -0
- package/apps/desktop/core/render-plan.ts +442 -0
- package/apps/desktop/core/scene-cache.ts +174 -0
- package/apps/desktop/core/scene-sampling.ts +448 -0
- package/apps/desktop/core/spatial-compositor.ts +22 -0
- package/apps/desktop/core/spatial-durability.ts +99 -0
- package/apps/desktop/core/spatial-project.ts +169 -0
- package/apps/desktop/core/spatial-shot-clock.ts +32 -0
- package/apps/desktop/core/speech-analysis.ts +333 -0
- package/apps/desktop/core/storage.ts +970 -0
- package/apps/desktop/core/time-map.ts +91 -0
- package/apps/desktop/core/time.ts +61 -0
- package/apps/desktop/core/video-effects.ts +660 -0
- package/apps/desktop/dist/cli/NebulaSans-Bold-26se8aek.otf +0 -0
- package/apps/desktop/dist/cli/NebulaSans-Bold-bcz7y08t.woff2 +0 -0
- package/apps/desktop/dist/cli/NebulaSans-Book-5ax05zvn.woff2 +0 -0
- package/apps/desktop/dist/cli/NebulaSans-Book-8cenzchw.otf +0 -0
- package/apps/desktop/dist/cli/main.js +5086 -0
- package/apps/desktop/html-overlay/catalog.ts +171 -0
- package/apps/desktop/html-overlay/contracts.ts +372 -0
- package/apps/desktop/html-overlay/execution-profile.ts +77 -0
- package/apps/desktop/html-overlay/index.ts +98 -0
- package/apps/desktop/html-overlay/libraries.ts +263 -0
- package/apps/desktop/html-overlay/random.ts +49 -0
- package/apps/desktop/html-overlay/runtime.ts +813 -0
- package/apps/desktop/html-overlay/scaffolds.ts +792 -0
- package/apps/desktop/html-overlay/spark-runtime.ts +88 -0
- package/apps/desktop/html-overlay/spatial-splat-runtime.ts +60 -0
- package/apps/desktop/html-overlay/spatial.ts +720 -0
- package/apps/desktop/studio/assets/README.md +39 -0
- package/apps/desktop/studio/assets/contracts.ts +129 -0
- package/apps/desktop/studio/assets/network.ts +64 -0
- package/apps/desktop/studio/assets/poly-haven.ts +148 -0
- package/apps/desktop/studio/drivers/blender_driver.py +466 -0
- package/apps/desktop/studio/drivers/cadquery_driver.py +176 -0
- package/apps/desktop/studio/education/driver.py +308 -0
- package/apps/desktop/studio/education/lesson.py +180 -0
- package/apps/desktop/studio/education/toolkit.py +249 -0
- package/apps/desktop/workflows/chaptered-demo.ts +48 -0
- package/apps/desktop/workflows/creative-iteration.ts +107 -0
- package/apps/desktop/workflows/creative-selection.ts +92 -0
- package/apps/desktop/workflows/fragments.ts +486 -0
- package/apps/desktop/workflows/index.ts +93 -0
- package/apps/desktop/workflows/polished-screen-demo.ts +128 -0
- package/apps/desktop/workflows/scene-direction.ts +12 -0
- package/apps/desktop/workflows/social-variants.ts +75 -0
- package/apps/desktop/workflows/talking-head-cleanup.ts +53 -0
- package/dist/NebulaSans-Bold-26se8aek.otf +0 -0
- package/dist/NebulaSans-Bold-bcz7y08t.woff2 +0 -0
- package/dist/NebulaSans-Book-5ax05zvn.woff2 +0 -0
- package/dist/NebulaSans-Book-8cenzchw.otf +0 -0
- package/dist/cli.js +479 -0
- package/dist/code/advanced.js +270 -0
- package/dist/code/index.js +2240 -0
- package/dist/generate.js +24 -0
- package/dist/host-resources.js +26 -0
- package/dist/index-42zsesc1.js +2257 -0
- package/dist/index-7308egqr.js +48 -0
- package/dist/index-8txs6fkn.js +712 -0
- package/dist/index-mcy8z0br.js +192 -0
- package/dist/index-p63wavx0.js +2029 -0
- package/dist/index-r7gdhmsp.js +424 -0
- package/dist/index-sh6xbav6.js +1219 -0
- package/dist/index-z1w83f81.js +4 -0
- package/dist/index-z7239b4h.js +2363 -0
- package/dist/index-zfeg9hmw.js +2013 -0
- package/dist/index.js +241 -0
- package/dist/operations.js +29 -0
- package/dist/skill-install-dh0ntqba.js +10 -0
- package/dist/vectorize/worker.js +142 -0
- package/dist/workflow.js +16 -0
- package/docs/README.md +38 -0
- package/docs/studio.md +215 -0
- package/examples/studio/blender/README.md +26 -0
- package/examples/studio/blender/character.py +152 -0
- package/examples/studio/blender/cloth.py +101 -0
- package/examples/studio/blender/color_chart.py +29 -0
- package/examples/studio/blender/fluid.py +78 -0
- package/examples/studio/blender/import_model.py +74 -0
- package/examples/studio/blender/product.py +53 -0
- package/examples/studio/blender/shaded_street.py +481 -0
- package/examples/studio/blender/studio_scene.py +147 -0
- package/examples/studio/cadquery/README.md +13 -0
- package/examples/studio/cadquery/bracket.py +36 -0
- package/examples/studio/cadquery/import_step.py +12 -0
- package/examples/studio/education/README.md +46 -0
- package/examples/studio/education/lesson.json +99 -0
- package/examples/studio/education/scene.py +7 -0
- package/examples/studio/hybrid-scene.ts +96 -0
- package/examples/studio/native-workflow.ts +14 -0
- package/examples/studio/vgpu/README.md +64 -0
- package/examples/studio/vgpu/heat-field.mjs +290 -0
- package/package.json +304 -0
- package/packages/scene/src/index.ts +1 -0
- package/packages/scene/src/scene.ts +215 -0
- package/schema/diagram.schema.json +334 -0
- package/skills/slopcamera/SKILL.md +45 -0
- package/skills/slopcamera/agents/openai.yaml +4 -0
- package/skills/slopcamera/references/customization.md +77 -0
- package/skills/slopcamera/references/diagrams.md +153 -0
- package/skills/slopcamera/references/directed-scenes.md +42 -0
- package/skills/slopcamera/references/directing-video.md +18 -0
- package/skills/slopcamera/references/educational-video.md +26 -0
- package/skills/slopcamera/references/gateway-media.md +134 -0
- package/skills/slopcamera/references/install.md +62 -0
- package/skills/slopcamera/references/native-studio.md +31 -0
- package/skills/slopcamera/references/reference-led-3d.md +140 -0
- package/skills/slopcamera/references/rubber-stamp-examples/poster-example-1.jpg +0 -0
- package/skills/slopcamera/references/rubber-stamp-examples/poster-example-2.jpg +0 -0
- package/skills/slopcamera/references/rubber-stamp-examples/stamp-style-1.png +0 -0
- package/skills/slopcamera/references/rubber-stamp-examples/stamp-style-2.png +0 -0
- package/skills/slopcamera/references/rubber-stamp-field-notes.md +144 -0
- package/skills/slopcamera/references/vectorization.md +29 -0
- package/skills/slopcamera/references/video-projects.md +187 -0
- package/skills/slopcamera/references/visual-communication.md +120 -0
- package/skills/slopcamera/references/workflows-sdk.md +45 -0
- package/skills/slopcamera/scripts/compose-rubber-stamp-field-note.ts +229 -0
- package/src/artifacts.ts +106 -0
- package/src/assets/fonts/nebula-sans/LICENSE.txt +96 -0
- package/src/assets/fonts/nebula-sans/NebulaSans-Bold.otf +0 -0
- package/src/assets/fonts/nebula-sans/NebulaSans-Bold.woff2 +0 -0
- package/src/assets/fonts/nebula-sans/NebulaSans-Book.otf +0 -0
- package/src/assets/fonts/nebula-sans/NebulaSans-Book.woff2 +0 -0
- package/src/assets/fonts/nebula-sans/PROVENANCE.md +21 -0
- package/src/assets.d.ts +9 -0
- package/src/cli.ts +601 -0
- package/src/cloud-errors.ts +26 -0
- package/src/code/advanced.ts +10 -0
- package/src/code/boundary.ts +21 -0
- package/src/code/canonical-json.ts +129 -0
- package/src/code/compiler.ts +953 -0
- package/src/code/contracts.ts +576 -0
- package/src/code/define-workflow.ts +334 -0
- package/src/code/errors.ts +41 -0
- package/src/code/graph-builder.ts +945 -0
- package/src/code/index.ts +121 -0
- package/src/code/json-snapshot.ts +899 -0
- package/src/code/json-utf8.ts +88 -0
- package/src/code/portable-builder.ts +136 -0
- package/src/code/projection.ts +378 -0
- package/src/code/public-operations.ts +450 -0
- package/src/code/runtime.ts +580 -0
- package/src/code/sha256.ts +36 -0
- package/src/config.ts +180 -0
- package/src/desktop.ts +253 -0
- package/src/fs.ts +10 -0
- package/src/generate.ts +704 -0
- package/src/host-resource-posix.ts +161 -0
- package/src/host-resources.ts +1749 -0
- package/src/icons.ts +58 -0
- package/src/index.ts +200 -0
- package/src/label-layout.ts +130 -0
- package/src/layout.ts +413 -0
- package/src/lint.ts +127 -0
- package/src/mcp/boundary.ts +354 -0
- package/src/mcp/index.ts +33 -0
- package/src/mcp/server.ts +272 -0
- package/src/mcp/tools.ts +963 -0
- package/src/mcp/types.ts +71 -0
- package/src/operations.ts +775 -0
- package/src/parse.ts +861 -0
- package/src/process-environment.ts +17 -0
- package/src/render.ts +437 -0
- package/src/skill-install.ts +49 -0
- package/src/spatial-scene/authoring.ts +27 -0
- package/src/spatial-scene/camera-track.ts +83 -0
- package/src/spatial-scene/contracts.ts +277 -0
- package/src/spatial-scene/evaluate.ts +145 -0
- package/src/spatial-scene/gltf.ts +558 -0
- package/src/spatial-scene/identity.ts +260 -0
- package/src/spatial-scene/index.ts +10 -0
- package/src/spatial-scene/inspect.ts +68 -0
- package/src/spatial-scene/math.ts +280 -0
- package/src/spatial-scene/patch.ts +131 -0
- package/src/spatial-scene/time.ts +53 -0
- package/src/studio/contracts.ts +129 -0
- package/src/studio/index.ts +2 -0
- package/src/studio/plan.ts +156 -0
- package/src/studio/shared.ts +41 -0
- package/src/theme.ts +68 -0
- package/src/tldr.ts +474 -0
- package/src/types.ts +212 -0
- package/src/vectorize/archive.ts +101 -0
- package/src/vectorize/command.ts +797 -0
- package/src/vectorize/index.ts +27 -0
- package/src/vectorize/limits.ts +61 -0
- package/src/vectorize/metrics.ts +351 -0
- package/src/vectorize/pixels.ts +323 -0
- package/src/vectorize/supervisor.ts +339 -0
- package/src/vectorize/svg.ts +188 -0
- package/src/vectorize/tool.ts +542 -0
- package/src/vectorize/types.ts +113 -0
- package/src/vectorize/vectorize.ts +634 -0
- package/src/vectorize/worker-protocol.ts +40 -0
- package/src/vectorize/worker.ts +209 -0
- package/src/version.ts +1 -0
- package/src/workflow.ts +409 -0
|
@@ -0,0 +1,939 @@
|
|
|
1
|
+
import {
|
|
2
|
+
lstat,
|
|
3
|
+
realpath,
|
|
4
|
+
} from "node:fs/promises";
|
|
5
|
+
import {
|
|
6
|
+
isAbsolute,
|
|
7
|
+
join,
|
|
8
|
+
relative,
|
|
9
|
+
sep,
|
|
10
|
+
} from "node:path";
|
|
11
|
+
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
ProjectAssetIdSchema,
|
|
16
|
+
ProjectCaptionRequestSchema,
|
|
17
|
+
ProjectPlacementIdSchema,
|
|
18
|
+
ProjectStreamIdSchema,
|
|
19
|
+
RecordingIdSchema,
|
|
20
|
+
RepositoryRelativePathSchema,
|
|
21
|
+
Sha256Schema,
|
|
22
|
+
SpeechAnalysisV1Schema,
|
|
23
|
+
type ProjectCaptionRequest,
|
|
24
|
+
type ProjectRenderPlanV1,
|
|
25
|
+
type SpeechAnalysisV1,
|
|
26
|
+
type VideoProjectV1,
|
|
27
|
+
} from "../../../contracts";
|
|
28
|
+
import {
|
|
29
|
+
assertProjectRenderPlanComposition,
|
|
30
|
+
canonicalJson,
|
|
31
|
+
canonicalJsonSha256,
|
|
32
|
+
compileProjectCaptionCues,
|
|
33
|
+
compileProjectRenderPlan,
|
|
34
|
+
createNodeBundleFileSystem,
|
|
35
|
+
hashPlacementSync,
|
|
36
|
+
hashProjectStructure,
|
|
37
|
+
sha256Hex,
|
|
38
|
+
type BundleFileSystem,
|
|
39
|
+
type ProjectMetadataContext,
|
|
40
|
+
type ProjectRenderSettings,
|
|
41
|
+
} from "../../../core";
|
|
42
|
+
import {
|
|
43
|
+
loadRecordingEvents,
|
|
44
|
+
openRecording,
|
|
45
|
+
} from "../../../cli/bundle-service";
|
|
46
|
+
import type { ApplicationContext } from "../../context";
|
|
47
|
+
import { ApplicationError } from "../../errors";
|
|
48
|
+
import type { OperationDefinition } from "../../operation";
|
|
49
|
+
import {
|
|
50
|
+
ProjectEditRevisionDocumentSchema,
|
|
51
|
+
ProjectEditRevisionRenderInputSchema,
|
|
52
|
+
ProjectRenderPlanDocumentSchema,
|
|
53
|
+
ProjectRenderPlanReferenceSchema,
|
|
54
|
+
RenderableProjectEditRevisionReferenceSchema,
|
|
55
|
+
type ProjectEditRevisionDocument,
|
|
56
|
+
type ProjectEditRevisionRenderInput,
|
|
57
|
+
type ProjectRenderPlanDocument,
|
|
58
|
+
type ProjectRenderPlanReference,
|
|
59
|
+
type RenderableProjectEditRevisionReference,
|
|
60
|
+
} from "../../receipts";
|
|
61
|
+
import { throwIfAborted } from "../shared";
|
|
62
|
+
import {
|
|
63
|
+
prepareProjectCaptionPlan,
|
|
64
|
+
type PreparedProjectCaptionArtifact,
|
|
65
|
+
type PreparedProjectCaptionPlan,
|
|
66
|
+
} from "./project-caption-plan";
|
|
67
|
+
|
|
68
|
+
const MAXIMUM_PROJECT_RENDER_PLAN_BYTES = 256 * 1024 * 1024;
|
|
69
|
+
|
|
70
|
+
export const ProjectRenderSettingsSchema = z.strictObject({
|
|
71
|
+
background: z.string().regex(/^#[0-9a-fA-F]{8}$/u).optional(),
|
|
72
|
+
frameRate: z.number().finite().positive().max(240).optional(),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
export const ProjectRenderSettingsV2Schema = ProjectRenderSettingsSchema.extend({
|
|
76
|
+
captions: ProjectCaptionRequestSchema,
|
|
77
|
+
}).strict();
|
|
78
|
+
|
|
79
|
+
export const ProjectCaptionBindingSchema = z.strictObject({
|
|
80
|
+
analysisId: ProjectCaptionRequestSchema.shape.analysisId,
|
|
81
|
+
analysisPath: RepositoryRelativePathSchema,
|
|
82
|
+
analysisSha256: Sha256Schema,
|
|
83
|
+
assetId: ProjectAssetIdSchema,
|
|
84
|
+
placementId: ProjectPlacementIdSchema,
|
|
85
|
+
placementSyncSha256: Sha256Schema,
|
|
86
|
+
streamId: ProjectStreamIdSchema,
|
|
87
|
+
subjectIntegritySha256: Sha256Schema,
|
|
88
|
+
wordCount: z.number().int().safe().positive().max(1_000_000),
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
export const ProjectRenderMetadataBindingSchema = z.strictObject({
|
|
92
|
+
eventsSha256: Sha256Schema,
|
|
93
|
+
manifestSha256: Sha256Schema,
|
|
94
|
+
placementId: ProjectPlacementIdSchema,
|
|
95
|
+
recordingId: RecordingIdSchema,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
export const ProjectRenderPlanInputSchema = z.strictObject({
|
|
99
|
+
metadataBindings: z.array(ProjectRenderMetadataBindingSchema).max(256).optional(),
|
|
100
|
+
revision: ProjectEditRevisionRenderInputSchema,
|
|
101
|
+
settings: ProjectRenderSettingsSchema.optional(),
|
|
102
|
+
}).superRefine((input, context) => {
|
|
103
|
+
if (input.metadataBindings === undefined) return;
|
|
104
|
+
for (let index = 0; index < input.metadataBindings.length; index += 1) {
|
|
105
|
+
const current = input.metadataBindings[index]!;
|
|
106
|
+
const previous = input.metadataBindings[index - 1];
|
|
107
|
+
if (
|
|
108
|
+
previous !== undefined
|
|
109
|
+
&& previous.placementId.localeCompare(current.placementId) >= 0
|
|
110
|
+
) {
|
|
111
|
+
context.addIssue({
|
|
112
|
+
code: "custom",
|
|
113
|
+
message: "Render metadata bindings must have unique sorted placement IDs.",
|
|
114
|
+
path: ["metadataBindings", index, "placementId"],
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
export const ProjectRenderPlanInputV2Schema = z.strictObject({
|
|
121
|
+
captionBinding: ProjectCaptionBindingSchema.optional(),
|
|
122
|
+
metadataBindings: z.array(ProjectRenderMetadataBindingSchema).max(256).optional(),
|
|
123
|
+
revision: ProjectEditRevisionRenderInputSchema,
|
|
124
|
+
settings: ProjectRenderSettingsV2Schema,
|
|
125
|
+
}).superRefine((input, context) => {
|
|
126
|
+
if (input.metadataBindings === undefined) return;
|
|
127
|
+
for (let index = 0; index < input.metadataBindings.length; index += 1) {
|
|
128
|
+
const current = input.metadataBindings[index]!;
|
|
129
|
+
const previous = input.metadataBindings[index - 1];
|
|
130
|
+
if (
|
|
131
|
+
previous !== undefined
|
|
132
|
+
&& previous.placementId.localeCompare(current.placementId) >= 0
|
|
133
|
+
) {
|
|
134
|
+
context.addIssue({
|
|
135
|
+
code: "custom",
|
|
136
|
+
message: "Render metadata bindings must have unique sorted placement IDs.",
|
|
137
|
+
path: ["metadataBindings", index, "placementId"],
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
export const ProjectRenderPlanOutputSchema = ProjectRenderPlanReferenceSchema;
|
|
144
|
+
|
|
145
|
+
export type ProjectRenderPlanInput = z.infer<
|
|
146
|
+
typeof ProjectRenderPlanInputSchema
|
|
147
|
+
>;
|
|
148
|
+
export type ProjectRenderPlanInputV2 = z.infer<
|
|
149
|
+
typeof ProjectRenderPlanInputV2Schema
|
|
150
|
+
>;
|
|
151
|
+
export type ProjectRenderPlanOutput = ProjectRenderPlanReference;
|
|
152
|
+
|
|
153
|
+
export function hashProjectCaptionSource(input: {
|
|
154
|
+
readonly binding: z.infer<typeof ProjectCaptionBindingSchema>;
|
|
155
|
+
readonly output: ProjectRenderPlanV1["output"];
|
|
156
|
+
readonly projectEditPlanSha256: string;
|
|
157
|
+
readonly revisionSha256: string;
|
|
158
|
+
readonly style: ProjectCaptionRequest["style"];
|
|
159
|
+
}): string {
|
|
160
|
+
return canonicalJsonSha256({
|
|
161
|
+
...input,
|
|
162
|
+
domain: "slopcamera.social-caption-source/v1",
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function isWithin(root: string, candidate: string): boolean {
|
|
167
|
+
const pathFromRoot = relative(root, candidate);
|
|
168
|
+
return pathFromRoot === "" || (
|
|
169
|
+
pathFromRoot !== ".."
|
|
170
|
+
&& !pathFromRoot.startsWith(`..${sep}`)
|
|
171
|
+
&& !isAbsolute(pathFromRoot)
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export async function exactProjectDirectory(
|
|
176
|
+
application: ApplicationContext,
|
|
177
|
+
projectId: string,
|
|
178
|
+
): Promise<string> {
|
|
179
|
+
let root: string;
|
|
180
|
+
try {
|
|
181
|
+
root = await realpath(application.paths.projectRoot);
|
|
182
|
+
} catch {
|
|
183
|
+
throw new ApplicationError(
|
|
184
|
+
"not-found",
|
|
185
|
+
"The project root does not exist.",
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
const requested = join(root, projectId);
|
|
189
|
+
let details;
|
|
190
|
+
try {
|
|
191
|
+
details = await lstat(requested);
|
|
192
|
+
} catch {
|
|
193
|
+
throw new ApplicationError("not-found", `Project does not exist: ${projectId}`);
|
|
194
|
+
}
|
|
195
|
+
if (details.isSymbolicLink() || !details.isDirectory()) {
|
|
196
|
+
throw new ApplicationError(
|
|
197
|
+
"unsafe-path",
|
|
198
|
+
`Project must be a physical directory: ${projectId}`,
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
const directory = await realpath(requested);
|
|
202
|
+
if (!isWithin(root, directory) || directory === root) {
|
|
203
|
+
throw new ApplicationError(
|
|
204
|
+
"unsafe-path",
|
|
205
|
+
`Project directory escapes the configured project root: ${projectId}`,
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
return directory;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export function renderableProjectEditRevisionReference(
|
|
212
|
+
input: ProjectEditRevisionRenderInput,
|
|
213
|
+
): RenderableProjectEditRevisionReference {
|
|
214
|
+
return RenderableProjectEditRevisionReferenceSchema.parse({
|
|
215
|
+
artifact: input.artifact,
|
|
216
|
+
baseGeneration: input.baseGeneration,
|
|
217
|
+
kind: input.kind,
|
|
218
|
+
outputGeometrySha256: input.outputGeometrySha256,
|
|
219
|
+
pixelHeight: input.pixelHeight,
|
|
220
|
+
pixelWidth: input.pixelWidth,
|
|
221
|
+
planId: input.planId,
|
|
222
|
+
projectEditPlanSha256: input.projectEditPlanSha256,
|
|
223
|
+
projectId: input.projectId,
|
|
224
|
+
projectSha256: input.projectSha256,
|
|
225
|
+
projectStructureSha256: input.projectStructureSha256,
|
|
226
|
+
revisionSha256: input.revisionSha256,
|
|
227
|
+
schemaVersion: input.schemaVersion,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function parseJson(text: string, label: string): unknown {
|
|
232
|
+
try {
|
|
233
|
+
return JSON.parse(text) as unknown;
|
|
234
|
+
} catch {
|
|
235
|
+
throw new ApplicationError("invalid-data", `${label} is not valid JSON.`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function utf8Bytes(text: string): number {
|
|
240
|
+
return new TextEncoder().encode(text).byteLength;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export async function loadExactProjectEditRevision(
|
|
244
|
+
application: ApplicationContext,
|
|
245
|
+
input: ProjectEditRevisionRenderInput,
|
|
246
|
+
): Promise<{
|
|
247
|
+
readonly directory: string;
|
|
248
|
+
readonly document: ProjectEditRevisionDocument;
|
|
249
|
+
readonly fileSystem: BundleFileSystem;
|
|
250
|
+
readonly reference: RenderableProjectEditRevisionReference;
|
|
251
|
+
}> {
|
|
252
|
+
const reference = renderableProjectEditRevisionReference(input);
|
|
253
|
+
const directory = await exactProjectDirectory(
|
|
254
|
+
application,
|
|
255
|
+
reference.projectId,
|
|
256
|
+
);
|
|
257
|
+
const fileSystem = createNodeBundleFileSystem(directory);
|
|
258
|
+
const text = await fileSystem.readText(reference.artifact.path);
|
|
259
|
+
if (
|
|
260
|
+
utf8Bytes(text) !== reference.artifact.bytes
|
|
261
|
+
|| sha256Hex(text) !== reference.artifact.sha256
|
|
262
|
+
) {
|
|
263
|
+
throw new ApplicationError(
|
|
264
|
+
"conflict",
|
|
265
|
+
"Immutable project edit revision artifact failed its byte and SHA-256 identity.",
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
const document = ProjectEditRevisionDocumentSchema.parse(
|
|
269
|
+
parseJson(text, "Project edit revision artifact"),
|
|
270
|
+
);
|
|
271
|
+
if (text !== `${canonicalJson(document)}\n`) {
|
|
272
|
+
throw new ApplicationError(
|
|
273
|
+
"conflict",
|
|
274
|
+
"Immutable project edit revision artifact is not its canonical document.",
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
if (
|
|
278
|
+
document.project.projectId !== reference.projectId
|
|
279
|
+
|| document.projectEditPlan.planId !== reference.planId
|
|
280
|
+
|| document.projectSha256 !== reference.projectSha256
|
|
281
|
+
|| document.projectEditPlanSha256 !== reference.projectEditPlanSha256
|
|
282
|
+
|| document.revisionSha256 !== reference.revisionSha256
|
|
283
|
+
|| hashProjectStructure(document.project)
|
|
284
|
+
!== reference.projectStructureSha256
|
|
285
|
+
) {
|
|
286
|
+
throw new ApplicationError(
|
|
287
|
+
"conflict",
|
|
288
|
+
"Immutable project edit revision reference does not match its frozen documents.",
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
return { directory, document, fileSystem, reference };
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function renderSettings(
|
|
295
|
+
input: z.infer<typeof ProjectRenderSettingsSchema> | undefined,
|
|
296
|
+
reference: RenderableProjectEditRevisionReference,
|
|
297
|
+
metadata: readonly ProjectMetadataContext[],
|
|
298
|
+
): ProjectRenderSettings {
|
|
299
|
+
return {
|
|
300
|
+
...(input?.background === undefined ? {} : { background: input.background }),
|
|
301
|
+
...(input?.frameRate === undefined ? {} : { frameRate: input.frameRate }),
|
|
302
|
+
metadata,
|
|
303
|
+
pixelHeight: reference.pixelHeight,
|
|
304
|
+
pixelWidth: reference.pixelWidth,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
async function projectMetadata(
|
|
309
|
+
artifactRoot: string,
|
|
310
|
+
project: VideoProjectV1,
|
|
311
|
+
plan: ProjectEditRevisionDocument["projectEditPlan"],
|
|
312
|
+
): Promise<{
|
|
313
|
+
readonly bindings: readonly z.infer<typeof ProjectRenderMetadataBindingSchema>[];
|
|
314
|
+
readonly contexts: readonly ProjectMetadataContext[];
|
|
315
|
+
}> {
|
|
316
|
+
const placementIds = new Set<string>(
|
|
317
|
+
plan.zooms.map(zoom => zoom.placementId),
|
|
318
|
+
);
|
|
319
|
+
if (
|
|
320
|
+
(
|
|
321
|
+
plan.effects.clicks.enabled
|
|
322
|
+
|| plan.effects.cursor.enabled
|
|
323
|
+
|| plan.effects.keystrokes.enabled
|
|
324
|
+
|| plan.effects.typedText.enabled
|
|
325
|
+
)
|
|
326
|
+
&& plan.effects.metadataPlacementId !== null
|
|
327
|
+
) {
|
|
328
|
+
placementIds.add(plan.effects.metadataPlacementId);
|
|
329
|
+
}
|
|
330
|
+
const loaded = await Promise.all([...placementIds].sort().map(async placementId => {
|
|
331
|
+
const placement = project.placements.find(
|
|
332
|
+
candidate => candidate.placementId === placementId,
|
|
333
|
+
);
|
|
334
|
+
if (placement === undefined) {
|
|
335
|
+
throw new ApplicationError(
|
|
336
|
+
"not-found",
|
|
337
|
+
`Unknown project metadata placement: ${placementId}`,
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
const asset = project.assets.find(
|
|
341
|
+
candidate => candidate.assetId === placement.assetId,
|
|
342
|
+
);
|
|
343
|
+
if (asset?.source.kind !== "recording") {
|
|
344
|
+
throw new ApplicationError(
|
|
345
|
+
"conflict",
|
|
346
|
+
`Placement ${placement.placementId} is not backed by a Slopcamera recording with window and input metadata.`,
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
const recording = await openRecording(artifactRoot, asset.source.recordingId);
|
|
350
|
+
const events = await loadRecordingEvents(recording);
|
|
351
|
+
return {
|
|
352
|
+
binding: ProjectRenderMetadataBindingSchema.parse({
|
|
353
|
+
eventsSha256: canonicalJsonSha256(events),
|
|
354
|
+
manifestSha256: canonicalJsonSha256(recording.manifest),
|
|
355
|
+
placementId: placement.placementId,
|
|
356
|
+
recordingId: asset.source.recordingId,
|
|
357
|
+
}),
|
|
358
|
+
context: {
|
|
359
|
+
events,
|
|
360
|
+
manifest: recording.manifest,
|
|
361
|
+
placementId: placement.placementId,
|
|
362
|
+
},
|
|
363
|
+
};
|
|
364
|
+
}));
|
|
365
|
+
return {
|
|
366
|
+
bindings: loaded.map(item => item.binding),
|
|
367
|
+
contexts: loaded.map(item => item.context),
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
interface LoadedProjectCaptions {
|
|
372
|
+
readonly analysis: SpeechAnalysisV1;
|
|
373
|
+
readonly binding: z.infer<typeof ProjectCaptionBindingSchema>;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
async function projectCaptions(
|
|
377
|
+
revision: Awaited<ReturnType<typeof loadExactProjectEditRevision>>,
|
|
378
|
+
requestInput: ProjectCaptionRequest,
|
|
379
|
+
): Promise<LoadedProjectCaptions> {
|
|
380
|
+
const request = ProjectCaptionRequestSchema.parse(requestInput);
|
|
381
|
+
const project = revision.document.project;
|
|
382
|
+
const reference = project.analyses.find(candidate => (
|
|
383
|
+
candidate.kind === "speech"
|
|
384
|
+
&& candidate.analysisId === request.analysisId
|
|
385
|
+
));
|
|
386
|
+
if (reference === undefined || reference.kind !== "speech") {
|
|
387
|
+
throw new ApplicationError(
|
|
388
|
+
"not-found",
|
|
389
|
+
`Unknown project speech analysis: ${request.analysisId}`,
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
const placement = project.placements.find(candidate => (
|
|
393
|
+
candidate.placementId === request.placementId
|
|
394
|
+
));
|
|
395
|
+
if (placement === undefined) {
|
|
396
|
+
throw new ApplicationError(
|
|
397
|
+
"not-found",
|
|
398
|
+
`Unknown caption placement: ${request.placementId}`,
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
if (!placement.enabled || placement.assetId !== reference.assetId) {
|
|
402
|
+
throw new ApplicationError(
|
|
403
|
+
"conflict",
|
|
404
|
+
"Caption placement must be enabled and contain the analyzed audio asset.",
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
const configuredAudio = placement.audio.find(candidate => (
|
|
408
|
+
candidate.streamId === reference.streamId
|
|
409
|
+
));
|
|
410
|
+
if (configuredAudio?.presentation.enabled !== true) {
|
|
411
|
+
throw new ApplicationError(
|
|
412
|
+
"conflict",
|
|
413
|
+
"Caption source audio must be enabled and audible on the selected placement.",
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
const asset = project.assets.find(candidate => (
|
|
417
|
+
candidate.assetId === reference.assetId
|
|
418
|
+
));
|
|
419
|
+
const stream = asset?.streams.find(candidate => (
|
|
420
|
+
candidate.streamId === reference.streamId
|
|
421
|
+
));
|
|
422
|
+
if (asset === undefined || stream?.kind !== "audio") {
|
|
423
|
+
throw new ApplicationError(
|
|
424
|
+
"conflict",
|
|
425
|
+
"Caption speech analysis no longer identifies a project audio stream.",
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
let text: string;
|
|
430
|
+
let analysis: SpeechAnalysisV1;
|
|
431
|
+
try {
|
|
432
|
+
text = await revision.fileSystem.readText(reference.path);
|
|
433
|
+
analysis = SpeechAnalysisV1Schema.parse(
|
|
434
|
+
parseJson(text, "Speech analysis artifact"),
|
|
435
|
+
);
|
|
436
|
+
} catch (error) {
|
|
437
|
+
if (error instanceof ApplicationError) throw error;
|
|
438
|
+
throw new ApplicationError(
|
|
439
|
+
"invalid-data",
|
|
440
|
+
`Speech analysis ${reference.analysisId} is unreadable or invalid.`,
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
if (
|
|
444
|
+
text !== `${canonicalJson(analysis)}\n`
|
|
445
|
+
|| sha256Hex(text) !== reference.sha256
|
|
446
|
+
|| analysis.analysisId !== reference.analysisId
|
|
447
|
+
|| analysis.subject.assetId !== reference.assetId
|
|
448
|
+
|| analysis.subject.streamId !== reference.streamId
|
|
449
|
+
|| analysis.durationUs !== asset.durationUs
|
|
450
|
+
) {
|
|
451
|
+
throw new ApplicationError(
|
|
452
|
+
"conflict",
|
|
453
|
+
`Speech analysis ${reference.analysisId} is stale or failed its exact identity check.`,
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
const subjectIntegritySha256 = canonicalJsonSha256({
|
|
457
|
+
assetDurationUs: asset.durationUs,
|
|
458
|
+
stream,
|
|
459
|
+
});
|
|
460
|
+
if (analysis.subject.integritySha256 !== subjectIntegritySha256) {
|
|
461
|
+
throw new ApplicationError(
|
|
462
|
+
"conflict",
|
|
463
|
+
`Speech analysis ${reference.analysisId} no longer matches its audio stream.`,
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
if (
|
|
467
|
+
analysis.result.status !== "transcribed"
|
|
468
|
+
|| analysis.result.words.length === 0
|
|
469
|
+
) {
|
|
470
|
+
throw new ApplicationError(
|
|
471
|
+
"unsupported-plan",
|
|
472
|
+
"Caption export requires a speech analysis with timed words.",
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
if (
|
|
476
|
+
reference.wordCount !== analysis.result.words.length
|
|
477
|
+
|| reference.fillerCount !== analysis.result.fillers.length
|
|
478
|
+
) {
|
|
479
|
+
throw new ApplicationError(
|
|
480
|
+
"invalid-data",
|
|
481
|
+
`Speech analysis reference ${reference.analysisId} disagrees with its artifact counts.`,
|
|
482
|
+
);
|
|
483
|
+
}
|
|
484
|
+
return {
|
|
485
|
+
analysis,
|
|
486
|
+
binding: ProjectCaptionBindingSchema.parse({
|
|
487
|
+
analysisId: reference.analysisId,
|
|
488
|
+
analysisPath: reference.path,
|
|
489
|
+
analysisSha256: reference.sha256,
|
|
490
|
+
assetId: reference.assetId,
|
|
491
|
+
placementId: placement.placementId,
|
|
492
|
+
placementSyncSha256: hashPlacementSync(placement),
|
|
493
|
+
streamId: reference.streamId,
|
|
494
|
+
subjectIntegritySha256,
|
|
495
|
+
wordCount: analysis.result.words.length,
|
|
496
|
+
}),
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
export async function bindProjectRenderPlanInput(
|
|
501
|
+
application: ApplicationContext,
|
|
502
|
+
input: unknown,
|
|
503
|
+
): Promise<ProjectRenderPlanInput> {
|
|
504
|
+
const parsed = ProjectRenderPlanInputSchema.parse(input);
|
|
505
|
+
const revision = await loadExactProjectEditRevision(
|
|
506
|
+
application,
|
|
507
|
+
parsed.revision,
|
|
508
|
+
);
|
|
509
|
+
const metadata = await projectMetadata(
|
|
510
|
+
application.paths.artifactRoot,
|
|
511
|
+
revision.document.project,
|
|
512
|
+
revision.document.projectEditPlan,
|
|
513
|
+
);
|
|
514
|
+
return ProjectRenderPlanInputSchema.parse({
|
|
515
|
+
...parsed,
|
|
516
|
+
metadataBindings: metadata.bindings,
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
export async function bindProjectRenderPlanInputV2(
|
|
521
|
+
application: ApplicationContext,
|
|
522
|
+
input: unknown,
|
|
523
|
+
): Promise<ProjectRenderPlanInputV2> {
|
|
524
|
+
const parsed = ProjectRenderPlanInputV2Schema.parse(input);
|
|
525
|
+
const revision = await loadExactProjectEditRevision(
|
|
526
|
+
application,
|
|
527
|
+
parsed.revision,
|
|
528
|
+
);
|
|
529
|
+
const [metadata, captions] = await Promise.all([
|
|
530
|
+
projectMetadata(
|
|
531
|
+
application.paths.artifactRoot,
|
|
532
|
+
revision.document.project,
|
|
533
|
+
revision.document.projectEditPlan,
|
|
534
|
+
),
|
|
535
|
+
projectCaptions(revision, parsed.settings.captions),
|
|
536
|
+
]);
|
|
537
|
+
return ProjectRenderPlanInputV2Schema.parse({
|
|
538
|
+
...parsed,
|
|
539
|
+
captionBinding: captions.binding,
|
|
540
|
+
metadataBindings: metadata.bindings,
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function projectRenderPlanDocument(
|
|
545
|
+
plan: ProjectRenderPlanV1,
|
|
546
|
+
revision: ProjectEditRevisionDocument,
|
|
547
|
+
reference: RenderableProjectEditRevisionReference,
|
|
548
|
+
): ProjectRenderPlanDocument {
|
|
549
|
+
return ProjectRenderPlanDocumentSchema.parse({
|
|
550
|
+
kind: "slopcamera.project-render-plan-document",
|
|
551
|
+
outputGeometrySha256: reference.outputGeometrySha256,
|
|
552
|
+
plan: assertProjectRenderPlanComposition(plan),
|
|
553
|
+
projectEditPlanSha256: revision.projectEditPlanSha256,
|
|
554
|
+
projectSha256: revision.projectSha256,
|
|
555
|
+
renderPlanSha256: canonicalJsonSha256(plan),
|
|
556
|
+
revisionSha256: revision.revisionSha256,
|
|
557
|
+
schemaVersion: 1,
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
export async function loadExactProjectRenderPlan(
|
|
562
|
+
application: ApplicationContext,
|
|
563
|
+
referenceInput: ProjectRenderPlanReference,
|
|
564
|
+
): Promise<{
|
|
565
|
+
readonly directory: string;
|
|
566
|
+
readonly document: ProjectRenderPlanDocument;
|
|
567
|
+
readonly fileSystem: BundleFileSystem;
|
|
568
|
+
readonly reference: ProjectRenderPlanReference;
|
|
569
|
+
}> {
|
|
570
|
+
const reference = ProjectRenderPlanReferenceSchema.parse(referenceInput);
|
|
571
|
+
const directory = await exactProjectDirectory(
|
|
572
|
+
application,
|
|
573
|
+
reference.projectId,
|
|
574
|
+
);
|
|
575
|
+
const fileSystem = createNodeBundleFileSystem(directory);
|
|
576
|
+
const text = await fileSystem.readText(reference.artifact.path);
|
|
577
|
+
if (
|
|
578
|
+
utf8Bytes(text) !== reference.artifact.bytes
|
|
579
|
+
|| sha256Hex(text) !== reference.artifact.sha256
|
|
580
|
+
) {
|
|
581
|
+
throw new ApplicationError(
|
|
582
|
+
"conflict",
|
|
583
|
+
"Immutable project render plan artifact failed its byte and SHA-256 identity.",
|
|
584
|
+
);
|
|
585
|
+
}
|
|
586
|
+
const document = ProjectRenderPlanDocumentSchema.parse(
|
|
587
|
+
parseJson(text, "Project render plan artifact"),
|
|
588
|
+
);
|
|
589
|
+
if (
|
|
590
|
+
text !== `${canonicalJson(document)}\n`
|
|
591
|
+
|| document.plan.projectId !== reference.projectId
|
|
592
|
+
|| document.plan.planSha256 !== reference.planSha256
|
|
593
|
+
|| document.outputGeometrySha256 !== reference.outputGeometrySha256
|
|
594
|
+
|| document.projectEditPlanSha256 !== reference.projectEditPlanSha256
|
|
595
|
+
|| document.projectSha256 !== reference.projectSha256
|
|
596
|
+
|| document.renderPlanSha256 !== reference.renderPlanSha256
|
|
597
|
+
|| document.revisionSha256 !== reference.revisionSha256
|
|
598
|
+
) {
|
|
599
|
+
throw new ApplicationError(
|
|
600
|
+
"conflict",
|
|
601
|
+
"Immutable project render plan reference does not match its exact document.",
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
assertProjectRenderPlanComposition(document.plan);
|
|
605
|
+
return { directory, document, fileSystem, reference };
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
async function publishProjectRenderPlan(
|
|
609
|
+
application: ApplicationContext,
|
|
610
|
+
beforePublication: (() => Promise<void>) | undefined,
|
|
611
|
+
revision: Awaited<ReturnType<typeof loadExactProjectEditRevision>>,
|
|
612
|
+
plan: ProjectRenderPlanV1,
|
|
613
|
+
): Promise<ProjectRenderPlanReference> {
|
|
614
|
+
const document = projectRenderPlanDocument(
|
|
615
|
+
plan,
|
|
616
|
+
revision.document,
|
|
617
|
+
revision.reference,
|
|
618
|
+
);
|
|
619
|
+
const contents = `${canonicalJson(document)}\n`;
|
|
620
|
+
const artifact = {
|
|
621
|
+
bytes: utf8Bytes(contents),
|
|
622
|
+
path: "",
|
|
623
|
+
sha256: sha256Hex(contents),
|
|
624
|
+
};
|
|
625
|
+
if (artifact.bytes > MAXIMUM_PROJECT_RENDER_PLAN_BYTES) {
|
|
626
|
+
throw new ApplicationError(
|
|
627
|
+
"unsupported-plan",
|
|
628
|
+
"Project render plan exceeds the 256 MiB structured-artifact limit.",
|
|
629
|
+
{ artifactBytes: artifact.bytes },
|
|
630
|
+
);
|
|
631
|
+
}
|
|
632
|
+
const reference = ProjectRenderPlanReferenceSchema.parse({
|
|
633
|
+
artifact: {
|
|
634
|
+
...artifact,
|
|
635
|
+
path: `renders/plans/${artifact.sha256}.json`,
|
|
636
|
+
},
|
|
637
|
+
kind: "slopcamera.project-render-plan-reference",
|
|
638
|
+
outputGeometrySha256: revision.reference.outputGeometrySha256,
|
|
639
|
+
planSha256: plan.planSha256,
|
|
640
|
+
projectEditPlanSha256: revision.document.projectEditPlanSha256,
|
|
641
|
+
projectId: revision.document.project.projectId,
|
|
642
|
+
projectSha256: revision.document.projectSha256,
|
|
643
|
+
renderPlanSha256: document.renderPlanSha256,
|
|
644
|
+
revisionSha256: revision.document.revisionSha256,
|
|
645
|
+
schemaVersion: 1,
|
|
646
|
+
});
|
|
647
|
+
if (revision.fileSystem.writeTextNoReplace === undefined) {
|
|
648
|
+
throw new ApplicationError(
|
|
649
|
+
"internal",
|
|
650
|
+
"Project storage does not support immutable render-plan publication.",
|
|
651
|
+
);
|
|
652
|
+
}
|
|
653
|
+
await beforePublication?.();
|
|
654
|
+
await revision.fileSystem.writeTextNoReplace(
|
|
655
|
+
reference.artifact.path,
|
|
656
|
+
contents,
|
|
657
|
+
);
|
|
658
|
+
const published = await loadExactProjectRenderPlan(
|
|
659
|
+
application,
|
|
660
|
+
reference,
|
|
661
|
+
);
|
|
662
|
+
if (canonicalJson(published.document) !== canonicalJson(document)) {
|
|
663
|
+
throw new ApplicationError(
|
|
664
|
+
"conflict",
|
|
665
|
+
"Published project render plan contains different bytes.",
|
|
666
|
+
);
|
|
667
|
+
}
|
|
668
|
+
return reference;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
async function publishProjectCaptionArtifacts(
|
|
672
|
+
revision: Awaited<ReturnType<typeof loadExactProjectEditRevision>>,
|
|
673
|
+
artifacts: readonly PreparedProjectCaptionArtifact[],
|
|
674
|
+
signal: AbortSignal,
|
|
675
|
+
): Promise<void> {
|
|
676
|
+
if (revision.fileSystem.writeTextNoReplace === undefined) {
|
|
677
|
+
throw new ApplicationError(
|
|
678
|
+
"internal",
|
|
679
|
+
"Project storage does not support immutable caption-asset publication.",
|
|
680
|
+
);
|
|
681
|
+
}
|
|
682
|
+
for (const artifact of artifacts) {
|
|
683
|
+
throwIfAborted(signal);
|
|
684
|
+
const disposition = await revision.fileSystem.writeTextNoReplace(
|
|
685
|
+
artifact.path,
|
|
686
|
+
artifact.contents,
|
|
687
|
+
);
|
|
688
|
+
const installed = await revision.fileSystem.readText(artifact.path);
|
|
689
|
+
if (
|
|
690
|
+
installed !== artifact.contents
|
|
691
|
+
|| sha256Hex(installed) !== artifact.sha256
|
|
692
|
+
) {
|
|
693
|
+
throw new ApplicationError(
|
|
694
|
+
"conflict",
|
|
695
|
+
disposition === "exists"
|
|
696
|
+
? `Caption asset path already contains different bytes: ${artifact.path}`
|
|
697
|
+
: `Published caption asset failed read-back verification: ${artifact.path}`,
|
|
698
|
+
);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
export const projectRenderPlanOperationDefinition = {
|
|
704
|
+
inputSchema: ProjectRenderPlanInputSchema,
|
|
705
|
+
inputSchemaId: "slopcamera.operation.render.project-plan.input/v1",
|
|
706
|
+
kind: "render.project-plan",
|
|
707
|
+
lifecycle: {
|
|
708
|
+
kind: "local-artifact",
|
|
709
|
+
execute: async (context, input) => {
|
|
710
|
+
throwIfAborted(context.abortSignal);
|
|
711
|
+
if (
|
|
712
|
+
context.workflow !== undefined
|
|
713
|
+
&& input.metadataBindings === undefined
|
|
714
|
+
) {
|
|
715
|
+
throw new ApplicationError(
|
|
716
|
+
"incompatible",
|
|
717
|
+
"Workflow render planning requires exact recording-metadata bindings.",
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
const revision = await loadExactProjectEditRevision(
|
|
721
|
+
context.application,
|
|
722
|
+
input.revision,
|
|
723
|
+
);
|
|
724
|
+
const metadata = await projectMetadata(
|
|
725
|
+
context.application.paths.artifactRoot,
|
|
726
|
+
revision.document.project,
|
|
727
|
+
revision.document.projectEditPlan,
|
|
728
|
+
);
|
|
729
|
+
if (
|
|
730
|
+
input.metadataBindings !== undefined
|
|
731
|
+
&& canonicalJson(input.metadataBindings) !== canonicalJson(metadata.bindings)
|
|
732
|
+
) {
|
|
733
|
+
throw new ApplicationError(
|
|
734
|
+
"conflict",
|
|
735
|
+
"Recording metadata changed after the render-plan node was bound.",
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
let plan;
|
|
739
|
+
try {
|
|
740
|
+
plan = compileProjectRenderPlan(
|
|
741
|
+
revision.document.project,
|
|
742
|
+
revision.document.projectEditPlan,
|
|
743
|
+
renderSettings(input.settings, revision.reference, metadata.contexts),
|
|
744
|
+
);
|
|
745
|
+
} catch (error) {
|
|
746
|
+
if (error instanceof TypeError) {
|
|
747
|
+
throw new ApplicationError("conflict", error.message);
|
|
748
|
+
}
|
|
749
|
+
throw error;
|
|
750
|
+
}
|
|
751
|
+
throwIfAborted(context.abortSignal);
|
|
752
|
+
const workflow = context.workflow;
|
|
753
|
+
return await publishProjectRenderPlan(
|
|
754
|
+
context.application,
|
|
755
|
+
workflow === undefined
|
|
756
|
+
? undefined
|
|
757
|
+
: () => workflow.beforePublication(),
|
|
758
|
+
revision,
|
|
759
|
+
plan,
|
|
760
|
+
);
|
|
761
|
+
},
|
|
762
|
+
},
|
|
763
|
+
outputSchema: ProjectRenderPlanOutputSchema,
|
|
764
|
+
outputSchemaId: "slopcamera.operation.render.project-plan.output/v1",
|
|
765
|
+
policy: {
|
|
766
|
+
cache: "content-addressed",
|
|
767
|
+
cancellable: true,
|
|
768
|
+
effect: "local-derived-write",
|
|
769
|
+
maxDurationMs: 2 * 60_000,
|
|
770
|
+
maxFanOut: 0,
|
|
771
|
+
maxInputBytes: 256 * 1_024,
|
|
772
|
+
maxOutputBytes: 8_192,
|
|
773
|
+
preparation: ["recording-metadata"],
|
|
774
|
+
resources: [
|
|
775
|
+
{ amount: 1, resource: "cpu" },
|
|
776
|
+
{ amount: 1, resource: "local-io" },
|
|
777
|
+
],
|
|
778
|
+
resume: "deterministic",
|
|
779
|
+
},
|
|
780
|
+
receiptReference: output => output.artifact.path,
|
|
781
|
+
summarize: output => ({
|
|
782
|
+
fields: {
|
|
783
|
+
planArtifactSha256: output.artifact.sha256,
|
|
784
|
+
planSha256: output.planSha256,
|
|
785
|
+
projectId: output.projectId,
|
|
786
|
+
revisionSha256: output.revisionSha256,
|
|
787
|
+
},
|
|
788
|
+
kind: "render.project-plan",
|
|
789
|
+
}),
|
|
790
|
+
version: 1,
|
|
791
|
+
} satisfies OperationDefinition<
|
|
792
|
+
"render.project-plan",
|
|
793
|
+
ProjectRenderPlanInput,
|
|
794
|
+
ProjectRenderPlanOutput
|
|
795
|
+
>;
|
|
796
|
+
|
|
797
|
+
export const projectRenderPlanOperationDefinitionV2 = {
|
|
798
|
+
inputSchema: ProjectRenderPlanInputV2Schema,
|
|
799
|
+
inputSchemaId: "slopcamera.operation.render.project-plan.input/v2",
|
|
800
|
+
kind: "render.project-plan",
|
|
801
|
+
lifecycle: {
|
|
802
|
+
kind: "local-artifact",
|
|
803
|
+
execute: async (context, input) => {
|
|
804
|
+
throwIfAborted(context.abortSignal);
|
|
805
|
+
if (
|
|
806
|
+
context.workflow !== undefined
|
|
807
|
+
&& (
|
|
808
|
+
input.captionBinding === undefined
|
|
809
|
+
|| input.metadataBindings === undefined
|
|
810
|
+
)
|
|
811
|
+
) {
|
|
812
|
+
throw new ApplicationError(
|
|
813
|
+
"incompatible",
|
|
814
|
+
"Workflow captioned render planning requires exact caption and recording-metadata bindings.",
|
|
815
|
+
);
|
|
816
|
+
}
|
|
817
|
+
const revision = await loadExactProjectEditRevision(
|
|
818
|
+
context.application,
|
|
819
|
+
input.revision,
|
|
820
|
+
);
|
|
821
|
+
const [metadata, captions] = await Promise.all([
|
|
822
|
+
projectMetadata(
|
|
823
|
+
context.application.paths.artifactRoot,
|
|
824
|
+
revision.document.project,
|
|
825
|
+
revision.document.projectEditPlan,
|
|
826
|
+
),
|
|
827
|
+
projectCaptions(revision, input.settings.captions),
|
|
828
|
+
]);
|
|
829
|
+
if (
|
|
830
|
+
input.metadataBindings !== undefined
|
|
831
|
+
&& canonicalJson(input.metadataBindings) !== canonicalJson(metadata.bindings)
|
|
832
|
+
) {
|
|
833
|
+
throw new ApplicationError(
|
|
834
|
+
"conflict",
|
|
835
|
+
"Recording metadata changed after the render-plan node was bound.",
|
|
836
|
+
);
|
|
837
|
+
}
|
|
838
|
+
if (
|
|
839
|
+
input.captionBinding !== undefined
|
|
840
|
+
&& canonicalJson(input.captionBinding) !== canonicalJson(captions.binding)
|
|
841
|
+
) {
|
|
842
|
+
throw new ApplicationError(
|
|
843
|
+
"conflict",
|
|
844
|
+
"Caption source changed after the render-plan node was bound.",
|
|
845
|
+
);
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
let basePlan: ProjectRenderPlanV1;
|
|
849
|
+
try {
|
|
850
|
+
basePlan = compileProjectRenderPlan(
|
|
851
|
+
revision.document.project,
|
|
852
|
+
revision.document.projectEditPlan,
|
|
853
|
+
renderSettings(input.settings, revision.reference, metadata.contexts),
|
|
854
|
+
);
|
|
855
|
+
} catch (error) {
|
|
856
|
+
if (error instanceof TypeError) {
|
|
857
|
+
throw new ApplicationError("conflict", error.message);
|
|
858
|
+
}
|
|
859
|
+
throw error;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
let prepared: PreparedProjectCaptionPlan;
|
|
863
|
+
try {
|
|
864
|
+
const cues = compileProjectCaptionCues({
|
|
865
|
+
analysis: captions.analysis,
|
|
866
|
+
output: basePlan.output,
|
|
867
|
+
placementId: input.settings.captions.placementId,
|
|
868
|
+
plan: revision.document.projectEditPlan,
|
|
869
|
+
project: revision.document.project,
|
|
870
|
+
});
|
|
871
|
+
prepared = prepareProjectCaptionPlan({
|
|
872
|
+
cues,
|
|
873
|
+
plan: basePlan,
|
|
874
|
+
sourceSha256: hashProjectCaptionSource({
|
|
875
|
+
binding: captions.binding,
|
|
876
|
+
output: basePlan.output,
|
|
877
|
+
projectEditPlanSha256: revision.document.projectEditPlanSha256,
|
|
878
|
+
revisionSha256: revision.document.revisionSha256,
|
|
879
|
+
style: input.settings.captions.style,
|
|
880
|
+
}),
|
|
881
|
+
style: input.settings.captions.style,
|
|
882
|
+
});
|
|
883
|
+
} catch (error) {
|
|
884
|
+
if (error instanceof TypeError || error instanceof RangeError) {
|
|
885
|
+
throw new ApplicationError("unsupported-plan", error.message);
|
|
886
|
+
}
|
|
887
|
+
throw error;
|
|
888
|
+
}
|
|
889
|
+
const plan = prepared.plan;
|
|
890
|
+
|
|
891
|
+
throwIfAborted(context.abortSignal);
|
|
892
|
+
await context.workflow?.beforePublication();
|
|
893
|
+
throwIfAborted(context.abortSignal);
|
|
894
|
+
await publishProjectCaptionArtifacts(
|
|
895
|
+
revision,
|
|
896
|
+
prepared.artifacts,
|
|
897
|
+
context.abortSignal,
|
|
898
|
+
);
|
|
899
|
+
return await publishProjectRenderPlan(
|
|
900
|
+
context.application,
|
|
901
|
+
undefined,
|
|
902
|
+
revision,
|
|
903
|
+
plan,
|
|
904
|
+
);
|
|
905
|
+
},
|
|
906
|
+
},
|
|
907
|
+
outputSchema: ProjectRenderPlanOutputSchema,
|
|
908
|
+
outputSchemaId: "slopcamera.operation.render.project-plan.output/v2",
|
|
909
|
+
policy: {
|
|
910
|
+
cache: "content-addressed",
|
|
911
|
+
cancellable: true,
|
|
912
|
+
effect: "local-derived-write",
|
|
913
|
+
maxDurationMs: 2 * 60_000,
|
|
914
|
+
maxFanOut: 0,
|
|
915
|
+
maxInputBytes: 256 * 1_024,
|
|
916
|
+
maxOutputBytes: 8_192,
|
|
917
|
+
preparation: ["recording-metadata"],
|
|
918
|
+
resources: [
|
|
919
|
+
{ amount: 1, resource: "cpu" },
|
|
920
|
+
{ amount: 1, resource: "local-io" },
|
|
921
|
+
],
|
|
922
|
+
resume: "deterministic",
|
|
923
|
+
},
|
|
924
|
+
receiptReference: output => output.artifact.path,
|
|
925
|
+
summarize: output => ({
|
|
926
|
+
fields: {
|
|
927
|
+
planArtifactSha256: output.artifact.sha256,
|
|
928
|
+
planSha256: output.planSha256,
|
|
929
|
+
projectId: output.projectId,
|
|
930
|
+
revisionSha256: output.revisionSha256,
|
|
931
|
+
},
|
|
932
|
+
kind: "render.project-plan",
|
|
933
|
+
}),
|
|
934
|
+
version: 2,
|
|
935
|
+
} satisfies OperationDefinition<
|
|
936
|
+
"render.project-plan",
|
|
937
|
+
ProjectRenderPlanInputV2,
|
|
938
|
+
ProjectRenderPlanOutput
|
|
939
|
+
>;
|