@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,250 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
canonicalJson,
|
|
5
|
+
createNodeBundleFileSystem,
|
|
6
|
+
saveImmutableText,
|
|
7
|
+
sha256Hex,
|
|
8
|
+
type BundleFileSystem,
|
|
9
|
+
} from "../../../core";
|
|
10
|
+
import type { ApplicationContext } from "../../context";
|
|
11
|
+
import {
|
|
12
|
+
CreativeCandidateReferenceV1Schema,
|
|
13
|
+
CreativeCandidateV1Schema,
|
|
14
|
+
CreativeImmutableArtifactSchema,
|
|
15
|
+
creativeCandidateReferenceV1,
|
|
16
|
+
variantMatrixReferenceV1,
|
|
17
|
+
VariantMatrixReferenceV1Schema,
|
|
18
|
+
VariantMatrixV1Schema,
|
|
19
|
+
variantSelectionReferenceV1,
|
|
20
|
+
VariantSelectionReferenceV1Schema,
|
|
21
|
+
VariantSelectionV1Schema,
|
|
22
|
+
type CreativeCandidateReferenceV1,
|
|
23
|
+
type CreativeCandidateV1,
|
|
24
|
+
type VariantMatrixReferenceV1,
|
|
25
|
+
type VariantMatrixV1,
|
|
26
|
+
type VariantSelectionReferenceV1,
|
|
27
|
+
type VariantSelectionV1,
|
|
28
|
+
} from "../../creative-iteration";
|
|
29
|
+
import { ApplicationError } from "../../errors";
|
|
30
|
+
import {
|
|
31
|
+
ProjectEditRevisionDocumentSchema,
|
|
32
|
+
ProjectRenderReceiptV2Schema,
|
|
33
|
+
type ProjectEditRevisionDocument,
|
|
34
|
+
} from "../../receipts";
|
|
35
|
+
import { exactProjectDirectory } from "../render/project-plan";
|
|
36
|
+
|
|
37
|
+
type CreativeImmutableArtifact = z.infer<
|
|
38
|
+
typeof CreativeImmutableArtifactSchema
|
|
39
|
+
>;
|
|
40
|
+
|
|
41
|
+
function parseJson(text: string, label: string): unknown {
|
|
42
|
+
try {
|
|
43
|
+
return JSON.parse(text) as unknown;
|
|
44
|
+
} catch {
|
|
45
|
+
throw new ApplicationError(
|
|
46
|
+
"invalid-data",
|
|
47
|
+
`${label} is not valid JSON.`,
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function creativeProjectFileSystem(
|
|
53
|
+
application: ApplicationContext,
|
|
54
|
+
projectId: string,
|
|
55
|
+
): Promise<BundleFileSystem> {
|
|
56
|
+
return createNodeBundleFileSystem(await exactProjectDirectory(
|
|
57
|
+
application,
|
|
58
|
+
projectId,
|
|
59
|
+
));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function publishCreativeDocument<Document>(input: {
|
|
63
|
+
readonly document: Document;
|
|
64
|
+
readonly fileSystem: BundleFileSystem;
|
|
65
|
+
readonly path: string;
|
|
66
|
+
readonly schema: z.ZodType<Document>;
|
|
67
|
+
}): Promise<CreativeImmutableArtifact> {
|
|
68
|
+
const document = input.schema.parse(input.document);
|
|
69
|
+
const contents = `${canonicalJson(document)}\n`;
|
|
70
|
+
const artifact = CreativeImmutableArtifactSchema.parse({
|
|
71
|
+
bytes: new TextEncoder().encode(contents).byteLength,
|
|
72
|
+
path: input.path,
|
|
73
|
+
sha256: sha256Hex(contents),
|
|
74
|
+
});
|
|
75
|
+
await saveImmutableText(
|
|
76
|
+
input.fileSystem,
|
|
77
|
+
artifact.path,
|
|
78
|
+
contents,
|
|
79
|
+
artifact.sha256,
|
|
80
|
+
);
|
|
81
|
+
return artifact;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export async function loadCreativeDocument<Document>(input: {
|
|
85
|
+
readonly artifact: CreativeImmutableArtifact;
|
|
86
|
+
readonly fileSystem: BundleFileSystem;
|
|
87
|
+
readonly label: string;
|
|
88
|
+
readonly schema: z.ZodType<Document>;
|
|
89
|
+
}): Promise<Document> {
|
|
90
|
+
const artifact = CreativeImmutableArtifactSchema.parse(input.artifact);
|
|
91
|
+
const contents = await input.fileSystem.readText(artifact.path);
|
|
92
|
+
const bytes = new TextEncoder().encode(contents).byteLength;
|
|
93
|
+
if (bytes !== artifact.bytes || sha256Hex(contents) !== artifact.sha256) {
|
|
94
|
+
throw new ApplicationError(
|
|
95
|
+
"conflict",
|
|
96
|
+
`${input.label} physical artifact hash or byte count changed.`,
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
const document = input.schema.parse(parseJson(contents, input.label));
|
|
100
|
+
if (contents !== `${canonicalJson(document)}\n`) {
|
|
101
|
+
throw new ApplicationError(
|
|
102
|
+
"invalid-data",
|
|
103
|
+
`${input.label} is not canonical immutable JSON.`,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
return document;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export async function loadCreativeCandidate(input: {
|
|
110
|
+
readonly fileSystem: BundleFileSystem;
|
|
111
|
+
readonly reference: CreativeCandidateReferenceV1;
|
|
112
|
+
}): Promise<CreativeCandidateV1> {
|
|
113
|
+
const reference = CreativeCandidateReferenceV1Schema.parse(input.reference);
|
|
114
|
+
const candidate = await loadCreativeDocument({
|
|
115
|
+
artifact: reference.artifact,
|
|
116
|
+
fileSystem: input.fileSystem,
|
|
117
|
+
label: "Creative candidate",
|
|
118
|
+
schema: CreativeCandidateV1Schema,
|
|
119
|
+
});
|
|
120
|
+
const expected = creativeCandidateReferenceV1({
|
|
121
|
+
artifact: reference.artifact,
|
|
122
|
+
candidate,
|
|
123
|
+
});
|
|
124
|
+
if (canonicalJson(expected) !== canonicalJson(reference)) {
|
|
125
|
+
throw new ApplicationError(
|
|
126
|
+
"conflict",
|
|
127
|
+
"Creative candidate document does not match its reference.",
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
return candidate;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export async function loadVariantMatrix(input: {
|
|
134
|
+
readonly fileSystem: BundleFileSystem;
|
|
135
|
+
readonly reference: VariantMatrixReferenceV1;
|
|
136
|
+
}): Promise<VariantMatrixV1> {
|
|
137
|
+
const reference = VariantMatrixReferenceV1Schema.parse(input.reference);
|
|
138
|
+
const matrix = await loadCreativeDocument({
|
|
139
|
+
artifact: reference.artifact,
|
|
140
|
+
fileSystem: input.fileSystem,
|
|
141
|
+
label: "Variant matrix",
|
|
142
|
+
schema: VariantMatrixV1Schema,
|
|
143
|
+
});
|
|
144
|
+
const expected = variantMatrixReferenceV1({
|
|
145
|
+
artifact: reference.artifact,
|
|
146
|
+
matrix,
|
|
147
|
+
});
|
|
148
|
+
if (canonicalJson(expected) !== canonicalJson(reference)) {
|
|
149
|
+
throw new ApplicationError(
|
|
150
|
+
"conflict",
|
|
151
|
+
"Variant matrix document does not match its reference.",
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
return matrix;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export async function loadVariantSelection(input: {
|
|
158
|
+
readonly fileSystem: BundleFileSystem;
|
|
159
|
+
readonly reference: VariantSelectionReferenceV1;
|
|
160
|
+
}): Promise<VariantSelectionV1> {
|
|
161
|
+
const reference = VariantSelectionReferenceV1Schema.parse(input.reference);
|
|
162
|
+
const selection = await loadCreativeDocument({
|
|
163
|
+
artifact: reference.artifact,
|
|
164
|
+
fileSystem: input.fileSystem,
|
|
165
|
+
label: "Variant selection",
|
|
166
|
+
schema: VariantSelectionV1Schema,
|
|
167
|
+
});
|
|
168
|
+
const expected = variantSelectionReferenceV1({
|
|
169
|
+
artifact: reference.artifact,
|
|
170
|
+
selection,
|
|
171
|
+
});
|
|
172
|
+
if (canonicalJson(expected) !== canonicalJson(reference)) {
|
|
173
|
+
throw new ApplicationError(
|
|
174
|
+
"conflict",
|
|
175
|
+
"Variant selection document does not match its reference.",
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
return selection;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export async function loadCandidateRevision(input: {
|
|
182
|
+
readonly candidate: CreativeCandidateV1;
|
|
183
|
+
readonly fileSystem: BundleFileSystem;
|
|
184
|
+
}): Promise<ProjectEditRevisionDocument> {
|
|
185
|
+
const candidate = CreativeCandidateV1Schema.parse(input.candidate);
|
|
186
|
+
const revision = await loadCreativeDocument({
|
|
187
|
+
artifact: candidate.revision.artifact,
|
|
188
|
+
fileSystem: input.fileSystem,
|
|
189
|
+
label: "Creative candidate revision",
|
|
190
|
+
schema: ProjectEditRevisionDocumentSchema,
|
|
191
|
+
});
|
|
192
|
+
if (
|
|
193
|
+
revision.revisionSha256 !== candidate.revision.revisionSha256
|
|
194
|
+
|| revision.projectSha256 !== candidate.revision.projectSha256
|
|
195
|
+
|| revision.projectEditPlanSha256
|
|
196
|
+
!== candidate.revision.projectEditPlanSha256
|
|
197
|
+
|| revision.projectSha256 !== candidate.base.generation.projectSha256
|
|
198
|
+
) {
|
|
199
|
+
throw new ApplicationError(
|
|
200
|
+
"conflict",
|
|
201
|
+
"Creative candidate revision document does not match its candidate.",
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
return revision;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export async function verifyCandidateRender(input: {
|
|
208
|
+
readonly candidate: CreativeCandidateV1;
|
|
209
|
+
readonly fileSystem: BundleFileSystem;
|
|
210
|
+
readonly render: CreativeCandidateV1["renders"][number];
|
|
211
|
+
}): Promise<void> {
|
|
212
|
+
const receipt = await loadCreativeDocument({
|
|
213
|
+
artifact: {
|
|
214
|
+
bytes: input.render.receipt.bytes,
|
|
215
|
+
path: input.render.receipt.path,
|
|
216
|
+
sha256: input.render.receipt.sha256,
|
|
217
|
+
},
|
|
218
|
+
fileSystem: input.fileSystem,
|
|
219
|
+
label: `Creative candidate render receipt ${input.render.name}`,
|
|
220
|
+
schema: ProjectRenderReceiptV2Schema,
|
|
221
|
+
});
|
|
222
|
+
if (
|
|
223
|
+
receipt.receiptSha256 !== input.render.receipt.receiptSha256
|
|
224
|
+
|| receipt.run.nodePlanSha256 !== input.render.receipt.nodePlanSha256
|
|
225
|
+
|| receipt.output.sha256 !== input.render.output.sha256
|
|
226
|
+
|| canonicalJson(receipt.output) !== canonicalJson(input.render.output)
|
|
227
|
+
|| receipt.output.revisionSha256 !== input.candidate.revision.revisionSha256
|
|
228
|
+
) {
|
|
229
|
+
throw new ApplicationError(
|
|
230
|
+
"conflict",
|
|
231
|
+
`Creative candidate render ${input.render.name} is not bound to its verified receipt.`,
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
if (input.fileSystem.inspectFile === undefined) {
|
|
235
|
+
throw new ApplicationError(
|
|
236
|
+
"internal",
|
|
237
|
+
"Project storage does not support immutable render verification.",
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
const integrity = await input.fileSystem.inspectFile(input.render.output.path);
|
|
241
|
+
if (
|
|
242
|
+
integrity.bytes !== input.render.output.bytes
|
|
243
|
+
|| integrity.sha256 !== input.render.output.sha256
|
|
244
|
+
) {
|
|
245
|
+
throw new ApplicationError(
|
|
246
|
+
"conflict",
|
|
247
|
+
`Creative candidate render ${input.render.name} bytes changed after verification.`,
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
AudioEffectsTransformV1Schema,
|
|
7
|
+
type AudioEffectsTransformV1,
|
|
8
|
+
} from "../../../contracts";
|
|
9
|
+
import {
|
|
10
|
+
type ExpectedLocalMediaInput,
|
|
11
|
+
type LocalMediaTransformResult,
|
|
12
|
+
} from "../../../cli/media-effects-service";
|
|
13
|
+
import {
|
|
14
|
+
type ProbedMedia,
|
|
15
|
+
} from "../../../cli/media-ingest";
|
|
16
|
+
import type {
|
|
17
|
+
ApplicationContext,
|
|
18
|
+
ApplicationProcessRunner,
|
|
19
|
+
} from "../../context";
|
|
20
|
+
import { ApplicationError } from "../../errors";
|
|
21
|
+
import type { OperationDefinition, OperationExecutionContext } from "../../operation";
|
|
22
|
+
import { operationResource, operationValidation, runStandaloneOperation, type OperationEffectFailure } from "../../operation-effects";
|
|
23
|
+
import { createMediaTransformPlatform, MediaTransformPlatform } from "./transform-platform";
|
|
24
|
+
import {
|
|
25
|
+
MediaCapabilityBindingsSchema,
|
|
26
|
+
bindExpectedMediaCapabilities,
|
|
27
|
+
mediaCapabilityCommand,
|
|
28
|
+
mediaCapabilityVersion,
|
|
29
|
+
} from "./capabilities";
|
|
30
|
+
import {
|
|
31
|
+
MAXIMUM_MEDIA_EFFECT_INPUT_BYTES,
|
|
32
|
+
MediaArtifactReferenceSchema,
|
|
33
|
+
MediaArtifactRequestSchema,
|
|
34
|
+
bindRepositoryMedia,
|
|
35
|
+
} from "./shared";
|
|
36
|
+
|
|
37
|
+
const MAXIMUM_EFFECT_DURATION_US = 24 * 60 * 60 * 1_000_000;
|
|
38
|
+
|
|
39
|
+
export const MediaAudioEffectsReceiptSchema = z.strictObject({
|
|
40
|
+
createdAt: z.string().datetime({ offset: true }),
|
|
41
|
+
ffmpegVersion: z.string().min(1).max(256),
|
|
42
|
+
ffprobeVersion: z.string().min(1).max(256),
|
|
43
|
+
filterGraph: z.string().min(1).max(256 * 1024),
|
|
44
|
+
input: MediaArtifactReferenceSchema,
|
|
45
|
+
kind: z.union([
|
|
46
|
+
z.literal("slopcamera.local-media-transform-receipt"),
|
|
47
|
+
z.literal("studio.local-media-transform-receipt"),
|
|
48
|
+
]),
|
|
49
|
+
operation: z.literal("audio-effects"),
|
|
50
|
+
output: MediaArtifactReferenceSchema.extend({
|
|
51
|
+
durationUs: z.number().int().safe().positive(),
|
|
52
|
+
}).strict(),
|
|
53
|
+
schemaVersion: z.literal(1),
|
|
54
|
+
sourceDurationUs: z.number().int().safe().positive(),
|
|
55
|
+
transform: AudioEffectsTransformV1Schema,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
export const MediaAudioEffectsInputSchema = z.strictObject({
|
|
59
|
+
capabilityBindings: MediaCapabilityBindingsSchema.optional(),
|
|
60
|
+
input: MediaArtifactRequestSchema,
|
|
61
|
+
transform: AudioEffectsTransformV1Schema,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
export const BoundMediaAudioEffectsInputSchema =
|
|
65
|
+
MediaAudioEffectsInputSchema.extend({
|
|
66
|
+
input: MediaArtifactReferenceSchema,
|
|
67
|
+
}).strict();
|
|
68
|
+
|
|
69
|
+
export const MediaAudioEffectsOutputSchema = z.strictObject({
|
|
70
|
+
artifact: MediaArtifactReferenceSchema,
|
|
71
|
+
created: z.boolean(),
|
|
72
|
+
durationUs: z.number().int().safe().positive(),
|
|
73
|
+
filterGraph: z.string().min(1).max(256 * 1024),
|
|
74
|
+
receipt: MediaArtifactReferenceSchema,
|
|
75
|
+
transform: AudioEffectsTransformV1Schema,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
export type MediaAudioEffectsInput = z.infer<typeof MediaAudioEffectsInputSchema>;
|
|
79
|
+
export type BoundMediaAudioEffectsInput =
|
|
80
|
+
z.infer<typeof BoundMediaAudioEffectsInputSchema>;
|
|
81
|
+
export type MediaAudioEffectsOutput =
|
|
82
|
+
z.infer<typeof MediaAudioEffectsOutputSchema>;
|
|
83
|
+
|
|
84
|
+
export interface AudioEffectsExecutionOptions {
|
|
85
|
+
readonly expectedInput: ExpectedLocalMediaInput;
|
|
86
|
+
readonly ffmpeg: string;
|
|
87
|
+
readonly inputPath: string;
|
|
88
|
+
readonly outputPath: string;
|
|
89
|
+
readonly runner: ApplicationProcessRunner;
|
|
90
|
+
readonly transform: AudioEffectsTransformV1;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
type AudioEffectsExecutor = (
|
|
94
|
+
options: AudioEffectsExecutionOptions,
|
|
95
|
+
) => Promise<LocalMediaTransformResult<AudioEffectsTransformV1>>;
|
|
96
|
+
|
|
97
|
+
type MediaProbeExecutor = (
|
|
98
|
+
ffprobe: string,
|
|
99
|
+
runner: ApplicationProcessRunner,
|
|
100
|
+
path: string,
|
|
101
|
+
) => Promise<ProbedMedia>;
|
|
102
|
+
|
|
103
|
+
export interface MediaAudioEffectsOperationDependencies {
|
|
104
|
+
readonly probe?: MediaProbeExecutor;
|
|
105
|
+
readonly render?: AudioEffectsExecutor;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export async function bindMediaAudioEffectsInput(
|
|
109
|
+
application: ApplicationContext,
|
|
110
|
+
input: unknown,
|
|
111
|
+
signal: AbortSignal,
|
|
112
|
+
): Promise<BoundMediaAudioEffectsInput> {
|
|
113
|
+
const parsed = MediaAudioEffectsInputSchema.parse(input);
|
|
114
|
+
const [bound, capabilityBindings] = await Promise.all([
|
|
115
|
+
bindRepositoryMedia(
|
|
116
|
+
application,
|
|
117
|
+
parsed.input,
|
|
118
|
+
signal,
|
|
119
|
+
MAXIMUM_MEDIA_EFFECT_INPUT_BYTES,
|
|
120
|
+
),
|
|
121
|
+
bindExpectedMediaCapabilities(
|
|
122
|
+
application,
|
|
123
|
+
["ffmpeg", "ffprobe"],
|
|
124
|
+
parsed.capabilityBindings,
|
|
125
|
+
),
|
|
126
|
+
]);
|
|
127
|
+
return BoundMediaAudioEffectsInputSchema.parse({
|
|
128
|
+
...parsed,
|
|
129
|
+
capabilityBindings,
|
|
130
|
+
input: bound.artifact,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function audioOutputExtension(transform: AudioEffectsTransformV1): string {
|
|
135
|
+
if (transform.output.kind === "preserve-video") {
|
|
136
|
+
return transform.output.profile === "aac" ? ".mp4" : ".mkv";
|
|
137
|
+
}
|
|
138
|
+
switch (transform.output.profile) {
|
|
139
|
+
case "wav-pcm-s16le": return ".wav";
|
|
140
|
+
case "flac": return ".flac";
|
|
141
|
+
case "mp3": return ".mp3";
|
|
142
|
+
case "aac": return ".m4a";
|
|
143
|
+
case "opus": return ".opus";
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function assertProbeEnvelope(probe: ProbedMedia): void {
|
|
148
|
+
if (
|
|
149
|
+
probe.durationUs <= 0
|
|
150
|
+
|| probe.durationUs > MAXIMUM_EFFECT_DURATION_US
|
|
151
|
+
|| probe.streams.length > 64
|
|
152
|
+
) {
|
|
153
|
+
throw new ApplicationError(
|
|
154
|
+
"invalid-data",
|
|
155
|
+
"Media exceeds the local-effects duration or stream-count limit.",
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function selectedAudioDuration(
|
|
161
|
+
probe: ProbedMedia,
|
|
162
|
+
transform: AudioEffectsTransformV1,
|
|
163
|
+
): number {
|
|
164
|
+
assertProbeEnvelope(probe);
|
|
165
|
+
const audioStreams = probe.streams.filter(
|
|
166
|
+
stream => stream.codec_type === "audio",
|
|
167
|
+
);
|
|
168
|
+
const selected = audioStreams[transform.audioStreamIndex];
|
|
169
|
+
if (selected === undefined) {
|
|
170
|
+
throw new ApplicationError(
|
|
171
|
+
"usage",
|
|
172
|
+
`Audio stream ${String(transform.audioStreamIndex)} is out of range.`,
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
if (transform.output.kind === "preserve-video") {
|
|
176
|
+
const preserveVideo = transform.output;
|
|
177
|
+
const videoStreams = probe.streams.filter(
|
|
178
|
+
stream => stream.codec_type === "video",
|
|
179
|
+
);
|
|
180
|
+
const selectedVideo = preserveVideo.inputVideoStreamIndex === undefined
|
|
181
|
+
? videoStreams[preserveVideo.videoStreamIndex]
|
|
182
|
+
: videoStreams.find(
|
|
183
|
+
stream => stream.index === preserveVideo.inputVideoStreamIndex,
|
|
184
|
+
);
|
|
185
|
+
if (selectedVideo === undefined) {
|
|
186
|
+
throw new ApplicationError(
|
|
187
|
+
"usage",
|
|
188
|
+
"The selected preserve-video stream is out of range.",
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return selected.assetRange.endUs - selected.assetRange.startUs;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function verifiedAudioDuration(
|
|
196
|
+
probe: ProbedMedia,
|
|
197
|
+
sourceDurationUs: number,
|
|
198
|
+
preserveVideo: boolean,
|
|
199
|
+
): number {
|
|
200
|
+
assertProbeEnvelope(probe);
|
|
201
|
+
const audio = probe.streams.find(stream => stream.codec_type === "audio");
|
|
202
|
+
if (audio === undefined) {
|
|
203
|
+
throw new ApplicationError(
|
|
204
|
+
"invalid-data",
|
|
205
|
+
"Derived audio-effects media contains no audio stream.",
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
if (
|
|
209
|
+
preserveVideo
|
|
210
|
+
&& !probe.streams.some(stream => stream.codec_type === "video")
|
|
211
|
+
) {
|
|
212
|
+
throw new ApplicationError(
|
|
213
|
+
"invalid-data",
|
|
214
|
+
"Derived preserve-video media contains no video stream.",
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
const durationUs = audio.assetRange.endUs - audio.assetRange.startUs;
|
|
218
|
+
if (durationUs + 250_000 < sourceDurationUs) {
|
|
219
|
+
throw new ApplicationError(
|
|
220
|
+
"invalid-data",
|
|
221
|
+
"Derived audio coverage is shorter than the selected source stream.",
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
if (durationUs > sourceDurationUs + 31_000_000) {
|
|
225
|
+
throw new ApplicationError(
|
|
226
|
+
"invalid-data",
|
|
227
|
+
"Derived audio exceeds the bounded delay/reverb tail.",
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
return durationUs;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** Native transform owner; foreign work settles before workspace release. */
|
|
234
|
+
export function mediaAudioEffectsProgram(input: MediaAudioEffectsInput): Effect.Effect<
|
|
235
|
+
MediaAudioEffectsOutput, OperationEffectFailure, MediaTransformPlatform
|
|
236
|
+
> {
|
|
237
|
+
return Effect.gen(function*() {
|
|
238
|
+
const platform = yield* MediaTransformPlatform;
|
|
239
|
+
yield* platform.checkAbort();
|
|
240
|
+
const parsed = yield* operationValidation("input", () => MediaAudioEffectsInputSchema.parse(input));
|
|
241
|
+
const boundMedia = yield* platform.bindInput(parsed.input);
|
|
242
|
+
const boundInput = yield* operationValidation("input", () => BoundMediaAudioEffectsInputSchema.parse({
|
|
243
|
+
...parsed, input: boundMedia.artifact,
|
|
244
|
+
}));
|
|
245
|
+
yield* platform.assertCapabilities(parsed.capabilityBindings);
|
|
246
|
+
const capabilityBindings = parsed.capabilityBindings ?? (yield* platform.bindCapabilities());
|
|
247
|
+
const { ffmpeg, ffprobe } = yield* operationValidation("capability", () => ({
|
|
248
|
+
ffmpeg: mediaCapabilityCommand(capabilityBindings, "ffmpeg"),
|
|
249
|
+
ffprobe: mediaCapabilityCommand(capabilityBindings, "ffprobe"),
|
|
250
|
+
}));
|
|
251
|
+
const runner = yield* platform.runner(capabilityBindings);
|
|
252
|
+
const sourceProbe = yield* platform.probe(ffprobe, runner, boundMedia.absolutePath);
|
|
253
|
+
const sourceDurationUs = yield* operationValidation("media", () => selectedAudioDuration(sourceProbe, boundInput.transform));
|
|
254
|
+
return yield* operationResource(
|
|
255
|
+
platform.workspace(),
|
|
256
|
+
workspace => Effect.gen(function*() {
|
|
257
|
+
const extension = yield* operationValidation("input", () => audioOutputExtension(boundInput.transform));
|
|
258
|
+
const stagedPath = yield* platform.stagedPath(workspace, "audio", extension);
|
|
259
|
+
const rendered = yield* platform.renderAudio({
|
|
260
|
+
expectedInput: boundMedia.expectedInput,
|
|
261
|
+
ffmpeg,
|
|
262
|
+
inputPath: boundMedia.absolutePath,
|
|
263
|
+
outputPath: stagedPath,
|
|
264
|
+
runner,
|
|
265
|
+
transform: boundInput.transform,
|
|
266
|
+
});
|
|
267
|
+
yield* platform.checkAbort();
|
|
268
|
+
const outputProbe = yield* platform.probe(ffprobe, runner, stagedPath);
|
|
269
|
+
const durationUs = yield* operationValidation("media", () => verifiedAudioDuration(
|
|
270
|
+
outputProbe, sourceDurationUs, boundInput.transform.output.kind === "preserve-video",
|
|
271
|
+
));
|
|
272
|
+
// Existing native publication fences still inspect the AbortSignal.
|
|
273
|
+
// Once entered, retain the whole output/receipt/checkpoint continuation.
|
|
274
|
+
return yield* Effect.uninterruptible(Effect.gen(function*() {
|
|
275
|
+
const published = yield* platform.publishMedia(stagedPath, extension);
|
|
276
|
+
yield* operationValidation("publication", () => {
|
|
277
|
+
if (published.artifact.bytes !== rendered.bytes || published.artifact.sha256 !== rendered.sha256) {
|
|
278
|
+
throw new ApplicationError("conflict", "Audio-effects service result disagrees with the published bytes.");
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
const createdAt = yield* platform.now();
|
|
282
|
+
const receiptBody = yield* operationValidation("output", () => MediaAudioEffectsReceiptSchema.parse({
|
|
283
|
+
createdAt,
|
|
284
|
+
ffmpegVersion: mediaCapabilityVersion(capabilityBindings, "ffmpeg"),
|
|
285
|
+
ffprobeVersion: mediaCapabilityVersion(capabilityBindings, "ffprobe"),
|
|
286
|
+
filterGraph: rendered.filterGraph,
|
|
287
|
+
input: boundInput.input,
|
|
288
|
+
kind: "slopcamera.local-media-transform-receipt",
|
|
289
|
+
operation: "audio-effects",
|
|
290
|
+
output: { ...published.artifact, durationUs },
|
|
291
|
+
schemaVersion: 1,
|
|
292
|
+
sourceDurationUs,
|
|
293
|
+
transform: rendered.transform,
|
|
294
|
+
}));
|
|
295
|
+
const receipt = yield* platform.publishReceipt(receiptBody, workspace);
|
|
296
|
+
const output = yield* operationValidation("output", () => MediaAudioEffectsOutputSchema.parse({
|
|
297
|
+
artifact: published.artifact,
|
|
298
|
+
created: published.created,
|
|
299
|
+
durationUs,
|
|
300
|
+
filterGraph: rendered.filterGraph,
|
|
301
|
+
receipt,
|
|
302
|
+
transform: rendered.transform,
|
|
303
|
+
}));
|
|
304
|
+
yield* platform.checkpoint({
|
|
305
|
+
inputSchemaId: "slopcamera.operation.media.audio-effects.input/v1",
|
|
306
|
+
kind: "media.audio-effects",
|
|
307
|
+
outputSchemaId: "slopcamera.operation.media.audio-effects.output/v1",
|
|
308
|
+
version: 1,
|
|
309
|
+
}, output);
|
|
310
|
+
return output;
|
|
311
|
+
}));
|
|
312
|
+
}),
|
|
313
|
+
workspace => platform.dispose(workspace),
|
|
314
|
+
);
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export function createMediaAudioEffectsOperationDefinition(
|
|
319
|
+
dependencies: MediaAudioEffectsOperationDependencies = {},
|
|
320
|
+
): OperationDefinition<
|
|
321
|
+
"media.audio-effects",
|
|
322
|
+
MediaAudioEffectsInput,
|
|
323
|
+
MediaAudioEffectsOutput
|
|
324
|
+
> {
|
|
325
|
+
const executeEffect = (context: OperationExecutionContext, input: MediaAudioEffectsInput) =>
|
|
326
|
+
mediaAudioEffectsProgram(input).pipe(Effect.provideService(MediaTransformPlatform, createMediaTransformPlatform(context, {
|
|
327
|
+
...(dependencies.probe === undefined ? {} : { probe: dependencies.probe }),
|
|
328
|
+
...(dependencies.render === undefined ? {} : { renderAudio: dependencies.render }),
|
|
329
|
+
})));
|
|
330
|
+
return {
|
|
331
|
+
inputSchema: MediaAudioEffectsInputSchema,
|
|
332
|
+
inputSchemaId: "slopcamera.operation.media.audio-effects.input/v1",
|
|
333
|
+
kind: "media.audio-effects",
|
|
334
|
+
lifecycle: {
|
|
335
|
+
kind: "local-artifact",
|
|
336
|
+
execute: async (context, input) => await runStandaloneOperation(executeEffect(context, input)),
|
|
337
|
+
executeEffect,
|
|
338
|
+
},
|
|
339
|
+
outputSchema: MediaAudioEffectsOutputSchema,
|
|
340
|
+
outputSchemaId: "slopcamera.operation.media.audio-effects.output/v1",
|
|
341
|
+
policy: {
|
|
342
|
+
cache: "exact-run",
|
|
343
|
+
cancellable: true,
|
|
344
|
+
effect: "local-derived-write",
|
|
345
|
+
maxDurationMs: 12 * 60 * 60_000,
|
|
346
|
+
maxFanOut: 0,
|
|
347
|
+
maxInputBytes: 512 * 1024,
|
|
348
|
+
maxOutputBytes: 512 * 1024,
|
|
349
|
+
preparation: ["local-media"],
|
|
350
|
+
resources: [
|
|
351
|
+
{ amount: 1, resource: "cpu" },
|
|
352
|
+
{ amount: 1, resource: "ffmpeg" },
|
|
353
|
+
{ amount: 1, resource: "local-io" },
|
|
354
|
+
{ amount: 1, resource: "output-publication" },
|
|
355
|
+
],
|
|
356
|
+
resume: "verified-receipt",
|
|
357
|
+
},
|
|
358
|
+
receiptReference: output => output.receipt.path,
|
|
359
|
+
summarize: output => ({
|
|
360
|
+
fields: {
|
|
361
|
+
bytes: output.artifact.bytes,
|
|
362
|
+
created: output.created,
|
|
363
|
+
durationUs: output.durationUs,
|
|
364
|
+
effects: output.transform.effects.length,
|
|
365
|
+
path: output.artifact.path,
|
|
366
|
+
receipt: output.receipt.path,
|
|
367
|
+
sha256: output.artifact.sha256,
|
|
368
|
+
},
|
|
369
|
+
kind: "media.audio-effects",
|
|
370
|
+
}),
|
|
371
|
+
version: 1,
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
export const mediaAudioEffectsOperationDefinition =
|
|
376
|
+
createMediaAudioEffectsOperationDefinition();
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
import type { ApplicationContext } from "../../context";
|
|
4
|
+
import {
|
|
5
|
+
MediaAudioEffectsInputSchema,
|
|
6
|
+
bindMediaAudioEffectsInput,
|
|
7
|
+
} from "./audio-effects";
|
|
8
|
+
import {
|
|
9
|
+
MediaColorGradeInputSchema,
|
|
10
|
+
bindMediaColorGradeInput,
|
|
11
|
+
} from "./color-grade";
|
|
12
|
+
import {
|
|
13
|
+
MediaIngestInputSchema,
|
|
14
|
+
bindMediaIngestInput,
|
|
15
|
+
} from "./ingest";
|
|
16
|
+
import {
|
|
17
|
+
HtmlOverlayInputSchema,
|
|
18
|
+
bindHtmlOverlayInput,
|
|
19
|
+
} from "./html-overlay";
|
|
20
|
+
import {
|
|
21
|
+
MediaOverlayInputSchema,
|
|
22
|
+
bindMediaOverlayInput,
|
|
23
|
+
} from "./overlay";
|
|
24
|
+
|
|
25
|
+
export const MEDIA_OPERATION_KINDS = [
|
|
26
|
+
"media.ingest",
|
|
27
|
+
"media.html-overlay",
|
|
28
|
+
"media.overlay",
|
|
29
|
+
"media.audio-effects",
|
|
30
|
+
"media.color-grade",
|
|
31
|
+
] as const;
|
|
32
|
+
|
|
33
|
+
export const MediaOperationKindSchema = z.enum(MEDIA_OPERATION_KINDS);
|
|
34
|
+
export type MediaOperationKind = z.infer<typeof MediaOperationKindSchema>;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Resolve a progressive path-only request into the exact byte identity that a
|
|
38
|
+
* workflow node plan hashes. Execution binds the same input again immediately
|
|
39
|
+
* before use, so planning does not substitute for the descriptor-pinned read.
|
|
40
|
+
*/
|
|
41
|
+
export async function bindMediaOperationInput(
|
|
42
|
+
application: ApplicationContext,
|
|
43
|
+
kind: MediaOperationKind,
|
|
44
|
+
input: unknown,
|
|
45
|
+
signal: AbortSignal = new AbortController().signal,
|
|
46
|
+
): Promise<unknown> {
|
|
47
|
+
switch (kind) {
|
|
48
|
+
case "media.ingest":
|
|
49
|
+
return await bindMediaIngestInput(
|
|
50
|
+
application,
|
|
51
|
+
MediaIngestInputSchema.parse(input),
|
|
52
|
+
signal,
|
|
53
|
+
);
|
|
54
|
+
case "media.overlay":
|
|
55
|
+
return await bindMediaOverlayInput(
|
|
56
|
+
application,
|
|
57
|
+
MediaOverlayInputSchema.parse(input),
|
|
58
|
+
signal,
|
|
59
|
+
);
|
|
60
|
+
case "media.html-overlay":
|
|
61
|
+
return await bindHtmlOverlayInput(
|
|
62
|
+
application,
|
|
63
|
+
HtmlOverlayInputSchema.parse(input),
|
|
64
|
+
signal,
|
|
65
|
+
);
|
|
66
|
+
case "media.audio-effects":
|
|
67
|
+
return await bindMediaAudioEffectsInput(
|
|
68
|
+
application,
|
|
69
|
+
MediaAudioEffectsInputSchema.parse(input),
|
|
70
|
+
signal,
|
|
71
|
+
);
|
|
72
|
+
case "media.color-grade":
|
|
73
|
+
return await bindMediaColorGradeInput(
|
|
74
|
+
application,
|
|
75
|
+
MediaColorGradeInputSchema.parse(input),
|
|
76
|
+
signal,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
}
|