@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,284 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ManualProjectCameraMoveInputV2Schema,
|
|
3
|
+
ManualProjectZoomInputV3Schema,
|
|
4
|
+
type ManualProjectCameraMoveInputV2,
|
|
5
|
+
type ManualProjectZoomInputV3,
|
|
6
|
+
type OrderedProjectEditV2,
|
|
7
|
+
type OrderedProjectEditV3,
|
|
8
|
+
} from "../application/operations/derive/edit-batch";
|
|
9
|
+
import {
|
|
10
|
+
CameraPoseSchema,
|
|
11
|
+
type CameraPose,
|
|
12
|
+
type Easing,
|
|
13
|
+
type ProjectEditPlanV1,
|
|
14
|
+
type ProjectPlacementId,
|
|
15
|
+
type SourceInterval,
|
|
16
|
+
type ZoomOperation,
|
|
17
|
+
type ZoomTarget,
|
|
18
|
+
} from "../contracts";
|
|
19
|
+
import type { OperationInputValue } from "./contracts";
|
|
20
|
+
|
|
21
|
+
type MetadataEffects = ProjectEditPlanV1["effects"];
|
|
22
|
+
|
|
23
|
+
/** Binding-free manual move accepted by a checked v2 project commit. */
|
|
24
|
+
export type ManualCameraMoveInput = ManualProjectCameraMoveInputV2;
|
|
25
|
+
|
|
26
|
+
/** Binding-free manual zoom accepted by a checked v3 project commit. */
|
|
27
|
+
export type ManualZoomInput = Omit<
|
|
28
|
+
ManualProjectZoomInputV3,
|
|
29
|
+
"displayId" | "placementId"
|
|
30
|
+
> & {
|
|
31
|
+
readonly displayId?: string;
|
|
32
|
+
readonly placementId?: NonNullable<
|
|
33
|
+
ManualProjectZoomInputV3["placementId"]
|
|
34
|
+
>;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type SetMetadataEffectsEdit = Extract<
|
|
38
|
+
OrderedProjectEditV2,
|
|
39
|
+
{ readonly kind: "set-metadata-effects" }
|
|
40
|
+
>;
|
|
41
|
+
|
|
42
|
+
export type AddManualCameraMovesEdit = Extract<
|
|
43
|
+
OrderedProjectEditV2,
|
|
44
|
+
{ readonly kind: "add-manual-camera-moves" }
|
|
45
|
+
>;
|
|
46
|
+
|
|
47
|
+
export type RemoveCameraMovesEdit = Extract<
|
|
48
|
+
OrderedProjectEditV2,
|
|
49
|
+
{ readonly kind: "remove-camera-moves" }
|
|
50
|
+
>;
|
|
51
|
+
|
|
52
|
+
export type RemoveZoomsEdit = Extract<
|
|
53
|
+
OrderedProjectEditV2,
|
|
54
|
+
{ readonly kind: "remove-zooms" }
|
|
55
|
+
>;
|
|
56
|
+
|
|
57
|
+
export type AddManualZoomsEdit = Extract<
|
|
58
|
+
OrderedProjectEditV3,
|
|
59
|
+
{ readonly kind: "add-manual-zooms" }
|
|
60
|
+
>;
|
|
61
|
+
|
|
62
|
+
export interface MetadataEffectsOptions {
|
|
63
|
+
readonly clicks: OperationInputValue<MetadataEffects["clicks"]>;
|
|
64
|
+
readonly cursor: OperationInputValue<MetadataEffects["cursor"]>;
|
|
65
|
+
readonly keystrokes: OperationInputValue<MetadataEffects["keystrokes"]>;
|
|
66
|
+
readonly metadataPlacementId: OperationInputValue<ProjectPlacementId | null>;
|
|
67
|
+
readonly typedText: OperationInputValue<MetadataEffects["typedText"]>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Replace the complete interaction-effect configuration in one explicit edit.
|
|
72
|
+
* Requiring every field prevents an agent from depending on hidden prior state.
|
|
73
|
+
*/
|
|
74
|
+
export function setMetadataEffects(
|
|
75
|
+
input: MetadataEffectsOptions,
|
|
76
|
+
): OperationInputValue<SetMetadataEffectsEdit> {
|
|
77
|
+
return {
|
|
78
|
+
clicks: input.clicks,
|
|
79
|
+
cursor: input.cursor,
|
|
80
|
+
keystrokes: input.keystrokes,
|
|
81
|
+
kind: "set-metadata-effects",
|
|
82
|
+
metadataPlacementId: input.metadataPlacementId,
|
|
83
|
+
typedText: input.typedText,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Conservative screen-demo polish. Typed text stays off because capture is
|
|
89
|
+
* opt-in and secure-field suppression must never be weakened by a workflow.
|
|
90
|
+
*/
|
|
91
|
+
export function polishedInteractionEffects(
|
|
92
|
+
metadataPlacementId: OperationInputValue<ProjectPlacementId>,
|
|
93
|
+
): OperationInputValue<SetMetadataEffectsEdit> {
|
|
94
|
+
return setMetadataEffects({
|
|
95
|
+
clicks: {
|
|
96
|
+
color: "#5B8CFF",
|
|
97
|
+
durationUs: 450_000,
|
|
98
|
+
enabled: true,
|
|
99
|
+
radiusPx: 28,
|
|
100
|
+
style: "ring",
|
|
101
|
+
},
|
|
102
|
+
cursor: {
|
|
103
|
+
enabled: true,
|
|
104
|
+
scale: 1.15,
|
|
105
|
+
smoothing: {
|
|
106
|
+
algorithm: "exponential",
|
|
107
|
+
strength: 0.42,
|
|
108
|
+
},
|
|
109
|
+
style: "captured",
|
|
110
|
+
},
|
|
111
|
+
keystrokes: {
|
|
112
|
+
enabled: true,
|
|
113
|
+
holdUs: 900_000,
|
|
114
|
+
maxKeys: 6,
|
|
115
|
+
position: "bottom-center",
|
|
116
|
+
secureText: "hide",
|
|
117
|
+
},
|
|
118
|
+
metadataPlacementId,
|
|
119
|
+
typedText: { enabled: false },
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function addManualCameraMoves(
|
|
124
|
+
cameraMoves: readonly OperationInputValue<ManualProjectCameraMoveInputV2>[],
|
|
125
|
+
): OperationInputValue<AddManualCameraMovesEdit> {
|
|
126
|
+
return {
|
|
127
|
+
cameraMoves,
|
|
128
|
+
kind: "add-manual-camera-moves",
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function addManualZooms(
|
|
133
|
+
zooms: readonly (
|
|
134
|
+
| ManualZoomInput
|
|
135
|
+
| OperationInputValue<ManualProjectZoomInputV3>
|
|
136
|
+
)[],
|
|
137
|
+
): OperationInputValue<AddManualZoomsEdit> {
|
|
138
|
+
return {
|
|
139
|
+
kind: "add-manual-zooms",
|
|
140
|
+
zooms,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function removeCameraMoves(
|
|
145
|
+
cameraMoveIds: readonly OperationInputValue<
|
|
146
|
+
ManualProjectCameraMoveInputV2["cameraMoveId"]
|
|
147
|
+
>[],
|
|
148
|
+
): OperationInputValue<RemoveCameraMovesEdit> {
|
|
149
|
+
return {
|
|
150
|
+
cameraMoveIds,
|
|
151
|
+
kind: "remove-camera-moves",
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function removeZooms(
|
|
156
|
+
zoomIds: readonly OperationInputValue<ZoomOperation["zoomId"]>[],
|
|
157
|
+
): OperationInputValue<RemoveZoomsEdit> {
|
|
158
|
+
return {
|
|
159
|
+
kind: "remove-zooms",
|
|
160
|
+
zoomIds,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export interface ManualZoomOptions {
|
|
165
|
+
readonly displayId?: string;
|
|
166
|
+
readonly easing?: Easing;
|
|
167
|
+
readonly enterDurationUs?: number;
|
|
168
|
+
readonly exitDurationUs?: number;
|
|
169
|
+
readonly placementId?: string;
|
|
170
|
+
readonly range: SourceInterval;
|
|
171
|
+
readonly scale: number;
|
|
172
|
+
readonly target: ZoomTarget;
|
|
173
|
+
readonly zoomId: string;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Construct binding-free manual zoom intent. The host selects omitted
|
|
178
|
+
* placement/display identities and binds the exact recording manifest and
|
|
179
|
+
* placement synchronization during v3 commit planning.
|
|
180
|
+
*/
|
|
181
|
+
export function manualZoom(
|
|
182
|
+
input: ManualZoomOptions,
|
|
183
|
+
): ManualZoomInput {
|
|
184
|
+
const durationUs = input.range.endUs - input.range.startUs;
|
|
185
|
+
const defaultTransitionUs = Math.min(
|
|
186
|
+
300_000,
|
|
187
|
+
Math.max(0, Math.floor(durationUs / 2)),
|
|
188
|
+
);
|
|
189
|
+
const parsed = ManualProjectZoomInputV3Schema.parse({
|
|
190
|
+
...input,
|
|
191
|
+
easing: input.easing ?? { kind: "ease-in-out" },
|
|
192
|
+
enterDurationUs: input.enterDurationUs ?? defaultTransitionUs,
|
|
193
|
+
exitDurationUs: input.exitDurationUs ?? defaultTransitionUs,
|
|
194
|
+
});
|
|
195
|
+
return {
|
|
196
|
+
easing: parsed.easing,
|
|
197
|
+
enterDurationUs: parsed.enterDurationUs,
|
|
198
|
+
exitDurationUs: parsed.exitDurationUs,
|
|
199
|
+
range: parsed.range,
|
|
200
|
+
scale: parsed.scale,
|
|
201
|
+
target: parsed.target,
|
|
202
|
+
zoomId: parsed.zoomId,
|
|
203
|
+
...(parsed.displayId === undefined
|
|
204
|
+
? {}
|
|
205
|
+
: { displayId: parsed.displayId }),
|
|
206
|
+
...(parsed.placementId === undefined
|
|
207
|
+
? {}
|
|
208
|
+
: { placementId: parsed.placementId }),
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export interface PreparedCameraPoseInput {
|
|
213
|
+
readonly centerX: number;
|
|
214
|
+
readonly centerY: number;
|
|
215
|
+
readonly zoom: number;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function preparedCameraPose(
|
|
219
|
+
input: PreparedCameraPoseInput,
|
|
220
|
+
): CameraPose {
|
|
221
|
+
return CameraPoseSchema.parse({
|
|
222
|
+
...input,
|
|
223
|
+
space: "prepared-video-layer-normalized-v1",
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export interface CameraReframeOptions extends Omit<
|
|
228
|
+
ManualProjectCameraMoveInputV2,
|
|
229
|
+
"cameraMoveId" | "keyframes" | "placementId" | "streamId"
|
|
230
|
+
> {
|
|
231
|
+
readonly cameraMoveId: string;
|
|
232
|
+
readonly easing?: Easing;
|
|
233
|
+
readonly from: PreparedCameraPoseInput;
|
|
234
|
+
readonly placementId: string;
|
|
235
|
+
readonly streamId: string;
|
|
236
|
+
readonly to: PreparedCameraPoseInput;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Construct a binding-free manual camera move with exact endpoint keyframes.
|
|
241
|
+
* The checked commit binds it to the current placement synchronization and
|
|
242
|
+
* prepared-layer geometry.
|
|
243
|
+
*/
|
|
244
|
+
export function cameraReframe(
|
|
245
|
+
input: CameraReframeOptions,
|
|
246
|
+
): ManualProjectCameraMoveInputV2 {
|
|
247
|
+
const easing = input.easing ?? { kind: "ease-in-out" };
|
|
248
|
+
return ManualProjectCameraMoveInputV2Schema.parse({
|
|
249
|
+
cameraMoveId: input.cameraMoveId,
|
|
250
|
+
keyframes: [
|
|
251
|
+
{
|
|
252
|
+
outgoingEasing: easing,
|
|
253
|
+
pose: preparedCameraPose(input.from),
|
|
254
|
+
projectTimeUs: input.projectRange.startUs,
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
outgoingEasing: { kind: "linear" },
|
|
258
|
+
pose: preparedCameraPose(input.to),
|
|
259
|
+
projectTimeUs: input.projectRange.endUs,
|
|
260
|
+
},
|
|
261
|
+
],
|
|
262
|
+
placementId: input.placementId,
|
|
263
|
+
projectRange: input.projectRange,
|
|
264
|
+
streamId: input.streamId,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export interface CameraPushOptions extends Omit<CameraReframeOptions, "from"> {
|
|
269
|
+
readonly from?: PreparedCameraPoseInput;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Construct the common two-keyframe push-in move. Use cameraReframe when the
|
|
274
|
+
* endpoint may zoom out or only pan.
|
|
275
|
+
*/
|
|
276
|
+
export function cameraPush(
|
|
277
|
+
input: CameraPushOptions,
|
|
278
|
+
): ManualProjectCameraMoveInputV2 {
|
|
279
|
+
const from = input.from ?? { centerX: 0.5, centerY: 0.5, zoom: 1 };
|
|
280
|
+
if (input.to.zoom <= from.zoom) {
|
|
281
|
+
throw new RangeError("A camera push must increase zoom from its first pose.");
|
|
282
|
+
}
|
|
283
|
+
return cameraReframe({ ...input, from });
|
|
284
|
+
}
|
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
import { ApplicationError } from "../application/errors";
|
|
2
|
+
import {
|
|
3
|
+
canonicalJson,
|
|
4
|
+
sha256Hex,
|
|
5
|
+
} from "../core/canonical-json";
|
|
6
|
+
import {
|
|
7
|
+
isOperationGraphNode,
|
|
8
|
+
type AuthoredWorkflowGraphV1,
|
|
9
|
+
type CandidateDescriptor,
|
|
10
|
+
type GraphInputValue,
|
|
11
|
+
type JsonValue,
|
|
12
|
+
} from "./contracts";
|
|
13
|
+
import type { NodeExecutionPlanningRequest } from "./scheduler";
|
|
14
|
+
import {
|
|
15
|
+
WorkflowFileCandidateSchema,
|
|
16
|
+
type WorkflowFileCandidate,
|
|
17
|
+
} from "./file-candidate";
|
|
18
|
+
|
|
19
|
+
const FILE_CANDIDATE_DOMAIN = "studio.workflow.file-candidate/v1";
|
|
20
|
+
|
|
21
|
+
interface FileClaim {
|
|
22
|
+
readonly bytes?: number;
|
|
23
|
+
readonly mediaType?: string;
|
|
24
|
+
readonly path: string;
|
|
25
|
+
readonly sha256?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function record(value: unknown): value is Readonly<Record<string, unknown>> {
|
|
29
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function reference(value: unknown): boolean {
|
|
33
|
+
return record(value) && Object.hasOwn(value, "$ref");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function field(value: unknown, key: string): unknown {
|
|
37
|
+
return record(value) ? value[key] : undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function claim(value: unknown): FileClaim | undefined {
|
|
41
|
+
if (!record(value) || reference(value) || typeof value.path !== "string") {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
...(typeof value.bytes === "number" ? { bytes: value.bytes } : {}),
|
|
46
|
+
...(typeof value.mediaType === "string"
|
|
47
|
+
? { mediaType: value.mediaType }
|
|
48
|
+
: {}),
|
|
49
|
+
path: value.path,
|
|
50
|
+
...(typeof value.sha256 === "string" ? { sha256: value.sha256 } : {}),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function array(value: unknown): readonly unknown[] {
|
|
55
|
+
return Array.isArray(value) ? value : [];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function optionalClaim(value: unknown): readonly FileClaim[] {
|
|
59
|
+
const parsed = claim(value);
|
|
60
|
+
return parsed === undefined ? [] : [parsed];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function optionalPathClaim(value: unknown): readonly FileClaim[] {
|
|
64
|
+
if (typeof value === "string") return [{ path: value }];
|
|
65
|
+
return optionalClaim(value);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Returns only fields whose operation contract grants local-file authority.
|
|
70
|
+
* Other path-looking values (for example output destinations) are excluded.
|
|
71
|
+
*/
|
|
72
|
+
export function operationFileClaims(
|
|
73
|
+
operation: string,
|
|
74
|
+
input: GraphInputValue | JsonValue,
|
|
75
|
+
): readonly FileClaim[] {
|
|
76
|
+
if (!record(input)) return [];
|
|
77
|
+
if (operation === "slopcamera.studio.run") return optionalClaim(field(input, "bundle"));
|
|
78
|
+
if (operation === "scene.render") {
|
|
79
|
+
return [
|
|
80
|
+
...optionalClaim(field(input, "source")),
|
|
81
|
+
...array(field(input, "assets")).flatMap(asset => optionalClaim(field(asset, "artifact"))),
|
|
82
|
+
];
|
|
83
|
+
}
|
|
84
|
+
if (operation === "media.ingest") {
|
|
85
|
+
return optionalClaim(Reflect.get(input, "source"));
|
|
86
|
+
}
|
|
87
|
+
if (operation === "media.overlay") {
|
|
88
|
+
const source = field(input, "source");
|
|
89
|
+
return record(source)
|
|
90
|
+
? optionalClaim(field(source, "artifact"))
|
|
91
|
+
: [];
|
|
92
|
+
}
|
|
93
|
+
if (operation === "media.html-overlay") {
|
|
94
|
+
return [
|
|
95
|
+
...optionalClaim(field(input, "document")),
|
|
96
|
+
...array(field(input, "resources")).flatMap((resource) => (
|
|
97
|
+
record(resource)
|
|
98
|
+
? optionalClaim(field(resource, "artifact"))
|
|
99
|
+
: []
|
|
100
|
+
)),
|
|
101
|
+
];
|
|
102
|
+
}
|
|
103
|
+
if (
|
|
104
|
+
operation === "media.audio-effects"
|
|
105
|
+
|| operation === "media.color-grade"
|
|
106
|
+
) {
|
|
107
|
+
return optionalClaim(Reflect.get(input, "input"));
|
|
108
|
+
}
|
|
109
|
+
if (
|
|
110
|
+
operation === "slopcamera.diagram.check"
|
|
111
|
+
|| operation === "slopcamera.diagram.render"
|
|
112
|
+
) {
|
|
113
|
+
return optionalPathClaim(Reflect.get(input, "path"));
|
|
114
|
+
}
|
|
115
|
+
if (operation === "slopcamera.image.vectorize") {
|
|
116
|
+
return optionalPathClaim(Reflect.get(input, "inputPath"));
|
|
117
|
+
}
|
|
118
|
+
if (operation === "gateway.image") {
|
|
119
|
+
return [
|
|
120
|
+
...array(Reflect.get(input, "images")).flatMap(
|
|
121
|
+
value => claim(value) ?? [],
|
|
122
|
+
),
|
|
123
|
+
...optionalClaim(Reflect.get(input, "mask")),
|
|
124
|
+
];
|
|
125
|
+
}
|
|
126
|
+
if (operation === "gateway.video") {
|
|
127
|
+
const frames = array(Reflect.get(input, "frames")).flatMap((value) => {
|
|
128
|
+
if (!record(value)) return [];
|
|
129
|
+
return optionalClaim(value.source);
|
|
130
|
+
});
|
|
131
|
+
return [
|
|
132
|
+
...frames,
|
|
133
|
+
...optionalClaim(Reflect.get(input, "promptImage")),
|
|
134
|
+
...array(Reflect.get(input, "references")).flatMap(
|
|
135
|
+
value => claim(value) ?? [],
|
|
136
|
+
),
|
|
137
|
+
];
|
|
138
|
+
}
|
|
139
|
+
if (operation === "gateway.transcription") {
|
|
140
|
+
return optionalClaim(Reflect.get(input, "audio"));
|
|
141
|
+
}
|
|
142
|
+
return [];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function descriptorBody(
|
|
146
|
+
input: Omit<CandidateDescriptor, "descriptorSha256">,
|
|
147
|
+
): Omit<CandidateDescriptor, "descriptorSha256"> {
|
|
148
|
+
return {
|
|
149
|
+
...(input.bytes === undefined ? {} : { bytes: input.bytes }),
|
|
150
|
+
id: input.id,
|
|
151
|
+
kind: input.kind,
|
|
152
|
+
...(input.mediaType === undefined ? {} : { mediaType: input.mediaType }),
|
|
153
|
+
...(input.sha256 === undefined ? {} : { sha256: input.sha256 }),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function fileCandidateDescriptor(
|
|
158
|
+
input: Omit<CandidateDescriptor, "descriptorSha256">,
|
|
159
|
+
): CandidateDescriptor {
|
|
160
|
+
const body = descriptorBody(input);
|
|
161
|
+
return {
|
|
162
|
+
...body,
|
|
163
|
+
descriptorSha256: sha256Hex(
|
|
164
|
+
`${FILE_CANDIDATE_DOMAIN}\0${canonicalJson(body)}`,
|
|
165
|
+
),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function assertDescriptorIntegrity(candidate: CandidateDescriptor): void {
|
|
170
|
+
const expected = fileCandidateDescriptor(descriptorBody(candidate));
|
|
171
|
+
if (candidate.descriptorSha256 !== expected.descriptorSha256) {
|
|
172
|
+
throw new ApplicationError(
|
|
173
|
+
"invalid-data",
|
|
174
|
+
`Static file candidate ${candidate.id} has an invalid descriptor digest.`,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function collectLiteralFileCandidates(
|
|
180
|
+
graph: AuthoredWorkflowGraphV1,
|
|
181
|
+
): readonly CandidateDescriptor[] {
|
|
182
|
+
const claims = new Map<string, FileClaim>();
|
|
183
|
+
for (const node of graph.nodes) {
|
|
184
|
+
if (!isOperationGraphNode(node)) continue;
|
|
185
|
+
const operation = node.executor.operation.kind;
|
|
186
|
+
for (const authored of operationFileClaims(operation, node.input)) {
|
|
187
|
+
const existing = claims.get(authored.path);
|
|
188
|
+
if (
|
|
189
|
+
existing !== undefined
|
|
190
|
+
&& (
|
|
191
|
+
(
|
|
192
|
+
existing.bytes !== undefined
|
|
193
|
+
&& authored.bytes !== undefined
|
|
194
|
+
&& existing.bytes !== authored.bytes
|
|
195
|
+
)
|
|
196
|
+
|| (
|
|
197
|
+
existing.sha256 !== undefined
|
|
198
|
+
&& authored.sha256 !== undefined
|
|
199
|
+
&& existing.sha256 !== authored.sha256
|
|
200
|
+
)
|
|
201
|
+
|| (
|
|
202
|
+
existing.mediaType !== undefined
|
|
203
|
+
&& authored.mediaType !== undefined
|
|
204
|
+
&& existing.mediaType !== authored.mediaType
|
|
205
|
+
)
|
|
206
|
+
)
|
|
207
|
+
) {
|
|
208
|
+
throw new ApplicationError(
|
|
209
|
+
"conflict",
|
|
210
|
+
`File candidate ${authored.path} was authored with conflicting descriptors.`,
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
claims.set(authored.path, {
|
|
214
|
+
...(existing?.bytes ?? authored.bytes) === undefined
|
|
215
|
+
? {}
|
|
216
|
+
: { bytes: existing?.bytes ?? authored.bytes },
|
|
217
|
+
...(existing?.mediaType ?? authored.mediaType) === undefined
|
|
218
|
+
? {}
|
|
219
|
+
: { mediaType: existing?.mediaType ?? authored.mediaType },
|
|
220
|
+
path: authored.path,
|
|
221
|
+
...(existing?.sha256 ?? authored.sha256) === undefined
|
|
222
|
+
? {}
|
|
223
|
+
: { sha256: existing?.sha256 ?? authored.sha256 },
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return [...claims.values()]
|
|
228
|
+
.map(candidate => fileCandidateDescriptor({
|
|
229
|
+
...(candidate.bytes === undefined ? {} : { bytes: candidate.bytes }),
|
|
230
|
+
id: candidate.path,
|
|
231
|
+
kind: "file",
|
|
232
|
+
...(candidate.mediaType === undefined
|
|
233
|
+
? {}
|
|
234
|
+
: { mediaType: candidate.mediaType }),
|
|
235
|
+
...(candidate.sha256 === undefined ? {} : { sha256: candidate.sha256 }),
|
|
236
|
+
}))
|
|
237
|
+
.sort((left, right) => left.id.localeCompare(right.id));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function declaredCandidate(
|
|
241
|
+
declaration: WorkflowFileCandidate,
|
|
242
|
+
): CandidateDescriptor {
|
|
243
|
+
return fileCandidateDescriptor({
|
|
244
|
+
...(declaration.bytes === undefined ? {} : { bytes: declaration.bytes }),
|
|
245
|
+
id: declaration.path,
|
|
246
|
+
kind: "file",
|
|
247
|
+
...(declaration.mediaType === undefined
|
|
248
|
+
? {}
|
|
249
|
+
: { mediaType: declaration.mediaType }),
|
|
250
|
+
...(declaration.sha256 === undefined
|
|
251
|
+
? {}
|
|
252
|
+
: { sha256: declaration.sha256 }),
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export function collectDeclaredFileCandidates(
|
|
257
|
+
value: unknown,
|
|
258
|
+
): readonly CandidateDescriptor[] {
|
|
259
|
+
if (Array.isArray(value)) return value.flatMap(collectDeclaredFileCandidates);
|
|
260
|
+
if (!record(value)) return [];
|
|
261
|
+
if (
|
|
262
|
+
Reflect.get(value, "kind") === "file"
|
|
263
|
+
&& Reflect.get(value, "version") === "slopcamera-workflow-file-candidate-v1"
|
|
264
|
+
) {
|
|
265
|
+
return [declaredCandidate(WorkflowFileCandidateSchema.parse(value))];
|
|
266
|
+
}
|
|
267
|
+
return Object.values(value).flatMap(collectDeclaredFileCandidates);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export function mergeFileCandidateDescriptors(
|
|
271
|
+
groups: readonly (readonly CandidateDescriptor[])[],
|
|
272
|
+
): readonly CandidateDescriptor[] {
|
|
273
|
+
const merged = new Map<string, FileClaim>();
|
|
274
|
+
for (const candidate of groups.flat()) {
|
|
275
|
+
if (candidate.kind !== "file") continue;
|
|
276
|
+
assertDescriptorIntegrity(candidate);
|
|
277
|
+
const existing = merged.get(candidate.id);
|
|
278
|
+
const next: FileClaim = {
|
|
279
|
+
...(candidate.bytes === undefined ? {} : { bytes: candidate.bytes }),
|
|
280
|
+
...(candidate.mediaType === undefined
|
|
281
|
+
? {}
|
|
282
|
+
: { mediaType: candidate.mediaType }),
|
|
283
|
+
path: candidate.id,
|
|
284
|
+
...(candidate.sha256 === undefined ? {} : { sha256: candidate.sha256 }),
|
|
285
|
+
};
|
|
286
|
+
if (
|
|
287
|
+
existing !== undefined
|
|
288
|
+
&& (
|
|
289
|
+
(
|
|
290
|
+
existing.bytes !== undefined
|
|
291
|
+
&& next.bytes !== undefined
|
|
292
|
+
&& existing.bytes !== next.bytes
|
|
293
|
+
)
|
|
294
|
+
|| (
|
|
295
|
+
existing.sha256 !== undefined
|
|
296
|
+
&& next.sha256 !== undefined
|
|
297
|
+
&& existing.sha256 !== next.sha256
|
|
298
|
+
)
|
|
299
|
+
|| (
|
|
300
|
+
existing.mediaType !== undefined
|
|
301
|
+
&& next.mediaType !== undefined
|
|
302
|
+
&& existing.mediaType !== next.mediaType
|
|
303
|
+
)
|
|
304
|
+
)
|
|
305
|
+
) {
|
|
306
|
+
throw new ApplicationError(
|
|
307
|
+
"conflict",
|
|
308
|
+
`File candidate ${candidate.id} was declared with conflicting descriptors.`,
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
merged.set(candidate.id, {
|
|
312
|
+
...(existing?.bytes ?? next.bytes) === undefined
|
|
313
|
+
? {}
|
|
314
|
+
: { bytes: existing?.bytes ?? next.bytes },
|
|
315
|
+
...(existing?.mediaType ?? next.mediaType) === undefined
|
|
316
|
+
? {}
|
|
317
|
+
: { mediaType: existing?.mediaType ?? next.mediaType },
|
|
318
|
+
path: candidate.id,
|
|
319
|
+
...(existing?.sha256 ?? next.sha256) === undefined
|
|
320
|
+
? {}
|
|
321
|
+
: { sha256: existing?.sha256 ?? next.sha256 },
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
return [...merged.values()]
|
|
325
|
+
.map(candidate => fileCandidateDescriptor({
|
|
326
|
+
...(candidate.bytes === undefined ? {} : { bytes: candidate.bytes }),
|
|
327
|
+
id: candidate.path,
|
|
328
|
+
kind: "file",
|
|
329
|
+
...(candidate.mediaType === undefined
|
|
330
|
+
? {}
|
|
331
|
+
: { mediaType: candidate.mediaType }),
|
|
332
|
+
...(candidate.sha256 === undefined ? {} : { sha256: candidate.sha256 }),
|
|
333
|
+
}))
|
|
334
|
+
.sort((left, right) => left.id.localeCompare(right.id));
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function exactClaimMatches(left: FileClaim, right: FileClaim): boolean {
|
|
338
|
+
return left.path === right.path
|
|
339
|
+
&& (left.bytes === undefined || left.bytes === right.bytes)
|
|
340
|
+
&& (left.sha256 === undefined || left.sha256 === right.sha256)
|
|
341
|
+
&& (left.mediaType === undefined || left.mediaType === right.mediaType);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function candidateMatches(
|
|
345
|
+
requested: FileClaim,
|
|
346
|
+
candidate: CandidateDescriptor,
|
|
347
|
+
): boolean {
|
|
348
|
+
return requested.path === candidate.id
|
|
349
|
+
&& (candidate.bytes === undefined || candidate.bytes === requested.bytes)
|
|
350
|
+
&& (
|
|
351
|
+
candidate.sha256 === undefined
|
|
352
|
+
|| candidate.sha256 === requested.sha256
|
|
353
|
+
)
|
|
354
|
+
&& (
|
|
355
|
+
candidate.mediaType === undefined
|
|
356
|
+
|| candidate.mediaType === requested.mediaType
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function collectExactClaims(value: unknown): readonly FileClaim[] {
|
|
361
|
+
if (Array.isArray(value)) return value.flatMap(collectExactClaims);
|
|
362
|
+
if (!record(value)) return [];
|
|
363
|
+
const direct = claim(value);
|
|
364
|
+
const nested = Object.values(value).flatMap(collectExactClaims);
|
|
365
|
+
if (
|
|
366
|
+
direct === undefined
|
|
367
|
+
|| direct.bytes === undefined
|
|
368
|
+
|| direct.sha256 === undefined
|
|
369
|
+
) {
|
|
370
|
+
return nested;
|
|
371
|
+
}
|
|
372
|
+
return [direct, ...nested];
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function hostDependencyClaims(
|
|
376
|
+
request: NodeExecutionPlanningRequest,
|
|
377
|
+
): readonly FileClaim[] {
|
|
378
|
+
const nodes = new Map(
|
|
379
|
+
request.graphPlan.graph.nodes.map(node => [node.key, node]),
|
|
380
|
+
);
|
|
381
|
+
return Object.entries(request.dependencyOutputs).flatMap(
|
|
382
|
+
([nodeKey, output]) => {
|
|
383
|
+
const node = nodes.get(nodeKey);
|
|
384
|
+
return node !== undefined && isOperationGraphNode(node)
|
|
385
|
+
? collectExactClaims(output.value)
|
|
386
|
+
: [];
|
|
387
|
+
},
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
export function assertOperationFileProvenance(
|
|
392
|
+
request: NodeExecutionPlanningRequest,
|
|
393
|
+
): void {
|
|
394
|
+
const claims = operationFileClaims(
|
|
395
|
+
request.operation.kind,
|
|
396
|
+
request.resolvedInput,
|
|
397
|
+
);
|
|
398
|
+
if (claims.length === 0) return;
|
|
399
|
+
const candidates = request.graphPlan.staticBindings.candidates.filter(
|
|
400
|
+
candidate => candidate.kind === "file",
|
|
401
|
+
);
|
|
402
|
+
for (const candidate of candidates) assertDescriptorIntegrity(candidate);
|
|
403
|
+
const hostClaims = hostDependencyClaims(request);
|
|
404
|
+
for (const requested of claims) {
|
|
405
|
+
const authorizedByPlan = candidates.some(candidate =>
|
|
406
|
+
candidateMatches(requested, candidate));
|
|
407
|
+
const authorizedByHost = hostClaims.some(candidate =>
|
|
408
|
+
exactClaimMatches(requested, candidate));
|
|
409
|
+
if (!authorizedByPlan && !authorizedByHost) {
|
|
410
|
+
throw new ApplicationError(
|
|
411
|
+
"authorization-required",
|
|
412
|
+
`Node ${request.node.key} received undeclared local media: ${requested.path}`,
|
|
413
|
+
{
|
|
414
|
+
operation: request.operation.kind,
|
|
415
|
+
path: requested.path,
|
|
416
|
+
},
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|