@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,542 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto"
|
|
2
|
+
import { constants } from "node:fs"
|
|
3
|
+
import {
|
|
4
|
+
chmod,
|
|
5
|
+
mkdir,
|
|
6
|
+
open,
|
|
7
|
+
realpath,
|
|
8
|
+
rename,
|
|
9
|
+
rm,
|
|
10
|
+
writeFile,
|
|
11
|
+
type FileHandle,
|
|
12
|
+
} from "node:fs/promises"
|
|
13
|
+
import { homedir } from "node:os"
|
|
14
|
+
import { dirname, join, resolve } from "node:path"
|
|
15
|
+
import { extractVTracerArchive } from "./archive.js"
|
|
16
|
+
import { runBoundedCommand } from "./command.js"
|
|
17
|
+
import { type VectorizeDeadline } from "./limits.js"
|
|
18
|
+
import { sha256 } from "./metrics.js"
|
|
19
|
+
import { VectorizeError } from "./types.js"
|
|
20
|
+
|
|
21
|
+
export const VTRACER_VERSION = "0.6.4"
|
|
22
|
+
|
|
23
|
+
interface VTracerRelease {
|
|
24
|
+
readonly archiveSha256: string
|
|
25
|
+
readonly binarySha256: string
|
|
26
|
+
readonly format: "tar.gz" | "zip"
|
|
27
|
+
readonly url: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const frozenRelease = (release: VTracerRelease): Readonly<VTracerRelease> =>
|
|
31
|
+
Object.freeze(release)
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Hashes were independently recomputed from the five official GitHub release
|
|
35
|
+
* assets and their extracted binaries on 2026-07-25.
|
|
36
|
+
*/
|
|
37
|
+
export const vtracerReleases = Object.freeze({
|
|
38
|
+
"darwin-arm64": frozenRelease({
|
|
39
|
+
archiveSha256: "4a597fd2df8b961d60620df40a7436109427d86e5c028758e6e8796b02d3d996",
|
|
40
|
+
binarySha256: "77e495bbe212448240387fba3b6d8bc62ba20ecfb6f3c22967e51600f1cc6e66",
|
|
41
|
+
format: "tar.gz",
|
|
42
|
+
url: `https://github.com/visioncortex/vtracer/releases/download/${VTRACER_VERSION}/vtracer-aarch64-apple-darwin.tar.gz`,
|
|
43
|
+
}),
|
|
44
|
+
"darwin-x64": frozenRelease({
|
|
45
|
+
archiveSha256: "f0d755292c2602d772d63d658a3498b23eca8b5620d4b92a991bd035d5abed16",
|
|
46
|
+
binarySha256: "0f9f88f989b757e27973a5c4b42665153070183d0787656ee8af2249ab326b78",
|
|
47
|
+
format: "tar.gz",
|
|
48
|
+
url: `https://github.com/visioncortex/vtracer/releases/download/${VTRACER_VERSION}/vtracer-x86_64-apple-darwin.tar.gz`,
|
|
49
|
+
}),
|
|
50
|
+
"linux-arm64": frozenRelease({
|
|
51
|
+
archiveSha256: "cbd05ad4f491d12dd139ada61485ca1d24db9f981cbe1658632a083cd0ac1a71",
|
|
52
|
+
binarySha256: "a4b33b6c4066a6b9187802c6efc8b89e211318e12a17164b9d1dd1f29ac5e502",
|
|
53
|
+
format: "tar.gz",
|
|
54
|
+
url: `https://github.com/visioncortex/vtracer/releases/download/${VTRACER_VERSION}/vtracer-aarch64-unknown-linux-musl.tar.gz`,
|
|
55
|
+
}),
|
|
56
|
+
"linux-x64": frozenRelease({
|
|
57
|
+
archiveSha256: "9290ba0c90e224d6d212836dff5491407c1718bcb72f80b2b5a4a01816df5e40",
|
|
58
|
+
binarySha256: "6f31499257076bd94de3e976844cf7ca5643f1e194a2bf0599b13f3719452aec",
|
|
59
|
+
format: "tar.gz",
|
|
60
|
+
url: `https://github.com/visioncortex/vtracer/releases/download/${VTRACER_VERSION}/vtracer-x86_64-unknown-linux-musl.tar.gz`,
|
|
61
|
+
}),
|
|
62
|
+
"win32-x64": frozenRelease({
|
|
63
|
+
archiveSha256: "6b5bc17a6b017129ee40461df254f65d16f3b494c001a8541d41861066b716bf",
|
|
64
|
+
binarySha256: "4ad8d35e566cd15caf582063b8349bd082b8fa2bd461e99d116fc63ad8fdeca0",
|
|
65
|
+
format: "zip",
|
|
66
|
+
url: `https://github.com/visioncortex/vtracer/releases/download/${VTRACER_VERSION}/vtracer-x86_64-pc-windows-msvc.zip`,
|
|
67
|
+
}),
|
|
68
|
+
}) satisfies Readonly<Record<string, Readonly<VTracerRelease>>>
|
|
69
|
+
|
|
70
|
+
export interface VTracerTool {
|
|
71
|
+
/**
|
|
72
|
+
* A conversion-private executable copied through one verified source handle.
|
|
73
|
+
* Callers must remove its containing conversion directory when finished.
|
|
74
|
+
*/
|
|
75
|
+
readonly path: string
|
|
76
|
+
readonly sha256: string
|
|
77
|
+
readonly source: "official-release" | "override"
|
|
78
|
+
readonly version: string
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const MAX_ARCHIVE_BYTES = 4 * 1_024 * 1_024
|
|
82
|
+
const MAX_TOOL_BYTES = 16 * 1_024 * 1_024
|
|
83
|
+
const FILE_CHUNK_BYTES = 64 * 1_024
|
|
84
|
+
|
|
85
|
+
function renamedEnvironmentValue(canonical: `SLOPCAMERA_${string}`): string | undefined {
|
|
86
|
+
return process.env[canonical]
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export async function ensureVTracer(
|
|
90
|
+
deadline: VectorizeDeadline,
|
|
91
|
+
privateDirectory: string,
|
|
92
|
+
cacheDirectory?: string,
|
|
93
|
+
): Promise<VTracerTool> {
|
|
94
|
+
const override = renamedEnvironmentValue("SLOPCAMERA_VTRACER_PATH")
|
|
95
|
+
if (override !== undefined) {
|
|
96
|
+
return copyAndInspectVTracer(
|
|
97
|
+
resolve(override),
|
|
98
|
+
resolve(privateDirectory),
|
|
99
|
+
"override",
|
|
100
|
+
deadline,
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const key = `${process.platform}-${process.arch}`
|
|
105
|
+
const release = vtracerReleases[key as keyof typeof vtracerReleases]
|
|
106
|
+
if (release === undefined) {
|
|
107
|
+
throw new VectorizeError(
|
|
108
|
+
"tool_platform",
|
|
109
|
+
`VTracer is not pinned for ${process.platform}/${process.arch}.`,
|
|
110
|
+
{ arch: process.arch, platform: process.platform },
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const cacheRoot = resolve(cacheDirectory ?? defaultCacheDirectory())
|
|
115
|
+
const suffix = process.platform === "win32" ? ".exe" : ""
|
|
116
|
+
const toolPath = join(
|
|
117
|
+
cacheRoot,
|
|
118
|
+
"tools",
|
|
119
|
+
`vtracer-${VTRACER_VERSION}-${process.platform}-${process.arch}${suffix}`,
|
|
120
|
+
)
|
|
121
|
+
const cachedHash = await hashCachedTool(toolPath, deadline)
|
|
122
|
+
if (cachedHash !== release.binarySha256) {
|
|
123
|
+
await removeInvalidCachedTool(toolPath)
|
|
124
|
+
await installOfficialVTracer(toolPath, release, deadline)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return copyAndInspectVTracer(
|
|
128
|
+
toolPath,
|
|
129
|
+
resolve(privateDirectory),
|
|
130
|
+
"official-release",
|
|
131
|
+
deadline,
|
|
132
|
+
release.binarySha256,
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function defaultCacheDirectory(): string {
|
|
137
|
+
const explicit = renamedEnvironmentValue("SLOPCAMERA_CACHE_DIR")
|
|
138
|
+
if (explicit !== undefined && explicit.trim() !== "") return explicit
|
|
139
|
+
if (process.platform === "win32") {
|
|
140
|
+
return join(process.env.LOCALAPPDATA ?? homedir(), "slopcamera")
|
|
141
|
+
}
|
|
142
|
+
if (process.platform === "darwin") return join(homedir(), "Library", "Caches", "slopcamera")
|
|
143
|
+
return join(process.env.XDG_CACHE_HOME ?? join(homedir(), ".cache"), "slopcamera")
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async function installOfficialVTracer(
|
|
147
|
+
toolPath: string,
|
|
148
|
+
release: Readonly<VTracerRelease>,
|
|
149
|
+
deadline: VectorizeDeadline,
|
|
150
|
+
): Promise<void> {
|
|
151
|
+
deadline.assert("VTracer download")
|
|
152
|
+
const archive = await downloadBounded(release.url, deadline, MAX_ARCHIVE_BYTES)
|
|
153
|
+
const archiveSha256 = sha256(archive)
|
|
154
|
+
if (archiveSha256 !== release.archiveSha256) {
|
|
155
|
+
throw new VectorizeError(
|
|
156
|
+
"tool_integrity",
|
|
157
|
+
`VTracer archive checksum mismatch: ${archiveSha256}`,
|
|
158
|
+
{ actual: archiveSha256, expected: release.archiveSha256 },
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
const binary = extractVTracerArchive(archive, release.format)
|
|
162
|
+
if (binary.byteLength < 1 || binary.byteLength > MAX_TOOL_BYTES) {
|
|
163
|
+
throw new VectorizeError(
|
|
164
|
+
"tool_integrity",
|
|
165
|
+
"VTracer binary exceeds its verified installation limit.",
|
|
166
|
+
{ bytes: binary.byteLength, maximumBytes: MAX_TOOL_BYTES },
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
const binarySha256 = sha256(binary)
|
|
170
|
+
if (binarySha256 !== release.binarySha256) {
|
|
171
|
+
throw new VectorizeError(
|
|
172
|
+
"tool_integrity",
|
|
173
|
+
`VTracer binary checksum mismatch: ${binarySha256}`,
|
|
174
|
+
{ actual: binarySha256, expected: release.binarySha256 },
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
deadline.assert("VTracer installation")
|
|
179
|
+
await mkdir(dirname(toolPath), { recursive: true })
|
|
180
|
+
const stagedPath = `${toolPath}.${randomUUID()}.tmp`
|
|
181
|
+
try {
|
|
182
|
+
await writeFile(stagedPath, binary, { flag: "wx", mode: 0o700 })
|
|
183
|
+
if (process.platform !== "win32") await chmod(stagedPath, 0o500)
|
|
184
|
+
deadline.assert("VTracer installation")
|
|
185
|
+
try {
|
|
186
|
+
await rename(stagedPath, toolPath)
|
|
187
|
+
} catch (error) {
|
|
188
|
+
const concurrentHash = await hashCachedTool(toolPath, deadline)
|
|
189
|
+
if (concurrentHash === release.binarySha256) {
|
|
190
|
+
await rm(stagedPath, { force: true })
|
|
191
|
+
} else {
|
|
192
|
+
throw error
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
} catch (error) {
|
|
196
|
+
await rm(stagedPath, { force: true })
|
|
197
|
+
if (error instanceof VectorizeError) throw error
|
|
198
|
+
throw new VectorizeError(
|
|
199
|
+
"tool_integrity",
|
|
200
|
+
"Could not publish the verified VTracer binary.",
|
|
201
|
+
{},
|
|
202
|
+
{ cause: error },
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const installedHash = await hashCachedTool(toolPath, deadline)
|
|
207
|
+
if (installedHash !== release.binarySha256) {
|
|
208
|
+
throw new VectorizeError(
|
|
209
|
+
"tool_integrity",
|
|
210
|
+
"Installed VTracer differs from the verified release binary.",
|
|
211
|
+
{ actual: installedHash, expected: release.binarySha256 },
|
|
212
|
+
)
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async function copyAndInspectVTracer(
|
|
217
|
+
sourcePath: string,
|
|
218
|
+
privateDirectory: string,
|
|
219
|
+
source: VTracerTool["source"],
|
|
220
|
+
deadline: VectorizeDeadline,
|
|
221
|
+
expectedSha256?: string,
|
|
222
|
+
): Promise<VTracerTool> {
|
|
223
|
+
deadline.assert("VTracer private copy")
|
|
224
|
+
await mkdir(privateDirectory, { mode: 0o700, recursive: true })
|
|
225
|
+
const suffix = process.platform === "win32" ? ".exe" : ""
|
|
226
|
+
const privatePath = join(privateDirectory, `vtracer-${randomUUID()}${suffix}`)
|
|
227
|
+
const failureCode = source === "official-release" ? "tool_integrity" : "tool_version"
|
|
228
|
+
let sourceHandle: FileHandle | undefined
|
|
229
|
+
let targetHandle: FileHandle | undefined
|
|
230
|
+
let copiedSha256: string
|
|
231
|
+
try {
|
|
232
|
+
const resolvedSourcePath = await realpath(sourcePath)
|
|
233
|
+
deadline.assert("VTracer private copy")
|
|
234
|
+
sourceHandle = await open(resolvedSourcePath, boundedReadFlags())
|
|
235
|
+
const metadata = await sourceHandle.stat()
|
|
236
|
+
assertBoundedRegularTool(metadata, sourcePath)
|
|
237
|
+
targetHandle = await open(privatePath, "wx", 0o500)
|
|
238
|
+
copiedSha256 = await copyAndHash(
|
|
239
|
+
sourceHandle,
|
|
240
|
+
targetHandle,
|
|
241
|
+
MAX_TOOL_BYTES,
|
|
242
|
+
deadline,
|
|
243
|
+
)
|
|
244
|
+
await targetHandle.sync()
|
|
245
|
+
deadline.assert("VTracer private copy")
|
|
246
|
+
} catch (error) {
|
|
247
|
+
if (error instanceof VectorizeError) throw error
|
|
248
|
+
throw new VectorizeError(
|
|
249
|
+
failureCode,
|
|
250
|
+
"VTracer could not be copied into the private conversion directory.",
|
|
251
|
+
{},
|
|
252
|
+
{ cause: error },
|
|
253
|
+
)
|
|
254
|
+
} finally {
|
|
255
|
+
await Promise.allSettled([sourceHandle?.close(), targetHandle?.close()])
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
if (copiedSha256.length === 0) {
|
|
260
|
+
throw new VectorizeError(failureCode, "VTracer executable is empty.")
|
|
261
|
+
}
|
|
262
|
+
if (expectedSha256 !== undefined && copiedSha256 !== expectedSha256) {
|
|
263
|
+
throw new VectorizeError(
|
|
264
|
+
"tool_integrity",
|
|
265
|
+
"VTracer changed before it could be copied for conversion.",
|
|
266
|
+
{ actual: copiedSha256, expected: expectedSha256 },
|
|
267
|
+
)
|
|
268
|
+
}
|
|
269
|
+
if (process.platform !== "win32") await chmod(privatePath, 0o500)
|
|
270
|
+
const privateSha256 = await hashRegularFile(
|
|
271
|
+
privatePath,
|
|
272
|
+
MAX_TOOL_BYTES,
|
|
273
|
+
deadline,
|
|
274
|
+
failureCode,
|
|
275
|
+
)
|
|
276
|
+
if (privateSha256 !== copiedSha256) {
|
|
277
|
+
throw new VectorizeError(
|
|
278
|
+
"tool_integrity",
|
|
279
|
+
"The private VTracer copy failed its integrity check.",
|
|
280
|
+
{ actual: privateSha256, expected: copiedSha256 },
|
|
281
|
+
)
|
|
282
|
+
}
|
|
283
|
+
return inspectVTracer(
|
|
284
|
+
privatePath,
|
|
285
|
+
source,
|
|
286
|
+
deadline,
|
|
287
|
+
copiedSha256,
|
|
288
|
+
expectedSha256,
|
|
289
|
+
)
|
|
290
|
+
} catch (error) {
|
|
291
|
+
await rm(privatePath, { force: true })
|
|
292
|
+
throw error
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async function inspectVTracer(
|
|
297
|
+
path: string,
|
|
298
|
+
source: VTracerTool["source"],
|
|
299
|
+
deadline: VectorizeDeadline,
|
|
300
|
+
copiedSha256: string,
|
|
301
|
+
expectedSha256?: string,
|
|
302
|
+
): Promise<VTracerTool> {
|
|
303
|
+
deadline.assert("VTracer inspection")
|
|
304
|
+
const { stderr, stdout } = await runBoundedCommand(
|
|
305
|
+
[path, "--version"],
|
|
306
|
+
deadline.remainingMs(),
|
|
307
|
+
"tool_version",
|
|
308
|
+
)
|
|
309
|
+
if (!new RegExp(`\\b${VTRACER_VERSION.replaceAll(".", "\\.")}\\b`, "u").test(`${stdout}\n${stderr}`)) {
|
|
310
|
+
throw new VectorizeError(
|
|
311
|
+
"tool_version",
|
|
312
|
+
`Expected VTracer ${VTRACER_VERSION}.`,
|
|
313
|
+
)
|
|
314
|
+
}
|
|
315
|
+
const afterInspectionSha256 = await hashRegularFile(
|
|
316
|
+
path,
|
|
317
|
+
MAX_TOOL_BYTES,
|
|
318
|
+
deadline,
|
|
319
|
+
"tool_integrity",
|
|
320
|
+
)
|
|
321
|
+
if (
|
|
322
|
+
afterInspectionSha256 !== copiedSha256 ||
|
|
323
|
+
(expectedSha256 !== undefined && afterInspectionSha256 !== expectedSha256)
|
|
324
|
+
) {
|
|
325
|
+
throw new VectorizeError(
|
|
326
|
+
"tool_integrity",
|
|
327
|
+
"The private VTracer executable changed during inspection.",
|
|
328
|
+
{
|
|
329
|
+
actual: afterInspectionSha256,
|
|
330
|
+
expected: expectedSha256 ?? copiedSha256,
|
|
331
|
+
},
|
|
332
|
+
)
|
|
333
|
+
}
|
|
334
|
+
return {
|
|
335
|
+
path,
|
|
336
|
+
sha256: afterInspectionSha256,
|
|
337
|
+
source,
|
|
338
|
+
version: VTRACER_VERSION,
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
async function hashCachedTool(
|
|
343
|
+
path: string,
|
|
344
|
+
deadline: VectorizeDeadline,
|
|
345
|
+
): Promise<string | undefined> {
|
|
346
|
+
try {
|
|
347
|
+
return await hashRegularFile(
|
|
348
|
+
path,
|
|
349
|
+
MAX_TOOL_BYTES,
|
|
350
|
+
deadline,
|
|
351
|
+
"tool_integrity",
|
|
352
|
+
)
|
|
353
|
+
} catch (error) {
|
|
354
|
+
if (error instanceof VectorizeError && error.code === "timeout") throw error
|
|
355
|
+
if (isFileSystemError(error, "ENOENT")) return undefined
|
|
356
|
+
return undefined
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
async function removeInvalidCachedTool(path: string): Promise<void> {
|
|
361
|
+
try {
|
|
362
|
+
await rm(path, { force: true })
|
|
363
|
+
} catch (error) {
|
|
364
|
+
throw new VectorizeError(
|
|
365
|
+
"tool_integrity",
|
|
366
|
+
"Could not remove an invalid cached VTracer binary.",
|
|
367
|
+
{},
|
|
368
|
+
{ cause: error },
|
|
369
|
+
)
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
async function hashRegularFile(
|
|
374
|
+
path: string,
|
|
375
|
+
maximumBytes: number,
|
|
376
|
+
deadline: VectorizeDeadline,
|
|
377
|
+
failureCode: "tool_integrity" | "tool_version",
|
|
378
|
+
): Promise<string> {
|
|
379
|
+
let handle: FileHandle | undefined
|
|
380
|
+
try {
|
|
381
|
+
deadline.assert("VTracer hash")
|
|
382
|
+
const resolvedPath = await realpath(path)
|
|
383
|
+
deadline.assert("VTracer hash")
|
|
384
|
+
handle = await open(resolvedPath, boundedReadFlags())
|
|
385
|
+
const metadata = await handle.stat()
|
|
386
|
+
if (!metadata.isFile() || metadata.size < 1 || metadata.size > maximumBytes) {
|
|
387
|
+
throw new VectorizeError(
|
|
388
|
+
failureCode,
|
|
389
|
+
"VTracer must be a non-empty regular file within its size limit.",
|
|
390
|
+
{ bytes: metadata.size, maximumBytes },
|
|
391
|
+
)
|
|
392
|
+
}
|
|
393
|
+
const hash = createHash("sha256")
|
|
394
|
+
const buffer = Buffer.allocUnsafe(Math.min(FILE_CHUNK_BYTES, maximumBytes + 1))
|
|
395
|
+
let bytes = 0
|
|
396
|
+
while (true) {
|
|
397
|
+
deadline.assert("VTracer hash")
|
|
398
|
+
const maximumRead = Math.min(buffer.byteLength, maximumBytes - bytes + 1)
|
|
399
|
+
const { bytesRead } = await handle.read(buffer, 0, maximumRead, null)
|
|
400
|
+
if (bytesRead === 0) break
|
|
401
|
+
bytes += bytesRead
|
|
402
|
+
if (bytes > maximumBytes) {
|
|
403
|
+
throw new VectorizeError(
|
|
404
|
+
failureCode,
|
|
405
|
+
"VTracer grew beyond its executable size limit while being hashed.",
|
|
406
|
+
{ bytes, maximumBytes },
|
|
407
|
+
)
|
|
408
|
+
}
|
|
409
|
+
hash.update(buffer.subarray(0, bytesRead))
|
|
410
|
+
}
|
|
411
|
+
deadline.assert("VTracer hash")
|
|
412
|
+
return hash.digest("hex")
|
|
413
|
+
} catch (error) {
|
|
414
|
+
if (error instanceof VectorizeError) throw error
|
|
415
|
+
throw new VectorizeError(
|
|
416
|
+
failureCode,
|
|
417
|
+
"VTracer executable could not be read safely.",
|
|
418
|
+
{},
|
|
419
|
+
{ cause: error },
|
|
420
|
+
)
|
|
421
|
+
} finally {
|
|
422
|
+
await handle?.close().catch(() => undefined)
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
async function copyAndHash(
|
|
427
|
+
source: FileHandle,
|
|
428
|
+
target: FileHandle,
|
|
429
|
+
maximumBytes: number,
|
|
430
|
+
deadline: VectorizeDeadline,
|
|
431
|
+
): Promise<string> {
|
|
432
|
+
const hash = createHash("sha256")
|
|
433
|
+
const buffer = Buffer.allocUnsafe(Math.min(FILE_CHUNK_BYTES, maximumBytes + 1))
|
|
434
|
+
let bytes = 0
|
|
435
|
+
while (true) {
|
|
436
|
+
deadline.assert("VTracer private copy")
|
|
437
|
+
const maximumRead = Math.min(buffer.byteLength, maximumBytes - bytes + 1)
|
|
438
|
+
const { bytesRead } = await source.read(buffer, 0, maximumRead, null)
|
|
439
|
+
if (bytesRead === 0) break
|
|
440
|
+
bytes += bytesRead
|
|
441
|
+
if (bytes > maximumBytes) {
|
|
442
|
+
throw new VectorizeError(
|
|
443
|
+
"tool_integrity",
|
|
444
|
+
"VTracer grew beyond its executable size limit while being copied.",
|
|
445
|
+
{ bytes, maximumBytes },
|
|
446
|
+
)
|
|
447
|
+
}
|
|
448
|
+
hash.update(buffer.subarray(0, bytesRead))
|
|
449
|
+
let written = 0
|
|
450
|
+
while (written < bytesRead) {
|
|
451
|
+
const result = await target.write(
|
|
452
|
+
buffer,
|
|
453
|
+
written,
|
|
454
|
+
bytesRead - written,
|
|
455
|
+
null,
|
|
456
|
+
)
|
|
457
|
+
if (result.bytesWritten < 1) {
|
|
458
|
+
throw new VectorizeError(
|
|
459
|
+
"tool_integrity",
|
|
460
|
+
"VTracer private copy stopped before completion.",
|
|
461
|
+
)
|
|
462
|
+
}
|
|
463
|
+
written += result.bytesWritten
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
return bytes === 0 ? "" : hash.digest("hex")
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
function assertBoundedRegularTool(
|
|
470
|
+
metadata: Awaited<ReturnType<FileHandle["stat"]>>,
|
|
471
|
+
path: string,
|
|
472
|
+
): void {
|
|
473
|
+
if (!metadata.isFile() || metadata.size < 1 || metadata.size > MAX_TOOL_BYTES) {
|
|
474
|
+
throw new VectorizeError(
|
|
475
|
+
"tool_version",
|
|
476
|
+
`VTracer must be a non-empty regular file: ${path}`,
|
|
477
|
+
{ bytes: metadata.size, maximumBytes: MAX_TOOL_BYTES },
|
|
478
|
+
)
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
async function downloadBounded(
|
|
483
|
+
url: string,
|
|
484
|
+
deadline: VectorizeDeadline,
|
|
485
|
+
maximumBytes: number,
|
|
486
|
+
): Promise<Uint8Array> {
|
|
487
|
+
const controller = new AbortController()
|
|
488
|
+
const timer = setTimeout(() => controller.abort(), deadline.remainingMs())
|
|
489
|
+
try {
|
|
490
|
+
const response = await fetch(url, {
|
|
491
|
+
headers: { "user-agent": "hraness-slopcamera-vectorizer" },
|
|
492
|
+
redirect: "follow",
|
|
493
|
+
signal: controller.signal,
|
|
494
|
+
})
|
|
495
|
+
if (!response.ok || response.body === null) {
|
|
496
|
+
throw new VectorizeError(
|
|
497
|
+
"tool_download",
|
|
498
|
+
`Could not download VTracer: ${response.status} ${response.statusText}`,
|
|
499
|
+
)
|
|
500
|
+
}
|
|
501
|
+
const declaredLength = Number.parseInt(response.headers.get("content-length") ?? "", 10)
|
|
502
|
+
if (Number.isFinite(declaredLength) && declaredLength > maximumBytes) {
|
|
503
|
+
throw new VectorizeError("tool_download", "VTracer archive exceeds its download limit.")
|
|
504
|
+
}
|
|
505
|
+
const chunks: Uint8Array[] = []
|
|
506
|
+
const reader = response.body.getReader()
|
|
507
|
+
let bytes = 0
|
|
508
|
+
while (true) {
|
|
509
|
+
deadline.assert("VTracer download")
|
|
510
|
+
const { done, value } = await reader.read()
|
|
511
|
+
if (done) break
|
|
512
|
+
bytes += value.byteLength
|
|
513
|
+
if (bytes > maximumBytes) {
|
|
514
|
+
await reader.cancel()
|
|
515
|
+
throw new VectorizeError("tool_download", "VTracer archive exceeds its download limit.")
|
|
516
|
+
}
|
|
517
|
+
chunks.push(value)
|
|
518
|
+
}
|
|
519
|
+
return Uint8Array.from(Buffer.concat(chunks, bytes))
|
|
520
|
+
} catch (error) {
|
|
521
|
+
if (error instanceof VectorizeError) throw error
|
|
522
|
+
if (controller.signal.aborted) {
|
|
523
|
+
throw new VectorizeError("timeout", "VTracer download exceeded the conversion time limit.")
|
|
524
|
+
}
|
|
525
|
+
throw new VectorizeError("tool_download", "Could not download VTracer.", {}, { cause: error })
|
|
526
|
+
} finally {
|
|
527
|
+
clearTimeout(timer)
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
function isFileSystemError(error: unknown, code: string): boolean {
|
|
532
|
+
return (
|
|
533
|
+
error instanceof Error &&
|
|
534
|
+
"code" in error &&
|
|
535
|
+
(error as NodeJS.ErrnoException).code === code
|
|
536
|
+
)
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
function boundedReadFlags(): number {
|
|
540
|
+
if (process.platform === "win32") return constants.O_RDONLY
|
|
541
|
+
return constants.O_RDONLY | constants.O_NONBLOCK | constants.O_NOFOLLOW
|
|
542
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
export const vectorizeProfileNames = ["balanced", "detailed", "photo"] as const
|
|
2
|
+
|
|
3
|
+
export type VectorizeProfile = (typeof vectorizeProfileNames)[number]
|
|
4
|
+
export type VectorizeRepresentation = "color-paths" | "alpha-mask"
|
|
5
|
+
export type VectorizeOutputMode = "color" | "duotone"
|
|
6
|
+
export type VectorizeInput = string | Uint8Array | ArrayBuffer
|
|
7
|
+
|
|
8
|
+
export interface VectorizeLimits {
|
|
9
|
+
readonly maxDecodedPixels: number
|
|
10
|
+
readonly maxDimension: number
|
|
11
|
+
readonly maxDurationMs: number
|
|
12
|
+
readonly maxInputBytes: number
|
|
13
|
+
readonly maxOutputBytes: number
|
|
14
|
+
readonly maxPaths: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface VectorizeOptions {
|
|
18
|
+
/**
|
|
19
|
+
* Pixels below this alpha byte are removed before tracing. Slopcamera also
|
|
20
|
+
* evaluates an alpha-preserving candidate when the removed mass is material.
|
|
21
|
+
*/
|
|
22
|
+
readonly alphaCutoff?: number
|
|
23
|
+
/** Directory used for the checksum-pinned VTracer binary. */
|
|
24
|
+
readonly cacheDirectory?: string
|
|
25
|
+
/** Replace traced colors with a primary and secondary color. */
|
|
26
|
+
readonly duotone?: readonly [primary: string, secondary: string]
|
|
27
|
+
/**
|
|
28
|
+
* Advanced host authority inherited by the isolated worker and tracer.
|
|
29
|
+
* At most sixteen unique open descriptors are accepted. Values are never
|
|
30
|
+
* recorded in provenance or receipts.
|
|
31
|
+
*/
|
|
32
|
+
readonly inheritedFileDescriptors?: readonly number[]
|
|
33
|
+
/** Bound a conversion more tightly than the package hard limits. */
|
|
34
|
+
readonly limits?: Partial<VectorizeLimits>
|
|
35
|
+
/** Atomically replace this file after every quality gate succeeds. */
|
|
36
|
+
readonly outputPath?: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type VectorizeErrorCode =
|
|
40
|
+
| "input_limit"
|
|
41
|
+
| "invalid_input"
|
|
42
|
+
| "output_limit"
|
|
43
|
+
| "quality_limit"
|
|
44
|
+
| "timeout"
|
|
45
|
+
| "tool_download"
|
|
46
|
+
| "tool_integrity"
|
|
47
|
+
| "tool_platform"
|
|
48
|
+
| "tool_version"
|
|
49
|
+
| "trace_failed"
|
|
50
|
+
| "unsafe_svg"
|
|
51
|
+
|
|
52
|
+
export class VectorizeError extends Error {
|
|
53
|
+
readonly code: VectorizeErrorCode
|
|
54
|
+
readonly details: Readonly<Record<string, unknown>>
|
|
55
|
+
|
|
56
|
+
constructor(
|
|
57
|
+
code: VectorizeErrorCode,
|
|
58
|
+
message: string,
|
|
59
|
+
details: Readonly<Record<string, unknown>> = {},
|
|
60
|
+
options?: ErrorOptions,
|
|
61
|
+
) {
|
|
62
|
+
super(message, options)
|
|
63
|
+
this.name = "VectorizeError"
|
|
64
|
+
this.code = code
|
|
65
|
+
this.details = details
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface VectorizeQualityReceipt {
|
|
70
|
+
readonly alphaRmse: number
|
|
71
|
+
readonly colorRmse: number
|
|
72
|
+
readonly outsideAlphaRatio: number
|
|
73
|
+
readonly sampleHeight: number
|
|
74
|
+
readonly sampleWidth: number
|
|
75
|
+
readonly supportRecall: number
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface VectorizeProvenance {
|
|
79
|
+
readonly arch: NodeJS.Architecture
|
|
80
|
+
readonly platform: NodeJS.Platform
|
|
81
|
+
readonly sharp: string
|
|
82
|
+
/** Sorted version metadata reported by the loaded Sharp runtime. */
|
|
83
|
+
readonly sharpVersions: Readonly<Record<string, string>>
|
|
84
|
+
readonly vips: string
|
|
85
|
+
readonly vtracerSha256: string
|
|
86
|
+
readonly vtracerSource: "official-release" | "override"
|
|
87
|
+
readonly vtracerVersion: string
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface VectorizeReceipt {
|
|
91
|
+
readonly alphaCutoff: number
|
|
92
|
+
readonly bytes: number
|
|
93
|
+
readonly candidatesEvaluated: number
|
|
94
|
+
readonly format: string
|
|
95
|
+
readonly height: number
|
|
96
|
+
readonly inputBytes: number
|
|
97
|
+
readonly outputMode: VectorizeOutputMode
|
|
98
|
+
readonly pathCount: number
|
|
99
|
+
readonly profile: VectorizeProfile
|
|
100
|
+
readonly provenance: VectorizeProvenance
|
|
101
|
+
readonly quality: VectorizeQualityReceipt
|
|
102
|
+
readonly receiptVersion: 1
|
|
103
|
+
readonly representation: VectorizeRepresentation
|
|
104
|
+
readonly sourceSha256: string
|
|
105
|
+
readonly svgSha256: string
|
|
106
|
+
readonly width: number
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface VectorizeResult {
|
|
110
|
+
readonly outputPath: string | null
|
|
111
|
+
readonly receipt: VectorizeReceipt
|
|
112
|
+
readonly svg: string
|
|
113
|
+
}
|