cozyclay 2.1.1 → 3.0.1
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 +88 -0
- package/README.md +12 -30
- package/bin/agent/agent-routes.mjs +43 -116
- package/bin/agent/agent-runner.mjs +14 -15
- 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/session-store.mjs +2 -2
- package/bin/agent/studio-context-budget.mjs +93 -0
- 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 +3 -3
- 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-BWCqdgWA.js +1 -0
- package/dist/assets/app-DfTCxaKk.js +5087 -0
- package/dist/assets/app-f-fkV5uP.css +1 -0
- package/dist/assets/compile-worker-BxKXh07s.js +5 -0
- package/dist/assets/fflate.module-KbGj5-H6.js +5 -0
- package/dist/assets/{first-shot-handoff-msLI71g1.js → first-shot-handoff-DNO8kv_N.js} +1 -1
- package/dist/assets/{landing-ChUvB_zL.js → landing-yfZaJkWc.js} +1 -1
- package/dist/assets/scene-asset-sync-U3kS2rvK.js +20 -0
- package/dist/assets/scene-asset-sync-_S8NQl5B.css +1 -0
- package/dist/assets/workflow-D78eiKTa.js +179 -0
- package/dist/cozyclay-package.json +1 -1
- package/dist/index.html +3 -3
- 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 +151 -2625
- package/src/agent-progress.js +80 -0
- package/src/agent-turn-mark.js +30 -0
- package/src/analytics.js +1 -19
- package/src/app-context.js +12 -2
- 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 +10 -8
- 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 +214 -0
- package/src/commands/objects.js +6 -4
- package/src/commands/shot.js +55 -13
- 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 +53 -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 +47 -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 +60 -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 +42 -413
- 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 +79 -0
- package/src/motion3/compile-worker.js +10 -0
- package/src/motion3/compiler/anchors.js +113 -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 +197 -0
- package/src/motion3/compiler/math.js +72 -0
- package/src/motion3/compiler/pose.js +177 -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 +144 -0
- package/src/motion3/compiler/validate-parts.js +118 -0
- package/src/motion3/compiler/validate.js +122 -0
- package/src/motion3/compiler/vocab.js +74 -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 +177 -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 +204 -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/people.js +82 -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 +125 -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 +195 -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 +44 -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/people-checks.js +37 -0
- package/src/motion3/qa/severity.js +38 -0
- package/src/motion3/qa/spec-checks.js +65 -0
- package/src/motion3/qa/style.js +228 -0
- package/src/motion3/qa/wrist-checks.js +69 -0
- package/src/motion3/qa/wrist-obs.js +60 -0
- package/src/motion3/refs.js +129 -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 +113 -0
- package/src/motion3/schema/types.js +149 -0
- package/src/motion3/search.js +117 -0
- package/src/motion3/second-hand.js +112 -0
- package/src/motion3/settle.js +136 -0
- package/src/motion3/skills/basic.js +83 -0
- package/src/motion3/skills/book.js +76 -0
- package/src/motion3/skills/car.js +145 -0
- package/src/motion3/skills/flip.js +200 -0
- package/src/motion3/skills/frames-auto.js +29 -0
- package/src/motion3/skills/impact.js +109 -0
- package/src/motion3/skills/index.js +85 -0
- package/src/motion3/skills/info.js +80 -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/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/qa-label.js +25 -0
- 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 +9 -51
- 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/TopBar.jsx +5 -84
- package/src/shell/ViewportToolbar.jsx +10 -70
- 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/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 +16 -14
- package/src/studio-agent-commands.js +19 -16
- package/src/studio-agent-motion.js +9 -9
- package/src/studio-agent-protocol.js +47 -77
- package/src/studio-app-binding.js +27 -47
- 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 +15 -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 +109 -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/knip-baseline.json +0 -179
- package/tools/loc-baseline.json +31 -81
- 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 -125
- package/bin/agent/motion-runtime.mjs +0 -76
- package/dist/ardy/cskel27-rest.json +0 -557
- package/dist/assets/analytics-C9pMpW7s.js +0 -1
- package/dist/assets/app-B_2XB9rw.css +0 -1
- package/dist/assets/app-DAuvKPP8.js +0 -4920
- package/dist/assets/config-Bx7pYsD9.js +0 -1
- package/dist/assets/config-DeoFTYzb.css +0 -1
- package/dist/assets/demo-CvKuBgy5.js +0 -1
- package/dist/assets/scene-asset-sync-DKkpjKBV.js +0 -19
- package/dist/assets/scene-asset-sync-DjJ_cJ2R.css +0 -1
- package/dist/assets/ticket-BPk2Ni95.js +0 -1
- package/dist/assets/workflow-jJRBR94Z.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 -182
- 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
package/src/motion/generation.js
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
// Pure request construction. The caller supplies one rolled seed and sampled
|
|
2
|
-
// poses; no rig, store, random source, notification or network is touched here.
|
|
3
|
-
import { StudioProtocolError, compileStudioBeats } from '../studio-agent-protocol.js';
|
|
4
|
-
import { TIMELINE_FRAME_FPS as FPS } from '../timeline-fps.js';
|
|
5
|
-
import { judgeAuthoredPath, alignArdyPath } from '../ardy/waypoints.js';
|
|
6
|
-
import { planPosePin, PIN_BLOCKED } from '../ardy/pose-pin.js';
|
|
7
|
-
import { blocksFromRequest, replayPayload, replayTruncated } from '../take-recipe.js';
|
|
8
|
-
|
|
9
|
-
// Same half-open timeline schedule used by the editor: gaps carry the base
|
|
10
|
-
// prompt, and spans shorter than three frames cannot carry a generation.
|
|
11
|
-
function promptSchedule(clips, clipFrames, prompt) {
|
|
12
|
-
const segments = [];
|
|
13
|
-
let cursor = 0;
|
|
14
|
-
for (const clip of clips) {
|
|
15
|
-
const startFrame = Math.max(cursor, Math.min(clipFrames, clip.startFrame));
|
|
16
|
-
const endFrame = Math.max(startFrame, Math.min(clipFrames, clip.endFrame));
|
|
17
|
-
if (startFrame > cursor) segments.push({ startFrame: cursor, endFrame: startFrame, prompt });
|
|
18
|
-
if (endFrame - startFrame >= 3) segments.push({ startFrame, endFrame, prompt: clip.text.trim() || prompt });
|
|
19
|
-
cursor = Math.max(cursor, endFrame);
|
|
20
|
-
if (cursor >= clipFrames) break;
|
|
21
|
-
}
|
|
22
|
-
if (cursor < clipFrames) segments.push({ startFrame: cursor, endFrame: clipFrames, prompt });
|
|
23
|
-
if (!segments.length) segments.push({ startFrame: 0, endFrame: clipFrames, prompt });
|
|
24
|
-
return segments;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function generationArgs({ characterId, source }) {
|
|
28
|
-
if (source.kind !== 'generate') throw generationRefusal('CAPABILITY_MISSING', 'Artifact reuse is handled by the retained artifact runtime.');
|
|
29
|
-
const schedule = compileStudioBeats(source);
|
|
30
|
-
return { characterId, durationSeconds: schedule.durationSeconds,
|
|
31
|
-
blocks: schedule.blocks.map((block, index) => ({ id: `generation-block-${index}`, text: block.text, startFrame: block.startFrame, endFrame: block.endFrameExclusive })),
|
|
32
|
-
...(source.seed === undefined ? {} : { seed: source.seed }) };
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function generationRefusal(code, message, uiMessage = message) {
|
|
36
|
-
return Object.assign(new StudioProtocolError(code, message), { uiMessage });
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function buildGenerationRequest({ character, prompt, durationSeconds, seed,
|
|
40
|
-
waypoints = [], waypointMode = waypoints.length > 0, blocks = [], motion = null,
|
|
41
|
-
ikKeys = new Map(), startFromPose = false, posePlacement = 'start', frame = 0, poses = [],
|
|
42
|
-
preserveStrength = 0.5, recipe = null, fresh = false,
|
|
43
|
-
}) {
|
|
44
|
-
const text = String(prompt ?? '').trim();
|
|
45
|
-
if (!text) throw generationRefusal('INVALID_ARGUMENT', 'Motion prompt is required - describe what the subject should do before generating');
|
|
46
|
-
if (text.length > 500) throw generationRefusal('INVALID_ARGUMENT', `Motion prompt is capped at 500 characters (currently ${text.length}) - shorten it before generating`);
|
|
47
|
-
const ikFrames = [...ikKeys.keys()].sort((a, b) => a - b);
|
|
48
|
-
const duration = motion && ikFrames.length ? motion.frames / motion.fps : Number(durationSeconds);
|
|
49
|
-
const clipFrames = Math.round(duration * FPS);
|
|
50
|
-
if (!Number.isFinite(duration) || duration < 1 || duration > 1200) throw generationRefusal('INVALID_RANGE', 'Duration must be between 1 and 1200 seconds');
|
|
51
|
-
if (!Number.isInteger(seed) || seed < 0 || seed > 2 ** 31 - 1) throw generationRefusal('INVALID_ARGUMENT', 'Seed must be an integer in 0..2147483647');
|
|
52
|
-
const warnings = [];
|
|
53
|
-
const clips = blocks.filter(clip => clip.text.trim()).sort((a, b) => a.startFrame - b.startFrame);
|
|
54
|
-
const segments = promptSchedule(clips, clipFrames, text), hasPromptSchedule = segments.length > 1;
|
|
55
|
-
const rootPath = waypointMode ? [{ frame: 0, x: character.x, z: character.z, heading: null }, ...waypoints] : [];
|
|
56
|
-
if (waypointMode) {
|
|
57
|
-
if (!waypoints.length) throw generationRefusal('INVALID_RANGE', 'Add at least one root destination before generating');
|
|
58
|
-
if (rootPath.length > 32) throw generationRefusal('INVALID_RANGE', 'The root path is capped at 32 sparse waypoints');
|
|
59
|
-
if (waypoints.some(point => point.frame <= 0 || point.frame >= clipFrames)) throw generationRefusal('INVALID_RANGE', `Root waypoint frames must stay inside 1..${clipFrames - 1}`);
|
|
60
|
-
const verdict = judgeAuthoredPath(rootPath, FPS, clipFrames, { chained: hasPromptSchedule });
|
|
61
|
-
if (verdict.errors.length) throw generationRefusal('INVALID_RANGE', `Not generated - ${verdict.errors[0]}`);
|
|
62
|
-
warnings.push(...verdict.warnings);
|
|
63
|
-
}
|
|
64
|
-
const longBlock = clips.length && segments.find(segment => segment.endFrame - segment.startFrame > 5 * FPS);
|
|
65
|
-
if (longBlock) throw generationRefusal('INVALID_RANGE', `Not generated - prompt blocks are capped at 5 s; split the ${((longBlock.endFrame - longBlock.startFrame) / FPS).toFixed(1)} s block`);
|
|
66
|
-
if (segments.some(segment => segment.prompt.length > 500)) throw generationRefusal('INVALID_ARGUMENT', 'Each motion prompt block is capped at 500 characters');
|
|
67
|
-
const aligned = waypointMode ? alignArdyPath(rootPath, character.rot, 32) : null;
|
|
68
|
-
const editedSegments = motion?.url && hasPromptSchedule
|
|
69
|
-
? segments.filter(segment => ikFrames.some(at => at >= segment.startFrame && at < segment.endFrame)) : [];
|
|
70
|
-
const hasBlockEdits = editedSegments.length > 0;
|
|
71
|
-
const last = clipFrames - 1;
|
|
72
|
-
const poseFrame = posePlacement === 'end' ? last : posePlacement === 'middle' ? Math.round(last / 2)
|
|
73
|
-
: posePlacement === 'playhead' ? Math.max(0, Math.min(last, frame)) : 0;
|
|
74
|
-
const plan = planPosePin({ startFromPose, poseFrame, hasPromptSchedule, hasBlockEdits, waypointMode, ikFrames, clipFrames, segments, editedSegments });
|
|
75
|
-
if (plan.blockedBy === PIN_BLOCKED.SCHEDULE) warnings.push('Prompt blocks and a pose start cannot be combined - generating from the prompt alone.');
|
|
76
|
-
const body = { prompt: text, duration, posePin: plan.pin, seed };
|
|
77
|
-
let committedEditKeys = [];
|
|
78
|
-
if (plan.pin && !hasBlockEdits) body.poses = poses.map(entry => ({ ...entry }));
|
|
79
|
-
if (aligned) {
|
|
80
|
-
Object.assign(body, { waypoints: aligned.waypoints, rootMargin: 0.08, historyFrames: 4 * FPS });
|
|
81
|
-
if (hasPromptSchedule && !hasBlockEdits) body.segments = segments;
|
|
82
|
-
} else if (hasBlockEdits) {
|
|
83
|
-
const entries = plan.frames.map(at => ({ frame: at, tracks: [...(ikKeys.get(at)?.keys() ?? [])], pose: poses.find(entry => entry.frame === at)?.pose }));
|
|
84
|
-
committedEditKeys = entries.map(({ frame, tracks }) => ({ frame, tracks }));
|
|
85
|
-
body.motionEdit = { sourceMotion: motion.url, startFrame: Math.min(...editedSegments.map(segment => segment.startFrame)),
|
|
86
|
-
endFrame: Math.max(...editedSegments.map(segment => segment.endFrame)), contextBefore: 40, contextAfter: 20, edits: entries };
|
|
87
|
-
} else if (hasPromptSchedule) body.segments = segments;
|
|
88
|
-
const requestBlocks = blocksFromRequest(body, FPS), recipeBlocks = recipe?.blocks;
|
|
89
|
-
const durationFits = motion?.frames > 0 && Math.abs(motion.frames / FPS - duration) <= 1 / FPS + 1e-9;
|
|
90
|
-
const promptMatches = body.motionEdit !== undefined || (recipeBlocks?.length === requestBlocks.length && recipeBlocks.every((block, i) => block.prompt.trim() === requestBlocks[i].prompt.trim()));
|
|
91
|
-
if (!fresh && motion?.url && preserveStrength > 0 && durationFits && promptMatches && !body.segments) {
|
|
92
|
-
body.preserve = { sourceMotion: motion.url, strength: preserveStrength,
|
|
93
|
-
editRanges: editedSegments.map(({ startFrame, endFrame }) => {
|
|
94
|
-
const tracks = [...new Set(ikFrames.filter(at => at >= startFrame && at < endFrame).flatMap(at => [...ikKeys.get(at).keys()]))];
|
|
95
|
-
return { startFrame, endFrame, ...(tracks.length ? { tracks } : {}) };
|
|
96
|
-
}).filter(range => range.endFrame > range.startFrame) };
|
|
97
|
-
}
|
|
98
|
-
const replay = fresh || hasBlockEdits || !Number.isInteger(recipe?.seed) ? [] : replayPayload(recipe);
|
|
99
|
-
if (replay.length) {
|
|
100
|
-
body.replay = replay;
|
|
101
|
-
if (replayTruncated(recipe)) warnings.push(`Only ${replay.length} refinements can be replayed at once - the first ${replay.length} carry over`);
|
|
102
|
-
}
|
|
103
|
-
return { body, rootRotationDeg: aligned?.rotationDeg ?? character.rot, warnings,
|
|
104
|
-
constraintFrames: plan.frames, hasBlockEdits, hasPromptSchedule, committedEditKeys };
|
|
105
|
-
}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { ko } from "./locale.js";
|
|
2
|
-
|
|
3
|
-
export function motionReadinessMessage(state) {
|
|
4
|
-
switch (state) {
|
|
5
|
-
case "loading": return ko("Checking motion generation…", "모션 생성 연결 확인 중…");
|
|
6
|
-
case "ready": return ko("Ready for this motion request", "이 모션 요청을 실행할 수 있어요");
|
|
7
|
-
case "not_configured": return ko("No motion backend configured", "모션 백엔드가 설정되지 않았어요");
|
|
8
|
-
case "unsupported_route": return ko("This route cannot run the selected motion request", "현재 경로는 이 모션 요청을 지원하지 않아요");
|
|
9
|
-
default: return ko("The motion backend is unavailable", "모션 백엔드를 사용할 수 없어요");
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function MotionReadiness({ state, checking, onSetup, onRetry }) {
|
|
14
|
-
const unavailable = state === "unavailable";
|
|
15
|
-
return (
|
|
16
|
-
<div className="motion-readiness" data-state={state} aria-busy={checking || state === "loading"}>
|
|
17
|
-
<span role="status">{motionReadinessMessage(state)}</span>
|
|
18
|
-
{state !== "ready" && state !== "loading" && (
|
|
19
|
-
<button
|
|
20
|
-
type="button"
|
|
21
|
-
className="btn ghost"
|
|
22
|
-
data-testid="motion-readiness-action"
|
|
23
|
-
disabled={checking}
|
|
24
|
-
onClick={unavailable ? onRetry : () => onSetup()}
|
|
25
|
-
>
|
|
26
|
-
{checking ? ko("Checking…", "확인 중…")
|
|
27
|
-
: unavailable ? ko("Retry connection", "연결 다시 확인")
|
|
28
|
-
: state === "unsupported_route" ? ko("Switch route in Settings", "설정에서 경로 변경")
|
|
29
|
-
: ko("Open Settings", "설정 열기")}
|
|
30
|
-
</button>
|
|
31
|
-
)}
|
|
32
|
-
</div>
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function MotionSetup({ state, checking, onRetry }) {
|
|
37
|
-
const localStudio = ["localhost", "127.0.0.1", "[::1]"].includes(window.location.hostname);
|
|
38
|
-
const port = localStudio ? window.location.port || "5180" : "5180";
|
|
39
|
-
return (
|
|
40
|
-
<section className="motion-setup" data-testid="motion-setup" data-state={state} aria-label={ko("Motion generation setup", "모션 생성 설정")}>
|
|
41
|
-
<p role="status">{motionReadinessMessage(state)}</p>
|
|
42
|
-
<p>{ko(
|
|
43
|
-
"Motion generation is optional. Sample motion, camera editing and exports work without it. Your scene and prompt blocks stay here while you set it up.",
|
|
44
|
-
"모션 생성은 선택 사항이에요. 연결 없이도 샘플 모션, 카메라 편집, 내보내기를 쓸 수 있어요. 설정하는 동안 씬과 프롬프트 블록은 그대로 유지됩니다.",
|
|
45
|
-
)}</p>
|
|
46
|
-
<p>{ko("Use an existing motion generation host, then restart your local launcher with:", "기존 모션 생성 호스트를 사용하려면 로컬 실행기에 다음 환경변수를 설정하세요:")}</p>
|
|
47
|
-
<code>{`CCLAY_KIMODO_HOST=user@your-gpu-box npx cozyclay --port ${port} --no-open`}</code>
|
|
48
|
-
<p>{localStudio ? ko(
|
|
49
|
-
"Keep this tab open. After restarting the bridge on the same port, retry the connection below.",
|
|
50
|
-
"이 탭은 열어 두세요. 같은 포트에서 브리지를 다시 시작한 뒤 아래에서 연결을 확인하세요.",
|
|
51
|
-
) : ko(
|
|
52
|
-
"New generation runs in the local Studio. Save your project here, then open that file in the local Studio to carry over your edits.",
|
|
53
|
-
"새 모션 생성은 로컬 Studio에서 실행해요. 여기서 프로젝트를 저장하고 로컬 Studio에서 그 파일을 열면 편집 내용을 이어갈 수 있어요.",
|
|
54
|
-
)}</p>
|
|
55
|
-
<p>{ko(
|
|
56
|
-
"Local MLX/cpp supports one unconstrained prompt. Sequencing, pinned poses, paths and preserve need the CUDA/SSH route. Line editing needs a configured line-edit route.",
|
|
57
|
-
"로컬 MLX/cpp는 제약 없는 단일 프롬프트를 지원해요. 블록 연결, 포즈 고정, 경로, 테이크 보존에는 CUDA/SSH가 필요하고, 라인 편집에는 라인 편집 경로 설정이 필요해요.",
|
|
58
|
-
)}</p>
|
|
59
|
-
<a href="https://github.com/NomaDamas/CozyClay/blob/main/docs/kimodo-setup.md" target="_blank" rel="noreferrer">
|
|
60
|
-
{ko("Motion generation setup and route guide", "모션 생성 설정 및 경로 안내")}
|
|
61
|
-
</a>
|
|
62
|
-
<button type="button" className="btn" data-testid="motion-health-retry" disabled={checking} onClick={onRetry}>
|
|
63
|
-
{checking ? ko("Checking…", "확인 중…") : ko("Recheck connection", "연결 다시 확인")}
|
|
64
|
-
</button>
|
|
65
|
-
</section>
|
|
66
|
-
);
|
|
67
|
-
}
|
package/src/motion-readiness.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { motionPreflightReason } from "./analytics.js";
|
|
2
|
-
|
|
3
|
-
/** Return the line-edit capability advertised by the structured health payload. */
|
|
4
|
-
export function hasLineEditCapability(health) {
|
|
5
|
-
if (health?.ok !== true) return false;
|
|
6
|
-
const capabilities = health.capabilities ?? health.features;
|
|
7
|
-
return Array.isArray(capabilities) ? capabilities.includes("lineEdit") : capabilities?.lineEdit === true;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Decide whether the hosted demo take may be seeded onto the stage. The seed
|
|
12
|
-
* is for a session with no bridge (the hosted demo). The first healthy probe
|
|
13
|
-
* latches `bridgeSeenOk`: after it, a failed probe is a blip, not "no bridge",
|
|
14
|
-
* and must never load the demo take onto the character.
|
|
15
|
-
*/
|
|
16
|
-
export function demoSeedGate(health, bridgeSeenOk = false) {
|
|
17
|
-
const seenOk = bridgeSeenOk || Boolean(health?.ok);
|
|
18
|
-
return { bridgeSeenOk: seenOk, seed: Boolean(health) && !health.ok && !seenOk };
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Derive the generation affordance state from bridge health and request shape.
|
|
23
|
-
* Health is deliberately interpreted through the analytics preflight contract;
|
|
24
|
-
* error prose is never used to classify a route.
|
|
25
|
-
*/
|
|
26
|
-
export function motionReadiness(health, options = {}) {
|
|
27
|
-
if (health === null || health === undefined) return "loading";
|
|
28
|
-
const lineEditSupported = Object.prototype.hasOwnProperty.call(options, "lineEditSupported")
|
|
29
|
-
? options.lineEditSupported === true
|
|
30
|
-
: hasLineEditCapability(health);
|
|
31
|
-
const reason = motionPreflightReason(health, {
|
|
32
|
-
body: options.body ?? {},
|
|
33
|
-
lineEditSupported,
|
|
34
|
-
});
|
|
35
|
-
if (reason === null) return "ready";
|
|
36
|
-
if (reason === "unconfigured") return "not_configured";
|
|
37
|
-
if (reason === "unreachable") return "unavailable";
|
|
38
|
-
if (reason === "unsupported_route") return "unsupported_route";
|
|
39
|
-
return "unavailable";
|
|
40
|
-
}
|
package/src/motion-resources.js
DELETED
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Motion resources: a validated ARDY npz archive packaged so a `.cclayproject`
|
|
3
|
-
* can carry the take itself instead of a bridge URL that dies with the run.
|
|
4
|
-
*
|
|
5
|
-
* A record is content-addressed: `motionId` is the SHA-256 of the raw archive
|
|
6
|
-
* bytes, so a scene's `character.motionRef.motionId` names the exact bytes it
|
|
7
|
-
* was authored against. Decoding re-derives the hash and re-runs the full npz
|
|
8
|
-
* validation, so a tampered or truncated record is rejected before playback
|
|
9
|
-
* ever sees it. Every rejection is an NpzError carrying a machine-readable
|
|
10
|
-
* `code` (bad-base64 | length-mismatch | hash-mismatch | bad-npz | too-large).
|
|
11
|
-
*
|
|
12
|
-
* No React, no three.js, no fetch: this module runs identically in the
|
|
13
|
-
* browser and under Node (Web Crypto + atob/btoa are available in both).
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import { decodeMotionNpz, NpzError } from "./ardy/npz.js";
|
|
17
|
-
|
|
18
|
-
/** Per-motion raw archive cap; matches the npz reader's download cap. */
|
|
19
|
-
export const MOTION_MAX_BYTES = 192 * 1024 * 1024;
|
|
20
|
-
|
|
21
|
-
const MOTION_ID_RE = /^[0-9a-f]{64}$/i;
|
|
22
|
-
const BASE64_RE = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
23
|
-
|
|
24
|
-
function codedError(code, message) {
|
|
25
|
-
const error = new NpzError(message);
|
|
26
|
-
error.code = code;
|
|
27
|
-
return error;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function asBytes(value) {
|
|
31
|
-
if (value instanceof Uint8Array) return value;
|
|
32
|
-
if (value instanceof ArrayBuffer) return new Uint8Array(value);
|
|
33
|
-
if (ArrayBuffer.isView(value)) return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
|
34
|
-
throw new TypeError("motion bytes must be a Uint8Array or ArrayBuffer");
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** Lowercase hex SHA-256 of raw bytes — the motionId of an archive. */
|
|
38
|
-
export async function sha256Hex(value) {
|
|
39
|
-
const bytes = asBytes(value);
|
|
40
|
-
const subtle = globalThis.crypto?.subtle;
|
|
41
|
-
if (!subtle) throw new Error("Web Crypto SHA-256 is unavailable");
|
|
42
|
-
const digest = await subtle.digest("SHA-256", bytes);
|
|
43
|
-
let hex = "";
|
|
44
|
-
for (const byte of new Uint8Array(digest)) hex += byte.toString(16).padStart(2, "0");
|
|
45
|
-
return hex;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function bytesToBase64(bytes) {
|
|
49
|
-
let binary = "";
|
|
50
|
-
for (let offset = 0; offset < bytes.length; offset += 0x8000) {
|
|
51
|
-
binary += String.fromCharCode(...bytes.subarray(offset, offset + 0x8000));
|
|
52
|
-
}
|
|
53
|
-
return btoa(binary);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function base64ToBytes(text) {
|
|
57
|
-
if (typeof text !== "string" || text.length % 4 !== 0 || !BASE64_RE.test(text)) {
|
|
58
|
-
throw codedError("bad-base64", "motion resource data is not valid base64");
|
|
59
|
-
}
|
|
60
|
-
let binary;
|
|
61
|
-
try {
|
|
62
|
-
binary = atob(text);
|
|
63
|
-
} catch {
|
|
64
|
-
throw codedError("bad-base64", "motion resource data is not valid base64");
|
|
65
|
-
}
|
|
66
|
-
const bytes = new Uint8Array(binary.length);
|
|
67
|
-
for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i);
|
|
68
|
-
return bytes;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/** Decoded byte count of a base64 string, without decoding it. */
|
|
72
|
-
function base64DecodedLength(text) {
|
|
73
|
-
let padding = 0;
|
|
74
|
-
if (text.endsWith("==")) padding = 2;
|
|
75
|
-
else if (text.endsWith("=")) padding = 1;
|
|
76
|
-
return (text.length / 4) * 3 - padding;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const META_KEYS = ["prompt", "sourceUrl", "personScale", "createdAt"];
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Validate an npz archive and package it as a project motion record:
|
|
83
|
-
* { motionId, encoding: "base64", data, bytes, frames, fps, name?, meta? }.
|
|
84
|
-
*
|
|
85
|
-
* `meta` may carry `name` (kept top-level) and prompt / sourceUrl /
|
|
86
|
-
* personScale / createdAt (kept under `meta`). personScale defaults to the
|
|
87
|
-
* value the archive itself carries so the record is self-describing.
|
|
88
|
-
*/
|
|
89
|
-
export async function encodeMotionResource(value, meta = {}) {
|
|
90
|
-
const bytes = asBytes(value);
|
|
91
|
-
if (bytes.byteLength > MOTION_MAX_BYTES) {
|
|
92
|
-
throw codedError("too-large", `motion resource is ${bytes.byteLength} bytes, over the ${MOTION_MAX_BYTES} byte cap`);
|
|
93
|
-
}
|
|
94
|
-
let motion;
|
|
95
|
-
try {
|
|
96
|
-
motion = await decodeMotionNpz(bytes);
|
|
97
|
-
} catch (error) {
|
|
98
|
-
if (error instanceof NpzError && !error.code) error.code = "bad-npz";
|
|
99
|
-
throw error;
|
|
100
|
-
}
|
|
101
|
-
const motionId = await sha256Hex(bytes);
|
|
102
|
-
const record = {
|
|
103
|
-
motionId,
|
|
104
|
-
encoding: "base64",
|
|
105
|
-
data: bytesToBase64(bytes),
|
|
106
|
-
bytes: bytes.byteLength,
|
|
107
|
-
frames: motion.frames,
|
|
108
|
-
fps: motion.fps,
|
|
109
|
-
};
|
|
110
|
-
const source = meta && typeof meta === "object" ? meta : {};
|
|
111
|
-
if (typeof source.name === "string" && source.name) record.name = source.name;
|
|
112
|
-
const extra = { personScale: motion.personScale };
|
|
113
|
-
for (const key of META_KEYS) {
|
|
114
|
-
if (source[key] !== undefined && source[key] !== null) extra[key] = source[key];
|
|
115
|
-
}
|
|
116
|
-
record.meta = extra;
|
|
117
|
-
return record;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Unpack a motion record back into a decoded motion. Checks, in order: size
|
|
122
|
-
* cap, base64 syntax, declared byte length, SHA-256 against motionId, then
|
|
123
|
-
* the full npz validation. Returns the decodeMotionNpz result plus
|
|
124
|
-
* { motionId, sourceBytes } — exactly what loadMotionFromUrl returns, so the
|
|
125
|
-
* restore path treats an embedded take and a downloaded one the same way.
|
|
126
|
-
*/
|
|
127
|
-
export async function decodeMotionResource(record) {
|
|
128
|
-
if (!record || typeof record !== "object") {
|
|
129
|
-
throw codedError("bad-base64", "motion resource must be an object with base64 data");
|
|
130
|
-
}
|
|
131
|
-
if (Number.isFinite(record.bytes) && record.bytes > MOTION_MAX_BYTES) {
|
|
132
|
-
throw codedError("too-large", `motion resource declares ${record.bytes} bytes, over the ${MOTION_MAX_BYTES} byte cap`);
|
|
133
|
-
}
|
|
134
|
-
if (record.encoding !== "base64" || typeof record.data !== "string") {
|
|
135
|
-
throw codedError("bad-base64", "motion resource must contain base64 data");
|
|
136
|
-
}
|
|
137
|
-
if (base64DecodedLength(record.data) > MOTION_MAX_BYTES) {
|
|
138
|
-
throw codedError("too-large", `motion resource data decodes past the ${MOTION_MAX_BYTES} byte cap`);
|
|
139
|
-
}
|
|
140
|
-
const bytes = base64ToBytes(record.data);
|
|
141
|
-
if (bytes.byteLength > MOTION_MAX_BYTES) {
|
|
142
|
-
throw codedError("too-large", `motion resource is ${bytes.byteLength} bytes, over the ${MOTION_MAX_BYTES} byte cap`);
|
|
143
|
-
}
|
|
144
|
-
if (!Number.isInteger(record.bytes) || record.bytes !== bytes.byteLength) {
|
|
145
|
-
throw codedError("length-mismatch", `motion resource declares ${record.bytes} bytes but carries ${bytes.byteLength}`);
|
|
146
|
-
}
|
|
147
|
-
const declaredId = typeof record.motionId === "string" && MOTION_ID_RE.test(record.motionId) ? record.motionId.toLowerCase() : null;
|
|
148
|
-
const motionId = await sha256Hex(bytes);
|
|
149
|
-
if (declaredId !== motionId) {
|
|
150
|
-
throw codedError("hash-mismatch", "motion resource bytes do not match its motionId");
|
|
151
|
-
}
|
|
152
|
-
let motion;
|
|
153
|
-
try {
|
|
154
|
-
motion = await decodeMotionNpz(bytes);
|
|
155
|
-
} catch (error) {
|
|
156
|
-
if (error instanceof NpzError && !error.code) error.code = "bad-npz";
|
|
157
|
-
throw error;
|
|
158
|
-
}
|
|
159
|
-
return { ...motion, motionId, sourceBytes: bytes };
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Where a character's motion should come from on restore. Embedded bytes win
|
|
164
|
-
* over a URL: the URL is a bridge run that may be gone, the bytes are here.
|
|
165
|
-
* { kind: "embedded", record } | { kind: "url", url } | { kind: "missing" }
|
|
166
|
-
*/
|
|
167
|
-
export function resolveMotionSource(motionRef, motionsById) {
|
|
168
|
-
if (!motionRef || typeof motionRef !== "object") return { kind: "missing" };
|
|
169
|
-
if (typeof motionRef.motionId === "string" && motionsById && typeof motionsById.get === "function") {
|
|
170
|
-
const record = motionsById.get(motionRef.motionId.toLowerCase());
|
|
171
|
-
if (record) return { kind: "embedded", record };
|
|
172
|
-
}
|
|
173
|
-
if (typeof motionRef.url === "string" && motionRef.url) return { kind: "url", url: motionRef.url };
|
|
174
|
-
return { kind: "missing" };
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
export { loadMotionFromUrl } from "./ardy/npz.js";
|
package/src/motion-store.js
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
const MOTION_DB_NAME = "cozyclay.motions";
|
|
2
|
-
const MOTION_DB_VERSION = 1;
|
|
3
|
-
const MOTION_STORE_NAME = "motions";
|
|
4
|
-
|
|
5
|
-
const validId = (id) => typeof id === "string" && /^[0-9a-f]{64}$/i.test(id);
|
|
6
|
-
|
|
7
|
-
function normalizeMotion(record) {
|
|
8
|
-
if (!record || typeof record !== "object" || !validId(record.motionId)) return null;
|
|
9
|
-
if (record.encoding !== "base64" || typeof record.data !== "string") return null;
|
|
10
|
-
return { ...record, motionId: record.motionId.toLowerCase(), stored: true };
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function openMotionDb(factory = globalThis.indexedDB) {
|
|
14
|
-
if (!factory) return Promise.reject(new Error("IndexedDB is unavailable"));
|
|
15
|
-
return new Promise((resolve, reject) => {
|
|
16
|
-
const req = factory.open(MOTION_DB_NAME, MOTION_DB_VERSION);
|
|
17
|
-
req.onupgradeneeded = () => {
|
|
18
|
-
if (!req.result.objectStoreNames.contains(MOTION_STORE_NAME)) req.result.createObjectStore(MOTION_STORE_NAME, { keyPath: "motionId" });
|
|
19
|
-
};
|
|
20
|
-
req.onsuccess = () => resolve(req.result);
|
|
21
|
-
req.onerror = () => reject(req.error);
|
|
22
|
-
req.onblocked = () => reject(new Error("the motion database is blocked by another tab"));
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const request = (req) => new Promise((resolve, reject) => {
|
|
27
|
-
req.onsuccess = () => resolve(req.result);
|
|
28
|
-
req.onerror = () => reject(req.error);
|
|
29
|
-
});
|
|
30
|
-
const complete = (tx) => new Promise((resolve, reject) => {
|
|
31
|
-
tx.oncomplete = resolve;
|
|
32
|
-
tx.onerror = () => reject(tx.error);
|
|
33
|
-
tx.onabort = () => reject(tx.error);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
export async function putMotion(db, record) {
|
|
37
|
-
const value = normalizeMotion(record);
|
|
38
|
-
if (!value) throw new TypeError("putMotion needs a valid motion record");
|
|
39
|
-
const tx = db.transaction(MOTION_STORE_NAME, "readwrite");
|
|
40
|
-
tx.objectStore(MOTION_STORE_NAME).put(value);
|
|
41
|
-
await complete(tx);
|
|
42
|
-
return value;
|
|
43
|
-
}
|
|
44
|
-
export async function getMotion(db, id) {
|
|
45
|
-
if (!validId(id)) return null;
|
|
46
|
-
const tx = db.transaction(MOTION_STORE_NAME, "readonly");
|
|
47
|
-
return normalizeMotion(await request(tx.objectStore(MOTION_STORE_NAME).get(id.toLowerCase())));
|
|
48
|
-
}
|
|
49
|
-
export async function listMotionIds(db) {
|
|
50
|
-
const tx = db.transaction(MOTION_STORE_NAME, "readonly");
|
|
51
|
-
return (await request(tx.objectStore(MOTION_STORE_NAME).getAllKeys()) ?? []).filter(validId).map((id) => id.toLowerCase());
|
|
52
|
-
}
|
|
53
|
-
export async function deleteMotion(db, id) {
|
|
54
|
-
if (!validId(id)) return false;
|
|
55
|
-
const tx = db.transaction(MOTION_STORE_NAME, "readwrite");
|
|
56
|
-
tx.objectStore(MOTION_STORE_NAME).delete(id.toLowerCase());
|
|
57
|
-
await complete(tx);
|
|
58
|
-
return true;
|
|
59
|
-
}
|
|
60
|
-
export async function sweepMotions(db, referencedIds) {
|
|
61
|
-
const keep = new Set([...referencedIds ?? []].map((id) => String(id).toLowerCase()));
|
|
62
|
-
const ids = await listMotionIds(db);
|
|
63
|
-
await Promise.all(ids.filter((id) => !keep.has(id)).map((id) => deleteMotion(db, id)));
|
|
64
|
-
return ids.filter((id) => !keep.has(id));
|
|
65
|
-
}
|