@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,775 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto"
|
|
2
|
+
import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises"
|
|
3
|
+
import { dirname, join, resolve } from "node:path"
|
|
4
|
+
import { builtInIcons } from "./icons.js"
|
|
5
|
+
import { lintDiagram } from "./lint.js"
|
|
6
|
+
import { parseDiagramSpec } from "./parse.js"
|
|
7
|
+
import { renderPng, renderSvg } from "./render.js"
|
|
8
|
+
import { serializeTldr } from "./tldr.js"
|
|
9
|
+
import type { DiagramConfig, LintFinding, RenderArtifacts } from "./types.js"
|
|
10
|
+
import {
|
|
11
|
+
generateSlopcameraImageFile,
|
|
12
|
+
slopcameraMaximumPromptBytes,
|
|
13
|
+
type GeneratedSlopcameraImageFile,
|
|
14
|
+
type SlopcameraGenerateDependencies,
|
|
15
|
+
type SlopcameraImageModel,
|
|
16
|
+
} from "./generate.js"
|
|
17
|
+
import {
|
|
18
|
+
vectorizeImage,
|
|
19
|
+
type VectorizeReceipt,
|
|
20
|
+
} from "./vectorize/index.js"
|
|
21
|
+
import {
|
|
22
|
+
createDefaultHostResourceCoordinator,
|
|
23
|
+
type HostResourceClaim,
|
|
24
|
+
type HostResourceCoordinator,
|
|
25
|
+
type HostResourceLease,
|
|
26
|
+
} from "./host-resources.js"
|
|
27
|
+
|
|
28
|
+
export const slopcameraOperationCodes = [
|
|
29
|
+
"slopcamera.diagram.check",
|
|
30
|
+
"slopcamera.diagram.render",
|
|
31
|
+
"slopcamera.image.vectorize",
|
|
32
|
+
"slopcamera.image.generate",
|
|
33
|
+
] as const
|
|
34
|
+
|
|
35
|
+
export type SlopcameraOperationCode = (typeof slopcameraOperationCodes)[number]
|
|
36
|
+
|
|
37
|
+
export interface SlopcameraOperationDescriptor {
|
|
38
|
+
readonly code: SlopcameraOperationCode
|
|
39
|
+
readonly title: string
|
|
40
|
+
readonly description: string
|
|
41
|
+
readonly execution: "gateway" | "local"
|
|
42
|
+
readonly authentication: "environment" | "none"
|
|
43
|
+
readonly destructive: boolean
|
|
44
|
+
readonly idempotent: boolean
|
|
45
|
+
readonly inputSchema: Readonly<Record<string, unknown>>
|
|
46
|
+
/** Immutable operation-owned physical host admission claims. */
|
|
47
|
+
readonly resources: readonly HostResourceClaim[]
|
|
48
|
+
readonly transport?: {
|
|
49
|
+
readonly method: "POST"
|
|
50
|
+
readonly authority: "https://ai-gateway.vercel.sh/v4/ai"
|
|
51
|
+
readonly authorization: "bearer"
|
|
52
|
+
readonly retry: "never"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export class SlopcameraOperationError extends Error {
|
|
57
|
+
readonly code:
|
|
58
|
+
| "INVALID_OPERATION"
|
|
59
|
+
| "INVALID_OPERATION_INPUT"
|
|
60
|
+
| "INVALID_SEARCH"
|
|
61
|
+
|
|
62
|
+
constructor(
|
|
63
|
+
code: SlopcameraOperationError["code"],
|
|
64
|
+
message: string,
|
|
65
|
+
) {
|
|
66
|
+
super(`[${code}] ${message}`)
|
|
67
|
+
this.name = "SlopcameraOperationError"
|
|
68
|
+
this.code = code
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const modelSchema = {
|
|
73
|
+
type: "string",
|
|
74
|
+
minLength: 3,
|
|
75
|
+
maxLength: 256,
|
|
76
|
+
pattern: "^[a-zA-Z0-9][a-zA-Z0-9._-]*/[a-zA-Z0-9][a-zA-Z0-9._:-]*$",
|
|
77
|
+
} as const
|
|
78
|
+
|
|
79
|
+
const pathSchema = {
|
|
80
|
+
type: "string",
|
|
81
|
+
minLength: 1,
|
|
82
|
+
maxLength: 4_096,
|
|
83
|
+
} as const
|
|
84
|
+
|
|
85
|
+
function deepFreeze<T>(value: T): T {
|
|
86
|
+
if (typeof value !== "object" || value === null || Object.isFrozen(value)) {
|
|
87
|
+
return value
|
|
88
|
+
}
|
|
89
|
+
for (const nested of Object.values(value)) deepFreeze(nested)
|
|
90
|
+
return Object.freeze(value)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export const slopcameraOperationRegistry: readonly SlopcameraOperationDescriptor[] =
|
|
94
|
+
deepFreeze([
|
|
95
|
+
{
|
|
96
|
+
code: "slopcamera.diagram.check",
|
|
97
|
+
title: "Check diagram",
|
|
98
|
+
description:
|
|
99
|
+
"Parse and lint a checked Slopcamera diagram source without changing its files.",
|
|
100
|
+
execution: "local",
|
|
101
|
+
authentication: "none",
|
|
102
|
+
destructive: false,
|
|
103
|
+
idempotent: true,
|
|
104
|
+
inputSchema: {
|
|
105
|
+
type: "object",
|
|
106
|
+
additionalProperties: false,
|
|
107
|
+
required: ["path"],
|
|
108
|
+
properties: { path: pathSchema },
|
|
109
|
+
},
|
|
110
|
+
resources: [
|
|
111
|
+
{ resource: "cpu", amount: 1 },
|
|
112
|
+
{ resource: "local-io", amount: 1 },
|
|
113
|
+
],
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
code: "slopcamera.diagram.render",
|
|
117
|
+
title: "Render diagram",
|
|
118
|
+
description:
|
|
119
|
+
"Render a checked Slopcamera diagram source to its replaceable light, dark, PNG, SVG, and tldraw artifacts.",
|
|
120
|
+
execution: "local",
|
|
121
|
+
authentication: "none",
|
|
122
|
+
destructive: true,
|
|
123
|
+
idempotent: true,
|
|
124
|
+
inputSchema: {
|
|
125
|
+
type: "object",
|
|
126
|
+
additionalProperties: false,
|
|
127
|
+
required: ["path"],
|
|
128
|
+
properties: {
|
|
129
|
+
path: pathSchema,
|
|
130
|
+
outDirectory: pathSchema,
|
|
131
|
+
scale: {
|
|
132
|
+
type: "number",
|
|
133
|
+
exclusiveMinimum: 0,
|
|
134
|
+
maximum: 4,
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
resources: [
|
|
139
|
+
{ resource: "cpu", amount: 1 },
|
|
140
|
+
{ resource: "local-io", amount: 1 },
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
code: "slopcamera.image.vectorize",
|
|
145
|
+
title: "Vectorize image",
|
|
146
|
+
description:
|
|
147
|
+
"Convert a local caller-owned raster into a bounded inert SVG without authentication or network access.",
|
|
148
|
+
execution: "local",
|
|
149
|
+
authentication: "none",
|
|
150
|
+
destructive: true,
|
|
151
|
+
idempotent: false,
|
|
152
|
+
inputSchema: {
|
|
153
|
+
type: "object",
|
|
154
|
+
additionalProperties: false,
|
|
155
|
+
required: ["inputPath", "outputPath"],
|
|
156
|
+
properties: {
|
|
157
|
+
inputPath: pathSchema,
|
|
158
|
+
outputPath: pathSchema,
|
|
159
|
+
duotone: {
|
|
160
|
+
type: "array",
|
|
161
|
+
minItems: 2,
|
|
162
|
+
maxItems: 2,
|
|
163
|
+
items: {
|
|
164
|
+
type: "string",
|
|
165
|
+
pattern: "^#[a-fA-F0-9]{3}(?:[a-fA-F0-9]{3})?$",
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
alphaCutoff: { type: "integer", minimum: 1, maximum: 64 },
|
|
169
|
+
timeoutMs: { type: "integer", minimum: 1, maximum: 300_000 },
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
resources: [
|
|
173
|
+
{ resource: "cpu", amount: 1 },
|
|
174
|
+
{ resource: "local-io", amount: 1 },
|
|
175
|
+
],
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
code: "slopcamera.image.generate",
|
|
179
|
+
title: "Generate image",
|
|
180
|
+
description:
|
|
181
|
+
"Generate one bounded image directly through Vercel AI Gateway with an environment credential and no client retry.",
|
|
182
|
+
execution: "gateway",
|
|
183
|
+
authentication: "environment",
|
|
184
|
+
destructive: true,
|
|
185
|
+
idempotent: false,
|
|
186
|
+
inputSchema: {
|
|
187
|
+
type: "object",
|
|
188
|
+
additionalProperties: false,
|
|
189
|
+
required: ["model", "prompt", "outputPath"],
|
|
190
|
+
properties: {
|
|
191
|
+
model: modelSchema,
|
|
192
|
+
prompt: {
|
|
193
|
+
type: "string",
|
|
194
|
+
minLength: 1,
|
|
195
|
+
maxLength: slopcameraMaximumPromptBytes,
|
|
196
|
+
},
|
|
197
|
+
outputPath: pathSchema,
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
resources: [
|
|
201
|
+
{ resource: "local-io", amount: 1 },
|
|
202
|
+
{ resource: "network", amount: 1 },
|
|
203
|
+
{ resource: "paid-call", amount: 1 },
|
|
204
|
+
],
|
|
205
|
+
transport: {
|
|
206
|
+
method: "POST",
|
|
207
|
+
authority: "https://ai-gateway.vercel.sh/v4/ai",
|
|
208
|
+
authorization: "bearer",
|
|
209
|
+
retry: "never",
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
] satisfies readonly SlopcameraOperationDescriptor[])
|
|
213
|
+
|
|
214
|
+
export interface CheckSlopcameraOperationInput {
|
|
215
|
+
readonly path: string
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export interface RenderSlopcameraOperationInput extends CheckSlopcameraOperationInput {
|
|
219
|
+
readonly outDirectory?: string
|
|
220
|
+
readonly scale?: number
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export interface VectorizeSlopcameraOperationInput {
|
|
224
|
+
readonly inputPath: string
|
|
225
|
+
readonly outputPath: string
|
|
226
|
+
readonly duotone?: readonly [string, string]
|
|
227
|
+
readonly alphaCutoff?: number
|
|
228
|
+
readonly timeoutMs?: number
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export interface GenerateSlopcameraOperationInput {
|
|
232
|
+
readonly model: SlopcameraImageModel
|
|
233
|
+
readonly prompt: string
|
|
234
|
+
readonly outputPath: string
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export interface SlopcameraOperationInputMap {
|
|
238
|
+
readonly "slopcamera.diagram.check": CheckSlopcameraOperationInput
|
|
239
|
+
readonly "slopcamera.diagram.render": RenderSlopcameraOperationInput
|
|
240
|
+
readonly "slopcamera.image.vectorize": VectorizeSlopcameraOperationInput
|
|
241
|
+
readonly "slopcamera.image.generate": GenerateSlopcameraOperationInput
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export interface SlopcameraOperationResultMap {
|
|
245
|
+
readonly "slopcamera.diagram.check": {
|
|
246
|
+
readonly findings: readonly LintFinding[]
|
|
247
|
+
readonly configPath: null
|
|
248
|
+
}
|
|
249
|
+
readonly "slopcamera.diagram.render": {
|
|
250
|
+
readonly artifacts: RenderArtifacts
|
|
251
|
+
readonly findings: readonly LintFinding[]
|
|
252
|
+
readonly configPath: null
|
|
253
|
+
}
|
|
254
|
+
readonly "slopcamera.image.vectorize": {
|
|
255
|
+
readonly outputPath: string
|
|
256
|
+
readonly receipt: VectorizeReceipt
|
|
257
|
+
}
|
|
258
|
+
readonly "slopcamera.image.generate": GeneratedSlopcameraImageFile
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function operationFailure(message: string): never {
|
|
262
|
+
throw new SlopcameraOperationError("INVALID_OPERATION_INPUT", message)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
266
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function record(
|
|
270
|
+
value: unknown,
|
|
271
|
+
allowedKeys: readonly string[],
|
|
272
|
+
): Record<string, unknown> {
|
|
273
|
+
if (!isRecord(value)) operationFailure("Operation input must be an object.")
|
|
274
|
+
const unknown = Object.keys(value).filter((key) => !allowedKeys.includes(key))
|
|
275
|
+
if (unknown.length > 0) {
|
|
276
|
+
operationFailure(`Unsupported operation input field: ${unknown[0]}.`)
|
|
277
|
+
}
|
|
278
|
+
return value
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function pathValue(value: unknown, name: string): string {
|
|
282
|
+
if (
|
|
283
|
+
typeof value !== "string" ||
|
|
284
|
+
value.length < 1 ||
|
|
285
|
+
value.length > 4_096 ||
|
|
286
|
+
value.includes("\0")
|
|
287
|
+
) {
|
|
288
|
+
operationFailure(`${name} must be a non-empty bounded local path.`)
|
|
289
|
+
}
|
|
290
|
+
return value
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function parseCheck(value: unknown): CheckSlopcameraOperationInput {
|
|
294
|
+
const input = record(value, ["path"])
|
|
295
|
+
return { path: pathValue(input.path, "path") }
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function parseRender(value: unknown): RenderSlopcameraOperationInput {
|
|
299
|
+
const input = record(value, ["path", "outDirectory", "scale"])
|
|
300
|
+
const scale = input.scale
|
|
301
|
+
if (
|
|
302
|
+
scale !== undefined &&
|
|
303
|
+
(typeof scale !== "number" ||
|
|
304
|
+
!Number.isFinite(scale) ||
|
|
305
|
+
scale <= 0 ||
|
|
306
|
+
scale > 4)
|
|
307
|
+
) {
|
|
308
|
+
operationFailure("scale must be greater than zero and no more than 4.")
|
|
309
|
+
}
|
|
310
|
+
return {
|
|
311
|
+
path: pathValue(input.path, "path"),
|
|
312
|
+
...(input.outDirectory === undefined
|
|
313
|
+
? {}
|
|
314
|
+
: { outDirectory: pathValue(input.outDirectory, "outDirectory") }),
|
|
315
|
+
...(scale === undefined ? {} : { scale }),
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function parseVectorize(value: unknown): VectorizeSlopcameraOperationInput {
|
|
320
|
+
const input = record(value, [
|
|
321
|
+
"inputPath",
|
|
322
|
+
"outputPath",
|
|
323
|
+
"duotone",
|
|
324
|
+
"alphaCutoff",
|
|
325
|
+
"timeoutMs",
|
|
326
|
+
])
|
|
327
|
+
const inputPath = pathValue(input.inputPath, "inputPath")
|
|
328
|
+
const outputPath = pathValue(input.outputPath, "outputPath")
|
|
329
|
+
if (!outputPath.toLowerCase().endsWith(".svg")) {
|
|
330
|
+
operationFailure("outputPath must end in .svg.")
|
|
331
|
+
}
|
|
332
|
+
const duotone = input.duotone
|
|
333
|
+
if (
|
|
334
|
+
duotone !== undefined &&
|
|
335
|
+
(!Array.isArray(duotone) ||
|
|
336
|
+
duotone.length !== 2 ||
|
|
337
|
+
duotone.some(
|
|
338
|
+
(color) =>
|
|
339
|
+
typeof color !== "string" ||
|
|
340
|
+
!/^#[a-f0-9]{3}(?:[a-f0-9]{3})?$/iu.test(color),
|
|
341
|
+
))
|
|
342
|
+
) {
|
|
343
|
+
operationFailure("duotone must contain exactly two #rgb or #rrggbb colors.")
|
|
344
|
+
}
|
|
345
|
+
const alphaCutoff = input.alphaCutoff
|
|
346
|
+
if (
|
|
347
|
+
alphaCutoff !== undefined &&
|
|
348
|
+
(!Number.isInteger(alphaCutoff) ||
|
|
349
|
+
(alphaCutoff as number) < 1 ||
|
|
350
|
+
(alphaCutoff as number) > 64)
|
|
351
|
+
) {
|
|
352
|
+
operationFailure("alphaCutoff must be an integer from 1 through 64.")
|
|
353
|
+
}
|
|
354
|
+
const timeoutMs = input.timeoutMs
|
|
355
|
+
if (
|
|
356
|
+
timeoutMs !== undefined &&
|
|
357
|
+
(!Number.isInteger(timeoutMs) ||
|
|
358
|
+
(timeoutMs as number) < 1 ||
|
|
359
|
+
(timeoutMs as number) > 300_000)
|
|
360
|
+
) {
|
|
361
|
+
operationFailure("timeoutMs must be an integer from 1 through 300000.")
|
|
362
|
+
}
|
|
363
|
+
return {
|
|
364
|
+
inputPath,
|
|
365
|
+
outputPath,
|
|
366
|
+
...(duotone === undefined
|
|
367
|
+
? {}
|
|
368
|
+
: { duotone: duotone as unknown as readonly [string, string] }),
|
|
369
|
+
...(alphaCutoff === undefined ? {} : { alphaCutoff: alphaCutoff as number }),
|
|
370
|
+
...(timeoutMs === undefined ? {} : { timeoutMs: timeoutMs as number }),
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function parseGenerate(value: unknown): GenerateSlopcameraOperationInput {
|
|
375
|
+
const input = record(value, ["model", "prompt", "outputPath"])
|
|
376
|
+
if (
|
|
377
|
+
typeof input.model !== "string" ||
|
|
378
|
+
input.model.length > 256 ||
|
|
379
|
+
!/^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._:-]*$/iu.test(input.model)
|
|
380
|
+
) {
|
|
381
|
+
operationFailure("model must be a bounded Vercel AI Gateway provider/model id.")
|
|
382
|
+
}
|
|
383
|
+
if (
|
|
384
|
+
typeof input.prompt !== "string" ||
|
|
385
|
+
input.prompt.trim().length < 1 ||
|
|
386
|
+
/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/u.test(input.prompt) ||
|
|
387
|
+
Buffer.byteLength(input.prompt, "utf8") > slopcameraMaximumPromptBytes
|
|
388
|
+
) {
|
|
389
|
+
operationFailure(
|
|
390
|
+
`prompt must be non-empty and no more than ${slopcameraMaximumPromptBytes} UTF-8 bytes.`,
|
|
391
|
+
)
|
|
392
|
+
}
|
|
393
|
+
const outputPath = pathValue(input.outputPath, "outputPath")
|
|
394
|
+
if (!/\.(?:jpe?g|png|webp)$/iu.test(outputPath)) {
|
|
395
|
+
operationFailure("outputPath must end in .png, .jpg, .jpeg, or .webp.")
|
|
396
|
+
}
|
|
397
|
+
return {
|
|
398
|
+
model: input.model as SlopcameraImageModel,
|
|
399
|
+
prompt: input.prompt,
|
|
400
|
+
outputPath,
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
export function parseSlopcameraOperationInput<C extends SlopcameraOperationCode>(
|
|
405
|
+
code: C,
|
|
406
|
+
input: unknown,
|
|
407
|
+
): SlopcameraOperationInputMap[C] {
|
|
408
|
+
switch (code) {
|
|
409
|
+
case "slopcamera.diagram.check":
|
|
410
|
+
return parseCheck(input) as SlopcameraOperationInputMap[C]
|
|
411
|
+
case "slopcamera.diagram.render":
|
|
412
|
+
return parseRender(input) as SlopcameraOperationInputMap[C]
|
|
413
|
+
case "slopcamera.image.vectorize":
|
|
414
|
+
return parseVectorize(input) as SlopcameraOperationInputMap[C]
|
|
415
|
+
case "slopcamera.image.generate":
|
|
416
|
+
return parseGenerate(input) as SlopcameraOperationInputMap[C]
|
|
417
|
+
default:
|
|
418
|
+
throw new SlopcameraOperationError(
|
|
419
|
+
"INVALID_OPERATION",
|
|
420
|
+
"Unknown Slopcamera operation code.",
|
|
421
|
+
)
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
export function isSlopcameraOperationCode(
|
|
426
|
+
value: string,
|
|
427
|
+
): value is SlopcameraOperationCode {
|
|
428
|
+
return slopcameraOperationCodes.includes(value as SlopcameraOperationCode)
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
export function slopcameraOperationHostResourceClaims(
|
|
432
|
+
code: SlopcameraOperationCode,
|
|
433
|
+
): readonly HostResourceClaim[] {
|
|
434
|
+
const descriptor = slopcameraOperationRegistry.find(
|
|
435
|
+
(candidate) => candidate.code === code,
|
|
436
|
+
)
|
|
437
|
+
if (descriptor === undefined) {
|
|
438
|
+
throw new SlopcameraOperationError(
|
|
439
|
+
"INVALID_OPERATION",
|
|
440
|
+
"Unknown Slopcamera operation code.",
|
|
441
|
+
)
|
|
442
|
+
}
|
|
443
|
+
return descriptor.resources
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
export function searchSlopcameraOperations(
|
|
447
|
+
query = "",
|
|
448
|
+
limit = slopcameraOperationRegistry.length,
|
|
449
|
+
): readonly SlopcameraOperationDescriptor[] {
|
|
450
|
+
if (
|
|
451
|
+
typeof query !== "string" ||
|
|
452
|
+
query.length > 200 ||
|
|
453
|
+
/[\u0000-\u001f\u007f]/u.test(query) ||
|
|
454
|
+
!Number.isInteger(limit) ||
|
|
455
|
+
limit < 1 ||
|
|
456
|
+
limit > 20
|
|
457
|
+
) {
|
|
458
|
+
throw new SlopcameraOperationError(
|
|
459
|
+
"INVALID_SEARCH",
|
|
460
|
+
"Search requires a bounded query and a limit from 1 through 20.",
|
|
461
|
+
)
|
|
462
|
+
}
|
|
463
|
+
const terms = query
|
|
464
|
+
.toLowerCase()
|
|
465
|
+
.split(/\s+/u)
|
|
466
|
+
.filter((term) => term.length > 0)
|
|
467
|
+
return slopcameraOperationRegistry
|
|
468
|
+
.filter((operation) => {
|
|
469
|
+
const haystack =
|
|
470
|
+
`${operation.code} ${operation.title} ${operation.description}`.toLowerCase()
|
|
471
|
+
return terms.every((term) => haystack.includes(term))
|
|
472
|
+
})
|
|
473
|
+
.slice(0, limit)
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export interface SlopcameraOperationDependencies extends SlopcameraGenerateDependencies {
|
|
477
|
+
/** Callback-scoped host authority inherited by operation subprocesses. */
|
|
478
|
+
readonly inheritedFileDescriptors?: readonly number[]
|
|
479
|
+
/** Optional coordinator override for deterministic hosts and tests. */
|
|
480
|
+
readonly hostResourceCoordinator?: HostResourceCoordinator
|
|
481
|
+
readonly signal?: AbortSignal
|
|
482
|
+
readonly waitTimeoutMilliseconds?: number
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
export interface SlopcameraOperationHostAdmissionOptions {
|
|
486
|
+
readonly hostResourceCoordinator?: HostResourceCoordinator
|
|
487
|
+
readonly signal?: AbortSignal
|
|
488
|
+
readonly waitTimeoutMilliseconds?: number
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function operationDependenciesWithLease(
|
|
492
|
+
dependencies: SlopcameraOperationDependencies,
|
|
493
|
+
lease: HostResourceLease,
|
|
494
|
+
): SlopcameraOperationDependencies {
|
|
495
|
+
const inheritedFileDescriptors = [
|
|
496
|
+
...(dependencies.inheritedFileDescriptors ?? []),
|
|
497
|
+
lease.inheritedFileDescriptor,
|
|
498
|
+
].filter((descriptor, index, descriptors) => (
|
|
499
|
+
descriptors.indexOf(descriptor) === index
|
|
500
|
+
))
|
|
501
|
+
if (
|
|
502
|
+
inheritedFileDescriptors.length > 16
|
|
503
|
+
|| inheritedFileDescriptors.some((descriptor) => (
|
|
504
|
+
!Number.isSafeInteger(descriptor)
|
|
505
|
+
|| descriptor < 0
|
|
506
|
+
|| descriptor > 2_147_483_647
|
|
507
|
+
))
|
|
508
|
+
) {
|
|
509
|
+
throw new SlopcameraOperationError(
|
|
510
|
+
"INVALID_OPERATION_INPUT",
|
|
511
|
+
"Operation host-resource inheritance exceeds its descriptor bound.",
|
|
512
|
+
)
|
|
513
|
+
}
|
|
514
|
+
const {
|
|
515
|
+
hostResourceCoordinator: _hostResourceCoordinator,
|
|
516
|
+
signal: _signal,
|
|
517
|
+
waitTimeoutMilliseconds: _waitTimeoutMilliseconds,
|
|
518
|
+
...operationDependencies
|
|
519
|
+
} = dependencies
|
|
520
|
+
return {
|
|
521
|
+
...operationDependencies,
|
|
522
|
+
inheritedFileDescriptors,
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
export async function withSlopcameraOperationHostAdmission<T>(
|
|
527
|
+
code: SlopcameraOperationCode,
|
|
528
|
+
callback: (lease: HostResourceLease) => T | Promise<T>,
|
|
529
|
+
options: SlopcameraOperationHostAdmissionOptions = {},
|
|
530
|
+
): Promise<T> {
|
|
531
|
+
const coordinator = options.hostResourceCoordinator
|
|
532
|
+
?? createDefaultHostResourceCoordinator()
|
|
533
|
+
return await coordinator.withLease(
|
|
534
|
+
slopcameraOperationHostResourceClaims(code),
|
|
535
|
+
async (lease) => {
|
|
536
|
+
await lease.assertOwned()
|
|
537
|
+
return await callback(lease)
|
|
538
|
+
},
|
|
539
|
+
{
|
|
540
|
+
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
541
|
+
...(options.waitTimeoutMilliseconds === undefined
|
|
542
|
+
? {}
|
|
543
|
+
: { waitTimeoutMilliseconds: options.waitTimeoutMilliseconds }),
|
|
544
|
+
},
|
|
545
|
+
)
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
const operationBuiltInConfig: DiagramConfig = Object.freeze({
|
|
549
|
+
icons: builtInIcons,
|
|
550
|
+
})
|
|
551
|
+
|
|
552
|
+
async function readOperationDiagram(path: string) {
|
|
553
|
+
const absolutePath = resolve(path)
|
|
554
|
+
let value: unknown
|
|
555
|
+
try {
|
|
556
|
+
value = JSON.parse(await readFile(absolutePath, "utf8"))
|
|
557
|
+
} catch (cause) {
|
|
558
|
+
throw new SlopcameraOperationError(
|
|
559
|
+
"INVALID_OPERATION_INPUT",
|
|
560
|
+
"Diagram source could not be read as JSON.",
|
|
561
|
+
)
|
|
562
|
+
}
|
|
563
|
+
const spec = parseDiagramSpec(value)
|
|
564
|
+
for (const shape of spec.shapes) {
|
|
565
|
+
if (
|
|
566
|
+
(shape.type === "rect" || shape.type === "ellipse") &&
|
|
567
|
+
shape.icon !== undefined &&
|
|
568
|
+
!Object.hasOwn(builtInIcons, shape.icon)
|
|
569
|
+
) {
|
|
570
|
+
throw new SlopcameraOperationError(
|
|
571
|
+
"INVALID_OPERATION_INPUT",
|
|
572
|
+
"Diagram requests an unavailable built-in icon.",
|
|
573
|
+
)
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
return { absolutePath, spec }
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
async function atomicOperationWrite(
|
|
580
|
+
path: string,
|
|
581
|
+
value: string | Uint8Array,
|
|
582
|
+
): Promise<void> {
|
|
583
|
+
const temporaryPath = join(
|
|
584
|
+
dirname(path),
|
|
585
|
+
`.${randomUUID()}.slopcamera-operation.tmp`,
|
|
586
|
+
)
|
|
587
|
+
try {
|
|
588
|
+
await writeFile(temporaryPath, value, { flag: "wx" })
|
|
589
|
+
await rename(temporaryPath, path)
|
|
590
|
+
} finally {
|
|
591
|
+
await rm(temporaryPath, { force: true }).catch(() => undefined)
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
async function checkOperationDiagram(path: string) {
|
|
596
|
+
const { spec } = await readOperationDiagram(path)
|
|
597
|
+
return {
|
|
598
|
+
findings: lintDiagram(spec),
|
|
599
|
+
configPath: null,
|
|
600
|
+
} as const
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
async function renderOperationDiagram(
|
|
604
|
+
input: RenderSlopcameraOperationInput,
|
|
605
|
+
) {
|
|
606
|
+
const { absolutePath, spec } = await readOperationDiagram(input.path)
|
|
607
|
+
const outputDirectory = resolve(input.outDirectory ?? dirname(absolutePath))
|
|
608
|
+
const scale = input.scale ?? 2
|
|
609
|
+
const [light, dark] = await Promise.all([
|
|
610
|
+
renderSvg(spec, "light", operationBuiltInConfig),
|
|
611
|
+
renderSvg(spec, "dark", operationBuiltInConfig),
|
|
612
|
+
])
|
|
613
|
+
const [lightPng, darkPng] = [
|
|
614
|
+
renderPng(light, operationBuiltInConfig, scale),
|
|
615
|
+
renderPng(dark, operationBuiltInConfig, scale),
|
|
616
|
+
]
|
|
617
|
+
const artifacts = {
|
|
618
|
+
spec: absolutePath,
|
|
619
|
+
tldr: join(outputDirectory, `${spec.name}.tldr`),
|
|
620
|
+
lightSvg: join(outputDirectory, `${spec.name}.light.svg`),
|
|
621
|
+
darkSvg: join(outputDirectory, `${spec.name}.dark.svg`),
|
|
622
|
+
lightPng: join(outputDirectory, `${spec.name}.light.png`),
|
|
623
|
+
darkPng: join(outputDirectory, `${spec.name}.dark.png`),
|
|
624
|
+
} satisfies RenderArtifacts
|
|
625
|
+
await mkdir(outputDirectory, { recursive: true })
|
|
626
|
+
await Promise.all([
|
|
627
|
+
atomicOperationWrite(
|
|
628
|
+
artifacts.tldr,
|
|
629
|
+
serializeTldr(spec, operationBuiltInConfig),
|
|
630
|
+
),
|
|
631
|
+
atomicOperationWrite(artifacts.lightSvg, light.svg),
|
|
632
|
+
atomicOperationWrite(artifacts.darkSvg, dark.svg),
|
|
633
|
+
atomicOperationWrite(artifacts.lightPng, lightPng),
|
|
634
|
+
atomicOperationWrite(artifacts.darkPng, darkPng),
|
|
635
|
+
])
|
|
636
|
+
return {
|
|
637
|
+
artifacts,
|
|
638
|
+
findings: lintDiagram(spec),
|
|
639
|
+
configPath: null,
|
|
640
|
+
} as const
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
async function executeSlopcameraOperationUncoordinated<
|
|
644
|
+
C extends SlopcameraOperationCode,
|
|
645
|
+
>(
|
|
646
|
+
code: C,
|
|
647
|
+
value: unknown,
|
|
648
|
+
dependencies: SlopcameraOperationDependencies = {},
|
|
649
|
+
): Promise<SlopcameraOperationResultMap[C]> {
|
|
650
|
+
const input = parseSlopcameraOperationInput(code, value)
|
|
651
|
+
switch (code) {
|
|
652
|
+
case "slopcamera.diagram.check": {
|
|
653
|
+
const options = input as CheckSlopcameraOperationInput
|
|
654
|
+
return (await checkOperationDiagram(options.path)) as SlopcameraOperationResultMap[C]
|
|
655
|
+
}
|
|
656
|
+
case "slopcamera.diagram.render": {
|
|
657
|
+
const options = input as RenderSlopcameraOperationInput
|
|
658
|
+
return (await renderOperationDiagram(options)) as SlopcameraOperationResultMap[C]
|
|
659
|
+
}
|
|
660
|
+
case "slopcamera.image.vectorize": {
|
|
661
|
+
const options = input as VectorizeSlopcameraOperationInput
|
|
662
|
+
const result = await vectorizeImage(options.inputPath, {
|
|
663
|
+
outputPath: options.outputPath,
|
|
664
|
+
...(options.duotone === undefined ? {} : { duotone: options.duotone }),
|
|
665
|
+
...(options.alphaCutoff === undefined
|
|
666
|
+
? {}
|
|
667
|
+
: { alphaCutoff: options.alphaCutoff }),
|
|
668
|
+
...(options.timeoutMs === undefined
|
|
669
|
+
? {}
|
|
670
|
+
: { limits: { maxDurationMs: options.timeoutMs } }),
|
|
671
|
+
...(dependencies.inheritedFileDescriptors === undefined
|
|
672
|
+
? {}
|
|
673
|
+
: {
|
|
674
|
+
inheritedFileDescriptors:
|
|
675
|
+
dependencies.inheritedFileDescriptors,
|
|
676
|
+
}),
|
|
677
|
+
})
|
|
678
|
+
if (result.outputPath === null) {
|
|
679
|
+
throw new SlopcameraOperationError(
|
|
680
|
+
"INVALID_OPERATION_INPUT",
|
|
681
|
+
"Vectorization did not publish its required output.",
|
|
682
|
+
)
|
|
683
|
+
}
|
|
684
|
+
return {
|
|
685
|
+
outputPath: result.outputPath,
|
|
686
|
+
receipt: result.receipt,
|
|
687
|
+
} as SlopcameraOperationResultMap[C]
|
|
688
|
+
}
|
|
689
|
+
case "slopcamera.image.generate": {
|
|
690
|
+
const options = input as GenerateSlopcameraOperationInput
|
|
691
|
+
return (await generateSlopcameraImageFile(
|
|
692
|
+
{
|
|
693
|
+
...options,
|
|
694
|
+
...(dependencies.signal === undefined
|
|
695
|
+
? {}
|
|
696
|
+
: { signal: dependencies.signal }),
|
|
697
|
+
},
|
|
698
|
+
dependencies,
|
|
699
|
+
)) as SlopcameraOperationResultMap[C]
|
|
700
|
+
}
|
|
701
|
+
default:
|
|
702
|
+
throw new SlopcameraOperationError(
|
|
703
|
+
"INVALID_OPERATION",
|
|
704
|
+
"Unknown Slopcamera operation code.",
|
|
705
|
+
)
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
/** Execute one operation under authority already held by a workflow node. */
|
|
710
|
+
export async function executeSlopcameraOperationWithLease<
|
|
711
|
+
C extends SlopcameraOperationCode,
|
|
712
|
+
>(
|
|
713
|
+
code: C,
|
|
714
|
+
value: unknown,
|
|
715
|
+
lease: HostResourceLease,
|
|
716
|
+
dependencies: SlopcameraOperationDependencies = {},
|
|
717
|
+
): Promise<SlopcameraOperationResultMap[C]> {
|
|
718
|
+
await lease.assertOwned()
|
|
719
|
+
const available = new Map<string, number>()
|
|
720
|
+
for (const claim of lease.claims) {
|
|
721
|
+
if (
|
|
722
|
+
typeof claim.resource !== "string"
|
|
723
|
+
|| !/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(claim.resource)
|
|
724
|
+
|| !Number.isSafeInteger(claim.amount)
|
|
725
|
+
|| claim.amount < 1
|
|
726
|
+
) {
|
|
727
|
+
throw new SlopcameraOperationError(
|
|
728
|
+
"INVALID_OPERATION",
|
|
729
|
+
"The active host-resource lease contains invalid claims.",
|
|
730
|
+
)
|
|
731
|
+
}
|
|
732
|
+
const total = (available.get(claim.resource) ?? 0) + claim.amount
|
|
733
|
+
if (!Number.isSafeInteger(total)) {
|
|
734
|
+
throw new SlopcameraOperationError(
|
|
735
|
+
"INVALID_OPERATION",
|
|
736
|
+
"The active host-resource lease contains invalid claims.",
|
|
737
|
+
)
|
|
738
|
+
}
|
|
739
|
+
available.set(claim.resource, total)
|
|
740
|
+
}
|
|
741
|
+
const missing = slopcameraOperationHostResourceClaims(code).filter(
|
|
742
|
+
claim => (available.get(claim.resource) ?? 0) < claim.amount,
|
|
743
|
+
)
|
|
744
|
+
if (missing.length > 0) {
|
|
745
|
+
throw new SlopcameraOperationError(
|
|
746
|
+
"INVALID_OPERATION",
|
|
747
|
+
`The active host-resource lease does not cover ${missing
|
|
748
|
+
.map(claim => `${claim.resource}:${String(claim.amount)}`)
|
|
749
|
+
.join(", ")}.`,
|
|
750
|
+
)
|
|
751
|
+
}
|
|
752
|
+
return await executeSlopcameraOperationUncoordinated(
|
|
753
|
+
code,
|
|
754
|
+
value,
|
|
755
|
+
operationDependenciesWithLease(dependencies, lease),
|
|
756
|
+
)
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
/** Execute one direct SDK operation under machine-wide resource admission. */
|
|
760
|
+
export async function executeSlopcameraOperation<C extends SlopcameraOperationCode>(
|
|
761
|
+
code: C,
|
|
762
|
+
value: unknown,
|
|
763
|
+
dependencies: SlopcameraOperationDependencies = {},
|
|
764
|
+
): Promise<SlopcameraOperationResultMap[C]> {
|
|
765
|
+
const input = parseSlopcameraOperationInput(code, value)
|
|
766
|
+
return await withSlopcameraOperationHostAdmission(
|
|
767
|
+
code,
|
|
768
|
+
async (lease) => await executeSlopcameraOperationUncoordinated(
|
|
769
|
+
code,
|
|
770
|
+
input,
|
|
771
|
+
operationDependenciesWithLease(dependencies, lease),
|
|
772
|
+
),
|
|
773
|
+
dependencies,
|
|
774
|
+
)
|
|
775
|
+
}
|