cozyclay 2.1.1 → 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 +60 -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 +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-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-msLI71g1.js → first-shot-handoff-CC4xe4HM.js} +1 -1
- package/dist/assets/{landing-ChUvB_zL.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/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 +150 -2624
- 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 +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 +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/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 +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 +12 -14
- package/src/studio-agent-commands.js +19 -16
- package/src/studio-agent-motion.js +9 -9
- package/src/studio-agent-protocol.js +41 -71
- package/src/studio-app-binding.js +25 -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/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 +8 -124
- 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/tools/bench/score.mjs
DELETED
|
@@ -1,343 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Mocap scorer (#431): compare a predicted cskel27 motion npz against a T1
|
|
4
|
-
* ground-truth render (tools/gt-render/render.mjs) by rendering the
|
|
5
|
-
* prediction through the SAME Studio rig and the SAME camera.
|
|
6
|
-
*
|
|
7
|
-
* node tools/bench/score.mjs --gt <T1 motion dir> --pred <motion.npz> --out <dir>
|
|
8
|
-
* [--box '{"min":[x,y,z],"max":[x,y,z]}'] [--pred-transform '<json>'] [--gt-npz <npz>]
|
|
9
|
-
*
|
|
10
|
-
* Ground truth is the rig as rendered (joints.json, mask/), so the prediction
|
|
11
|
-
* is read the same way: render.mjs --no-video --camera <gt camera.json>.
|
|
12
|
-
*
|
|
13
|
-
* Placements
|
|
14
|
-
* raw the prediction as the Studio places any take: frame 0 anchored on
|
|
15
|
-
* the subject, the take's own heading (plus --pred-transform, a scene
|
|
16
|
-
* calibration in the Studio's sceneCalibration semantics, if given).
|
|
17
|
-
* A constant offset baked into the npz is therefore invisible here;
|
|
18
|
-
* score.json reports it separately (npz.frame0RootOffsetM).
|
|
19
|
-
* aligned raw moved by the least-squares yaw + ground-plane translation that
|
|
20
|
-
* maps its first scored frame's joints onto the GT's, re-rendered so
|
|
21
|
-
* the mask IoU is of a real render.
|
|
22
|
-
*
|
|
23
|
-
* Timelines: the Studio plays every take at 24 fps (retimed on load). Each GT
|
|
24
|
-
* frame is paired with the nearest-in-time rendered pred frame; GT frames past
|
|
25
|
-
* the prediction's end are not scored. Both are reported in score.json.
|
|
26
|
-
*
|
|
27
|
-
* Writes <out>/score.json, <out>/frames.csv and the renders in <out>/render-*.
|
|
28
|
-
*/
|
|
29
|
-
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
30
|
-
import { execFileSync } from "node:child_process";
|
|
31
|
-
import { basename, dirname, join, resolve } from "node:path";
|
|
32
|
-
import { parseArgs } from "node:util";
|
|
33
|
-
import { decodeMotionNpz } from "../../src/ardy/npz.js";
|
|
34
|
-
import { mocapMetricsFromMotion } from "../ardy/mocap-metrics.mjs";
|
|
35
|
-
import { installSignalCleanup, spawnOwned, waitForExit } from "../process-supervisor.mjs";
|
|
36
|
-
import { startVite, terminateAll } from "../gt-render/browser.mjs";
|
|
37
|
-
import { isIdentityTransform, parseTransform, transformTake } from "../gt-render/take-transform.mjs";
|
|
38
|
-
import {
|
|
39
|
-
applyYawTranslation,
|
|
40
|
-
composeCalibration,
|
|
41
|
-
displacementXZ,
|
|
42
|
-
fitYawTranslation,
|
|
43
|
-
maskIoU,
|
|
44
|
-
mean,
|
|
45
|
-
meanJointError,
|
|
46
|
-
parseBox,
|
|
47
|
-
pathLengthXZ,
|
|
48
|
-
procrustesError,
|
|
49
|
-
ratio,
|
|
50
|
-
resampleToTimeline,
|
|
51
|
-
rootRelativeError,
|
|
52
|
-
trajectoryError,
|
|
53
|
-
} from "./metrics.mjs";
|
|
54
|
-
|
|
55
|
-
const ROOT = resolve(dirname(new URL(import.meta.url).pathname), "../..");
|
|
56
|
-
const RENDER = join(ROOT, "tools/gt-render/render.mjs");
|
|
57
|
-
const ROOT_JOINT = "Hips";
|
|
58
|
-
|
|
59
|
-
const USAGE = `usage: node tools/bench/score.mjs --gt <T1 motion dir> --pred <motion.npz> --out <dir> [options]
|
|
60
|
-
|
|
61
|
-
--gt <dir> T1 output for one motion (<dir>/mask/ + <dir>/<variant>/{camera,joints}.json),
|
|
62
|
-
or a --no-video render dir holding camera.json/joints.json/mask/
|
|
63
|
-
--pred <npz> predicted cskel27 motion npz
|
|
64
|
-
--out <dir> score.json, frames.csv, render-raw/, render-aligned/ [, render-gt/]
|
|
65
|
-
--box <json> '{"min":[x,y,z],"max":[x,y,z]}' scene box: skinned-vertex distance/penetration
|
|
66
|
-
--pred-transform <json> scene calibration for the raw placement (render.mjs --transform keys)
|
|
67
|
-
--gt-npz <npz> GT source npz (default: meta.json source.path); used for physical
|
|
68
|
-
metrics, the npz offset diagnostic and GT contact with --box
|
|
69
|
-
--url <origin> reuse a running Vite dev server of THIS checkout
|
|
70
|
-
--port <n> Vite port when starting one (default 5192)
|
|
71
|
-
--cdp-port <n> headless Chrome DevTools port for the renders (default 9232)`;
|
|
72
|
-
|
|
73
|
-
function parseOptions(argv) {
|
|
74
|
-
const { values } = parseArgs({
|
|
75
|
-
args: argv,
|
|
76
|
-
options: {
|
|
77
|
-
gt: { type: "string" },
|
|
78
|
-
pred: { type: "string" },
|
|
79
|
-
out: { type: "string" },
|
|
80
|
-
box: { type: "string" },
|
|
81
|
-
"pred-transform": { type: "string" },
|
|
82
|
-
"gt-npz": { type: "string" },
|
|
83
|
-
url: { type: "string" },
|
|
84
|
-
port: { type: "string", default: "5192" },
|
|
85
|
-
"cdp-port": { type: "string", default: "9232" },
|
|
86
|
-
help: { type: "boolean", short: "h", default: false },
|
|
87
|
-
},
|
|
88
|
-
});
|
|
89
|
-
if (values.help) {
|
|
90
|
-
console.log(USAGE);
|
|
91
|
-
process.exit(0);
|
|
92
|
-
}
|
|
93
|
-
const fail = (message) => {
|
|
94
|
-
console.error(`${message}\n\n${USAGE}`);
|
|
95
|
-
process.exit(2);
|
|
96
|
-
};
|
|
97
|
-
for (const key of ["gt", "pred", "out"]) if (!values[key]) fail(`--${key} is required`);
|
|
98
|
-
const pred = resolve(values.pred);
|
|
99
|
-
if (!existsSync(pred)) fail(`no such pred: ${pred}`);
|
|
100
|
-
let box = null;
|
|
101
|
-
let predTransform = null;
|
|
102
|
-
try {
|
|
103
|
-
if (values.box) box = parseBox(values.box);
|
|
104
|
-
predTransform = parseTransform(values["pred-transform"] ?? "{}");
|
|
105
|
-
} catch (error) {
|
|
106
|
-
fail(error.message);
|
|
107
|
-
}
|
|
108
|
-
const port = Number(values.port);
|
|
109
|
-
const cdpPort = Number(values["cdp-port"]);
|
|
110
|
-
if (!Number.isInteger(port) || !Number.isInteger(cdpPort)) fail("--port and --cdp-port must be integers");
|
|
111
|
-
return { gt: resolve(values.gt), pred, out: resolve(values.out), box, predTransform, gtNpz: values["gt-npz"] ? resolve(values["gt-npz"]) : null, url: values.url?.replace(/\/$/, "") ?? null, port, cdpPort };
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/** camera.json, joints.json, meta.json and mask/ of a GT motion dir. */
|
|
115
|
-
function loadGt(dir) {
|
|
116
|
-
const hasFiles = (d) => existsSync(join(d, "camera.json")) && existsSync(join(d, "joints.json"));
|
|
117
|
-
let files = hasFiles(dir) ? dir : null;
|
|
118
|
-
if (!files) {
|
|
119
|
-
const variants = readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory() && hasFiles(join(dir, e.name))).map((e) => e.name);
|
|
120
|
-
const pick = variants.includes("shaded") ? "shaded" : variants.sort()[0];
|
|
121
|
-
if (!pick) throw new Error(`${dir}: no camera.json/joints.json here or in a variant subdirectory`);
|
|
122
|
-
files = join(dir, pick);
|
|
123
|
-
}
|
|
124
|
-
const maskDir = [join(dir, "mask"), join(files, "mask"), join(dirname(files), "mask")].find((d) => existsSync(d));
|
|
125
|
-
if (!maskDir) throw new Error(`${dir}: no mask/ directory`);
|
|
126
|
-
const read = (name) => JSON.parse(readFileSync(join(files, name), "utf8"));
|
|
127
|
-
return { dir, files, cameraPath: join(files, "camera.json"), camera: read("camera.json"), joints: read("joints.json"), meta: existsSync(join(files, "meta.json")) ? read("meta.json") : null, sceneBox: existsSync(join(files, "scene.json")) ? read("scene.json").placement : null, maskDir };
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
async function render({ base, cdpPort, out, camera, transform, box, sceneBox, file, children }) {
|
|
131
|
-
const args = [RENDER, "--out", out, "--no-video", "--camera", camera, "--url", base, "--cdp-port", String(cdpPort)];
|
|
132
|
-
if (transform && !isIdentityTransform(transform)) args.push("--transform", JSON.stringify(transform));
|
|
133
|
-
if (box) args.push("--box", JSON.stringify(box));
|
|
134
|
-
if (sceneBox) args.push("--scene-box", JSON.stringify(sceneBox));
|
|
135
|
-
args.push(file);
|
|
136
|
-
const child = spawnOwned(process.execPath, args, { cwd: ROOT, stdio: ["ignore", "inherit", "inherit"] });
|
|
137
|
-
children.push(child);
|
|
138
|
-
const { code, signal } = await waitForExit(child);
|
|
139
|
-
children.splice(children.indexOf(child), 1);
|
|
140
|
-
if (code !== 0) throw new Error(`render.mjs ${basename(file)} failed (code ${code}, signal ${signal})`);
|
|
141
|
-
const dir = join(out, basename(file, ".npz"));
|
|
142
|
-
const read = (name) => JSON.parse(readFileSync(join(dir, name), "utf8"));
|
|
143
|
-
return { dir, joints: read("joints.json"), meta: read("meta.json"), contact: box ? read("contact.json") : null, maskDir: join(dir, "mask") };
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/** All masks of a dir as one gray buffer (frames * width * height). */
|
|
147
|
-
function readMasks(dir, frames, width, height) {
|
|
148
|
-
const buffer = execFileSync("ffmpeg", ["-hide_banner", "-loglevel", "error", "-start_number", "0", "-i", join(dir, "%06d.png"), "-frames:v", String(frames), "-f", "rawvideo", "-pix_fmt", "gray", "pipe:1"], { maxBuffer: frames * width * height + 1024 });
|
|
149
|
-
if (buffer.length !== frames * width * height) throw new Error(`${dir}: decoded ${buffer.length} mask bytes, expected ${frames} x ${width} x ${height}`);
|
|
150
|
-
return (i) => buffer.subarray(i * width * height, (i + 1) * width * height);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const rootOf = (joints) => joints.joints.findIndex((joint) => joint.cskel27 === ROOT_JOINT);
|
|
154
|
-
|
|
155
|
-
async function decodeFile(path) {
|
|
156
|
-
return decodeMotionNpz(new Uint8Array(readFileSync(path)));
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function physical(motion, path) {
|
|
160
|
-
const m = mocapMetricsFromMotion(motion, { path });
|
|
161
|
-
return { frames: m.frames, fps: m.fps, footSlideCmPerS: m.footSlideCmPerS, jitterMmPerS2: m.jitterMmPerS2, framesBelowFloor: m.framesBelowFloor, deepestBelowFloorCm: m.deepestBelowFloorCm, rootTravelM: m.rootTravelM };
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function contactSummary(contact, indices) {
|
|
165
|
-
if (!contact) return null;
|
|
166
|
-
const frames = indices.map((i) => contact.frames[i]);
|
|
167
|
-
const minDistance = Math.min(...frames.map((f) => f.minDistanceM));
|
|
168
|
-
const maxPenetration = Math.max(...frames.map((f) => f.maxPenetrationM));
|
|
169
|
-
return { basis: contact.basis, minDistanceM: minDistance, maxPenetrationM: maxPenetration, framesPenetrating: frames.filter((f) => f.maxPenetrationM > 0).length, framesTouching: frames.filter((f) => f.signedDistanceM <= 0.01).length };
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const r6 = (value) => (value === null || value === undefined || !Number.isFinite(value) ? value ?? null : Math.round(value * 1e6) / 1e6);
|
|
173
|
-
|
|
174
|
-
async function main() {
|
|
175
|
-
const options = parseOptions(process.argv.slice(2));
|
|
176
|
-
const children = [];
|
|
177
|
-
const removeSignals = installSignalCleanup(() => children, () => process.exit(130));
|
|
178
|
-
mkdirSync(options.out, { recursive: true });
|
|
179
|
-
const gt = loadGt(options.gt);
|
|
180
|
-
const { width, height } = gt.camera;
|
|
181
|
-
const gtNpz = options.gtNpz ?? (gt.meta?.source?.path && existsSync(gt.meta.source.path) ? gt.meta.source.path : null);
|
|
182
|
-
try {
|
|
183
|
-
const base = options.url ?? await startVite({ root: ROOT, port: options.port, children });
|
|
184
|
-
const common = { base, cdpPort: options.cdpPort, camera: gt.cameraPath, box: options.box, sceneBox: gt.sceneBox, children };
|
|
185
|
-
console.log(`[score] raw render of ${options.pred}`);
|
|
186
|
-
const raw = await render({ ...common, out: join(options.out, "render-raw"), transform: options.predTransform, file: options.pred });
|
|
187
|
-
|
|
188
|
-
// Timeline pairing, GT frame -> nearest pred frame.
|
|
189
|
-
const timeline = resampleToTimeline({ gtFrames: gt.joints.frames, gtFps: gt.joints.fps, predFrames: raw.joints.frames, predFps: raw.joints.fps });
|
|
190
|
-
const pairs = timeline.predIndex.map((k, i) => [i, k]).filter(([, k]) => k !== null);
|
|
191
|
-
if (!pairs.length) throw new Error("no GT frame falls inside the prediction's timeline");
|
|
192
|
-
const [gt0, pr0] = pairs[0];
|
|
193
|
-
|
|
194
|
-
// First-frame rigid alignment (yaw + ground-plane translation).
|
|
195
|
-
const alignment = fitYawTranslation(raw.joints.world[pr0], gt.joints.world[gt0]);
|
|
196
|
-
const anchor = raw.meta.placement.anchor;
|
|
197
|
-
const alignedTransform = composeCalibration(options.predTransform, anchor, alignment);
|
|
198
|
-
console.log(`[score] first-frame alignment: yaw ${alignment.yawDeg.toFixed(3)} deg, t (${alignment.tx.toFixed(4)}, ${alignment.tz.toFixed(4)}) m; aligned render`);
|
|
199
|
-
const aligned = await render({ ...common, out: join(options.out, "render-aligned"), transform: parseTransform(JSON.stringify(alignedTransform)), file: options.pred });
|
|
200
|
-
let rigidCheck = 0;
|
|
201
|
-
for (let k = 0; k < raw.joints.frames; k += 1) {
|
|
202
|
-
raw.joints.world[k].forEach((p, j) => {
|
|
203
|
-
const q = applyYawTranslation(p, alignment);
|
|
204
|
-
const a = aligned.joints.world[k][j];
|
|
205
|
-
rigidCheck = Math.max(rigidCheck, Math.hypot(q[0] - a[0], q[1] - a[1], q[2] - a[2]));
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
const gtRender = options.box && gtNpz ? await render({ ...common, out: join(options.out, "render-gt"), transform: null, file: gtNpz }) : null;
|
|
209
|
-
|
|
210
|
-
// Masks.
|
|
211
|
-
const gtMask = readMasks(gt.maskDir, gt.joints.frames, width, height);
|
|
212
|
-
const rawMask = readMasks(raw.maskDir, raw.joints.frames, width, height);
|
|
213
|
-
const alignedMask = readMasks(aligned.maskDir, aligned.joints.frames, width, height);
|
|
214
|
-
|
|
215
|
-
const root = rootOf(gt.joints);
|
|
216
|
-
if (root < 0 || rootOf(raw.joints) !== root) throw new Error("GT and pred joints.json disagree on the root joint");
|
|
217
|
-
if (gt.joints.joints.map((j) => j.bone).join() !== raw.joints.joints.map((j) => j.bone).join()) throw new Error("GT and pred joints.json list different joints");
|
|
218
|
-
|
|
219
|
-
const rows = pairs.map(([i, k]) => {
|
|
220
|
-
const g = gt.joints.world[i];
|
|
221
|
-
const pr = raw.joints.world[k];
|
|
222
|
-
const pa = aligned.joints.world[k];
|
|
223
|
-
return {
|
|
224
|
-
gtFrame: i,
|
|
225
|
-
predFrame: k,
|
|
226
|
-
timeS: i / gt.joints.fps,
|
|
227
|
-
mpjpeRawM: rootRelativeError(pr, g, root),
|
|
228
|
-
mpjpeAlignedM: rootRelativeError(pa, g, root),
|
|
229
|
-
paMpjpeM: procrustesError(pr, g),
|
|
230
|
-
jointErrRawM: meanJointError(pr, g),
|
|
231
|
-
jointErrAlignedM: meanJointError(pa, g),
|
|
232
|
-
rootErrRawM: Math.hypot(pr[root][0] - g[root][0], pr[root][1] - g[root][1], pr[root][2] - g[root][2]),
|
|
233
|
-
rootErrAlignedM: Math.hypot(pa[root][0] - g[root][0], pa[root][1] - g[root][1], pa[root][2] - g[root][2]),
|
|
234
|
-
iouRaw: maskIoU(rawMask(k), gtMask(i)).iou,
|
|
235
|
-
iouAligned: maskIoU(alignedMask(k), gtMask(i)).iou,
|
|
236
|
-
contactPredRaw: raw.contact?.frames[k] ?? null,
|
|
237
|
-
contactPredAligned: aligned.contact?.frames[k] ?? null,
|
|
238
|
-
contactGt: gtRender?.contact?.frames[i] ?? null,
|
|
239
|
-
};
|
|
240
|
-
});
|
|
241
|
-
const gtRoot = pairs.map(([i]) => gt.joints.world[i][root]);
|
|
242
|
-
const rawRoot = pairs.map(([, k]) => raw.joints.world[k][root]);
|
|
243
|
-
const alignedRoot = pairs.map(([, k]) => aligned.joints.world[k][root]);
|
|
244
|
-
const ateAligned = trajectoryError(alignedRoot, gtRoot);
|
|
245
|
-
const ateRaw = trajectoryError(rawRoot, gtRoot);
|
|
246
|
-
const last = rows[rows.length - 1];
|
|
247
|
-
|
|
248
|
-
// npz-level facts: physical metrics (tools/ardy/mocap-metrics.mjs on the
|
|
249
|
-
// take as loaded, i.e. with the scale/offsetY/yaw baked in) and the
|
|
250
|
-
// frame-0 root offset the Studio's anchoring discards.
|
|
251
|
-
const predTake = await decodeFile(options.pred);
|
|
252
|
-
const predLoaded = isIdentityTransform(options.predTransform) ? predTake : transformTake(predTake, options.predTransform).motion;
|
|
253
|
-
const gtTake = gtNpz ? await decodeFile(gtNpz) : null;
|
|
254
|
-
const npzOffset = gtTake ? Math.hypot(predTake.posedJoints[0] - gtTake.posedJoints[0], predTake.posedJoints[2] - gtTake.posedJoints[2]) : null;
|
|
255
|
-
|
|
256
|
-
const score = {
|
|
257
|
-
tool: "tools/bench/score.mjs",
|
|
258
|
-
createdAt: new Date().toISOString(),
|
|
259
|
-
gt: { dir: gt.dir, files: gt.files, camera: gt.cameraPath, npz: gtNpz, frames: gt.joints.frames, fps: gt.joints.fps },
|
|
260
|
-
pred: { npz: options.pred, transform: options.predTransform, npzFrames: predTake.frames, npzFps: predTake.fps, timelineFrames: raw.joints.frames, timelineFps: raw.joints.fps },
|
|
261
|
-
timeline: {
|
|
262
|
-
mapping: "each GT frame -> nearest-in-time rendered pred frame (Studio timeline, pred retimed to 24 fps on load); GT frames past the pred's end are not scored",
|
|
263
|
-
scoredFrames: timeline.scored,
|
|
264
|
-
droppedGtFrames: timeline.dropped,
|
|
265
|
-
exact: timeline.exact,
|
|
266
|
-
frameCountMismatch: gt.joints.frames !== raw.joints.frames ? { gt: gt.joints.frames, pred: raw.joints.frames } : null,
|
|
267
|
-
fpsMismatch: gt.joints.fps !== raw.joints.fps || (gtTake && predTake.fps !== gtTake.fps) ? { gtTimeline: gt.joints.fps, predTimeline: raw.joints.fps, gtNpz: gtTake?.fps ?? null, predNpz: predTake.fps } : null,
|
|
268
|
-
},
|
|
269
|
-
joints: { count: gt.joints.joints.length, root: ROOT_JOINT, source: "rendered Studio rig bones (joints.json), scene metres" },
|
|
270
|
-
alignment: {
|
|
271
|
-
method: "least-squares yaw about +Y + XZ translation of the first scored frame's joints (pred raw -> GT); height untouched",
|
|
272
|
-
yawDeg: r6(alignment.yawDeg),
|
|
273
|
-
translationM: [r6(alignment.tx), 0, r6(alignment.tz)],
|
|
274
|
-
renderTransform: alignedTransform,
|
|
275
|
-
anchor,
|
|
276
|
-
},
|
|
277
|
-
pose: {
|
|
278
|
-
mpjpeRootRelativeRawM: r6(mean(rows.map((r) => r.mpjpeRawM))),
|
|
279
|
-
mpjpeRootRelativeAlignedM: r6(mean(rows.map((r) => r.mpjpeAlignedM))),
|
|
280
|
-
paMpjpeM: r6(mean(rows.map((r) => r.paMpjpeM))),
|
|
281
|
-
},
|
|
282
|
-
trajectory: {
|
|
283
|
-
root: ROOT_JOINT,
|
|
284
|
-
ateAlignedM: { rmse: r6(ateAligned.rmse), mean: r6(ateAligned.mean), max: r6(ateAligned.max), final: r6(ateAligned.final) },
|
|
285
|
-
rootErrorRawM: { rmse: r6(ateRaw.rmse), mean: r6(ateRaw.mean), max: r6(ateRaw.max), final: r6(ateRaw.final) },
|
|
286
|
-
pathLengthM: { gt: r6(pathLengthXZ(gtRoot)), pred: r6(pathLengthXZ(alignedRoot)) },
|
|
287
|
-
pathLengthRatio: r6(ratio(pathLengthXZ(alignedRoot), pathLengthXZ(gtRoot))),
|
|
288
|
-
displacementM: { gt: r6(displacementXZ(gtRoot)), pred: r6(displacementXZ(alignedRoot)) },
|
|
289
|
-
displacementRatio: r6(ratio(displacementXZ(alignedRoot), displacementXZ(gtRoot))),
|
|
290
|
-
},
|
|
291
|
-
endpoints: {
|
|
292
|
-
firstFrame: { gtFrame: rows[0].gtFrame, jointErrorRawM: r6(rows[0].jointErrRawM), jointErrorAlignedM: r6(rows[0].jointErrAlignedM) },
|
|
293
|
-
lastFrame: { gtFrame: last.gtFrame, jointErrorRawM: r6(last.jointErrRawM), jointErrorAlignedM: r6(last.jointErrAlignedM) },
|
|
294
|
-
},
|
|
295
|
-
physical: {
|
|
296
|
-
source: "tools/ardy/mocap-metrics.mjs on the npz posed_joints as loaded (native fps, npz units)",
|
|
297
|
-
pred: physical(predLoaded, options.pred),
|
|
298
|
-
gt: gtTake ? physical(gtTake, gtNpz) : null,
|
|
299
|
-
},
|
|
300
|
-
overlap: {
|
|
301
|
-
maskIoURawMean: r6(mean(rows.map((r) => r.iouRaw))),
|
|
302
|
-
maskIoUAlignedMean: r6(mean(rows.map((r) => r.iouAligned))),
|
|
303
|
-
maskIoUAlignedMin: r6(Math.min(...rows.map((r) => r.iouAligned))),
|
|
304
|
-
},
|
|
305
|
-
contact: options.box ? {
|
|
306
|
-
box: options.box,
|
|
307
|
-
basis: "skinned-vertices",
|
|
308
|
-
predRaw: contactSummary(raw.contact, pairs.map(([, k]) => k)),
|
|
309
|
-
predAligned: contactSummary(aligned.contact, pairs.map(([, k]) => k)),
|
|
310
|
-
gt: gtRender ? contactSummary(gtRender.contact, pairs.map(([i]) => i)) : null,
|
|
311
|
-
gtNote: gtRender ? "GT npz re-rendered through the GT camera with --box" : "no GT npz available (pass --gt-npz)",
|
|
312
|
-
} : null,
|
|
313
|
-
npz: {
|
|
314
|
-
frame0RootOffsetM: r6(npzOffset),
|
|
315
|
-
note: "XZ distance between the pred and GT npz frame-0 roots (npz units). The Studio anchors every take's frame 0 on the subject, so this offset does not reach the renders above.",
|
|
316
|
-
},
|
|
317
|
-
checks: {
|
|
318
|
-
alignedRenderVsRigidMaxM: r6(rigidCheck),
|
|
319
|
-
},
|
|
320
|
-
files: { csv: "frames.csv", renders: ["render-raw", "render-aligned", ...(gtRender ? ["render-gt"] : [])] },
|
|
321
|
-
};
|
|
322
|
-
writeFileSync(join(options.out, "score.json"), `${JSON.stringify(score, null, "\t")}\n`);
|
|
323
|
-
const header = ["gt_frame", "pred_frame", "time_s", "mpjpe_rr_raw_m", "mpjpe_rr_aligned_m", "pa_mpjpe_m", "joint_err_raw_m", "joint_err_aligned_m", "root_err_raw_m", "root_err_aligned_m", "iou_raw", "iou_aligned"];
|
|
324
|
-
if (options.box) header.push("box_signed_pred_raw_m", "box_signed_pred_aligned_m", "box_signed_gt_m");
|
|
325
|
-
const csv = [header.join(",")];
|
|
326
|
-
for (const r of rows) {
|
|
327
|
-
const cells = [r.gtFrame, r.predFrame, r.timeS.toFixed(4), r.mpjpeRawM, r.mpjpeAlignedM, r.paMpjpeM, r.jointErrRawM, r.jointErrAlignedM, r.rootErrRawM, r.rootErrAlignedM, r.iouRaw, r.iouAligned].map((v) => (typeof v === "number" && !Number.isInteger(v) ? v.toFixed(6) : String(v)));
|
|
328
|
-
if (options.box) cells.push(r.contactPredRaw?.signedDistanceM ?? "", r.contactPredAligned?.signedDistanceM ?? "", r.contactGt?.signedDistanceM ?? "");
|
|
329
|
-
csv.push(cells.join(","));
|
|
330
|
-
}
|
|
331
|
-
writeFileSync(join(options.out, "frames.csv"), `${csv.join("\n")}\n`);
|
|
332
|
-
console.log(`[score] MPJPE rr raw ${(score.pose.mpjpeRootRelativeRawM * 1000).toFixed(1)} mm, aligned ${(score.pose.mpjpeRootRelativeAlignedM * 1000).toFixed(1)} mm, PA ${(score.pose.paMpjpeM * 1000).toFixed(1)} mm; ATE ${score.trajectory.ateAlignedM.rmse.toFixed(4)} m; IoU raw ${score.overlap.maskIoURawMean.toFixed(4)} aligned ${score.overlap.maskIoUAlignedMean.toFixed(4)}`);
|
|
333
|
-
console.log(`[score] wrote ${join(options.out, "score.json")}`);
|
|
334
|
-
} finally {
|
|
335
|
-
removeSignals();
|
|
336
|
-
await terminateAll(children);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
main().catch((error) => {
|
|
341
|
-
console.error(error?.stack || String(error));
|
|
342
|
-
process.exitCode = 1;
|
|
343
|
-
});
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Collect tools/bench/score.mjs results into one table (#431).
|
|
4
|
-
*
|
|
5
|
-
* node tools/bench/summarize.mjs [--md <file>] [--csv <file>] <score dir ...>
|
|
6
|
-
*
|
|
7
|
-
* Every argument is a score.mjs --out directory (holding score.json). The
|
|
8
|
-
* markdown table is printed to stdout and, with --md, written to that file;
|
|
9
|
-
* --csv writes the same columns as CSV (full precision).
|
|
10
|
-
*/
|
|
11
|
-
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
12
|
-
import { basename, dirname, join, resolve } from "node:path";
|
|
13
|
-
import { parseArgs } from "node:util";
|
|
14
|
-
|
|
15
|
-
const mm = (m) => (m === null || m === undefined ? null : m * 1000);
|
|
16
|
-
const cm = (m) => (m === null || m === undefined ? null : m * 100);
|
|
17
|
-
const resultFor = (dir) => {
|
|
18
|
-
const file = join(dirname(resolve(dir)), "result.json");
|
|
19
|
-
return existsSync(file) ? JSON.parse(readFileSync(file, "utf8")) : null;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
/** [header, unit note, value(score), markdown digits] */
|
|
23
|
-
const COLUMNS = [
|
|
24
|
-
["case", "", (s, dir) => basename(dir), null],
|
|
25
|
-
["frames", "scored/GT", (s) => `${s.timeline.scoredFrames}/${s.gt.frames}`, null],
|
|
26
|
-
["MPJPE raw", "mm, root-relative", (s) => mm(s.pose.mpjpeRootRelativeRawM), 1],
|
|
27
|
-
["MPJPE aligned", "mm, root-relative", (s) => mm(s.pose.mpjpeRootRelativeAlignedM), 1],
|
|
28
|
-
["PA-MPJPE", "mm", (s) => mm(s.pose.paMpjpeM), 1],
|
|
29
|
-
["align yaw", "deg", (s) => s.alignment.yawDeg, 2],
|
|
30
|
-
["align shift", "m (XZ)", (s) => Math.hypot(s.alignment.translationM[0], s.alignment.translationM[2]), 3],
|
|
31
|
-
["root err raw", "m RMSE", (s) => s.trajectory.rootErrorRawM.rmse, 3],
|
|
32
|
-
["ATE aligned", "m RMSE", (s) => s.trajectory.ateAlignedM.rmse, 3],
|
|
33
|
-
["path ratio", "pred/GT", (s) => s.trajectory.pathLengthRatio, 3],
|
|
34
|
-
["disp ratio", "pred/GT", (s) => s.trajectory.displacementRatio, 3],
|
|
35
|
-
["first err aligned", "m", (s) => s.endpoints.firstFrame.jointErrorAlignedM, 3],
|
|
36
|
-
["last err raw", "m", (s) => s.endpoints.lastFrame.jointErrorRawM, 3],
|
|
37
|
-
["last err aligned", "m", (s) => s.endpoints.lastFrame.jointErrorAlignedM, 3],
|
|
38
|
-
["IoU raw", "mean", (s) => s.overlap.maskIoURawMean, 4],
|
|
39
|
-
["IoU aligned", "mean", (s) => s.overlap.maskIoUAlignedMean, 4],
|
|
40
|
-
["foot slide pred", "cm/s", (s) => s.physical.pred.footSlideCmPerS, 2],
|
|
41
|
-
["foot slide GT", "cm/s", (s) => s.physical.gt?.footSlideCmPerS, 2],
|
|
42
|
-
["jitter pred", "mm/s^2", (s) => s.physical.pred.jitterMmPerS2, 0],
|
|
43
|
-
["jitter GT", "mm/s^2", (s) => s.physical.gt?.jitterMmPerS2, 0],
|
|
44
|
-
["below floor pred", "frames", (s) => s.physical.pred.framesBelowFloor, null],
|
|
45
|
-
["box min dist pred", "cm, aligned", (s) => cm(s.contact?.predAligned?.minDistanceM), 1],
|
|
46
|
-
["box min dist GT", "cm", (s) => cm(s.contact?.gt?.minDistanceM), 1],
|
|
47
|
-
["box penetration pred", "cm, aligned", (s) => cm(s.contact?.predAligned?.maxPenetrationM), 1],
|
|
48
|
-
["box penetration GT", "cm", (s) => cm(s.contact?.gt?.maxPenetrationM), 1],
|
|
49
|
-
["npz frame-0 offset", "m", (s) => s.npz.frame0RootOffsetM, 3],
|
|
50
|
-
["runtimeS", "s", (s, dir) => resultFor(dir)?.runtimeS, 2],
|
|
51
|
-
["peakVramMB", "MB", (s, dir) => resultFor(dir)?.peakVramMB, 0],
|
|
52
|
-
["LR switches", "count", (s, dir) => resultFor(dir)?.lrSwitches, null],
|
|
53
|
-
["occluded frames", "frames", (s, dir) => resultFor(dir)?.occludedFrames, null],
|
|
54
|
-
];
|
|
55
|
-
|
|
56
|
-
function main() {
|
|
57
|
-
const { values, positionals } = parseArgs({ args: process.argv.slice(2), allowPositionals: true, options: { md: { type: "string" }, csv: { type: "string" }, help: { type: "boolean", short: "h", default: false } } });
|
|
58
|
-
if (values.help || !positionals.length) {
|
|
59
|
-
console.log("usage: node tools/bench/summarize.mjs [--md <file>] [--csv <file>] <score dir ...>");
|
|
60
|
-
process.exit(values.help ? 0 : 2);
|
|
61
|
-
}
|
|
62
|
-
const rows = positionals.map((dir) => {
|
|
63
|
-
const file = join(resolve(dir), "score.json");
|
|
64
|
-
if (!existsSync(file)) throw new Error(`${dir}: no score.json`);
|
|
65
|
-
const score = JSON.parse(readFileSync(file, "utf8"));
|
|
66
|
-
return COLUMNS.map(([, , value]) => value(score, resolve(dir)));
|
|
67
|
-
});
|
|
68
|
-
const show = (value, digits) => (value === null || value === undefined ? "-" : typeof value === "number" && digits !== null ? value.toFixed(digits) : String(value));
|
|
69
|
-
const md = [
|
|
70
|
-
`| ${COLUMNS.map(([name, unit]) => (unit ? `${name} (${unit})` : name)).join(" | ")} |`,
|
|
71
|
-
`|${COLUMNS.map((_, i) => (i === 0 ? " --- " : " ---: ")).join("|")}|`,
|
|
72
|
-
...rows.map((row) => `| ${row.map((value, i) => show(value, COLUMNS[i][3])).join(" | ")} |`),
|
|
73
|
-
].join("\n");
|
|
74
|
-
console.log(md);
|
|
75
|
-
if (values.md) writeFileSync(values.md, `${md}\n`);
|
|
76
|
-
if (values.csv) {
|
|
77
|
-
const quote = (v) => {
|
|
78
|
-
const text = v === null || v === undefined ? "" : String(v);
|
|
79
|
-
return /[",\n]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
|
|
80
|
-
};
|
|
81
|
-
const csv = [COLUMNS.map(([name, unit]) => quote(unit ? `${name} (${unit})` : name)).join(","), ...rows.map((row) => row.map(quote).join(","))];
|
|
82
|
-
writeFileSync(values.csv, `${csv.join("\n")}\n`);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
main();
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
# CozyClay demo GPU worker
|
|
2
|
-
|
|
3
|
-
The demo worker is an outbound-only poller. It never calls `listen()` and does
|
|
4
|
-
not expose an HTTP, TCP, or WebSocket service. It claims one job, runs the
|
|
5
|
-
existing local ARDY runner, uploads the resulting `.npz`, and renews the
|
|
6
|
-
job-bound lease every 60 seconds while generation is running.
|
|
7
|
-
|
|
8
|
-
`index.mjs` owns polling/signals, `api-client.mjs` owns signed fetches,
|
|
9
|
-
`generate.mjs` supervises the local runner, `sign-core.mjs` is the pure HMAC
|
|
10
|
-
module, and `sign.mjs` is its curl-oriented CLI wrapper. The poller waits for
|
|
11
|
-
the local generation worker's warm ping before every claim; an idle/cold worker
|
|
12
|
-
is re-warmed before polling resumes.
|
|
13
|
-
|
|
14
|
-
## Install and configure
|
|
15
|
-
|
|
16
|
-
Install CozyClay and the local ARDY environment on the GPU box, then create an
|
|
17
|
-
environment file outside the repository:
|
|
18
|
-
|
|
19
|
-
```sh
|
|
20
|
-
mkdir -p ~/.cozyclay
|
|
21
|
-
umask 077
|
|
22
|
-
cat >~/.cozyclay/demo-worker.env <<'EOF'
|
|
23
|
-
CC_DEMO_API_BASE=https://api.cozyclay.org
|
|
24
|
-
CC_WORKER_ID=gpu-box-01
|
|
25
|
-
CC_WORKER_SECRET=replace-with-the-value-from-wrangler-secret
|
|
26
|
-
EOF
|
|
27
|
-
chmod 600 ~/.cozyclay/demo-worker.env
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
The three values above are read from the environment only. Do not commit the
|
|
31
|
-
file, put a secret in a command argument, or print request headers. Worker
|
|
32
|
-
logs report job ids and status, never `CC_WORKER_SECRET`, signatures, or lease
|
|
33
|
-
tokens.
|
|
34
|
-
|
|
35
|
-
Run it directly:
|
|
36
|
-
|
|
37
|
-
```sh
|
|
38
|
-
set -a
|
|
39
|
-
. ~/.cozyclay/demo-worker.env
|
|
40
|
-
set +a
|
|
41
|
-
node tools/demo-worker/index.mjs
|
|
42
|
-
# equivalent package script:
|
|
43
|
-
npm run demo:worker
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
Run the signing helper for a curl request (the secret is still taken only from
|
|
47
|
-
the environment):
|
|
48
|
-
|
|
49
|
-
```sh
|
|
50
|
-
CC_WORKER_SECRET=... CC_WORKER_ID=gpu-box-01 \
|
|
51
|
-
node tools/demo-worker/sign.mjs --method GET --path /worker/next
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## launchd (macOS)
|
|
55
|
-
|
|
56
|
-
Create `~/Library/LaunchAgents/org.cozyclay.demo-worker.plist` with paths
|
|
57
|
-
matching the installation:
|
|
58
|
-
|
|
59
|
-
```xml
|
|
60
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
61
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
|
62
|
-
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
63
|
-
<plist version="1.0"><dict>
|
|
64
|
-
<key>Label</key><string>org.cozyclay.demo-worker</string>
|
|
65
|
-
<key>ProgramArguments</key><array>
|
|
66
|
-
<string>/usr/local/bin/node</string>
|
|
67
|
-
<string>/path/to/CozyClay/tools/demo-worker/index.mjs</string>
|
|
68
|
-
</array>
|
|
69
|
-
<key>EnvironmentVariables</key><dict>
|
|
70
|
-
<key>CC_DEMO_API_BASE</key><string>https://api.cozyclay.org</string>
|
|
71
|
-
<key>CC_WORKER_ID</key><string>gpu-box-01</string>
|
|
72
|
-
<key>CC_WORKER_SECRET</key><string>read-from-a-protected-generated-plist</string>
|
|
73
|
-
</dict>
|
|
74
|
-
<key>RunAtLoad</key><true/>
|
|
75
|
-
<key>KeepAlive</key><true/>
|
|
76
|
-
<key>StandardOutPath</key><string>/var/tmp/cozyclay-demo-worker.log</string>
|
|
77
|
-
<key>StandardErrorPath</key><string>/var/tmp/cozyclay-demo-worker.err</string>
|
|
78
|
-
</dict></plist>
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
Prefer a generated plist readable only by the service account; do not leave
|
|
82
|
-
the real secret in a world-readable file. Load it with:
|
|
83
|
-
|
|
84
|
-
```sh
|
|
85
|
-
chmod 600 ~/Library/LaunchAgents/org.cozyclay.demo-worker.plist
|
|
86
|
-
launchctl bootstrap "gui/$(id -u)" ~/Library/LaunchAgents/org.cozyclay.demo-worker.plist
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
## systemd (Linux)
|
|
90
|
-
|
|
91
|
-
Store the env file at `/etc/cozyclay/demo-worker.env` with mode `0600`, then
|
|
92
|
-
install `/etc/systemd/system/cozyclay-demo-worker.service`:
|
|
93
|
-
|
|
94
|
-
```ini
|
|
95
|
-
[Unit]
|
|
96
|
-
Description=CozyClay outbound demo GPU worker
|
|
97
|
-
After=network-online.target
|
|
98
|
-
Wants=network-online.target
|
|
99
|
-
|
|
100
|
-
[Service]
|
|
101
|
-
Type=simple
|
|
102
|
-
WorkingDirectory=/opt/CozyClay
|
|
103
|
-
EnvironmentFile=/etc/cozyclay/demo-worker.env
|
|
104
|
-
ExecStart=/usr/bin/node /opt/CozyClay/tools/demo-worker/index.mjs
|
|
105
|
-
Restart=always
|
|
106
|
-
RestartSec=5
|
|
107
|
-
NoNewPrivileges=true
|
|
108
|
-
|
|
109
|
-
[Install]
|
|
110
|
-
WantedBy=multi-user.target
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
```sh
|
|
114
|
-
systemctl daemon-reload
|
|
115
|
-
systemctl enable --now cozyclay-demo-worker
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
## Verify inbound surface
|
|
119
|
-
|
|
120
|
-
Capture listeners before and after starting the worker. The diff should be
|
|
121
|
-
empty; an API connection in `ESTABLISHED` state is outbound and is not a
|
|
122
|
-
listener:
|
|
123
|
-
|
|
124
|
-
```sh
|
|
125
|
-
lsof -nP -iTCP -sTCP:LISTEN | sort > /tmp/cc-listen-before.txt
|
|
126
|
-
set -a; . ~/.cozyclay/demo-worker.env; set +a
|
|
127
|
-
npm run demo:worker &
|
|
128
|
-
worker_pid=$!
|
|
129
|
-
sleep 5
|
|
130
|
-
lsof -nP -iTCP -sTCP:LISTEN | sort > /tmp/cc-listen-after.txt
|
|
131
|
-
diff /tmp/cc-listen-before.txt /tmp/cc-listen-after.txt
|
|
132
|
-
kill -TERM "$worker_pid"
|
|
133
|
-
wait "$worker_pid" || true
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
If the diff is non-empty, stop the process and investigate before deployment.
|
|
137
|
-
|