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,306 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""setup-text-encoder.py - provision the ARDY text-encoder models without a
|
|
3
|
-
Hugging Face account, approval wait, or access token.
|
|
4
|
-
|
|
5
|
-
ARDY's text encoder is LLM2Vec on top of meta-llama/Meta-Llama-3-8B-Instruct.
|
|
6
|
-
The official Meta repository is gated (manual approval + token). This script
|
|
7
|
-
instead downloads a byte-pinned stack from public, ungated repositories:
|
|
8
|
-
|
|
9
|
-
base NousResearch/Meta-Llama-3-8B-Instruct (public mirror of the
|
|
10
|
-
official weights; every safetensors shard is verified against a
|
|
11
|
-
pinned SHA-256 before use)
|
|
12
|
-
adapters McGill-NLP/LLM2Vec-Meta-Llama-3-8B-Instruct-mntp
|
|
13
|
-
McGill-NLP/LLM2Vec-Meta-Llama-3-8B-Instruct-mntp-supervised
|
|
14
|
-
|
|
15
|
-
and lays them out for ARDY's `TEXT_ENCODERS_DIR` override
|
|
16
|
-
(ardy/model/llm2vec/llm2vec_wrapper.py), rewriting each adapter's
|
|
17
|
-
`base_model_name_or_path` to the local base directory so nothing ever
|
|
18
|
-
resolves back to the gated meta-llama repository at runtime.
|
|
19
|
-
|
|
20
|
-
Every revision is pinned to a full commit SHA and every LFS payload to a
|
|
21
|
-
SHA-256, so re-runs are reproducible and a tampered or changed upstream file
|
|
22
|
-
fails loudly instead of silently drifting the embeddings (and with them the
|
|
23
|
-
generated motion).
|
|
24
|
-
|
|
25
|
-
Licenses: the base weights are Meta Llama 3 Community License (the LICENSE
|
|
26
|
-
and USE_POLICY.md files are downloaded alongside the weights and must stay
|
|
27
|
-
there); the McGill adapters are MIT. This script only downloads for local
|
|
28
|
-
use - it does not redistribute anything.
|
|
29
|
-
|
|
30
|
-
Usage (typically on the ARDY box; stdlib only, python >= 3.8):
|
|
31
|
-
|
|
32
|
-
python3 setup-text-encoder.py # into ~/cclay-text-encoders
|
|
33
|
-
python3 setup-text-encoder.py --dest /srv/enc # custom destination
|
|
34
|
-
python3 setup-text-encoder.py --verify-only # re-hash an existing tree
|
|
35
|
-
|
|
36
|
-
Afterwards start the encoder service with the printed environment:
|
|
37
|
-
|
|
38
|
-
TEXT_ENCODERS_DIR=<dest> python scripts/run_text_encoder_server.py
|
|
39
|
-
"""
|
|
40
|
-
|
|
41
|
-
import argparse
|
|
42
|
-
import hashlib
|
|
43
|
-
import json
|
|
44
|
-
import os
|
|
45
|
-
import sys
|
|
46
|
-
import time
|
|
47
|
-
import urllib.error
|
|
48
|
-
import urllib.request
|
|
49
|
-
|
|
50
|
-
HF = "https://huggingface.co"
|
|
51
|
-
|
|
52
|
-
BASE_DIR = "NousResearch/Meta-Llama-3-8B-Instruct"
|
|
53
|
-
MNTP_DIR = "McGill-NLP/LLM2Vec-Meta-Llama-3-8B-Instruct-mntp"
|
|
54
|
-
# The mntp *download* lands in a -src dir: merge-text-encoder.py bakes the
|
|
55
|
-
# base+MNTP merge into the runtime MNTP_DIR path (transformers v5 dropped
|
|
56
|
-
# automatic adapter-dir resolution, so the runtime path must be a full model).
|
|
57
|
-
MNTP_SRC_DIR = MNTP_DIR + "-src"
|
|
58
|
-
SUP_DIR = "McGill-NLP/LLM2Vec-Meta-Llama-3-8B-Instruct-mntp-supervised"
|
|
59
|
-
|
|
60
|
-
# (local dir, repo id, pinned full commit SHA, files)
|
|
61
|
-
# Each file: (path, size, sha256-or-None). SHA-256 comes from the HF LFS
|
|
62
|
-
# metadata of the pinned revision; small non-LFS files carry only a size and
|
|
63
|
-
# are already immutable because the revision is a commit hash.
|
|
64
|
-
# Pins match nv-tlabs/ardy issue #9's reproduction stack.
|
|
65
|
-
MANIFEST = [
|
|
66
|
-
(
|
|
67
|
-
BASE_DIR,
|
|
68
|
-
"NousResearch/Meta-Llama-3-8B-Instruct",
|
|
69
|
-
"53346005fb0ef11d3b6a83b12c895cca40156b6c",
|
|
70
|
-
[
|
|
71
|
-
("LICENSE", 7801, None),
|
|
72
|
-
("USE_POLICY.md", 4696, None),
|
|
73
|
-
("config.json", 654, None),
|
|
74
|
-
("generation_config.json", 187, None),
|
|
75
|
-
("model-00001-of-00004.safetensors", 4976698672,
|
|
76
|
-
"d8cf9c4d0dd972e1a2131bfe656235ee98221679711a3beef6d46dadf0f20b5c"),
|
|
77
|
-
("model-00002-of-00004.safetensors", 4999802720,
|
|
78
|
-
"8d4782b4a69ef03845159ce1a15e272aadaaf134dc138d68f616098e8531729c"),
|
|
79
|
-
("model-00003-of-00004.safetensors", 4915916176,
|
|
80
|
-
"3acdd690e65c24f42a24581b8467af98bd3ca357444580f8012aacd2bd607921"),
|
|
81
|
-
("model-00004-of-00004.safetensors", 1168138808,
|
|
82
|
-
"67e9ad31c8c32abf3a55ee7fc7217b3ecb35fd3c74d98a5bd233e0e4d6964f46"),
|
|
83
|
-
("model.safetensors.index.json", 23950, None),
|
|
84
|
-
("special_tokens_map.json", 73, None),
|
|
85
|
-
("tokenizer.json", 9085698, None),
|
|
86
|
-
("tokenizer_config.json", 50977, None),
|
|
87
|
-
],
|
|
88
|
-
),
|
|
89
|
-
(
|
|
90
|
-
MNTP_SRC_DIR,
|
|
91
|
-
"McGill-NLP/LLM2Vec-Meta-Llama-3-8B-Instruct-mntp",
|
|
92
|
-
"31474e395ada192e8ed1586db6be79fb3b70c9c0",
|
|
93
|
-
[
|
|
94
|
-
("README.md", 3315, None),
|
|
95
|
-
("adapter_config.json", 794, None),
|
|
96
|
-
("adapter_model.safetensors", 167829552,
|
|
97
|
-
"de9c8736618a13173c6a1623cdef1b75e86c69317f1073ae82cd516ac36a632d"),
|
|
98
|
-
("attn_mask_utils.py", 11132, None),
|
|
99
|
-
("config.json", 781, None),
|
|
100
|
-
("modeling_llama_encoder.py", 2856, None),
|
|
101
|
-
("special_tokens_map.json", 335, None),
|
|
102
|
-
("tokenizer.json", 9085671, None),
|
|
103
|
-
("tokenizer_config.json", 51042, None),
|
|
104
|
-
],
|
|
105
|
-
),
|
|
106
|
-
(
|
|
107
|
-
SUP_DIR,
|
|
108
|
-
"McGill-NLP/LLM2Vec-Meta-Llama-3-8B-Instruct-mntp-supervised",
|
|
109
|
-
"baa8ebf04a1c2500e61288e7dad65e8ae42601a7",
|
|
110
|
-
[
|
|
111
|
-
("README.md", 62702, None),
|
|
112
|
-
("adapter_config.json", 794, None),
|
|
113
|
-
("adapter_model.safetensors", 167829552,
|
|
114
|
-
"53f8f94ebdf396667ba99dd96e78203edae27bbcdbd1cf5f12b611e1d916b225"),
|
|
115
|
-
],
|
|
116
|
-
),
|
|
117
|
-
]
|
|
118
|
-
|
|
119
|
-
# adapter_config.json is rewritten after download (base_model_name_or_path ->
|
|
120
|
-
# local base dir), so its byte size legitimately changes; everything else must
|
|
121
|
-
# keep the manifest size forever.
|
|
122
|
-
MUTATED = {"adapter_config.json"}
|
|
123
|
-
|
|
124
|
-
CHUNK = 1024 * 1024
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
def log(msg):
|
|
128
|
-
print("setup-text-encoder: %s" % msg, flush=True)
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
def fail(msg):
|
|
132
|
-
print("setup-text-encoder: ERROR: %s" % msg, file=sys.stderr, flush=True)
|
|
133
|
-
sys.exit(1)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
def sha256_file(path):
|
|
137
|
-
h = hashlib.sha256()
|
|
138
|
-
with open(path, "rb") as f:
|
|
139
|
-
while True:
|
|
140
|
-
block = f.read(CHUNK)
|
|
141
|
-
if not block:
|
|
142
|
-
break
|
|
143
|
-
h.update(block)
|
|
144
|
-
return h.hexdigest()
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
def file_ok(path, size, sha, verify_hash):
|
|
148
|
-
"""Is the on-disk file already the pinned artifact?"""
|
|
149
|
-
if not os.path.isfile(path):
|
|
150
|
-
return False
|
|
151
|
-
name = os.path.basename(path)
|
|
152
|
-
if name in MUTATED:
|
|
153
|
-
return True # rewritten in place; checked by rewrite_adapter_config
|
|
154
|
-
if os.path.getsize(path) != size:
|
|
155
|
-
return False
|
|
156
|
-
if sha is not None and verify_hash:
|
|
157
|
-
return sha256_file(path) == sha
|
|
158
|
-
return True
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
def download(url, dest, size, sha):
|
|
162
|
-
"""Resumable download to dest + '.partial', verified, then atomic rename."""
|
|
163
|
-
part = dest + ".partial"
|
|
164
|
-
os.makedirs(os.path.dirname(dest), exist_ok=True)
|
|
165
|
-
for attempt in range(1, 6):
|
|
166
|
-
try:
|
|
167
|
-
have = os.path.getsize(part) if os.path.isfile(part) else 0
|
|
168
|
-
if have > size:
|
|
169
|
-
os.remove(part)
|
|
170
|
-
have = 0
|
|
171
|
-
if have < size:
|
|
172
|
-
req = urllib.request.Request(url)
|
|
173
|
-
req.add_header("User-Agent", "cozyclay-setup-text-encoder/1")
|
|
174
|
-
if have:
|
|
175
|
-
req.add_header("Range", "bytes=%d-" % have)
|
|
176
|
-
with urllib.request.urlopen(req, timeout=60) as resp:
|
|
177
|
-
if have and resp.status != 206:
|
|
178
|
-
# Server ignored the Range header; start over.
|
|
179
|
-
have = 0
|
|
180
|
-
mode = "wb"
|
|
181
|
-
else:
|
|
182
|
-
mode = "ab" if have else "wb"
|
|
183
|
-
done = have
|
|
184
|
-
t0 = time.time()
|
|
185
|
-
with open(part, mode) as out:
|
|
186
|
-
while True:
|
|
187
|
-
block = resp.read(CHUNK)
|
|
188
|
-
if not block:
|
|
189
|
-
break
|
|
190
|
-
out.write(block)
|
|
191
|
-
done += len(block)
|
|
192
|
-
if size > CHUNK and time.time() - t0 > 5:
|
|
193
|
-
log(" ... %d / %d MiB" % (done // 2**20, size // 2**20))
|
|
194
|
-
t0 = time.time()
|
|
195
|
-
got = os.path.getsize(part)
|
|
196
|
-
if got != size:
|
|
197
|
-
raise IOError("size mismatch: got %d, want %d" % (got, size))
|
|
198
|
-
if sha is not None:
|
|
199
|
-
actual = sha256_file(part)
|
|
200
|
-
if actual != sha:
|
|
201
|
-
os.remove(part)
|
|
202
|
-
raise IOError("sha256 mismatch: got %s, want %s" % (actual, sha))
|
|
203
|
-
os.replace(part, dest)
|
|
204
|
-
return
|
|
205
|
-
except (urllib.error.URLError, IOError, OSError) as e:
|
|
206
|
-
if attempt == 5:
|
|
207
|
-
fail("giving up on %s after 5 attempts: %s" % (url, e))
|
|
208
|
-
log(" retry %d/5 for %s (%s)" % (attempt, os.path.basename(dest), e))
|
|
209
|
-
time.sleep(2 * attempt)
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
def rewrite_adapter_config(path, base_path):
|
|
213
|
-
"""Point the adapter at the local base so nothing resolves to gated repos."""
|
|
214
|
-
with open(path, "r", encoding="utf-8") as f:
|
|
215
|
-
cfg = json.load(f)
|
|
216
|
-
if cfg.get("base_model_name_or_path") == base_path:
|
|
217
|
-
return False
|
|
218
|
-
cfg["base_model_name_or_path"] = base_path
|
|
219
|
-
tmp = path + ".tmp"
|
|
220
|
-
with open(tmp, "w", encoding="utf-8") as f:
|
|
221
|
-
json.dump(cfg, f, indent=2)
|
|
222
|
-
f.write("\n")
|
|
223
|
-
os.replace(tmp, path)
|
|
224
|
-
return True
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
def main():
|
|
228
|
-
ap = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
|
229
|
-
ap.add_argument("--dest", default=os.environ.get(
|
|
230
|
-
"TEXT_ENCODERS_DIR", os.path.expanduser("~/cclay-text-encoders")),
|
|
231
|
-
help="target directory (default: $TEXT_ENCODERS_DIR or ~/cclay-text-encoders)")
|
|
232
|
-
ap.add_argument("--verify-only", action="store_true",
|
|
233
|
-
help="verify an existing tree (sizes + SHA-256), download nothing")
|
|
234
|
-
ap.add_argument("--print-plan", action="store_true",
|
|
235
|
-
help="list every pinned file and exit")
|
|
236
|
-
args = ap.parse_args()
|
|
237
|
-
|
|
238
|
-
dest = os.path.abspath(os.path.expanduser(args.dest))
|
|
239
|
-
os.makedirs(dest, exist_ok=True)
|
|
240
|
-
dest_real = os.path.realpath(dest)
|
|
241
|
-
|
|
242
|
-
def safe_join(*parts):
|
|
243
|
-
"""Join paths and verify the result stays within base."""
|
|
244
|
-
full = os.path.realpath(os.path.join(dest_real, *parts))
|
|
245
|
-
if full != dest_real and not full.startswith(dest_real + os.sep):
|
|
246
|
-
fail("path traversal detected: %s" % os.path.join(*parts))
|
|
247
|
-
return full
|
|
248
|
-
|
|
249
|
-
base_path = safe_join(BASE_DIR)
|
|
250
|
-
|
|
251
|
-
if args.print_plan:
|
|
252
|
-
for local_dir, repo, rev, files in MANIFEST:
|
|
253
|
-
print("%s @ %s" % (repo, rev))
|
|
254
|
-
for path, size, sha in files:
|
|
255
|
-
print(" %-40s %12d %s" % (path, size, sha or "-"))
|
|
256
|
-
return
|
|
257
|
-
|
|
258
|
-
total = sum(size for _, _, _, files in MANIFEST for _, size, _ in files)
|
|
259
|
-
missing = []
|
|
260
|
-
for local_dir, repo, rev, files in MANIFEST:
|
|
261
|
-
for path, size, sha in files:
|
|
262
|
-
target = safe_join(local_dir, path)
|
|
263
|
-
if not file_ok(target, size, sha, verify_hash=args.verify_only):
|
|
264
|
-
missing.append((local_dir, repo, rev, path, size, sha))
|
|
265
|
-
|
|
266
|
-
if args.verify_only:
|
|
267
|
-
if missing:
|
|
268
|
-
for local_dir, _, _, path, _, _ in missing:
|
|
269
|
-
print(" BAD/MISSING %s/%s" % (local_dir, path))
|
|
270
|
-
fail("%d file(s) failed verification under %s" % (len(missing), dest))
|
|
271
|
-
log("all %d files verified OK under %s" % (
|
|
272
|
-
sum(len(f) for _, _, _, f in MANIFEST), dest))
|
|
273
|
-
return
|
|
274
|
-
|
|
275
|
-
if missing:
|
|
276
|
-
todo = sum(m[4] for m in missing)
|
|
277
|
-
log("destination: %s" % dest)
|
|
278
|
-
log("downloading %d file(s), %.1f GiB of %.1f GiB total pinned payload"
|
|
279
|
-
% (len(missing), todo / 2**30, total / 2**30))
|
|
280
|
-
for local_dir, repo, rev, path, size, sha in missing:
|
|
281
|
-
url = "%s/%s/resolve/%s/%s?download=true" % (HF, repo, rev, path)
|
|
282
|
-
log("fetch %s/%s (%d bytes)" % (local_dir, path, size))
|
|
283
|
-
download(url, safe_join(local_dir, path), size, sha)
|
|
284
|
-
else:
|
|
285
|
-
log("all pinned files already present under %s" % dest)
|
|
286
|
-
|
|
287
|
-
for adapter_dir in (MNTP_SRC_DIR, SUP_DIR):
|
|
288
|
-
cfg = safe_join(adapter_dir, "adapter_config.json")
|
|
289
|
-
if rewrite_adapter_config(cfg, base_path):
|
|
290
|
-
log("repointed %s -> local base" % os.path.join(adapter_dir, "adapter_config.json"))
|
|
291
|
-
|
|
292
|
-
log("downloads verified. Bake the MNTP merge next (one-time, ~5 min):")
|
|
293
|
-
log(" cd <ardy checkout> && <ardy venv python> merge-text-encoder.py --dest %s" % dest)
|
|
294
|
-
print()
|
|
295
|
-
print("Start ARDY's text-encoder service (and any local ARDY run) with:")
|
|
296
|
-
print()
|
|
297
|
-
print(" export TEXT_ENCODERS_DIR=%s" % dest)
|
|
298
|
-
print(" python scripts/run_text_encoder_server.py")
|
|
299
|
-
print()
|
|
300
|
-
print("The base weights are Meta Llama 3 (Meta Llama 3 Community License;")
|
|
301
|
-
print("see %s/LICENSE)." % base_path)
|
|
302
|
-
print("This product is built with Meta Llama 3.")
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
if __name__ == "__main__":
|
|
306
|
-
main()
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
/** Rotation-only GVHMR SMPL -> cskel27 retarget.
|
|
2
|
-
* The argument is the member map returned by readNpz; plain arrays are also
|
|
3
|
-
* accepted for small callers. Arrays may be nested or flat typed buffers.
|
|
4
|
-
*
|
|
5
|
-
* PROPORTIONS. Mocap is the product here, so the take carries the filmed
|
|
6
|
-
* performer's body, not the canonical one: every cskel27 bone is scaled by
|
|
7
|
-
* the ratio of the performer's SMPL rest bone length to the canonical bone
|
|
8
|
-
* (`boneScale`, one factor per joint = the length of the bone ENDING at that
|
|
9
|
-
* joint). posedJoints are grown with FK over those scaled offsets, so foot
|
|
10
|
-
* placement, reach and stride are the performer's; playback applies the same
|
|
11
|
-
* factors to the bones it drives by rotation only (the arm chain). A bone the
|
|
12
|
-
* SMPL skeleton does not segment the same way (Spine..Spine3 are four links
|
|
13
|
-
* against SMPL's three; hand ends; thumb) takes the nearest measured chain's
|
|
14
|
-
* ratio so the body stays continuous. rootPos is the performer's pelvis
|
|
15
|
-
* height as filmed: with the legs at their real length there is no height
|
|
16
|
-
* ratio left to apply, and personScale is 1 by construction.
|
|
17
|
-
*/
|
|
18
|
-
import { CSKEL27_JOINTS, CSKEL27_PARENTS } from "../../src/ardy/cskel27.js";
|
|
19
|
-
import { deriveBoneOffsets, forwardKinematics, matMul, matTranspose } from "../../src/ardy/convert.js";
|
|
20
|
-
import { canonicalCskel27Reference } from "../../src/ardy/to-cskel27.js";
|
|
21
|
-
|
|
22
|
-
const SMPL_PARENTS = [-1, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 12, 13, 14, 16, 17, 18, 19, 20, 21];
|
|
23
|
-
const IDENTITY = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
|
|
24
|
-
const MAP = {
|
|
25
|
-
Hips: 0, Spine: 3, Spine1: 6, Spine2: 9, Spine3: 9, Neck: 12, Head: 15,
|
|
26
|
-
RightShoulder: 14, RightArm: 17, RightForeArm: 19, RightHand: 21,
|
|
27
|
-
LeftShoulder: 13, LeftArm: 16, LeftForeArm: 18, LeftHand: 20,
|
|
28
|
-
RightUpLeg: 2, RightLeg: 5, RightFoot: 8, RightToeBase: 11,
|
|
29
|
-
LeftUpLeg: 1, LeftLeg: 4, LeftFoot: 7, LeftToeBase: 10,
|
|
30
|
-
};
|
|
31
|
-
// Limb rest directions are meaningful (SMPL's A-pose versus cskel27's
|
|
32
|
-
// T-pose), so a constant swing aligns those chains without losing source
|
|
33
|
-
// twist. Torso, pelvis, neck and head are deliberately absent: SMPL's joint
|
|
34
|
-
// regressor forms an anatomical S inside the mesh and its head joint sits
|
|
35
|
-
// forward inside the skull. Those are not visible-pose corrections and must
|
|
36
|
-
// not deform the target character's bind shape.
|
|
37
|
-
const CHILD = {
|
|
38
|
-
RightShoulder: 17, RightArm: 19, RightForeArm: 21, RightHand: 23,
|
|
39
|
-
LeftShoulder: 16, LeftArm: 18, LeftForeArm: 20, LeftHand: 22,
|
|
40
|
-
RightUpLeg: 5, RightLeg: 8, RightFoot: 11,
|
|
41
|
-
LeftUpLeg: 4, LeftLeg: 7, LeftFoot: 10,
|
|
42
|
-
};
|
|
43
|
-
const BORROW = { RightHandEnd: "RightHand", RightHandThumb1: "RightHand", LeftHandEnd: "LeftHand", LeftHandThumb1: "LeftHand" };
|
|
44
|
-
const CORRECTION_BORROW = { RightToeBase: "RightFoot", LeftToeBase: "LeftFoot" };
|
|
45
|
-
// Bone lengths measured on the performer, keyed by the cskel27 joint the bone
|
|
46
|
-
// ENDS at, as [SMPL from, SMPL to] against [cskel27 from, cskel27 to]. Where
|
|
47
|
-
// the two skeletons segment a limb differently the measurement spans the
|
|
48
|
-
// whole segment on both sides and every link in it takes the one ratio:
|
|
49
|
-
// - torso: SMPL's three spine joints do not sit where cskel27's four do, so
|
|
50
|
-
// pelvis -> spine3 is one measurement over Spine..Spine3;
|
|
51
|
-
// - shoulder girdle: SMPL's collar is a short stub (8 cm) where Mixamo's
|
|
52
|
-
// clavicle is 16 cm, so spine3 -> shoulder covers Shoulder+Arm together
|
|
53
|
-
// (per-link it read 0.79 / 0.57 and would have pinched the shoulders);
|
|
54
|
-
// - neck, head, hand ends, thumb: geometry of the character, not a limb
|
|
55
|
-
// the mocap measures. cskel27's Neck link (25 cm) already lands on the
|
|
56
|
-
// Mixamo rig at 16 cm — the skin's neck is short by construction — so
|
|
57
|
-
// scaling it by SMPL's 0.84 sank the neck into the chest (measured:
|
|
58
|
-
// Spine2->Neck 16.5 -> 12.5 cm on the rig). Left at 1 like the head.
|
|
59
|
-
const LENGTH_SOURCE = {
|
|
60
|
-
Spine: [0, 9], Spine1: [0, 9], Spine2: [0, 9], Spine3: [0, 9],
|
|
61
|
-
RightShoulder: [9, 17], RightArm: [9, 17], RightForeArm: [17, 19], RightHand: [19, 21],
|
|
62
|
-
LeftShoulder: [9, 16], LeftArm: [9, 16], LeftForeArm: [16, 18], LeftHand: [18, 20],
|
|
63
|
-
RightUpLeg: [0, 2], RightLeg: [2, 5], RightFoot: [5, 8], RightToeBase: [8, 11],
|
|
64
|
-
LeftUpLeg: [0, 1], LeftLeg: [1, 4], LeftFoot: [4, 7], LeftToeBase: [7, 10],
|
|
65
|
-
};
|
|
66
|
-
const LENGTH_TARGET = {
|
|
67
|
-
Spine: ["Hips", "Spine3"], Spine1: ["Hips", "Spine3"], Spine2: ["Hips", "Spine3"], Spine3: ["Hips", "Spine3"],
|
|
68
|
-
RightShoulder: ["Spine3", "RightArm"], RightArm: ["Spine3", "RightArm"],
|
|
69
|
-
LeftShoulder: ["Spine3", "LeftArm"], LeftArm: ["Spine3", "LeftArm"],
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
function data(value) {
|
|
73
|
-
if (value && value.data !== undefined) return value.data;
|
|
74
|
-
if (Array.isArray(value)) return value.flat(Infinity);
|
|
75
|
-
return value;
|
|
76
|
-
}
|
|
77
|
-
function shape(value, expected, label) {
|
|
78
|
-
const d = data(value);
|
|
79
|
-
if (!d || d.length !== expected.reduce((a, b) => a * b, 1)) throw new Error(`${label} has the wrong size`);
|
|
80
|
-
return d;
|
|
81
|
-
}
|
|
82
|
-
function vec(a) { const n = Math.hypot(...a); return n > 1e-12 ? a.map((v) => v / n) : [1, 0, 0]; }
|
|
83
|
-
function cross(a, b) { return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]]; }
|
|
84
|
-
function dot(a, b) { return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; }
|
|
85
|
-
function axisAngle(axis, angle) {
|
|
86
|
-
const [x, y, z] = axis, s = Math.sin(angle), c = Math.cos(angle), t = 1 - c;
|
|
87
|
-
return [[t * x * x + c, t * x * y - s * z, t * x * z + s * y], [t * x * y + s * z, t * y * y + c, t * y * z - s * x], [t * x * z - s * y, t * y * z + s * x, t * z * z + c]];
|
|
88
|
-
}
|
|
89
|
-
function axisAngleMat(v) {
|
|
90
|
-
const angle = Math.hypot(...v); return angle < 1e-12 ? IDENTITY.map((r) => r.slice()) : axisAngle(v.map((x) => x / angle), angle);
|
|
91
|
-
}
|
|
92
|
-
function swing(from, to) {
|
|
93
|
-
const a = vec(from), b = vec(to), c = Math.max(-1, Math.min(1, dot(a, b)));
|
|
94
|
-
if (c > 1 - 1e-10) return IDENTITY.map((r) => r.slice());
|
|
95
|
-
let ax = cross(a, b), n = Math.hypot(...ax);
|
|
96
|
-
if (n < 1e-10) {
|
|
97
|
-
ax = Math.abs(a[0]) < 0.9 ? cross(a, [1, 0, 0]) : cross(a, [0, 1, 0]); n = Math.hypot(...ax);
|
|
98
|
-
return axisAngle(ax.map((x) => x / n), Math.PI);
|
|
99
|
-
}
|
|
100
|
-
return axisAngle(ax.map((x) => x / n), Math.acos(c));
|
|
101
|
-
}
|
|
102
|
-
function readAa(flat, frame, joint) { const o = (frame * 23 + joint - 1) * 3; return [flat[o], flat[o + 1], flat[o + 2]]; }
|
|
103
|
-
function sourceGlobals(go, pose, frames) {
|
|
104
|
-
const worlds = new Array(frames);
|
|
105
|
-
for (let f = 0; f < frames; f += 1) {
|
|
106
|
-
const out = new Array(24); out[0] = axisAngleMat([go[f * 3], go[f * 3 + 1], go[f * 3 + 2]]);
|
|
107
|
-
for (let j = 1; j < 24; j += 1) out[j] = matMul(out[SMPL_PARENTS[j]], axisAngleMat(readAa(pose, f, j)));
|
|
108
|
-
worlds[f] = out;
|
|
109
|
-
}
|
|
110
|
-
return worlds;
|
|
111
|
-
}
|
|
112
|
-
function writeMat(out, offset, m) { out[offset] = m[0][0]; out[offset + 1] = m[0][1]; out[offset + 2] = m[0][2]; out[offset + 3] = m[1][0]; out[offset + 4] = m[1][1]; out[offset + 5] = m[1][2]; out[offset + 6] = m[2][0]; out[offset + 7] = m[2][1]; out[offset + 8] = m[2][2]; }
|
|
113
|
-
function percentile(values, p) { const a = [...values].sort((x, y) => x - y); const x = (a.length - 1) * p, i = Math.floor(x), t = x - i; return a[i] * (1 - t) + (a[i + 1] ?? a[i]) * t; }
|
|
114
|
-
|
|
115
|
-
export function smplToCskel27Motion(members, { boneScale: boneScaleOption = "derive" } = {}) {
|
|
116
|
-
if (boneScaleOption !== "derive" && !(typeof boneScaleOption === "number" && Number.isFinite(boneScaleOption) && boneScaleOption > 0)) throw new Error('smplToCskel27Motion: boneScale must be "derive" or a positive number');
|
|
117
|
-
const go = data(members.smpl_global_orient), poseMember = members.smpl_body_pose;
|
|
118
|
-
const goShape = poseMember?.shape?.[0] ?? members.smpl_global_orient?.shape?.[0];
|
|
119
|
-
const frames = Number.isInteger(goShape) ? goShape : go.length / 3;
|
|
120
|
-
if (!Number.isInteger(frames) || frames < 1) throw new Error("smplToCskel27Motion: invalid frame count");
|
|
121
|
-
shape(go, [frames, 3], "smpl_global_orient");
|
|
122
|
-
const pose = shape(poseMember, [frames, 23, 3], "smpl_body_pose");
|
|
123
|
-
const transl = shape(members.smpl_transl, [frames, 3], "smpl_transl");
|
|
124
|
-
const rest = shape(members.smpl_rest_joints, [24, 3], "smpl_rest_joints");
|
|
125
|
-
const fpsValue = data(members.fps); const fps = Number(fpsValue?.[0] ?? fpsValue);
|
|
126
|
-
if (!(fps > 0)) throw new Error("smplToCskel27Motion: invalid fps");
|
|
127
|
-
const skeleton = canonicalCskel27Reference();
|
|
128
|
-
const cpos = skeleton.posed_joints;
|
|
129
|
-
const restVec = (j) => [rest[j * 3], rest[j * 3 + 1], rest[j * 3 + 2]];
|
|
130
|
-
const dist = (a, b) => Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2]);
|
|
131
|
-
const boneScale = new Float32Array(27).fill(typeof boneScaleOption === "number" ? boneScaleOption : 1);
|
|
132
|
-
for (let j = 1; j < 27 && boneScaleOption === "derive"; j += 1) {
|
|
133
|
-
const name = CSKEL27_JOINTS[j];
|
|
134
|
-
if (!LENGTH_SOURCE[name]) continue;
|
|
135
|
-
const [s0, s1] = LENGTH_SOURCE[name];
|
|
136
|
-
const source = dist(restVec(s0), restVec(s1));
|
|
137
|
-
const [t0, t1] = LENGTH_TARGET[name] ?? [CSKEL27_JOINTS[CSKEL27_PARENTS[j]], name];
|
|
138
|
-
const target = dist(cpos[CSKEL27_JOINTS.indexOf(t0)], cpos[CSKEL27_JOINTS.indexOf(t1)]);
|
|
139
|
-
if (source > 1e-6 && target > 1e-6) boneScale[j] = source / target;
|
|
140
|
-
}
|
|
141
|
-
const offsets = deriveBoneOffsets(cpos, skeleton.local_rot_mats).map((o, j) => [o[0] * boneScale[j], o[1] * boneScale[j], o[2] * boneScale[j]]);
|
|
142
|
-
const corrections = {};
|
|
143
|
-
for (const [name, sourceChild] of Object.entries(CHILD)) {
|
|
144
|
-
const c = CSKEL27_JOINTS.indexOf(name);
|
|
145
|
-
const targetChild = CSKEL27_PARENTS.findIndex((parent) => parent === c);
|
|
146
|
-
if (c < 0 || targetChild < 0) continue;
|
|
147
|
-
const source = MAP[name];
|
|
148
|
-
const a = [cpos[targetChild][0] - cpos[c][0], cpos[targetChild][1] - cpos[c][1], cpos[targetChild][2] - cpos[c][2]];
|
|
149
|
-
const b = [rest[sourceChild * 3] - rest[source * 3], rest[sourceChild * 3 + 1] - rest[source * 3 + 1], rest[sourceChild * 3 + 2] - rest[source * 3 + 2]];
|
|
150
|
-
corrections[name] = swing(a, b);
|
|
151
|
-
}
|
|
152
|
-
for (const [name, ancestor] of Object.entries(CORRECTION_BORROW)) corrections[name] = corrections[ancestor] ?? IDENTITY;
|
|
153
|
-
const worlds = sourceGlobals(go, pose, frames), rotMats = new Float32Array(frames * 27 * 9), rootPos = new Float32Array(frames * 3), posedJoints = new Float32Array(frames * 27 * 3);
|
|
154
|
-
// The legs now have the performer's length, so the pelvis rides at its
|
|
155
|
-
// filmed height: no ratio between the two skeletons is left to apply.
|
|
156
|
-
const rootScale = 1;
|
|
157
|
-
for (let f = 0; f < frames; f += 1) {
|
|
158
|
-
const globals = new Array(27);
|
|
159
|
-
for (let j = 0; j < 27; j += 1) {
|
|
160
|
-
const name = CSKEL27_JOINTS[j], ancestor = BORROW[name];
|
|
161
|
-
// body_pose is already a rotation FROM SMPL's zero pose. Torso/head
|
|
162
|
-
// therefore keep cskel27's bind shape; only visible limb rest-pose
|
|
163
|
-
// differences use the constant correction built above.
|
|
164
|
-
globals[j] = ancestor ? globals[CSKEL27_JOINTS.indexOf(ancestor)] : matMul(worlds[f][MAP[name]], corrections[name] ?? IDENTITY);
|
|
165
|
-
}
|
|
166
|
-
for (let j = 0; j < 27; j += 1) {
|
|
167
|
-
const p = CSKEL27_PARENTS[j], local = p === null ? globals[j] : matMul(matTranspose(globals[p]), globals[j]);
|
|
168
|
-
writeMat(rotMats, (f * 27 + j) * 9, local);
|
|
169
|
-
}
|
|
170
|
-
rootPos[f * 3] = transl[f * 3] * rootScale; rootPos[f * 3 + 1] = transl[f * 3 + 1] * rootScale; rootPos[f * 3 + 2] = transl[f * 3 + 2] * rootScale;
|
|
171
|
-
}
|
|
172
|
-
const readLocals = (f) => Array.from({ length: 27 }, (_, j) => { const o = (f * 27 + j) * 9; return [[rotMats[o], rotMats[o + 1], rotMats[o + 2]], [rotMats[o + 3], rotMats[o + 4], rotMats[o + 5]], [rotMats[o + 6], rotMats[o + 7], rotMats[o + 8]]]; });
|
|
173
|
-
const lows = new Array(frames);
|
|
174
|
-
for (let f = 0; f < frames; f += 1) { const p = forwardKinematics(readLocals(f), offsets, [rootPos[f * 3], rootPos[f * 3 + 1], rootPos[f * 3 + 2]]); lows[f] = Math.min(...[21, 22, 25, 26].map((j) => p[j][1])); }
|
|
175
|
-
const floor = percentile(lows, 0.1);
|
|
176
|
-
for (let f = 0; f < frames; f += 1) { rootPos[f * 3 + 1] -= floor; const p = forwardKinematics(readLocals(f), offsets, [rootPos[f * 3], rootPos[f * 3 + 1], rootPos[f * 3 + 2]]); for (let j = 0; j < 27; j += 1) posedJoints.set(p[j], (f * 27 + j) * 3); }
|
|
177
|
-
return { frames, fps, rotMats, rootPos, posedJoints, boneScale, personScale: 1, rawRootStart: [transl[0], transl[1], transl[2]] };
|
|
178
|
-
}
|
package/tools/ardy/visual-qa.mjs
DELETED
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Visual QA driver for CozyClay: drives headless Chrome over CDP with
|
|
3
|
-
* Node's built-in WebSocket. Loads the studio, generates "a person walk"
|
|
4
|
-
* with the default seed (2), then captures screenshots at several playback
|
|
5
|
-
* frames for pose review.
|
|
6
|
-
*
|
|
7
|
-
* Usage: node tools/ardy/visual-qa.mjs <outDir> [url]
|
|
8
|
-
* The default URL is the studio route (`/app/`), not the marketing landing page.
|
|
9
|
-
*/
|
|
10
|
-
import { writeFileSync, mkdirSync } from "node:fs";
|
|
11
|
-
|
|
12
|
-
const OUT = process.argv[2] || "/tmp/visual-qa";
|
|
13
|
-
const URL = process.argv[3] || "http://127.0.0.1:5180/app/";
|
|
14
|
-
mkdirSync(OUT, { recursive: true });
|
|
15
|
-
|
|
16
|
-
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
17
|
-
|
|
18
|
-
// --- minimal CDP client -----------------------------------------------------
|
|
19
|
-
async function newTab() {
|
|
20
|
-
const res = await fetch("http://127.0.0.1:9222/json/new?about:blank", { method: "PUT" });
|
|
21
|
-
const target = await res.json();
|
|
22
|
-
const ws = new WebSocket(target.webSocketDebuggerUrl);
|
|
23
|
-
await new Promise((resolve, reject) => {
|
|
24
|
-
ws.onopen = resolve;
|
|
25
|
-
ws.onerror = reject;
|
|
26
|
-
});
|
|
27
|
-
let id = 0;
|
|
28
|
-
const pending = new Map();
|
|
29
|
-
const consoleLog = [];
|
|
30
|
-
ws.onmessage = (event) => {
|
|
31
|
-
const msg = JSON.parse(event.data);
|
|
32
|
-
if (msg.id && pending.has(msg.id)) {
|
|
33
|
-
const { resolve, reject } = pending.get(msg.id);
|
|
34
|
-
pending.delete(msg.id);
|
|
35
|
-
if (msg.error) reject(new Error(msg.error.message));
|
|
36
|
-
else resolve(msg.result);
|
|
37
|
-
} else if (msg.method === "Runtime.consoleAPICalled") {
|
|
38
|
-
const type = msg.params.type;
|
|
39
|
-
const text = msg.params.args.map((a) => a.value ?? a.description ?? "").join(" ");
|
|
40
|
-
if (type === "error" || type === "warning") consoleLog.push(`[${type}] ${text}`);
|
|
41
|
-
} else if (msg.method === "Runtime.exceptionThrown") {
|
|
42
|
-
consoleLog.push(`[exception] ${msg.params.exceptionDetails.text} ${msg.params.exceptionDetails.exception?.description ?? ""}`);
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
const send = (method, params = {}) =>
|
|
46
|
-
new Promise((resolve, reject) => {
|
|
47
|
-
const msgId = ++id;
|
|
48
|
-
pending.set(msgId, { resolve, reject });
|
|
49
|
-
ws.send(JSON.stringify({ id: msgId, method, params }));
|
|
50
|
-
});
|
|
51
|
-
return { ws, send, consoleLog };
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
async function evaluate(tab, expression) {
|
|
55
|
-
const res = await tab.send("Runtime.evaluate", {
|
|
56
|
-
expression,
|
|
57
|
-
awaitPromise: true,
|
|
58
|
-
returnByValue: true,
|
|
59
|
-
});
|
|
60
|
-
if (res.exceptionDetails) {
|
|
61
|
-
throw new Error(`evaluate failed: ${JSON.stringify(res.exceptionDetails.exception?.description || res.exceptionDetails.text)}`);
|
|
62
|
-
}
|
|
63
|
-
return res.result.value;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async function shot(tab, name) {
|
|
67
|
-
const res = await tab.send("Page.captureScreenshot", { format: "png" });
|
|
68
|
-
writeFileSync(`${OUT}/${name}.png`, Buffer.from(res.data, "base64"));
|
|
69
|
-
console.log(`saved ${OUT}/${name}.png`);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// --- flow -------------------------------------------------------------------
|
|
73
|
-
const tab = await newTab();
|
|
74
|
-
await tab.send("Page.enable");
|
|
75
|
-
await tab.send("Runtime.enable");
|
|
76
|
-
await tab.send("Page.navigate", { url: URL });
|
|
77
|
-
await sleep(6000);
|
|
78
|
-
|
|
79
|
-
// Prompt Blocks replaced the old single "Generate motion" field. Select the
|
|
80
|
-
// first character, open the Prompt Blocks foldout, then add a block before
|
|
81
|
-
// looking for its prompt input and the top-bar Generate Motion action.
|
|
82
|
-
await evaluate(tab, `(() => {
|
|
83
|
-
const character = [...document.querySelectorAll('.hierarchy-row')].find((row) => /Character 1|Subject 1|인물 1/.test(row.textContent));
|
|
84
|
-
character?.click();
|
|
85
|
-
const foldout = [...document.querySelectorAll('.foldout-head')].find((button) => /Prompt Blocks|프롬프트 블록/.test(button.textContent));
|
|
86
|
-
foldout?.click();
|
|
87
|
-
const add = document.querySelector('.tl-track.prompts .tl-track-add') || document.querySelector('.tl-track-add[title*="prompt"]');
|
|
88
|
-
add?.click();
|
|
89
|
-
return true;
|
|
90
|
-
})()`);
|
|
91
|
-
await sleep(400);
|
|
92
|
-
const state0 = await evaluate(tab, `(() => {
|
|
93
|
-
const seed = document.querySelector('input[placeholder="empty = random"]');
|
|
94
|
-
const promptEl = document.querySelector('input[placeholder*="motion block"], input[placeholder*="모션 블록"]');
|
|
95
|
-
const gen = document.querySelector('[data-testid=topbar-generate]');
|
|
96
|
-
return { seedValue: seed?.value ?? null, hasGenerate: !!gen, generateDisabled: gen?.getAttribute('aria-disabled') === 'true', hasPrompt: !!promptEl };
|
|
97
|
-
})()`);
|
|
98
|
-
console.log("initial:", JSON.stringify(state0));
|
|
99
|
-
|
|
100
|
-
if (!state0.hasGenerate || !state0.hasPrompt) {
|
|
101
|
-
await shot(tab, "00-no-ui");
|
|
102
|
-
throw new Error("studio UI not ready");
|
|
103
|
-
}
|
|
104
|
-
if (state0.seedValue === null) throw new Error("Prompt Blocks panel did not open");
|
|
105
|
-
|
|
106
|
-
// Set the prompt and generate: focus the field, then type via the Input
|
|
107
|
-
// domain so React's controlled input sees real key events.
|
|
108
|
-
await evaluate(tab, `(() => {
|
|
109
|
-
const el = document.querySelector('input[placeholder*="motion block"], input[placeholder*="모션 블록"]');
|
|
110
|
-
if (!el) return false;
|
|
111
|
-
el.focus();
|
|
112
|
-
el.select?.();
|
|
113
|
-
return true;
|
|
114
|
-
})()`);
|
|
115
|
-
for (const key of ["a", " ", "p", "e", "r", "s", "o", "n", " ", "w", "a", "l", "k"]) {
|
|
116
|
-
await tab.send("Input.dispatchKeyEvent", { type: "keyDown", text: key });
|
|
117
|
-
await tab.send("Input.dispatchKeyEvent", { type: "keyUp", text: key });
|
|
118
|
-
}
|
|
119
|
-
await sleep(300);
|
|
120
|
-
await shot(tab, "01-before-generate");
|
|
121
|
-
const clicked = await evaluate(tab, `(() => {
|
|
122
|
-
const gen = document.querySelector('[data-testid=topbar-generate]');
|
|
123
|
-
if (!gen || gen.getAttribute('aria-disabled') === 'true') return false;
|
|
124
|
-
gen.click();
|
|
125
|
-
return true;
|
|
126
|
-
})()`);
|
|
127
|
-
if (!clicked) throw new Error("Kimodo bridge is unavailable or generation is disabled");
|
|
128
|
-
console.log("generate clicked, waiting for the clip...");
|
|
129
|
-
|
|
130
|
-
// Wait for "Motion loaded" or the PLAYBACK badge (generation ~12 s).
|
|
131
|
-
let loaded = false;
|
|
132
|
-
for (let i = 0; i < 40; i += 1) {
|
|
133
|
-
await sleep(1000);
|
|
134
|
-
const st = await evaluate(tab, `document.body.textContent.includes("Motion loaded") || document.body.textContent.includes("PLAYBACK")`);
|
|
135
|
-
if (st) { loaded = true; break; }
|
|
136
|
-
}
|
|
137
|
-
console.log("motion loaded:", loaded);
|
|
138
|
-
if (!loaded) {
|
|
139
|
-
await shot(tab, "02-generation-timeout");
|
|
140
|
-
throw new Error("motion did not load within 40 s");
|
|
141
|
-
}
|
|
142
|
-
await sleep(1500);
|
|
143
|
-
|
|
144
|
-
// Scrub to specific frames by clicking the timeline's accessible ruler. The
|
|
145
|
-
// old tick-text selector clicked labels, not the scrub surface, after the
|
|
146
|
-
// timeline was redesigned.
|
|
147
|
-
const frames = [0, 20, 40, 60, 79];
|
|
148
|
-
for (const f of frames) {
|
|
149
|
-
const point = await evaluate(tab, `(() => {
|
|
150
|
-
const lane = document.querySelector('[role="slider"][aria-label*="Scrub timeline"], [role="slider"][aria-label*="타임라인"]');
|
|
151
|
-
if (!lane) return null;
|
|
152
|
-
const rect = lane.getBoundingClientRect();
|
|
153
|
-
const max = Math.max(1, Number(lane.getAttribute('aria-valuemax')) || 1);
|
|
154
|
-
return { x: rect.left + rect.width * Math.min(1, ${f} / max), y: rect.top + rect.height / 2 };
|
|
155
|
-
})()`);
|
|
156
|
-
if (!point) throw new Error("timeline scrub ruler not found");
|
|
157
|
-
await tab.send("Input.dispatchMouseEvent", { type: "mouseMoved", x: point.x, y: point.y, button: "none" });
|
|
158
|
-
await tab.send("Input.dispatchMouseEvent", { type: "mousePressed", x: point.x, y: point.y, button: "left", clickCount: 1 });
|
|
159
|
-
await tab.send("Input.dispatchMouseEvent", { type: "mouseReleased", x: point.x, y: point.y, button: "left", clickCount: 1 });
|
|
160
|
-
await sleep(700);
|
|
161
|
-
await shot(tab, `10-frame-${String(f).padStart(2, "0")}`);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Play a couple of seconds for a mid-stride live shot.
|
|
165
|
-
await evaluate(tab, `(() => {
|
|
166
|
-
const play = [...document.querySelectorAll("button")].find(b => b.textContent.trim() === "▶");
|
|
167
|
-
play?.click();
|
|
168
|
-
return true;
|
|
169
|
-
})()`);
|
|
170
|
-
await sleep(1600);
|
|
171
|
-
await shot(tab, "20-playing-midstride");
|
|
172
|
-
await sleep(1600);
|
|
173
|
-
await shot(tab, "21-playing-later");
|
|
174
|
-
await evaluate(tab, `(() => {
|
|
175
|
-
const pause = [...document.querySelectorAll("button")].find(b => /❚❚|⏸/.test(b.textContent.trim()));
|
|
176
|
-
pause?.click();
|
|
177
|
-
return true;
|
|
178
|
-
})()`);
|
|
179
|
-
|
|
180
|
-
// Camera close-up on the subject for a detailed pose check.
|
|
181
|
-
await evaluate(tab, `(() => {
|
|
182
|
-
const chip = [...document.querySelectorAll("button")].find(b => /close-up/i.test(b.textContent));
|
|
183
|
-
chip?.click();
|
|
184
|
-
return true;
|
|
185
|
-
})()`);
|
|
186
|
-
await sleep(900);
|
|
187
|
-
await shot(tab, "30-closeup");
|
|
188
|
-
|
|
189
|
-
if (tab.consoleLog.length) {
|
|
190
|
-
console.log("--- console errors/warnings ---");
|
|
191
|
-
for (const line of [...new Set(tab.consoleLog)].slice(0, 15)) console.log(line);
|
|
192
|
-
} else {
|
|
193
|
-
console.log("no console errors/warnings captured");
|
|
194
|
-
}
|
|
195
|
-
tab.ws.close();
|
|
196
|
-
console.log("visual QA capture complete");
|