cozyclay 2.1.0 → 3.0.0
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/CHANGELOG.md +65 -0
- package/README.md +12 -30
- package/bin/agent/agent-routes.mjs +40 -113
- package/bin/agent/agent-runner.mjs +9 -10
- package/bin/agent/cliproxy-provider.mjs +135 -0
- package/bin/agent/credential-store.mjs +30 -7
- package/bin/agent/pi-tools.mjs +4 -1
- package/bin/agent/provider-keys.mjs +32 -0
- package/bin/agent/provider-login.mjs +110 -0
- package/bin/agent/provider-routes.mjs +99 -0
- package/bin/agent/providers.mjs +19 -97
- package/bin/agent/script-tool.mjs +17 -4
- package/bin/agent/studio-motion-reference.mjs +23 -0
- package/bin/agent/studio-prompt.mjs +1 -1
- package/bin/agent/studio-rebase-text.mjs +32 -0
- package/bin/agent/studio-rebase.mjs +26 -39
- package/bin/agent/studio-tools.mjs +9 -28
- package/bin/agent/tool-arguments.mjs +49 -0
- package/bin/agent/turn-frames.mjs +66 -0
- package/bin/codex-auth.mjs +9 -2
- package/bin/cozyclay.mjs +10 -116
- package/bin/live/cli.mjs +22 -6
- package/bin/mcp-runtime.mjs +2 -12
- package/bin/package-signature.mjs +0 -4
- package/dist/app/index.html +7 -6
- package/dist/assets/EXRLoader-CvVIvGVE.js +1 -0
- package/dist/assets/analytics-C9sUnndd.js +1 -0
- package/dist/assets/app-CCh--1Hc.js +5087 -0
- package/dist/assets/app-f-fkV5uP.css +1 -0
- package/dist/assets/compile-worker-CkxiD6lH.js +5 -0
- package/dist/assets/fflate.module-KbGj5-H6.js +5 -0
- package/dist/assets/{first-shot-handoff-BlT33CGf.js → first-shot-handoff-CC4xe4HM.js} +1 -1
- package/dist/assets/{landing-BSesrCpL.js → landing-DjHD8u7k.js} +1 -1
- package/dist/assets/scene-asset-sync-Hx_FzdOH.js +20 -0
- package/dist/assets/scene-asset-sync-_S8NQl5B.css +1 -0
- package/dist/assets/workflow-C4yKH1p4.js +179 -0
- package/dist/cozyclay-package.json +1 -1
- package/dist/index.html +3 -3
- package/dist/llms.txt +14 -0
- package/dist/sw.js +3 -6
- package/dist/workflow/index.html +5 -4
- package/mcp/LIVE-PROTOCOL.md +7 -47
- package/mcp/README.md +16 -26
- package/mcp/live-hub.mjs +6 -10
- package/mcp/package.json +1 -3
- package/mcp/tool-handlers.mjs +4 -168
- package/package.json +3 -22
- package/src/App.jsx +174 -2639
- package/src/agent-progress.js +79 -0
- package/src/agent-turn-mark.js +30 -0
- package/src/analytics.js +1 -19
- package/src/app-context.js +5 -0
- package/src/app-stage.jsx +2 -34
- package/src/asset-pane.jsx +6 -5
- package/src/camera-block.js +21 -0
- package/src/camera-direction.js +120 -0
- package/src/character-kind.js +3 -9
- package/src/command-bus.js +8 -6
- package/src/commands/cast.js +5 -34
- package/src/commands/elements/character.js +1 -1
- package/src/commands/elements/motion.js +1 -6
- package/src/commands/elements/object.js +6 -2
- package/src/commands/elements/shot.js +4 -1
- package/src/commands/elements.js +38 -14
- package/src/commands/index.js +3 -1
- package/src/commands/kits.js +37 -0
- package/src/commands/motion-timing.js +57 -0
- package/src/commands/motion.js +9 -152
- package/src/commands/motion3-compile.js +65 -0
- package/src/commands/motion3.js +207 -0
- package/src/commands/objects.js +6 -4
- package/src/commands/shot.js +49 -11
- package/src/commands/stage.js +14 -1
- package/src/commands/view.js +4 -1
- package/src/document-mirrors.js +42 -0
- package/src/document-store.js +4 -0
- package/src/domains/cast.js +19 -194
- package/src/domains/motion/motion-range-pin-rules.js +1 -1
- package/src/domains/motion/take-source.js +51 -0
- package/src/domains/motion.js +60 -1838
- package/src/domains/scenes.js +12 -55
- package/src/domains/shots.js +2 -2
- package/src/domains/stage.js +4 -2
- package/src/draft-ghost.js +46 -0
- package/src/draft-ghost.jsx +129 -0
- package/src/dualview.jsx +4 -3
- package/src/effects/EffectsLayer.jsx +112 -0
- package/src/effects/effect-meshes.js +232 -0
- package/src/effects/effect-record.js +103 -0
- package/src/effects/emitter.js +59 -0
- package/src/effects/simulate.js +261 -0
- package/src/effects/state.js +59 -0
- package/src/execution-telemetry.js +2 -3
- package/src/first-success-guide.jsx +5 -5
- package/src/ghost-set.js +257 -0
- package/src/held-object-edit.jsx +113 -0
- package/src/held-object-pose.js +175 -0
- package/src/hierarchy-model.js +1 -1
- package/src/hierarchy-panel.jsx +1 -1
- package/src/imported-mesh.jsx +103 -0
- package/src/kits/held.js +84 -0
- package/src/kits/index.js +70 -0
- package/src/kits/smoking.js +57 -0
- package/src/light-blockers.js +93 -0
- package/src/motion/beat-board-model.js +84 -0
- package/src/motion/beat-board.css +124 -0
- package/src/motion/beat-board.jsx +59 -0
- package/src/motion/center-of-mass.js +66 -0
- package/src/motion/fix-collisions.js +850 -0
- package/src/motion/physics-review.js +118 -0
- package/src/{ardy → motion}/physics-support.js +1 -76
- package/src/motion/rig-snapshot.js +22 -0
- package/src/motion/take-segments.jsx +140 -0
- package/src/{ardy → motion}/timeline-coordinates.js +0 -21
- package/src/{ardy → motion}/timeline.css +1 -2
- package/src/{ardy → motion}/timeline.jsx +48 -419
- package/src/motion/waypoints.js +76 -0
- package/src/motion3/beats.js +83 -0
- package/src/motion3/body.js +158 -0
- package/src/motion3/bones.js +47 -0
- package/src/motion3/carried.js +47 -0
- package/src/motion3/compile-job.js +75 -0
- package/src/motion3/compile-worker.js +10 -0
- package/src/motion3/compiler/anchors.js +105 -0
- package/src/motion3/compiler/arm-orient.js +128 -0
- package/src/motion3/compiler/channels.js +125 -0
- package/src/motion3/compiler/compile.js +205 -0
- package/src/motion3/compiler/gait-rhythm.js +97 -0
- package/src/motion3/compiler/grip.js +171 -0
- package/src/motion3/compiler/head-steady.js +48 -0
- package/src/motion3/compiler/hold.js +134 -0
- package/src/motion3/compiler/idle.js +113 -0
- package/src/motion3/compiler/limbs.js +170 -0
- package/src/motion3/compiler/math.js +72 -0
- package/src/motion3/compiler/pose.js +162 -0
- package/src/motion3/compiler/quat.js +49 -0
- package/src/motion3/compiler/skeleton.js +74 -0
- package/src/motion3/compiler/validate-common.js +32 -0
- package/src/motion3/compiler/validate-goals.js +118 -0
- package/src/motion3/compiler/validate-objects.js +143 -0
- package/src/motion3/compiler/validate-parts.js +116 -0
- package/src/motion3/compiler/validate.js +122 -0
- package/src/motion3/compiler/vocab.js +73 -0
- package/src/motion3/compiler/walk-pelvis.js +275 -0
- package/src/motion3/compiler/walk.js +207 -0
- package/src/motion3/compiler/wrist-clamp.js +32 -0
- package/src/motion3/delta.js +47 -0
- package/src/motion3/describe.js +174 -0
- package/src/motion3/docs.js +27 -0
- package/src/motion3/edit-carry.js +21 -0
- package/src/motion3/fix/ease.js +16 -0
- package/src/motion3/fix/ground.js +37 -0
- package/src/motion3/fix/index.js +52 -0
- package/src/motion3/fix/push.js +71 -0
- package/src/motion3/fix/reach.js +46 -0
- package/src/motion3/fix/retime.js +79 -0
- package/src/motion3/fix/self.js +211 -0
- package/src/motion3/fix/slide.js +70 -0
- package/src/motion3/fix/turn.js +51 -0
- package/src/motion3/follow.js +39 -0
- package/src/motion3/free-motion.js +173 -0
- package/src/motion3/grip-choice.js +130 -0
- package/src/motion3/hold-plan.js +157 -0
- package/src/motion3/json-patch.js +77 -0
- package/src/motion3/key-aim.js +105 -0
- package/src/motion3/key-edit.js +82 -0
- package/src/motion3/lenient.js +127 -0
- package/src/motion3/object-keys.js +236 -0
- package/src/motion3/object-pose.js +182 -0
- package/src/motion3/object-tracks.js +12 -0
- package/src/motion3/pipeline.js +102 -0
- package/src/motion3/playback.js +51 -0
- package/src/motion3/probes.js +91 -0
- package/src/motion3/qa/body-checks.js +34 -0
- package/src/motion3/qa/check-names.js +14 -0
- package/src/motion3/qa/checks.js +121 -0
- package/src/motion3/qa/contact-checks.js +51 -0
- package/src/motion3/qa/explain.js +80 -0
- package/src/motion3/qa/fmt.js +5 -0
- package/src/motion3/qa/foot-checks.js +72 -0
- package/src/motion3/qa/geometry.js +77 -0
- package/src/motion3/qa/goals.js +191 -0
- package/src/motion3/qa/ground.js +60 -0
- package/src/motion3/qa/hints-contact.js +179 -0
- package/src/motion3/qa/hints-goal.js +160 -0
- package/src/motion3/qa/hints-knee.js +24 -0
- package/src/motion3/qa/hints-motion.js +209 -0
- package/src/motion3/qa/hints-object.js +91 -0
- package/src/motion3/qa/index.js +42 -0
- package/src/motion3/qa/key-checks.js +82 -0
- package/src/motion3/qa/near.js +96 -0
- package/src/motion3/qa/object-checks.js +135 -0
- package/src/motion3/qa/patch.js +161 -0
- package/src/motion3/qa/severity.js +38 -0
- package/src/motion3/qa/spec-checks.js +65 -0
- package/src/motion3/qa/style.js +218 -0
- package/src/motion3/qa/wrist-checks.js +69 -0
- package/src/motion3/qa/wrist-obs.js +60 -0
- package/src/motion3/refs.js +120 -0
- package/src/motion3/repair-tune.js +121 -0
- package/src/motion3/repair.js +83 -0
- package/src/motion3/report.js +67 -0
- package/src/motion3/rigs/index.js +14 -0
- package/src/motion3/rigs/x-bot-tpose-body.json +1 -0
- package/src/motion3/rigs/x-bot-tpose.json +1 -0
- package/src/motion3/rigs/y-bot-tpose-body.json +1 -0
- package/src/motion3/rigs/y-bot-tpose.json +1 -0
- package/src/motion3/scene.js +60 -0
- package/src/motion3/schema/brief.js +89 -0
- package/src/motion3/schema/fields.js +44 -0
- package/src/motion3/schema/spec-examples.js +46 -0
- package/src/motion3/schema/topics.js +111 -0
- package/src/motion3/schema/types.js +147 -0
- package/src/motion3/search.js +117 -0
- package/src/motion3/second-hand.js +112 -0
- package/src/motion3/skills/basic.js +82 -0
- package/src/motion3/skills/book.js +76 -0
- package/src/motion3/skills/car.js +145 -0
- package/src/motion3/skills/frames-auto.js +29 -0
- package/src/motion3/skills/index.js +83 -0
- package/src/motion3/skills/info.js +65 -0
- package/src/motion3/skills/smoke-kit.js +109 -0
- package/src/motion3/skills/smoke.js +160 -0
- package/src/motion3/spec-store.js +59 -0
- package/src/motion3/take-edit.js +155 -0
- package/src/motion3/take.js +63 -0
- package/src/motion3/timeline.js +127 -0
- package/src/motion3/views.js +121 -0
- package/src/motion3/walk-plan.js +119 -0
- package/src/object-catalog.jsx +7 -1
- package/src/object-colors.js +98 -0
- package/src/object-gizmo-parts.js +115 -0
- package/src/object-gizmo.jsx +37 -122
- package/src/object-glow.js +139 -0
- package/src/object-hidden.js +33 -0
- package/src/object-look.js +196 -0
- package/src/object-size.js +10 -0
- package/src/object-travel.js +71 -6
- package/src/panels/CameraPanel.jsx +7 -7
- package/src/panels/HdrEnvironmentControls.jsx +59 -0
- package/src/panels/LightPanel.jsx +25 -4
- package/src/panels/ObjectLookPanel.jsx +88 -0
- package/src/panels/PoseImagePanel.jsx +1 -1
- package/src/panels/PosePanel.jsx +2 -17
- package/src/panels/RigControlPanel.jsx +5 -53
- package/src/panels/SubjectBox.jsx +4 -1
- package/src/panels/details.css +14 -0
- package/src/posestudio.jsx +4 -27
- package/src/project-browser.jsx +3 -4
- package/src/project-resources.js +18 -31
- package/src/project.js +13 -104
- package/src/props.jsx +25 -280
- package/src/rail-dot-drag.js +23 -0
- package/src/render-passes.js +10 -0
- package/src/resource-status.jsx +1 -0
- package/src/result-modal.jsx +4 -19
- package/src/room.jsx +20 -12
- package/src/scene-assets.js +8 -6
- package/src/scene-light-place.js +72 -0
- package/src/scene-lights.jsx +148 -0
- package/src/scene-objects.js +31 -139
- package/src/scenes.js +15 -105
- package/src/semantic-edit.js +2 -5
- package/src/set-pieces.jsx +183 -0
- package/src/settings-menu.jsx +3 -11
- package/src/shell/BottomDock.jsx +11 -53
- package/src/shell/CompositionGuideButton.jsx +20 -0
- package/src/shell/DetailsSlot.jsx +16 -200
- package/src/shell/GhostsMenuGroup.jsx +74 -0
- package/src/shell/LibrarySlot.jsx +1 -3
- package/src/shell/MenuBar.jsx +3 -3
- package/src/shell/PreferencesDialog.jsx +15 -264
- package/src/shell/PreferencesSlot.jsx +2 -8
- package/src/shell/ShotPreviewHeader.jsx +17 -0
- package/src/shell/TopBar.jsx +5 -83
- package/src/shell/ViewportToolbar.jsx +23 -93
- package/src/shell/agent-glass.css +19 -6
- package/src/shell/details-selection.js +42 -0
- package/src/shell/ghosts-menu.css +22 -0
- package/src/shell/glass-regions.css +29 -3
- package/src/shell/preferences.css +6 -1
- package/src/shell/viewport.css +12 -0
- package/src/shot-authoring.js +4 -7
- package/src/soft-shadows.js +37 -0
- package/src/stage-attach-frames.js +12 -12
- package/src/stage-canvas-rigs.jsx +3 -3
- package/src/stage-capture.jsx +42 -8
- package/src/stage-characters.jsx +22 -216
- package/src/stage-editor-gizmos.jsx +6 -6
- package/src/stage-environment.js +93 -0
- package/src/stage-hdr-builtins.js +107 -0
- package/src/stage-hdr-image.js +116 -0
- package/src/stage-hdr-library.js +70 -0
- package/src/stage-hdr-map.js +94 -0
- package/src/stage-hdr.js +130 -0
- package/src/stage-lighting.js +149 -0
- package/src/stage-occlusion.js +210 -0
- package/src/stage-sky.jsx +159 -0
- package/src/stage-startup-defaults.js +5 -9
- package/src/studio-actions.js +12 -14
- package/src/studio-agent-commands.js +46 -16
- package/src/studio-agent-motion.js +9 -9
- package/src/studio-agent-protocol.js +46 -73
- package/src/studio-app-binding.js +29 -45
- package/src/studio-asset-list.js +17 -0
- package/src/studio-contact-sheet.js +37 -5
- package/src/studio-elements.js +27 -7
- package/src/studio-entity-bounds.js +18 -0
- package/src/studio-error-codes.js +8 -0
- package/src/studio-export-framing.js +152 -0
- package/src/studio-lighting-check.js +103 -0
- package/src/studio-motion-visual.js +150 -0
- package/src/studio-schema-errors.js +80 -0
- package/src/studio-support-gap.js +74 -0
- package/src/styles.css +1 -19
- package/src/timeline-defaults.js +5 -0
- package/src/timeline-extent.js +8 -20
- package/src/tutorial-walk.js +17 -0
- package/src/ui-scrub.js +1 -1
- package/src/use-shot-camera-tracks.js +12 -0
- package/src/view-ghosts.js +97 -0
- package/src/view-ghosts.jsx +49 -0
- package/src/workflow/AgentPanel.jsx +47 -166
- package/src/workflow/AgentProgressCard.jsx +107 -0
- package/src/workflow/AgentReplyText.jsx +37 -0
- package/src/workflow/AgentTurnSteps.jsx +84 -0
- package/src/workflow/LoginPanel.jsx +140 -0
- package/src/workflow/WorkflowBuilder.jsx +7 -23
- package/src/workflow/agent-client.js +54 -51
- package/src/workflow/agent-panel.css +71 -1
- package/src/workflow/agent-progress-card.css +167 -0
- package/src/workflow/agent-progress-view.js +47 -0
- package/src/workflow/agent-turn-steps.css +113 -0
- package/src/workflow/agent-turn.js +191 -0
- package/src/workflow/login-client.js +34 -0
- package/src/workflow/motion-input.js +18 -31
- package/src/workflow/node-schema.js +1 -1
- package/src/workflow/reply-markdown.js +69 -0
- package/src/workflow/scene-asset-sync.js +8 -8
- package/tools/agent-session-stats.mjs +61 -0
- package/tools/dev-full.mjs +5 -56
- package/tools/eslint-baseline.json +12 -13
- package/tools/headless-studio.mjs +85 -0
- package/tools/knip-baseline.json +0 -179
- package/tools/loc-baseline.json +33 -83
- package/tools/motion3/extract-body.mjs +103 -0
- package/tools/motion3/extract-rig.mjs +85 -0
- package/tools/process-supervisor.mjs +0 -88
- package/tools/run-tests.mjs +9 -124
- package/bin/agent/motion-runtime.mjs +0 -76
- package/dist/ardy/cskel27-rest.json +0 -557
- package/dist/assets/analytics-B41FmPYI.js +0 -1
- package/dist/assets/app-Do-Og5Pu.css +0 -1
- package/dist/assets/app-DwP-tl2x.js +0 -4920
- package/dist/assets/config-Bx7pYsD9.js +0 -1
- package/dist/assets/config-DeoFTYzb.css +0 -1
- package/dist/assets/demo-Dt0qZfS7.js +0 -1
- package/dist/assets/scene-asset-sync-BCWDJB1d.js +0 -19
- package/dist/assets/scene-asset-sync-DjJ_cJ2R.css +0 -1
- package/dist/assets/ticket-jqMhqL88.js +0 -1
- package/dist/assets/workflow-Br-K7Srs.js +0 -179
- package/dist/demo/walk-then-stop.npz +0 -0
- package/mcp/ardy-prompts.mjs +0 -218
- package/src/ardy/auto-fix-panel.css +0 -108
- package/src/ardy/auto-physics.js +0 -1194
- package/src/ardy/client.js +0 -153
- package/src/ardy/convert.js +0 -313
- package/src/ardy/cskel27-neutral.js +0 -39
- package/src/ardy/cskel27.js +0 -77
- package/src/ardy/export.js +0 -165
- package/src/ardy/fix-collisions.js +0 -2257
- package/src/ardy/foot-lock.js +0 -230
- package/src/ardy/motion-calibration.js +0 -118
- package/src/ardy/motion-edit.js +0 -213
- package/src/ardy/motion-url.js +0 -55
- package/src/ardy/mp4-duration.js +0 -102
- package/src/ardy/npz.js +0 -595
- package/src/ardy/physics-panel.css +0 -21
- package/src/ardy/physics-panel.jsx +0 -89
- package/src/ardy/physics-refine.js +0 -111
- package/src/ardy/physics-review.js +0 -567
- package/src/ardy/physics-temporal.js +0 -54
- package/src/ardy/platform-fit-panel.css +0 -64
- package/src/ardy/platform-fit-panel.jsx +0 -41
- package/src/ardy/platform-fit.js +0 -326
- package/src/ardy/playback.js +0 -711
- package/src/ardy/pose-pin.js +0 -97
- package/src/ardy/prompt-clips.js +0 -30
- package/src/ardy/retime.js +0 -211
- package/src/ardy/root-drop.js +0 -369
- package/src/ardy/timeline-resize.js +0 -13
- package/src/ardy/to-cskel27.js +0 -225
- package/src/ardy/trim.js +0 -33
- package/src/ardy/waypoints.js +0 -369
- package/src/ardy-bridge-contract.js +0 -281
- package/src/domains/motion/motion-video-capture.js +0 -664
- package/src/i2v-motion-client.js +0 -77
- package/src/i2v-motion-studio.jsx +0 -180
- package/src/line-edit.js +0 -1348
- package/src/motion/generation.js +0 -105
- package/src/motion-readiness-ui.jsx +0 -67
- package/src/motion-readiness.js +0 -40
- package/src/motion-resources.js +0 -177
- package/src/motion-store.js +0 -65
- package/src/motion-trail.js +0 -601
- package/src/multimodel-ingest.js +0 -394
- package/src/panels/PromptBlocksPanel.jsx +0 -444
- package/src/panels/VideoCapturePanel.jsx +0 -181
- package/src/pose-extract/browser.js +0 -42
- package/src/pose-extract/detector.js +0 -100
- package/src/pose-extract/fit.js +0 -227
- package/src/pose-extract/image-frame.js +0 -51
- package/src/pose-extract/index.js +0 -58
- package/src/pose-extract/keyframes.js +0 -74
- package/src/pose-extract/landmarks.js +0 -162
- package/src/pose-extract/mirror.js +0 -153
- package/src/pose-extract/take.js +0 -131
- package/src/pose-extract/video-frames.js +0 -91
- package/src/take-recipe.js +0 -165
- package/src/trail-key-conflicts.js +0 -39
- package/src/trail-pick.js +0 -59
- package/src/use-i2v-capture-modal.js +0 -15
- package/tools/ardy/BRIDGE.md +0 -37
- package/tools/ardy/GVHMR-NPZ.md +0 -68
- package/tools/ardy/README.md +0 -124
- package/tools/ardy/__pycache__/cclay_gvhmr_worker.cpython-313.pyc +0 -0
- package/tools/ardy/artifacts.mjs +0 -34
- package/tools/ardy/bridge.mjs +0 -1732
- package/tools/ardy/bvh-cskel27.mjs +0 -1265
- package/tools/ardy/cclay_constrained_generate.py +0 -561
- package/tools/ardy/cclay_gvhmr_worker.py +0 -264
- package/tools/ardy/cclay_motion_edit.py +0 -510
- package/tools/ardy/cclay_sequence_generate.py +0 -705
- package/tools/ardy/cclay_worker.py +0 -304
- package/tools/ardy/dump-npz.py +0 -205
- package/tools/ardy/extract-rest.mjs +0 -299
- package/tools/ardy/extract.mjs +0 -507
- package/tools/ardy/footage.mjs +0 -467
- package/tools/ardy/gvhmr-floor.mjs +0 -102
- package/tools/ardy/gvhmr-to-cskel27.mjs +0 -21
- package/tools/ardy/gvhmr_fastpath.py +0 -168
- package/tools/ardy/gvhmr_trajectory.py +0 -240
- package/tools/ardy/merge-text-encoder.py +0 -160
- package/tools/ardy/mocap-metrics.mjs +0 -200
- package/tools/ardy/mocap-quality-gate.mjs +0 -110
- package/tools/ardy/motion-stabilize.mjs +0 -348
- package/tools/ardy/npz.mjs +0 -414
- package/tools/ardy/pose-to-npz.mjs +0 -106
- package/tools/ardy/prompt-limits.mjs +0 -5
- package/tools/ardy/run-edit-on-box.sh +0 -73
- package/tools/ardy/run-local.mjs +0 -233
- package/tools/ardy/run-on-box.sh +0 -593
- package/tools/ardy/run-sequence-on-box.sh +0 -177
- package/tools/ardy/runners/gvhmr-worker.mjs +0 -206
- package/tools/ardy/runners/index.mjs +0 -18
- package/tools/ardy/runners/local.mjs +0 -455
- package/tools/ardy/runners/proc.mjs +0 -125
- package/tools/ardy/runners/remote.mjs +0 -252
- package/tools/ardy/setup-local.mjs +0 -189
- package/tools/ardy/setup-text-encoder-on-box.sh +0 -93
- package/tools/ardy/setup-text-encoder.py +0 -306
- package/tools/ardy/smpl-cskel27.mjs +0 -178
- package/tools/ardy/visual-qa.mjs +0 -196
- package/tools/ardy/vq-car.mjs +0 -52
- package/tools/bench/EXP3.md +0 -61
- package/tools/bench/cclay_bench_extract_incam.py +0 -125
- package/tools/bench/cclay_bench_extract_obs.py +0 -204
- package/tools/bench/cclay_bench_runner.py +0 -42
- package/tools/bench/cube-contact.mjs +0 -162
- package/tools/bench/exp3.mjs +0 -152
- package/tools/bench/extract-bench-lib.mjs +0 -216
- package/tools/bench/extract-bench.mjs +0 -302
- package/tools/bench/fal-generate.mjs +0 -88
- package/tools/bench/fit/README.md +0 -189
- package/tools/bench/fit/camera.mjs +0 -59
- package/tools/bench/fit/contact.mjs +0 -419
- package/tools/bench/fit/footlock.mjs +0 -234
- package/tools/bench/fit/motion.mjs +0 -85
- package/tools/bench/fit/pin.mjs +0 -27
- package/tools/bench/fit/remote.mjs +0 -105
- package/tools/bench/fit-bench.mjs +0 -126
- package/tools/bench/fit-sanity.mjs +0 -45
- package/tools/bench/metrics.mjs +0 -313
- package/tools/bench/obs/depth.mjs +0 -258
- package/tools/bench/obs/extrinsics.mjs +0 -175
- package/tools/bench/obs/fit_mannequin_betas.py +0 -340
- package/tools/bench/obs/ground.mjs +0 -294
- package/tools/bench/obs/heading.mjs +0 -151
- package/tools/bench/obs/ladder.mjs +0 -555
- package/tools/bench/obs/mannequin-betas.json +0 -124
- package/tools/bench/obs/remote.mjs +0 -83
- package/tools/bench/obs/rest_joints.py +0 -52
- package/tools/bench/obs/ybot-targets.mjs +0 -49
- package/tools/bench/obs-bench.mjs +0 -520
- package/tools/bench/score.mjs +0 -343
- package/tools/bench/summarize.mjs +0 -86
- package/tools/demo-worker/README.md +0 -137
- package/tools/demo-worker/api-client.mjs +0 -233
- package/tools/demo-worker/generate.mjs +0 -301
- package/tools/demo-worker/index.mjs +0 -466
- package/tools/demo-worker/sign-core.mjs +0 -112
- package/tools/demo-worker/sign.mjs +0 -161
- package/tools/gt-render/browser.mjs +0 -159
- package/tools/gt-render/camera-math.mjs +0 -276
- package/tools/gt-render/page.mjs +0 -267
- package/tools/gt-render/render.mjs +0 -514
- package/tools/gt-render/scene-box.mjs +0 -31
- package/tools/gt-render/take-transform.mjs +0 -87
- package/tools/kimodo/compare-seam.mjs +0 -108
- package/tools/kimodo/constraints.mjs +0 -244
- package/tools/kimodo/edit.mjs +0 -259
- package/tools/kimodo/effector-constraints.mjs +0 -200
- package/tools/kimodo/generate.mjs +0 -646
- package/tools/kimodo/local-output.mjs +0 -265
- package/tools/kimodo/measure-pose.mjs +0 -65
- package/tools/kimodo/measure-preserve.mjs +0 -899
- package/tools/kimodo/measure-waypoints.mjs +0 -55
- package/tools/kimodo/mlx-generate.py +0 -111
- package/tools/kimodo/pose-constraints.mjs +0 -248
- package/tools/kimodo/preserve-mask.mjs +0 -543
- package/tools/kimodo/read-npz.mjs +0 -189
- package/tools/kimodo/run-edit-on-box.mjs +0 -155
- package/tools/kimodo/run-sequence-on-box.mjs +0 -178
- package/tools/kimodo/runner.mjs +0 -284
- package/tools/kimodo/seam-batch.mjs +0 -77
- package/tools/kimodo/setup-local.mjs +0 -30
- package/tools/kimodo/setup-on-box.sh +0 -339
- package/tools/kimodo/soma77-to-cskel27.mjs +0 -305
- package/tools/morphgs/assets/gen_truth.py +0 -39
- package/tools/morphgs/assets/mesh_ori_rig.txt +0 -27
- package/tools/morphgs/assets/playback-check.mjs +0 -30
- package/tools/morphgs/demo-gate.sh +0 -26
- package/tools/morphgs/fbx2morphgs.mjs +0 -68
- package/tools/morphgs/morphgs-to-cskel27.mjs +0 -105
- package/tools/morphgs/patches/preprocess_src-none-mode.patch +0 -76
- package/tools/morphgs/setup-on-cluster.sh +0 -126
- package/tools/projflow/driver.py +0 -1069
- package/tools/projflow/generate.mjs +0 -805
- package/tools/projflow/hml22-to-cskel27.mjs +0 -735
- package/tools/projflow/line-edit-job.mjs +0 -547
- package/tools/projflow/replay.mjs +0 -452
- package/tools/projflow/runner.mjs +0 -269
- package/tools/projflow/service.mjs +0 -742
- package/tools/track/DESIGN.md +0 -513
- package/tools/track/backfill-provenance.mjs +0 -116
- package/tools/track/budget.mjs +0 -8
- package/tools/track/check-rig.mjs +0 -209
- package/tools/track/diagnostics.schema.json +0 -60
- package/tools/track/export-rig.mjs +0 -256
- package/tools/track/fallback.mjs +0 -7
- package/tools/track/fk-parity-fixture.mjs +0 -161
- package/tools/track/gate.mjs +0 -349
- package/tools/track/masks.mjs +0 -318
- package/tools/track/metrics.mjs +0 -201
- package/tools/track/publish-obs.mjs +0 -108
- package/tools/track/py/check_env.py +0 -61
- package/tools/track/py/eval_lr.py +0 -229
- package/tools/track/py/lr_viterbi.py +0 -294
- package/tools/track/py/masks.py +0 -405
- package/tools/track/py/objective.py +0 -382
- package/tools/track/py/rig.py +0 -508
- package/tools/track/py/scene.py +0 -326
- package/tools/track/py/test_joint_indices.py +0 -123
- package/tools/track/py/test_lr_viterbi.py +0 -195
- package/tools/track/py/test_masks.py +0 -229
- package/tools/track/py/test_rig.py +0 -288
- package/tools/track/py/test_scene.py +0 -354
- package/tools/track/py/test_track.py +0 -328
- package/tools/track/py/track.py +0 -411
- package/tools/track/remote.mjs +0 -140
- package/tools/track/rig-dump.mjs +0 -227
- package/tools/track/run-box-tests.mjs +0 -14
- package/tools/track/run-box.mjs +0 -168
- package/tools/track/setup-box.sh +0 -59
- package/tools/track/study-2d.mjs +0 -728
- /package/src/{ardy → motion}/collision-blockers.js +0 -0
- /package/src/{ardy → motion}/ground.js +0 -0
- /package/src/{ardy → motion}/ik-drag.js +0 -0
- /package/src/{ardy → motion}/ik-key-json.js +0 -0
- /package/src/{ardy → motion}/ik.js +0 -0
- /package/src/{ardy → motion}/physics-surface.js +0 -0
- /package/src/{ardy → motion}/playback-clock.js +0 -0
- /package/src/{ardy → motion}/range-pin.js +0 -0
|
@@ -1,452 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* replay.mjs — recipe replay (contract C10) and the per-field line-edit rules
|
|
3
|
-
* both the C6 and the C10 paths validate with.
|
|
4
|
-
*
|
|
5
|
-
* A take is a RECIPE, not a result: seed + prompt blocks + the line edits the
|
|
6
|
-
* artist drew on top. Determinism makes the result reconstructible, so
|
|
7
|
-
* regenerating or extending a take no longer has to destroy its refinements —
|
|
8
|
-
* the app sends the stored line edits back as `replay` and the bridge re-applies
|
|
9
|
-
* them to the freshly generated take.
|
|
10
|
-
*
|
|
11
|
-
* WHY THIS IS ITS OWN MODULE and not more bridge code: everything here is pure
|
|
12
|
-
* arithmetic plus one injectable job runner, so the whole contract — the
|
|
13
|
-
* per-entry rules, the boundary warning, the sequential chaining — is testable
|
|
14
|
-
* on a laptop with no GPU and no sidecar. bridge.mjs keeps only the wiring:
|
|
15
|
-
* which take the chain starts from, where the artifacts go, and what it streams.
|
|
16
|
-
*
|
|
17
|
-
* THE CHAIN, stated once. Entry i reads entry i-1's OUTPUT; entry 0 reads the
|
|
18
|
-
* freshly generated take. Every output lands in the request's artifact dir as
|
|
19
|
-
* replay-<i>.npz and only the LAST one is registered — the intermediates are
|
|
20
|
-
* evidence, not takes. A failing entry is skipped and the chain continues from
|
|
21
|
-
* the last good file: a missing refinement beats a dead run.
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
import { join } from "node:path";
|
|
25
|
-
|
|
26
|
-
import { TRACK_TO_HML22_JOINT, UNMAPPABLE_TRACKS } from "./generate.mjs";
|
|
27
|
-
|
|
28
|
-
/** The tracks a line edit (C6) or a replay entry (C10) may name — the wrapper's
|
|
29
|
-
* own track -> hml22 joint table, never a copy of it. */
|
|
30
|
-
const LINE_EDIT_TRACK_IDS = Object.keys(TRACK_TO_HML22_JOINT);
|
|
31
|
-
const LINE_EDIT_POINTS_MIN = 2;
|
|
32
|
-
const LINE_EDIT_POINTS_MAX = 64; // matches src/line-edit.js MAX_LINE_POINTS
|
|
33
|
-
/** C6's OTHER gesture: 3D pins (`pins3d`), one per scrubbed moment.
|
|
34
|
-
*
|
|
35
|
-
* A pin is a keyframe — "the hand is HERE at frame 100" — and it is the shape
|
|
36
|
-
* ProjFlow is actually evaluated on: its whole spatial-control table is sparse
|
|
37
|
-
* keyframes (1, 2, 5, 49, 196 of them) with exact satisfaction and the flow
|
|
38
|
-
* prior filling the gaps. A drawn line is the dense end of the same mechanism.
|
|
39
|
-
*
|
|
40
|
-
* ONE is a legal edit, unlike points2d's two: a line needs two points to have a
|
|
41
|
-
* direction, a pin needs none. EIGHT is the cap, and it is a UX number rather
|
|
42
|
-
* than a solver one — 3 rows per pin is nothing next to 128 line rows — past
|
|
43
|
-
* eight pins in one edit the artist is keyframing by hand and wants the pose
|
|
44
|
-
* studio, not a reroute. */
|
|
45
|
-
export const LINE_EDIT_PINS_MIN = 1;
|
|
46
|
-
export const LINE_EDIT_PINS_MAX = 8;
|
|
47
|
-
/** C10's cap. A recipe is an authoring history, not a render queue: 16 chained
|
|
48
|
-
* box round trips is already a minute of wall time on top of the generation. */
|
|
49
|
-
export const REPLAY_MAX = 16;
|
|
50
|
-
/** Mirrors the bridge's own optional-seed cap; the bridge passes its constant in
|
|
51
|
-
* so the two can never disagree, and this default only serves direct callers. */
|
|
52
|
-
const DEFAULT_SEED_MAX = 2 ** 31 - 1;
|
|
53
|
-
/** Frames either side of a block boundary that a replayed edit is warned about.
|
|
54
|
-
* See boundaryWarningFor for why the number is small. */
|
|
55
|
-
export const BOUNDARY_MARGIN = 5;
|
|
56
|
-
/** Fields a replay entry may NOT carry: `replay` cannot be combined with the
|
|
57
|
-
* modes that splice or rewrite an existing take, because then "the take the
|
|
58
|
-
* replay applies to" has two candidate answers. */
|
|
59
|
-
const REPLAY_EXCLUSIVE = ["lineEdit", "motionEdit", "regenerateSegments"];
|
|
60
|
-
|
|
61
|
-
/* ------------------------------------------------------------------------- */
|
|
62
|
-
/* the per-field rules, shared by C6 and C10 */
|
|
63
|
-
/* ------------------------------------------------------------------------- */
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* The field checks a line-edit payload must pass, wherever it arrives from.
|
|
67
|
-
*
|
|
68
|
-
* `label` is the wire path the message names — "lineEdit" for a C6 request,
|
|
69
|
-
* "replay[3]" for the fourth entry of a C10 replay — and it is the ONLY
|
|
70
|
-
* difference between the two paths. The strings are otherwise byte-identical to
|
|
71
|
-
* the ones the C6 validator has always returned.
|
|
72
|
-
*
|
|
73
|
-
* What is NOT here, deliberately: `sourceMotion` (C6 requires it, C10 forbids
|
|
74
|
-
* it), the exclusivity rules, and `preview` (C6 only). Those belong to whoever
|
|
75
|
-
* calls this.
|
|
76
|
-
*
|
|
77
|
-
* Frame numbers are all on the app's 24 fps clip clock, exactly as sent; the
|
|
78
|
-
* 24 -> 20 conversion is line-edit-job.mjs's business and is not duplicated
|
|
79
|
-
* here or in the bridge.
|
|
80
|
-
*
|
|
81
|
-
* Returns an error message naming the offending field, or null when valid.
|
|
82
|
-
*/
|
|
83
|
-
export function validateLineEditFields(lineEdit, clipFrames, label = "lineEdit") {
|
|
84
|
-
if (!lineEdit || typeof lineEdit !== "object" || Array.isArray(lineEdit)) {
|
|
85
|
-
return `field '${label}' must be an object`;
|
|
86
|
-
}
|
|
87
|
-
// `chest` is a REAL pose-studio track and a real refusal: cskel27 Spine2 is
|
|
88
|
-
// one of the five joints the hml22 skeleton has no source for (S2), so there
|
|
89
|
-
// is nothing to constrain. Refused by name, with the alternative, rather than
|
|
90
|
-
// silently retargeted onto a neighbouring spine joint — which would put the
|
|
91
|
-
// drawn line on a curve the artist did not draw.
|
|
92
|
-
if (typeof lineEdit.track === "string" && Object.hasOwn(UNMAPPABLE_TRACKS, lineEdit.track)) {
|
|
93
|
-
return (
|
|
94
|
-
`field '${label}.track' "${lineEdit.track}" cannot be line-edited: ${UNMAPPABLE_TRACKS[lineEdit.track]}; ` +
|
|
95
|
-
"draw on spine or neck instead"
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
if (typeof lineEdit.track !== "string" || !Object.hasOwn(TRACK_TO_HML22_JOINT, lineEdit.track)) {
|
|
99
|
-
return (
|
|
100
|
-
`field '${label}.track' ${JSON.stringify(lineEdit.track)} is not a line-editable IK track id; ` +
|
|
101
|
-
`valid ids are ${LINE_EDIT_TRACK_IDS.join(", ")}`
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
const range = lineEdit.frameRange;
|
|
105
|
-
if (!range || typeof range !== "object" || Array.isArray(range)) {
|
|
106
|
-
return `field '${label}.frameRange' must be an object { startFrame, endFrame }`;
|
|
107
|
-
}
|
|
108
|
-
if (!Number.isInteger(range.startFrame) || !Number.isInteger(range.endFrame)) {
|
|
109
|
-
return `field '${label}.frameRange' startFrame and endFrame must be integers`;
|
|
110
|
-
}
|
|
111
|
-
// Half-open and inside the clip, like every other range that crosses this
|
|
112
|
-
// boundary. The clip length comes from `duration`, which the app derives from
|
|
113
|
-
// the take's own frame count; the job re-checks against the npz itself, since
|
|
114
|
-
// only that file knows how long the take really is.
|
|
115
|
-
if (range.startFrame < 0 || range.endFrame <= range.startFrame || range.endFrame > clipFrames) {
|
|
116
|
-
return `field '${label}.frameRange' must be a non-empty half-open range inside 0..${clipFrames}`;
|
|
117
|
-
}
|
|
118
|
-
// Two points need two frames: one constrained frame is a pin, not a line.
|
|
119
|
-
if (range.endFrame - range.startFrame < 2) {
|
|
120
|
-
return `field '${label}.frameRange' must span at least 2 frames`;
|
|
121
|
-
}
|
|
122
|
-
/* ---------------------------- pins3d (C6 v2) ---------------------------
|
|
123
|
-
* ONE EDIT IS ONE GESTURE. A stroke says "go along this shape" and pins say
|
|
124
|
-
* "be exactly here at these moments"; combining them would ask the box to
|
|
125
|
-
* satisfy a 2D path and a set of 3D points that were authored through
|
|
126
|
-
* different mental models and are only accidentally compatible. So pins3d
|
|
127
|
-
* REPLACES points2d and the camera — a pin is already in world metres and
|
|
128
|
-
* needs no lens to be read through, which is also why a pinned edit survives
|
|
129
|
-
* a camera move that would discard a drawn one.
|
|
130
|
-
*
|
|
131
|
-
* Checked BEFORE points2d so a pins payload never trips the "needs at least
|
|
132
|
-
* 2 points" message about a field it deliberately omitted. */
|
|
133
|
-
if (lineEdit.pins3d !== undefined) {
|
|
134
|
-
const pins = lineEdit.pins3d;
|
|
135
|
-
if (lineEdit.points2d !== undefined) {
|
|
136
|
-
return `field '${label}' must carry either points2d (a drawn line) or pins3d (3D pins), not both`;
|
|
137
|
-
}
|
|
138
|
-
if (!Array.isArray(pins) || pins.length < LINE_EDIT_PINS_MIN) {
|
|
139
|
-
return `field '${label}.pins3d' needs at least ${LINE_EDIT_PINS_MIN} pin`;
|
|
140
|
-
}
|
|
141
|
-
if (pins.length > LINE_EDIT_PINS_MAX) {
|
|
142
|
-
return `field '${label}.pins3d' is capped at ${LINE_EDIT_PINS_MAX} pins, got ${pins.length}`;
|
|
143
|
-
}
|
|
144
|
-
let previousFrame = -1;
|
|
145
|
-
for (let index = 0; index < pins.length; index += 1) {
|
|
146
|
-
const pin = pins[index];
|
|
147
|
-
if (!pin || typeof pin !== "object" || Array.isArray(pin)) {
|
|
148
|
-
return `field '${label}.pins3d[${index}]' must be an object { frame, position }`;
|
|
149
|
-
}
|
|
150
|
-
if (!Number.isInteger(pin.frame)) {
|
|
151
|
-
return `field '${label}.pins3d[${index}].frame' must be an integer app-clip frame`;
|
|
152
|
-
}
|
|
153
|
-
// Inside the range and half-open with it: a pin on endFrame names a
|
|
154
|
-
// frame the edit does not own, and the splice would throw it away.
|
|
155
|
-
if (pin.frame < range.startFrame || pin.frame >= range.endFrame) {
|
|
156
|
-
return `field '${label}.pins3d[${index}].frame' ${pin.frame} is outside frameRange ${range.startFrame}..${range.endFrame}`;
|
|
157
|
-
}
|
|
158
|
-
// STRICTLY ascending: two pins on one frame are two answers to the
|
|
159
|
-
// same question, and out-of-order pins mean the caller lost track of
|
|
160
|
-
// which pin it was editing.
|
|
161
|
-
if (pin.frame <= previousFrame) {
|
|
162
|
-
return `field '${label}.pins3d' frames must be strictly ascending, got ${pin.frame} after ${previousFrame}`;
|
|
163
|
-
}
|
|
164
|
-
previousFrame = pin.frame;
|
|
165
|
-
const position = pin.position;
|
|
166
|
-
if (!Array.isArray(position) || position.length !== 3 || !position.every((value) => typeof value === "number" && Number.isFinite(value))) {
|
|
167
|
-
return `field '${label}.pins3d[${index}].position' must be [x, y, z] finite numbers in world metres`;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
// The camera is a stroke's business. Refused rather than ignored, for the
|
|
171
|
-
// same reason a replay entry's sourceMotion is: silently dropping a field
|
|
172
|
-
// lets the caller believe it meant something.
|
|
173
|
-
if (lineEdit.camera !== undefined) {
|
|
174
|
-
return `field '${label}.camera' must be omitted for a pins3d edit — pins are world-space and need no lens`;
|
|
175
|
-
}
|
|
176
|
-
if (lineEdit.prompt !== undefined && typeof lineEdit.prompt !== "string") {
|
|
177
|
-
return `field '${label}.prompt' must be a string when present`;
|
|
178
|
-
}
|
|
179
|
-
return null;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const points = lineEdit.points2d;
|
|
183
|
-
if (!Array.isArray(points) || points.length < LINE_EDIT_POINTS_MIN) {
|
|
184
|
-
return `field '${label}.points2d' needs at least ${LINE_EDIT_POINTS_MIN} points`;
|
|
185
|
-
}
|
|
186
|
-
// The cap is the app's own, and it is a solver budget: the box builds two
|
|
187
|
-
// affine rows per point and factorises an m x m system every ODE step.
|
|
188
|
-
if (points.length > LINE_EDIT_POINTS_MAX) {
|
|
189
|
-
return `field '${label}.points2d' is capped at ${LINE_EDIT_POINTS_MAX} points, got ${points.length}`;
|
|
190
|
-
}
|
|
191
|
-
for (let index = 0; index < points.length; index += 1) {
|
|
192
|
-
const point = points[index];
|
|
193
|
-
if (!Array.isArray(point) || point.length !== 2 || !point.every((value) => typeof value === "number" && Number.isFinite(value))) {
|
|
194
|
-
return `field '${label}.points2d[${index}]' must be [u, v] finite numbers`;
|
|
195
|
-
}
|
|
196
|
-
// Viewport-NORMALISED, and enforced rather than clamped: a pixel
|
|
197
|
-
// coordinate here is the single most likely wire mistake and it produces a
|
|
198
|
-
// plausible-looking line through the wrong ray.
|
|
199
|
-
if (point[0] < 0 || point[0] > 1 || point[1] < 0 || point[1] > 1) {
|
|
200
|
-
return `field '${label}.points2d[${index}]' must be viewport-normalized into 0..1, got ${JSON.stringify(point)}`;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
const camera = lineEdit.camera;
|
|
204
|
-
if (!camera || typeof camera !== "object" || Array.isArray(camera)) {
|
|
205
|
-
return `field '${label}.camera' must be an object { fx, fy, cx, cy, R, t }`;
|
|
206
|
-
}
|
|
207
|
-
for (const key of ["fx", "fy", "cx", "cy"]) {
|
|
208
|
-
if (typeof camera[key] !== "number" || !Number.isFinite(camera[key])) {
|
|
209
|
-
return `field '${label}.camera.${key}' must be a finite number`;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
// A non-positive focal length means the uv/NDC flip was applied twice or not
|
|
213
|
-
// at all, and every solved position would be mirrored.
|
|
214
|
-
if (camera.fx <= 0 || camera.fy <= 0) {
|
|
215
|
-
return `field '${label}.camera' has a non-positive focal length — the uv convention is inverted`;
|
|
216
|
-
}
|
|
217
|
-
// The intrinsics live in the SAME units as points2d. A pixel focal length
|
|
218
|
-
// beside normalised points solves cleanly and lands nowhere near the stroke.
|
|
219
|
-
if (camera.fx > 50 || camera.fy > 50) {
|
|
220
|
-
return `field '${label}.camera' has PIXEL focal lengths (${camera.fx}, ${camera.fy}); normalize them by the viewport size, like points2d`;
|
|
221
|
-
}
|
|
222
|
-
if (!Array.isArray(camera.R) || camera.R.length !== 9 || !camera.R.every((value) => typeof value === "number" && Number.isFinite(value))) {
|
|
223
|
-
return `field '${label}.camera.R' must be 9 finite numbers (3x3 row-major, world-to-camera)`;
|
|
224
|
-
}
|
|
225
|
-
if (!Array.isArray(camera.t) || camera.t.length !== 3 || !camera.t.every((value) => typeof value === "number" && Number.isFinite(value))) {
|
|
226
|
-
return `field '${label}.camera.t' must be 3 finite numbers`;
|
|
227
|
-
}
|
|
228
|
-
if (lineEdit.prompt !== undefined && typeof lineEdit.prompt !== "string") {
|
|
229
|
-
return `field '${label}.prompt' must be a string when present`;
|
|
230
|
-
}
|
|
231
|
-
return null;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/* ------------------------------------------------------------------------- */
|
|
235
|
-
/* the request field (contract C10) */
|
|
236
|
-
/* ------------------------------------------------------------------------- */
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* `body.replay`: the line edits to re-apply to the take this request generates.
|
|
240
|
-
*
|
|
241
|
-
* Each entry is a C6 payload MINUS sourceMotion — the base is the take being
|
|
242
|
-
* generated right now, so a stored source id would name a take that is about to
|
|
243
|
-
* be superseded. Sending one anyway is refused rather than ignored: silently
|
|
244
|
-
* dropping it would let a client believe it chose the base.
|
|
245
|
-
*
|
|
246
|
-
* Returns an error message naming the offending field, or null when valid.
|
|
247
|
-
*/
|
|
248
|
-
export function validateReplay(body, clipFrames, { seedMax = DEFAULT_SEED_MAX } = {}) {
|
|
249
|
-
const replay = body?.replay;
|
|
250
|
-
if (!Array.isArray(replay)) {
|
|
251
|
-
return "field 'replay' must be an array of line-edit payloads (contract C10)";
|
|
252
|
-
}
|
|
253
|
-
// Exclusivity first, for the same reason C6 checks it first: a body carrying
|
|
254
|
-
// both is two run modes, and guessing which take the replay applies to is not
|
|
255
|
-
// the bridge's call. Plain generation, a prompt schedule and preserve all
|
|
256
|
-
// produce ONE fresh take, which is exactly what a replay needs.
|
|
257
|
-
for (const field of REPLAY_EXCLUSIVE) {
|
|
258
|
-
if (body[field] !== undefined) {
|
|
259
|
-
return (
|
|
260
|
-
`field 'replay' cannot be combined with ${field}: contract C10 replays onto the take this request ` +
|
|
261
|
-
"generates, and a spliced or rewritten take leaves the replay base ambiguous"
|
|
262
|
-
);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
if (replay.length > REPLAY_MAX) {
|
|
266
|
-
return `field 'replay' is capped at ${REPLAY_MAX} entries, got ${replay.length} (contract C10)`;
|
|
267
|
-
}
|
|
268
|
-
for (let index = 0; index < replay.length; index += 1) {
|
|
269
|
-
const entry = replay[index];
|
|
270
|
-
const label = `replay[${index}]`;
|
|
271
|
-
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
272
|
-
return `field '${label}' must be an object`;
|
|
273
|
-
}
|
|
274
|
-
if (entry.sourceMotion !== undefined) {
|
|
275
|
-
return (
|
|
276
|
-
`field '${label}.sourceMotion' must be omitted: contract C10 rebinds every replay entry to the take ` +
|
|
277
|
-
"this request generates"
|
|
278
|
-
);
|
|
279
|
-
}
|
|
280
|
-
// Preview is a 20-step draft for the interactive loop. A replay is the
|
|
281
|
-
// take's history being rebuilt, so it always runs at full quality — an
|
|
282
|
-
// entry asking otherwise is a client bug, not a preference.
|
|
283
|
-
if (entry.preview !== undefined) {
|
|
284
|
-
return `field '${label}.preview' must be omitted: contract C10 replays always run at full quality`;
|
|
285
|
-
}
|
|
286
|
-
if (entry.seed !== undefined && (!Number.isInteger(entry.seed) || entry.seed < 0 || entry.seed > seedMax)) {
|
|
287
|
-
return `field '${label}.seed' must be an integer in 0..${seedMax}`;
|
|
288
|
-
}
|
|
289
|
-
const error = validateLineEditFields(entry, clipFrames, label);
|
|
290
|
-
if (error) return error;
|
|
291
|
-
}
|
|
292
|
-
return null;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/* ------------------------------------------------------------------------- */
|
|
296
|
-
/* the boundary rule */
|
|
297
|
-
/* ------------------------------------------------------------------------- */
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* The INTERNAL block boundaries of a generated take: the frame where one
|
|
301
|
-
* prompt block's rollout hands over to the next.
|
|
302
|
-
*
|
|
303
|
-
* Why they matter (the user's catch, verified in pf-chain): block N+1 is
|
|
304
|
-
* conditioned on block N's PRE-edit tail. An edit that does not touch a block
|
|
305
|
-
* tail leaves that tail byte-identical, so replaying it reproduces the same
|
|
306
|
-
* chain exactly; an edit that DOES touch a tail changes what block N+1 was
|
|
307
|
-
* conditioned on, and the replayed take can drift after the seam.
|
|
308
|
-
*
|
|
309
|
-
* The clip end is excluded — there is no block after it to condition — and a
|
|
310
|
-
* request with no `segments` field (or with fewer than two blocks in it) is a
|
|
311
|
-
* single rollout, so it has no internal boundaries at all.
|
|
312
|
-
*
|
|
313
|
-
* `segments` entries are contiguous by the time they get here (the bridge
|
|
314
|
-
* refuses anything else), so their endFrames ARE the cumulative ones.
|
|
315
|
-
*/
|
|
316
|
-
export function blockBoundaries(segments, clipFrames) {
|
|
317
|
-
if (!Array.isArray(segments) || segments.length < 2) return [];
|
|
318
|
-
const boundaries = [];
|
|
319
|
-
for (const segment of segments) {
|
|
320
|
-
const end = segment?.endFrame;
|
|
321
|
-
if (!Number.isInteger(end) || end <= 0 || end >= clipFrames) continue;
|
|
322
|
-
if (!boundaries.includes(end)) boundaries.push(end);
|
|
323
|
-
}
|
|
324
|
-
return boundaries;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* Does this half-open app-frame range come close enough to a block boundary to
|
|
329
|
-
* be warned about?
|
|
330
|
-
*
|
|
331
|
-
* The window is [boundary - margin, boundary + margin] INCLUSIVE and the range
|
|
332
|
-
* is [startFrame, endFrame) — so the last constrained frame is endFrame - 1 and
|
|
333
|
-
* the two intersect iff `startFrame <= boundary + margin` and
|
|
334
|
-
* `endFrame - 1 >= boundary - margin`.
|
|
335
|
-
*
|
|
336
|
-
* The margin is small (5 frames, ~0.2 s) on purpose: the conditioning window the
|
|
337
|
-
* next block reads is the tail of the previous one, and a warning that fires on
|
|
338
|
-
* half the clip would be ignored. NON-BLOCKING by contract — it is a note on the
|
|
339
|
-
* report, never a refusal.
|
|
340
|
-
*/
|
|
341
|
-
export function boundaryWarningFor(frameRange, boundaries, margin = BOUNDARY_MARGIN) {
|
|
342
|
-
if (!frameRange || !Array.isArray(boundaries) || boundaries.length === 0) return false;
|
|
343
|
-
const { startFrame, endFrame } = frameRange;
|
|
344
|
-
if (!Number.isInteger(startFrame) || !Number.isInteger(endFrame)) return false;
|
|
345
|
-
return boundaries.some(
|
|
346
|
-
(boundary) => startFrame <= boundary + margin && endFrame - 1 >= boundary - margin
|
|
347
|
-
);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
/* ------------------------------------------------------------------------- */
|
|
351
|
-
/* the sequential run */
|
|
352
|
-
/* ------------------------------------------------------------------------- */
|
|
353
|
-
|
|
354
|
-
/**
|
|
355
|
-
* Re-apply a recipe's line edits to a freshly generated take, in order.
|
|
356
|
-
*
|
|
357
|
-
* @param {object} options
|
|
358
|
-
* @param {object[]} options.entries validated C10 entries, application order.
|
|
359
|
-
* @param {string} options.takePath the fresh take entry 0 edits.
|
|
360
|
-
* @param {string} options.artifactDir where replay-<i>.npz files are written.
|
|
361
|
-
* @param {number[]} [options.boundaries] internal block boundaries (app frames).
|
|
362
|
-
* @param {number} [options.boundaryMargin=5]
|
|
363
|
-
* @param {number} [options.appFps=24]
|
|
364
|
-
* @param {Function} options.runJob runLineEditJob, injected so the sequencing
|
|
365
|
-
* can be tested without a GPU — the chaining and the failure policy are the
|
|
366
|
-
* only logic here, and they are where the bugs would be.
|
|
367
|
-
* @param {(line: string) => void} [options.onStatus]
|
|
368
|
-
* @returns {Promise<{entries: object[], takePath: string}>} the per-entry report
|
|
369
|
-
* (C10 shape) and the path of the LAST GOOD take — what the caller registers.
|
|
370
|
-
*/
|
|
371
|
-
export async function runReplay({
|
|
372
|
-
entries,
|
|
373
|
-
takePath,
|
|
374
|
-
artifactDir,
|
|
375
|
-
boundaries = [],
|
|
376
|
-
boundaryMargin = BOUNDARY_MARGIN,
|
|
377
|
-
appFps = 24,
|
|
378
|
-
runJob,
|
|
379
|
-
onStatus,
|
|
380
|
-
} = {}) {
|
|
381
|
-
if (!Array.isArray(entries)) throw new Error("runReplay: entries must be an array");
|
|
382
|
-
if (!takePath) throw new Error("runReplay: takePath is required");
|
|
383
|
-
if (!artifactDir) throw new Error("runReplay: artifactDir is required");
|
|
384
|
-
if (typeof runJob !== "function") throw new Error("runReplay: runJob is required");
|
|
385
|
-
const status = typeof onStatus === "function" ? onStatus : () => {};
|
|
386
|
-
const report = [];
|
|
387
|
-
// The chain cursor. It only ever moves onto a file a job actually wrote, so a
|
|
388
|
-
// failed entry leaves it pointing at the last take that exists on disk.
|
|
389
|
-
let current = takePath;
|
|
390
|
-
for (let index = 0; index < entries.length; index += 1) {
|
|
391
|
-
const entry = entries[index];
|
|
392
|
-
const outputPath = join(artifactDir, `replay-${index}.npz`);
|
|
393
|
-
const boundaryWarning = boundaryWarningFor(entry.frameRange, boundaries, boundaryMargin);
|
|
394
|
-
status(
|
|
395
|
-
`[bridge] replay ${index + 1}/${entries.length}: ${entry.track} over frames ` +
|
|
396
|
-
`${entry.frameRange.startFrame}..${entry.frameRange.endFrame - 1}`
|
|
397
|
-
);
|
|
398
|
-
if (boundaryWarning) {
|
|
399
|
-
status(
|
|
400
|
-
`[bridge] replay ${index + 1}/${entries.length} WARNING: frames ` +
|
|
401
|
-
`${entry.frameRange.startFrame}..${entry.frameRange.endFrame - 1} sit within ${boundaryMargin} frames of a ` +
|
|
402
|
-
`block boundary (${boundaries.join(", ")}); the next block was conditioned on the UNEDITED tail, ` +
|
|
403
|
-
"so this edit may not replay exactly"
|
|
404
|
-
);
|
|
405
|
-
}
|
|
406
|
-
let ok = true;
|
|
407
|
-
let error;
|
|
408
|
-
let seamStartDelta = null;
|
|
409
|
-
let seamEndDelta = null;
|
|
410
|
-
try {
|
|
411
|
-
const meta = await runJob({
|
|
412
|
-
lineEdit: entry,
|
|
413
|
-
takePath: current,
|
|
414
|
-
outputPath,
|
|
415
|
-
appFps,
|
|
416
|
-
...(entry.seed === undefined ? {} : { seed: entry.seed }),
|
|
417
|
-
onStatus: status,
|
|
418
|
-
});
|
|
419
|
-
// The job's own output path, not the one we asked for: they are the same
|
|
420
|
-
// file, and reading it back from the meta keeps this loop honest about
|
|
421
|
-
// what actually got written.
|
|
422
|
-
current = meta?.output || outputPath;
|
|
423
|
-
seamStartDelta = Number.isFinite(meta?.seamStartDelta) ? meta.seamStartDelta : null;
|
|
424
|
-
seamEndDelta = Number.isFinite(meta?.seamEndDelta) ? meta.seamEndDelta : null;
|
|
425
|
-
status(
|
|
426
|
-
`[bridge] replay ${index + 1}/${entries.length} applied: seams ` +
|
|
427
|
-
`${seamStartDelta === null ? "n/a" : seamStartDelta.toFixed(4)} / ` +
|
|
428
|
-
`${seamEndDelta === null ? "n/a" : seamEndDelta.toFixed(4)} m`
|
|
429
|
-
);
|
|
430
|
-
} catch (err) {
|
|
431
|
-
// LOUD, and then on with the run: the take the artist asked for exists,
|
|
432
|
-
// and losing one refinement is recoverable by drawing it again. Losing
|
|
433
|
-
// the whole generation is not.
|
|
434
|
-
ok = false;
|
|
435
|
-
error = err?.message || String(err);
|
|
436
|
-
status(
|
|
437
|
-
`[bridge] replay ${index + 1}/${entries.length} FAILED (${entry.track}): ${error} — ` +
|
|
438
|
-
"continuing from the last good take"
|
|
439
|
-
);
|
|
440
|
-
}
|
|
441
|
-
report.push({
|
|
442
|
-
index,
|
|
443
|
-
track: entry.track,
|
|
444
|
-
ok,
|
|
445
|
-
boundaryWarning,
|
|
446
|
-
seamStartDelta,
|
|
447
|
-
seamEndDelta,
|
|
448
|
-
...(ok ? {} : { error }),
|
|
449
|
-
});
|
|
450
|
-
}
|
|
451
|
-
return { entries: report, takePath: current };
|
|
452
|
-
}
|