topview-3d-cli 0.1.0__py3-none-any.whl
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.
- topview_3d_cli/__init__.py +3 -0
- topview_3d_cli/__main__.py +5 -0
- topview_3d_cli/_runtime/builtin-assets/LICENSE +396 -0
- topview_3d_cli/_runtime/builtin-assets/NOTICE +24 -0
- topview_3d_cli/_runtime/builtin-assets/characters/a3d_char_30ec53c93eae4173946b38639691ebe0.glb +0 -0
- topview_3d_cli/_runtime/builtin-assets/characters/a3d_char_461b4f4b76ae4b198f439fa4f6f743b6.glb +0 -0
- topview_3d_cli/_runtime/builtin-assets/characters/a3d_char_7d220ac359844c80a8faaf0e25478477.glb +0 -0
- topview_3d_cli/_runtime/builtin-assets/characters/a3d_char_de37873a79244fbe8a0660ea08d1b4ab.glb +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-attack-jab.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-attack-lunge.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-attack-rear.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-crawling-forward-reaching-with-le.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-exaggerated-punch-with-right-hand.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-examine-fwd.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-holding-bow.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-jab-with-right-hand.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-on-hands-and-toes-right-leg-forwa.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-on-right-leg.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-push-up.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-pushing-backwards.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-pushing-with-both-hands.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-pushing-with-left-side-of-body.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-sliding.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-arms-spread-looking-upwards.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-examine-fwd.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-lean-fwd-right-hand-supporting.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-leaning-against-object-look-l.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-look-back.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-look-down-back.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-look-fwd-f.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-look-fwd-m.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-on-knees-arms-up.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-sneaking.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dance-l-toe-top-of-right-foot.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dance-legs-spread-lean-l.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dance-legs-spread-lean-r.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dance-legs-x.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dance-r-foot-arms-extended-backwards.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dance-r-toe-arms-up.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dance-r-toe-l-leg-up.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dance-toes-arch.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dance-toes-reach.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dyn-floating-bent-forwards.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dyn-floating-left-leg-bent-hands-spread.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dyn-floating-legs-bent-and-arms-spread.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dyn-floating-legs-raised-arms-extended-u.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dyn-l-toe-look-l.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dyn-leaping-forward-arms-extended-backwa.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dynamic-float-arch.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dynamic-float-dive.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-back-arched-arms-supp.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-legs-to-side-arms-up.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-legs-x-arms-supp.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-on-back-left-leg-bent-hands-head.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-on-back-left-leg-bent.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-on-back-legs-x-hands-head.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-on-left-side-left-arm-supporting-hea.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-on-left-side-left-arm-supporting.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-on-right-side-right-arm-supporting.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-on-right-side-right-hand-supporting.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-on-stomach-elbows-supporting.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-right-arm-supporting-l-arm-up.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-right-leg-forward-arms-supp.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-run-fwd.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-run-reach.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-sneaking-forward.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-sprinting.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-stepping-forward-look-l.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-walk-fwd-look-r.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-walk-fwd.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-walking-arms-on-hips.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-walking-right-arm-on-hip.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-walking.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-arms-holding-left-knee-reclining.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-arms-holding-right-knee.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-arms-resting-on-knees.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-arms-supp.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-arms-up.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-elbows-on-legs-lean-fwd.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-elbows-on-legs-lean-l.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-knees-up-hold.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-lean-back-arms-supp.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-lean-fwd-elbows-on-knees.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-lean-fwd-hands-in-front.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-legs-x-arms-x.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-legs-x-hands-head.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-legs-x-hold.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-legs-x-reclining.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-legs-x-right-arm-over-knee.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-reaching-hands-towards-toes.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-reaching-left-hand-towards-right-toe.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-reclined-facepalm.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-reclined-hands-head.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-reclined-left-leg-crossed-hands-head.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-reclined-left-leg-crossed.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-reclined-look-down.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-reclined-right-leg-crossed-hands-hea.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-rev-sit-hands-grasping.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-rev-sit-hands-relaxed.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-arms-behind-back.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-arms-down.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-arms-slightly-spread.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-bending-forward-arms-x-over-knees.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-hands-on-hips-look-back.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-l-hip-f.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-l-hip-m.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-l-leg-r-hip-l-arm-up.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-lean-back-arms-behind.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-lean-back-l-hip.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-left-elbow-resting-obj.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-left-leg-obj-left-hand-on-knee-r-h.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-left-leg-obj-left-hand-on-knee.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-legs-x-arms-up.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-legs-x-l-hip.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-legs-x-leaning-obj.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-legs-x-left-arm-obj-r-hip.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-look-down-r.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-look-fwd.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-look-l.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-look-r.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-r-arm-up-l-hip.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-r-foot-l-leg-up.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-r-foot-up.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-r-hip-l-arm-up.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-r-hip.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-right-hand-behind-head.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-right-leg-obj-arms-x.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-right-leg-obj-l-hip-right-hand-on.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_t-pose.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/manifest.json +2372 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-attack-jab.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-attack-lunge.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-attack-rear.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-crawling-forward-reaching-with-le.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-exaggerated-punch-with-right-hand.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-examine-fwd.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-holding-bow.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-jab-with-right-hand.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-on-hands-and-toes-right-leg-forwa.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-on-right-leg.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-push-up.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-pushing-backwards.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-pushing-with-both-hands.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-pushing-with-left-side-of-body.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-sliding.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-arms-spread-looking-upwards.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-examine-fwd.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-lean-fwd-right-hand-supporting.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-leaning-against-object-look-l.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-look-back.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-look-down-back.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-look-fwd-f.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-look-fwd-m.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-on-knees-arms-up.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-sneaking.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dance-l-toe-top-of-right-foot.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dance-legs-spread-lean-l.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dance-legs-spread-lean-r.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dance-legs-x.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dance-r-foot-arms-extended-backwards.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dance-r-toe-arms-up.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dance-r-toe-l-leg-up.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dance-toes-arch.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dance-toes-reach.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dyn-floating-bent-forwards.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dyn-floating-left-leg-bent-hands-spread.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dyn-floating-legs-bent-and-arms-spread.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dyn-floating-legs-raised-arms-extended-u.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dyn-l-toe-look-l.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dyn-leaping-forward-arms-extended-backwa.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dynamic-float-arch.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dynamic-float-dive.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-back-arched-arms-supp.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-legs-to-side-arms-up.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-legs-x-arms-supp.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-on-back-left-leg-bent-hands-head.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-on-back-left-leg-bent.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-on-back-legs-x-hands-head.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-on-left-side-left-arm-supporting-hea.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-on-left-side-left-arm-supporting.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-on-right-side-right-arm-supporting.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-on-right-side-right-hand-supporting.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-on-stomach-elbows-supporting.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-right-arm-supporting-l-arm-up.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-right-leg-forward-arms-supp.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-run-fwd.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-run-reach.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-sneaking-forward.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-sprinting.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-stepping-forward-look-l.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-walk-fwd-look-r.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-walk-fwd.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-walking-arms-on-hips.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-walking-right-arm-on-hip.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-walking.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-arms-holding-left-knee-reclining.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-arms-holding-right-knee.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-arms-resting-on-knees.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-arms-supp.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-arms-up.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-elbows-on-legs-lean-fwd.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-elbows-on-legs-lean-l.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-knees-up-hold.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-lean-back-arms-supp.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-lean-fwd-elbows-on-knees.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-lean-fwd-hands-in-front.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-legs-x-arms-x.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-legs-x-hands-head.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-legs-x-hold.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-legs-x-reclining.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-legs-x-right-arm-over-knee.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-reaching-hands-towards-toes.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-reaching-left-hand-towards-right-toe.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-reclined-facepalm.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-reclined-hands-head.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-reclined-left-leg-crossed-hands-head.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-reclined-left-leg-crossed.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-reclined-look-down.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-reclined-right-leg-crossed-hands-hea.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-rev-sit-hands-grasping.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-rev-sit-hands-relaxed.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-arms-behind-back.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-arms-down.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-arms-slightly-spread.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-bending-forward-arms-x-over-knees.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-hands-on-hips-look-back.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-l-hip-f.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-l-hip-m.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-l-leg-r-hip-l-arm-up.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-lean-back-arms-behind.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-lean-back-l-hip.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-left-elbow-resting-obj.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-left-leg-obj-left-hand-on-knee-r-h.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-left-leg-obj-left-hand-on-knee.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-legs-x-arms-up.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-legs-x-l-hip.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-legs-x-leaning-obj.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-legs-x-left-arm-obj-r-hip.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-look-down-r.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-look-fwd.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-look-l.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-look-r.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-r-arm-up-l-hip.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-r-foot-l-leg-up.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-r-foot-up.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-r-hip-l-arm-up.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-r-hip.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-right-hand-behind-head.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-right-leg-obj-arms-x.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-right-leg-obj-l-hip-right-hand-on.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_t-pose.json +1 -0
- topview_3d_cli/_runtime/director-cli/applyIntent.mjs +35 -0
- topview_3d_cli/_runtime/director-cli/browser.mjs +47 -0
- topview_3d_cli/_runtime/director-cli/cli.mjs +56 -0
- topview_3d_cli/_runtime/director-cli/editSequence.mjs +125 -0
- topview_3d_cli/_runtime/director-cli/evaluate.mjs +254 -0
- topview_3d_cli/_runtime/director-cli/mediaUrl.mjs +47 -0
- topview_3d_cli/_runtime/director-cli/node_modules/@topview/3d-builder/dist/draco/draco_decoder.js +34 -0
- topview_3d_cli/_runtime/director-cli/node_modules/@topview/3d-builder/dist/draco/draco_decoder.wasm +0 -0
- topview_3d_cli/_runtime/director-cli/node_modules/@topview/3d-builder/dist/draco/draco_wasm_wrapper.js +117 -0
- topview_3d_cli/_runtime/director-cli/node_modules/@topview/3d-builder/dist/engine/index.mjs +22219 -0
- topview_3d_cli/_runtime/director-cli/node_modules/@topview/3d-builder/dist/evaluate/index.mjs +4229 -0
- topview_3d_cli/_runtime/director-cli/node_modules/@topview/3d-builder/dist/headless/index.mjs +22832 -0
- topview_3d_cli/_runtime/director-cli/node_modules/@topview/3d-builder/package.json +75 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/LICENSE +21 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/build/three.core.js +59732 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/build/three.module.js +19552 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/controls/OrbitControls.js +1963 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/curves/NURBSCurve.js +155 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/curves/NURBSUtils.js +532 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/libs/draco/draco_decoder.js +34 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/libs/draco/draco_decoder.wasm +0 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/libs/draco/draco_wasm_wrapper.js +117 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/libs/fflate.module.js +2672 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/lines/LineMaterial.js +703 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/lines/LineSegments2.js +426 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/lines/LineSegmentsGeometry.js +298 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/loaders/DRACOLoader.js +739 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/loaders/FBXLoader.js +4580 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/loaders/GLTFLoader.js +4860 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/utils/BufferGeometryUtils.js +1434 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/utils/SkeletonUtils.js +496 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/package.json +123 -0
- topview_3d_cli/_runtime/director-cli/node_modules/zod/LICENSE +21 -0
- topview_3d_cli/_runtime/director-cli/node_modules/zod/index.js +4 -0
- topview_3d_cli/_runtime/director-cli/node_modules/zod/package.json +118 -0
- topview_3d_cli/_runtime/director-cli/node_modules/zod/v3/ZodError.js +133 -0
- topview_3d_cli/_runtime/director-cli/node_modules/zod/v3/errors.js +9 -0
- topview_3d_cli/_runtime/director-cli/node_modules/zod/v3/external.js +6 -0
- topview_3d_cli/_runtime/director-cli/node_modules/zod/v3/helpers/errorUtil.js +6 -0
- topview_3d_cli/_runtime/director-cli/node_modules/zod/v3/helpers/parseUtil.js +109 -0
- topview_3d_cli/_runtime/director-cli/node_modules/zod/v3/helpers/typeAliases.js +1 -0
- topview_3d_cli/_runtime/director-cli/node_modules/zod/v3/helpers/util.js +133 -0
- topview_3d_cli/_runtime/director-cli/node_modules/zod/v3/locales/en.js +109 -0
- topview_3d_cli/_runtime/director-cli/node_modules/zod/v3/types.js +3693 -0
- topview_3d_cli/_runtime/director-cli/package.json +6 -0
- topview_3d_cli/_runtime/director-cli/playwright.mjs +26 -0
- topview_3d_cli/_runtime/director-cli/render.mjs +336 -0
- topview_3d_cli/_runtime/director-cli/static/headless.html +124 -0
- topview_3d_cli/_runtime/director-cli/static/measure.mjs +61 -0
- topview_3d_cli/_runtime/director-cli/static/pose.mjs +71 -0
- topview_3d_cli/_runtime/director-cli/staticIntent.mjs +135 -0
- topview_3d_cli/_runtime/runtime.json +6 -0
- topview_3d_cli/bom.py +326 -0
- topview_3d_cli/contracts/director-v1/schema/director-document.schema.json +1097 -0
- topview_3d_cli/contracts/director-v1/schema/fcurves-compact-v1.schema.json +105 -0
- topview_3d_cli/director_camera_view.py +54 -0
- topview_3d_cli/director_document.py +793 -0
- topview_3d_cli/director_geometry.py +66 -0
- topview_3d_cli/director_operations.py +679 -0
- topview_3d_cli/director_static.py +134 -0
- topview_3d_cli/local_assets.py +248 -0
- topview_3d_cli/local_bom.py +108 -0
- topview_3d_cli/local_catalog.py +156 -0
- topview_3d_cli/local_cli.py +493 -0
- topview_3d_cli/local_edit.py +350 -0
- topview_3d_cli/local_errors.py +104 -0
- topview_3d_cli/local_inspect.py +302 -0
- topview_3d_cli/local_project.py +280 -0
- topview_3d_cli/local_read.py +139 -0
- topview_3d_cli/local_render.py +97 -0
- topview_3d_cli/node_batch.py +299 -0
- topview_3d_cli/renderer.py +75 -0
- topview_3d_cli/runtime.py +107 -0
- topview_3d_cli-0.1.0.dist-info/METADATA +48 -0
- topview_3d_cli-0.1.0.dist-info/RECORD +332 -0
- topview_3d_cli-0.1.0.dist-info/WHEEL +5 -0
- topview_3d_cli-0.1.0.dist-info/entry_points.txt +2 -0
- topview_3d_cli-0.1.0.dist-info/licenses/LICENSE +202 -0
- topview_3d_cli-0.1.0.dist-info/licenses/LICENSE-ASSETS +396 -0
- topview_3d_cli-0.1.0.dist-info/licenses/NOTICE +18 -0
- topview_3d_cli-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
"""Command line boundary for local Scene3D projects (contract: docs/topview-3d-cli.md)."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import re
|
|
8
|
+
import shutil
|
|
9
|
+
import subprocess
|
|
10
|
+
import sys
|
|
11
|
+
import uuid
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any, NoReturn
|
|
14
|
+
|
|
15
|
+
from topview_3d_cli.director_document import SCENE3D_OP_ID, new_scene_operation_id
|
|
16
|
+
from topview_3d_cli.director_static import delete_node_operations
|
|
17
|
+
from topview_3d_cli.local_assets import asset_catalog, builtin_asset_root, import_asset, public_asset_entry
|
|
18
|
+
from topview_3d_cli.local_bom import bom_checkpoint, bom_get
|
|
19
|
+
from topview_3d_cli.local_catalog import asset_search, asset_show, pose_catalog
|
|
20
|
+
from topview_3d_cli.local_edit import load_spec, node_batch, pose_batch
|
|
21
|
+
from topview_3d_cli.local_errors import EXIT_INVALID_INPUT, EXIT_OK, exit_code_for
|
|
22
|
+
from topview_3d_cli.local_project import (
|
|
23
|
+
CLI_VERSION,
|
|
24
|
+
LocalProjectError,
|
|
25
|
+
apply_operations,
|
|
26
|
+
init_project,
|
|
27
|
+
open_project,
|
|
28
|
+
project_status,
|
|
29
|
+
validate_project_document,
|
|
30
|
+
)
|
|
31
|
+
from topview_3d_cli.local_inspect import evaluate, inspect_nodes, inspect_views, parse_frames
|
|
32
|
+
from topview_3d_cli.local_read import camera_presets, document_view, renders_list, renders_show
|
|
33
|
+
from topview_3d_cli.local_render import render
|
|
34
|
+
from topview_3d_cli.renderer import node_path
|
|
35
|
+
from topview_3d_cli.runtime import PLAYWRIGHT_VERSION, builder_version, node_env, runtime
|
|
36
|
+
|
|
37
|
+
MIN_NODE = (20, 6)
|
|
38
|
+
BUILDER_DIST_FILES = ("evaluate/index.mjs", "engine/index.mjs", "headless/index.mjs", "draco/draco_decoder.wasm")
|
|
39
|
+
KINDS = ("character", "prop", "pose", "primitive")
|
|
40
|
+
RUNTIME_FILES = ("cli.mjs", "render.mjs", "playwright.mjs", "static/headless.html")
|
|
41
|
+
_ERROR_PREFIX = re.compile(r"^([A-Z][A-Z0-9_]+)(?::|$)")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _load_operations(path: str) -> dict[str, Any]:
|
|
45
|
+
try:
|
|
46
|
+
text = sys.stdin.read() if path == "-" else Path(path).read_text(encoding="utf-8")
|
|
47
|
+
value = json.loads(text)
|
|
48
|
+
except FileNotFoundError as exc:
|
|
49
|
+
raise LocalProjectError("OPERATIONS_NOT_FOUND", str(path)) from exc
|
|
50
|
+
except json.JSONDecodeError as exc:
|
|
51
|
+
raise LocalProjectError("OPERATIONS_JSON_INVALID", exc.msg) from exc
|
|
52
|
+
if isinstance(value, list):
|
|
53
|
+
value = {"operations": value}
|
|
54
|
+
if not isinstance(value, dict) or not isinstance(value.get("operations"), list):
|
|
55
|
+
raise LocalProjectError("OPERATIONS_JSON_INVALID", "expected an operation array or {\"operations\": [...]}")
|
|
56
|
+
if value.get("schemaVersion", 2) != 2:
|
|
57
|
+
raise LocalProjectError("OPERATIONS_JSON_INVALID", "schemaVersion must be 2")
|
|
58
|
+
expected = value.get("expectedSceneSequence")
|
|
59
|
+
if expected is not None and (type(expected) is not int or expected < 0):
|
|
60
|
+
raise LocalProjectError("OPERATIONS_JSON_INVALID", "expectedSceneSequence must be a non-negative integer")
|
|
61
|
+
batch_id = value.get("batchId")
|
|
62
|
+
if batch_id is not None and (not isinstance(batch_id, str) or not SCENE3D_OP_ID.match(batch_id)):
|
|
63
|
+
raise LocalProjectError("OPERATIONS_JSON_INVALID", "batchId must match [A-Za-z0-9_-]{1,128}")
|
|
64
|
+
for operation in value["operations"]:
|
|
65
|
+
if isinstance(operation, dict) and "operationId" not in operation:
|
|
66
|
+
operation["operationId"] = new_scene_operation_id("cli")
|
|
67
|
+
return value
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def document_apply(directory: str, operations_path: str, *, strict: bool, dry_run: bool) -> dict[str, Any]:
|
|
71
|
+
batch = _load_operations(operations_path)
|
|
72
|
+
return apply_operations(directory, batch["operations"], strict=strict,
|
|
73
|
+
batch_id=batch.get("batchId") or f"cli_{uuid.uuid4().hex[:10]}", dry_run=dry_run,
|
|
74
|
+
expected_sequence=batch.get("expectedSceneSequence"))
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def node_delete(directory: str, node_id: str, *, dry_run: bool) -> dict[str, Any]:
|
|
78
|
+
"""Delete one node plus the clips/fcurves/references that point at it, in one batch."""
|
|
79
|
+
project = open_project(directory)
|
|
80
|
+
try:
|
|
81
|
+
batch, summary = delete_node_operations(project.store.assemble(), node_id)
|
|
82
|
+
except ValueError as exc:
|
|
83
|
+
match = _ERROR_PREFIX.match(str(exc))
|
|
84
|
+
raise LocalProjectError(match.group(1) if match else "INTERNAL_ERROR", str(exc)) from exc
|
|
85
|
+
result = apply_operations(directory, batch["operations"], strict=True, batch_id=batch["batchId"], dry_run=dry_run)
|
|
86
|
+
return {**result, **summary}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _version_tuple(text: str) -> tuple[int, ...]:
|
|
90
|
+
return tuple(int(part) for part in re.findall(r"\d+", text)[:3])
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _run(command: list[str], *, cwd: Path | None = None, env: dict[str, str] | None = None,
|
|
94
|
+
timeout: int = 30) -> subprocess.CompletedProcess[str] | None:
|
|
95
|
+
try:
|
|
96
|
+
return subprocess.run(command, cwd=cwd, env=env, check=False, capture_output=True, text=True,
|
|
97
|
+
timeout=timeout)
|
|
98
|
+
except (OSError, subprocess.TimeoutExpired):
|
|
99
|
+
return None
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _check_node() -> dict[str, Any]:
|
|
103
|
+
node = shutil.which("node")
|
|
104
|
+
completed = _run([node, "--version"]) if node else None
|
|
105
|
+
version = completed.stdout.strip() if completed and completed.returncode == 0 else None
|
|
106
|
+
ok = bool(version) and _version_tuple(version) >= MIN_NODE
|
|
107
|
+
check: dict[str, Any] = {"ok": ok, "path": node, "version": version}
|
|
108
|
+
if not ok:
|
|
109
|
+
check["hint"] = "Install Node.js 20.6 or newer (https://nodejs.org/); npm ships with it."
|
|
110
|
+
return check
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def doctor() -> dict[str, Any]:
|
|
114
|
+
"""Every check has ``ok``; failing checks carry a ``hint``."""
|
|
115
|
+
checks: dict[str, dict[str, Any]] = {}
|
|
116
|
+
python_ok = sys.version_info >= (3, 11)
|
|
117
|
+
checks["python"] = {"ok": python_ok, "version": sys.version.split()[0], "executable": sys.executable}
|
|
118
|
+
if not python_ok:
|
|
119
|
+
checks["python"]["hint"] = "Install Python 3.11 or newer."
|
|
120
|
+
checks["node"] = _check_node()
|
|
121
|
+
|
|
122
|
+
try:
|
|
123
|
+
current = runtime()
|
|
124
|
+
except LocalProjectError as exc:
|
|
125
|
+
checks["runtime"] = {"ok": False, "code": exc.code, "error": str(exc),
|
|
126
|
+
"hint": "Reinstall topview-3d-cli (pip install --force-reinstall topview-3d-cli)."}
|
|
127
|
+
return {"ok": False, "cliVersion": CLI_VERSION, "checks": checks}
|
|
128
|
+
missing = [name for name in RUNTIME_FILES if not (current.director_cli / name).is_file()]
|
|
129
|
+
missing += [f"builder/{name}" for name in BUILDER_DIST_FILES if not (current.builder_dist / name).is_file()]
|
|
130
|
+
checks["runtime"] = {"ok": not missing, "mode": current.mode, "root": str(current.director_cli),
|
|
131
|
+
"missing": missing}
|
|
132
|
+
try:
|
|
133
|
+
checks["runtime"]["builderVersion"] = builder_version()
|
|
134
|
+
except LocalProjectError as exc:
|
|
135
|
+
checks["runtime"].update(ok=False, error=str(exc))
|
|
136
|
+
if not checks["runtime"]["ok"]:
|
|
137
|
+
checks["runtime"]["hint"] = (
|
|
138
|
+
"Run `pnpm -C editor install --frozen-lockfile` and "
|
|
139
|
+
"`pnpm -C editor --filter @topview/3d-builder build`." if current.mode == "workspace"
|
|
140
|
+
else "Reinstall topview-3d-cli; the package is incomplete.")
|
|
141
|
+
|
|
142
|
+
try:
|
|
143
|
+
catalog = asset_catalog()
|
|
144
|
+
counts = {kind: sum(1 for entry in catalog if entry["kind"] == kind) for kind in ("character", "pose", "primitive")}
|
|
145
|
+
checks["assets"] = {"ok": True, "root": str(builtin_asset_root()), "counts": counts}
|
|
146
|
+
except LocalProjectError as exc:
|
|
147
|
+
checks["assets"] = {"ok": False, "code": exc.code, "error": str(exc),
|
|
148
|
+
"hint": "Reinstall topview-3d-cli, or restore builtin-assets/ in a checkout."}
|
|
149
|
+
|
|
150
|
+
playwright = current.playwright_package
|
|
151
|
+
try:
|
|
152
|
+
playwright_version = json.loads(playwright.read_text(encoding="utf-8")).get("version")
|
|
153
|
+
except (OSError, json.JSONDecodeError):
|
|
154
|
+
playwright_version = None
|
|
155
|
+
checks["playwright"] = {"ok": playwright_version == PLAYWRIGHT_VERSION, "path": str(playwright.parent),
|
|
156
|
+
"version": playwright_version, "expected": PLAYWRIGHT_VERSION}
|
|
157
|
+
|
|
158
|
+
chromium: dict[str, Any] = {"ok": False}
|
|
159
|
+
if checks["node"]["ok"] and checks["playwright"]["ok"]:
|
|
160
|
+
script = ("const { loadPlaywright } = await import('./playwright.mjs');"
|
|
161
|
+
"console.log((await loadPlaywright()).chromium.executablePath())")
|
|
162
|
+
completed = _run([checks["node"]["path"], "--input-type=module", "-e", script],
|
|
163
|
+
cwd=current.director_cli, env=node_env(current))
|
|
164
|
+
path = completed.stdout.strip() if completed and completed.returncode == 0 else ""
|
|
165
|
+
chromium = {"ok": bool(path) and Path(path).exists(), "executablePath": path or None,
|
|
166
|
+
"browsersPath": os.environ.get("PLAYWRIGHT_BROWSERS_PATH")}
|
|
167
|
+
for name in ("playwright", "chromium"):
|
|
168
|
+
if name == "chromium":
|
|
169
|
+
checks["chromium"] = chromium
|
|
170
|
+
if not checks[name]["ok"]:
|
|
171
|
+
checks[name]["hint"] = "Run `topview-3d-cli browser ensure`."
|
|
172
|
+
if current.mode == "workspace":
|
|
173
|
+
pnpm = shutil.which("pnpm")
|
|
174
|
+
completed = _run([pnpm, "--version"]) if pnpm else None
|
|
175
|
+
version = completed.stdout.strip() if completed and completed.returncode == 0 else None
|
|
176
|
+
checks["pnpm"] = {"ok": bool(version), "path": pnpm, "version": version}
|
|
177
|
+
if not version:
|
|
178
|
+
checks["pnpm"]["hint"] = "Run `corepack enable pnpm` (ships with Node) or install pnpm 8+."
|
|
179
|
+
return {"ok": all(item["ok"] for item in checks.values()), "cliVersion": CLI_VERSION,
|
|
180
|
+
"runtime": current.mode, "checks": checks}
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def browser_ensure(*, with_deps: bool) -> dict[str, Any]:
|
|
184
|
+
"""Install the pinned Playwright (packaged mode: into the user cache) and its Chromium."""
|
|
185
|
+
current = runtime()
|
|
186
|
+
node = node_path()
|
|
187
|
+
installed = None
|
|
188
|
+
if current.mode == "packaged":
|
|
189
|
+
try:
|
|
190
|
+
installed = json.loads(current.playwright_package.read_text(encoding="utf-8")).get("version")
|
|
191
|
+
except (OSError, json.JSONDecodeError):
|
|
192
|
+
installed = None
|
|
193
|
+
if installed != PLAYWRIGHT_VERSION:
|
|
194
|
+
npm = shutil.which("npm")
|
|
195
|
+
if not npm:
|
|
196
|
+
raise LocalProjectError("NPM_UNAVAILABLE", "npm is not on PATH; it ships with Node.js")
|
|
197
|
+
current.node_prefix.mkdir(parents=True, exist_ok=True)
|
|
198
|
+
manifest = current.node_prefix / "package.json"
|
|
199
|
+
if not manifest.exists():
|
|
200
|
+
manifest.write_text('{"private": true}\n', encoding="utf-8")
|
|
201
|
+
process = subprocess.run(
|
|
202
|
+
[npm, "install", "--prefix", str(current.node_prefix), "--no-audit", "--no-fund",
|
|
203
|
+
"--omit=dev", "--save-exact", f"playwright@{PLAYWRIGHT_VERSION}"],
|
|
204
|
+
check=False, capture_output=True, text=True, encoding="utf-8")
|
|
205
|
+
if process.returncode:
|
|
206
|
+
raise LocalProjectError("PLAYWRIGHT_INSTALL_FAILED", (process.stderr or process.stdout).strip()[-2000:])
|
|
207
|
+
args = [node, "cli.mjs", "browser", "ensure", *(["--with-deps"] if with_deps else [])]
|
|
208
|
+
process = subprocess.run(args, cwd=current.director_cli, env=node_env(current), check=False,
|
|
209
|
+
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, encoding="utf-8")
|
|
210
|
+
if process.returncode:
|
|
211
|
+
raise LocalProjectError("BROWSER_INSTALL_FAILED", (process.stderr or process.stdout).strip()[-2000:])
|
|
212
|
+
lines = [line for line in process.stdout.splitlines() if line.strip()]
|
|
213
|
+
browser = json.loads(lines[-1]) if lines else {}
|
|
214
|
+
return {"ok": True, "runtime": current.mode, "nodePrefix": str(current.node_prefix),
|
|
215
|
+
"playwright": PLAYWRIGHT_VERSION, "browser": browser}
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def pose_apply(directory: str, node_id: str, pose_id: str, *, dry_run: bool) -> dict[str, Any]:
|
|
219
|
+
"""Compile a manifest pose onto a character's joint controls (a one-item ``pose batch``)."""
|
|
220
|
+
result = pose_batch(directory, {"items": [{"nodeId": node_id, "poseId": pose_id}]}, dry_run=dry_run)
|
|
221
|
+
pose = result["poses"][0]
|
|
222
|
+
return {**result, "nodeId": node_id, "poseId": pose["poseId"], "pose": pose["pose"]}
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def asset_list(directory: str | None, kind: str | None) -> dict[str, Any]:
|
|
226
|
+
paths = open_project(directory).paths if directory else None
|
|
227
|
+
catalog = [public_asset_entry(entry) for entry in asset_catalog(paths)
|
|
228
|
+
if kind is None or entry["kind"] == kind]
|
|
229
|
+
return {"ok": True, "count": len(catalog), "assets": catalog}
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def asset_import(args: argparse.Namespace) -> dict[str, Any]:
|
|
233
|
+
if args.builtin:
|
|
234
|
+
root = builtin_asset_root()
|
|
235
|
+
else:
|
|
236
|
+
root = open_project(args.directory).paths.assets
|
|
237
|
+
root.mkdir(parents=True, exist_ok=True)
|
|
238
|
+
entry = import_asset(root, args.file, kind=args.kind, asset_id=args.id, key=args.key, cover=args.cover,
|
|
239
|
+
name=args.name,
|
|
240
|
+
category=args.category, tags=args.tag, rig=args.rig,
|
|
241
|
+
license_name=args.license, source_url=args.source)
|
|
242
|
+
return {"ok": True, "root": str(root), "asset": entry}
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
class _Parser(argparse.ArgumentParser):
|
|
246
|
+
def error(self, message: str) -> NoReturn:
|
|
247
|
+
raise LocalProjectError("USAGE_INVALID", f"{self.prog}: {message}")
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def _parser() -> argparse.ArgumentParser:
|
|
251
|
+
parser = _Parser(prog="topview-3d-cli", description="Local Scene3D director projects.")
|
|
252
|
+
parser.add_argument("--version", action="version", version=f"topview-3d-cli {CLI_VERSION}")
|
|
253
|
+
commands = parser.add_subparsers(dest="command", required=True, parser_class=_Parser)
|
|
254
|
+
|
|
255
|
+
def group(name: str, help_text: str):
|
|
256
|
+
return commands.add_parser(name, help=help_text).add_subparsers(
|
|
257
|
+
dest=f"{name}_command", required=True, parser_class=_Parser)
|
|
258
|
+
|
|
259
|
+
project = group("project", "create and inspect projects")
|
|
260
|
+
init = project.add_parser("init")
|
|
261
|
+
init.add_argument("directory")
|
|
262
|
+
init.add_argument("--force", action="store_true", help="replace an existing project")
|
|
263
|
+
project.add_parser("status").add_argument("directory")
|
|
264
|
+
|
|
265
|
+
document = group("document", "read, validate and edit the director document")
|
|
266
|
+
get = document.add_parser("get", help="the stored document; --summary or --entity for smaller reads")
|
|
267
|
+
get.add_argument("directory")
|
|
268
|
+
get.add_argument("--summary", action="store_true", help="outline without curves or full nodes")
|
|
269
|
+
get.add_argument("--entity", help="one node id, clip id or fcurves__<nodeId>")
|
|
270
|
+
get.add_argument("--type", choices=("node", "clip", "fcurves"), help="entity type (inferred by default)")
|
|
271
|
+
get.add_argument("--include-curves", action="store_true", help="include curve keys for clips/fcurves")
|
|
272
|
+
document.add_parser("validate").add_argument("directory")
|
|
273
|
+
apply = document.add_parser("apply")
|
|
274
|
+
apply.add_argument("directory")
|
|
275
|
+
apply.add_argument("operations", help="operation batch JSON file, or - for stdin")
|
|
276
|
+
apply.add_argument("--strict", action="store_true", help="require expectedEntityVersion like the hosted service")
|
|
277
|
+
apply.add_argument("--dry-run", action="store_true", help="validate and report without writing")
|
|
278
|
+
|
|
279
|
+
node = group("node", "node-level edits")
|
|
280
|
+
delete = node.add_parser("delete", help="delete a node and everything that references it")
|
|
281
|
+
delete.add_argument("directory")
|
|
282
|
+
delete.add_argument("node_id")
|
|
283
|
+
delete.add_argument("--dry-run", action="store_true")
|
|
284
|
+
batch = node.add_parser("batch", help="add/update/delete/repeat nodes in one atomic batch")
|
|
285
|
+
batch.add_argument("directory")
|
|
286
|
+
batch.add_argument("spec", help="{\"changes\": [...]} JSON file, or - for stdin")
|
|
287
|
+
batch.add_argument("--dry-run", action="store_true", help="stage and validate without writing")
|
|
288
|
+
|
|
289
|
+
evaluate_parser = commands.add_parser("evaluate", help="numerical checks of the scene or of an unsaved plan")
|
|
290
|
+
evaluate_parser.add_argument("directory")
|
|
291
|
+
evaluate_parser.add_argument("plan", nargs="?", help="{\"changes\"|\"operations\": [...]} JSON file, or -")
|
|
292
|
+
evaluate_parser.add_argument("--frames", help="comma-separated frames (default 0)")
|
|
293
|
+
evaluate_parser.add_argument("--camera", help="evaluate through this camera")
|
|
294
|
+
|
|
295
|
+
inspect = group("inspect", "mesh measurements and camera views")
|
|
296
|
+
nodes = inspect.add_parser("nodes", help="bounds, grounding and pairwise distance/overlap (Chromium)")
|
|
297
|
+
nodes.add_argument("directory")
|
|
298
|
+
nodes.add_argument("node_ids", nargs="+")
|
|
299
|
+
nodes.add_argument("--frame", type=int, default=0)
|
|
300
|
+
views = inspect.add_parser("views", help="numbers plus renders for cameras; records evidence in the BOM")
|
|
301
|
+
views.add_argument("directory")
|
|
302
|
+
views.add_argument("views", nargs="?", help="views spec JSON file, or -")
|
|
303
|
+
views.add_argument("--camera", action="append", help="camera node id (repeatable; default: every camera)")
|
|
304
|
+
views.add_argument("--frames", help="comma-separated frames, at most 3 (default 0)")
|
|
305
|
+
views.add_argument("--primary", help="primary story camera (must be one of the views)")
|
|
306
|
+
|
|
307
|
+
render_command = commands.add_parser("render", help="render frames with the Node renderer")
|
|
308
|
+
render_command.add_argument("directory")
|
|
309
|
+
render_command.add_argument("payload", nargs="?")
|
|
310
|
+
renders = group("renders", "render runs stored in the project")
|
|
311
|
+
renders.add_parser("list").add_argument("directory")
|
|
312
|
+
show = renders.add_parser("show", help="path and sha256 of a contact sheet or frame PNG")
|
|
313
|
+
show.add_argument("directory")
|
|
314
|
+
show.add_argument("run_id", nargs="?", help="default: the latest run")
|
|
315
|
+
show.add_argument("--frame", type=int, help="a single frame instead of the contact sheet")
|
|
316
|
+
|
|
317
|
+
camera = group("camera", "camera helpers")
|
|
318
|
+
camera.add_parser("presets", help="camera preset ids for add_camera")
|
|
319
|
+
|
|
320
|
+
bom = group("bom", "plan / constraint record (.topview3d/bom.json)")
|
|
321
|
+
bom.add_parser("get").add_argument("directory")
|
|
322
|
+
checkpoint = bom.add_parser("checkpoint", help="merge a checkpoint patch (compare-and-set)")
|
|
323
|
+
checkpoint.add_argument("directory")
|
|
324
|
+
checkpoint.add_argument("patch", help="checkpoint JSON file, or - for stdin")
|
|
325
|
+
|
|
326
|
+
asset = group("asset", "offline asset manifests")
|
|
327
|
+
listing = asset.add_parser("list", help="built-in assets, plus a project's own when given")
|
|
328
|
+
listing.add_argument("directory", nargs="?")
|
|
329
|
+
listing.add_argument("--kind", choices=KINDS)
|
|
330
|
+
search = asset.add_parser("search", help="keyword search with filters and facets")
|
|
331
|
+
search.add_argument("query", nargs="?", default="")
|
|
332
|
+
search.add_argument("--project", dest="directory", help="also search <project>/.topview3d/assets")
|
|
333
|
+
search.add_argument("--kind", choices=KINDS)
|
|
334
|
+
search.add_argument("--category")
|
|
335
|
+
search.add_argument("--tag", action="append", help="required tag (repeatable)")
|
|
336
|
+
search.add_argument("--rig", choices=("mixamorig", "ual1"))
|
|
337
|
+
search.add_argument("--limit", type=int, default=20)
|
|
338
|
+
search.add_argument("--offset", type=int, default=0)
|
|
339
|
+
show_asset = asset.add_parser("show", help="one manifest entry with its local path and usage")
|
|
340
|
+
show_asset.add_argument("asset_id")
|
|
341
|
+
show_asset.add_argument("--project", dest="directory")
|
|
342
|
+
show_asset.add_argument("--kind", choices=KINDS)
|
|
343
|
+
importing = asset.add_parser("import", help="copy a model or pose file into an asset root")
|
|
344
|
+
target = importing.add_mutually_exclusive_group(required=True)
|
|
345
|
+
target.add_argument("--project", dest="directory", help="import into <project>/.topview3d/assets")
|
|
346
|
+
target.add_argument("--builtin", action="store_true", help="import into the built-in asset root")
|
|
347
|
+
importing.add_argument("file")
|
|
348
|
+
importing.add_argument("--kind", required=True, choices=("character", "prop", "pose"))
|
|
349
|
+
importing.add_argument("--id", required=True)
|
|
350
|
+
importing.add_argument("--key", help="asset key used by documents (metadata.modelUrl)")
|
|
351
|
+
importing.add_argument("--cover", help="cover image (.webp/.png/.jpg)")
|
|
352
|
+
importing.add_argument("--name")
|
|
353
|
+
importing.add_argument("--category")
|
|
354
|
+
importing.add_argument("--tag", action="append")
|
|
355
|
+
importing.add_argument("--rig", choices=("mixamorig", "ual1"))
|
|
356
|
+
importing.add_argument("--license")
|
|
357
|
+
importing.add_argument("--source")
|
|
358
|
+
|
|
359
|
+
pose = group("pose", "character poses from the asset manifests")
|
|
360
|
+
catalog = pose.add_parser("catalog", help="every pose with its category and tags")
|
|
361
|
+
catalog.add_argument("--project", dest="directory")
|
|
362
|
+
catalog.add_argument("--category")
|
|
363
|
+
catalog.add_argument("--tag")
|
|
364
|
+
pose_apply_parser = pose.add_parser("apply", help="apply a manifest pose to a character")
|
|
365
|
+
pose_apply_parser.add_argument("directory")
|
|
366
|
+
pose_apply_parser.add_argument("node_id")
|
|
367
|
+
pose_apply_parser.add_argument("pose_id", help="pose asset id (the a3d_pose_ prefix is optional)")
|
|
368
|
+
pose_apply_parser.add_argument("--dry-run", action="store_true")
|
|
369
|
+
pose_batch_parser = pose.add_parser("batch", help="pose several characters in one batch")
|
|
370
|
+
pose_batch_parser.add_argument("directory")
|
|
371
|
+
pose_batch_parser.add_argument("spec", help="{\"items\": [...]} JSON file, or - for stdin")
|
|
372
|
+
pose_batch_parser.add_argument("--dry-run", action="store_true")
|
|
373
|
+
|
|
374
|
+
doctor_parser = commands.add_parser("doctor", help="check Python, Node, the renderer runtime and Chromium")
|
|
375
|
+
doctor_parser.add_argument("--json", action="store_true", help="print the JSON report and always exit 0")
|
|
376
|
+
browser = group("browser", "renderer browser setup")
|
|
377
|
+
ensure = browser.add_parser("ensure", help="install the pinned Playwright and Chromium (idempotent)")
|
|
378
|
+
ensure.add_argument("--with-deps", action="store_true", help="also install Linux system packages (sudo)")
|
|
379
|
+
return parser
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def command_table() -> dict[str, set[str]]:
|
|
383
|
+
"""Every runnable command path (``""`` is the bare program) mapped to its long options."""
|
|
384
|
+
table: dict[str, set[str]] = {}
|
|
385
|
+
|
|
386
|
+
def walk(parser: argparse.ArgumentParser, path: tuple[str, ...]) -> None:
|
|
387
|
+
groups = [action for action in parser._actions if isinstance(action, argparse._SubParsersAction)]
|
|
388
|
+
if not groups:
|
|
389
|
+
table[" ".join(path)] = {option for action in parser._actions for option in action.option_strings
|
|
390
|
+
if option.startswith("--") and option != "--help"}
|
|
391
|
+
for group in groups:
|
|
392
|
+
for name, child in group.choices.items():
|
|
393
|
+
walk(child, (*path, name))
|
|
394
|
+
|
|
395
|
+
root = _parser()
|
|
396
|
+
table[""] = {"--version"}
|
|
397
|
+
walk(root, ())
|
|
398
|
+
return table
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
def _dispatch(args: argparse.Namespace) -> dict[str, Any]:
|
|
402
|
+
command = args.command
|
|
403
|
+
sub = getattr(args, f"{command}_command", None)
|
|
404
|
+
if command == "doctor":
|
|
405
|
+
return doctor()
|
|
406
|
+
if command == "browser":
|
|
407
|
+
return browser_ensure(with_deps=args.with_deps)
|
|
408
|
+
if command == "project":
|
|
409
|
+
return init_project(args.directory, force=args.force) if sub == "init" else project_status(args.directory)
|
|
410
|
+
if command == "document":
|
|
411
|
+
if sub == "get":
|
|
412
|
+
return document_view(args.directory, summary=args.summary, entity=args.entity,
|
|
413
|
+
entity_type=args.type, include_curves=args.include_curves)
|
|
414
|
+
if sub == "validate":
|
|
415
|
+
return validate_project_document(args.directory)
|
|
416
|
+
return document_apply(args.directory, args.operations, strict=args.strict, dry_run=args.dry_run)
|
|
417
|
+
if command == "node":
|
|
418
|
+
if sub == "batch":
|
|
419
|
+
return node_batch(args.directory, args.spec, dry_run=args.dry_run)
|
|
420
|
+
return node_delete(args.directory, args.node_id, dry_run=args.dry_run)
|
|
421
|
+
if command == "evaluate":
|
|
422
|
+
return evaluate(args.directory, args.plan, frames=parse_frames(args.frames), camera=args.camera)
|
|
423
|
+
if command == "inspect":
|
|
424
|
+
if sub == "nodes":
|
|
425
|
+
return inspect_nodes(args.directory, args.node_ids, frame=args.frame)
|
|
426
|
+
frames = parse_frames(args.frames) if args.frames else None
|
|
427
|
+
return inspect_views(args.directory, args.views, cameras=args.camera, frames=frames, primary=args.primary)
|
|
428
|
+
if command == "renders":
|
|
429
|
+
if sub == "list":
|
|
430
|
+
return renders_list(args.directory)
|
|
431
|
+
return renders_show(args.directory, args.run_id, frame=args.frame)
|
|
432
|
+
if command == "camera":
|
|
433
|
+
return camera_presets()
|
|
434
|
+
if command == "bom":
|
|
435
|
+
if sub == "get":
|
|
436
|
+
return bom_get(args.directory)
|
|
437
|
+
return bom_checkpoint(args.directory, load_spec(args.patch, "BOM_CHECKPOINT_INVALID"))
|
|
438
|
+
if command == "asset":
|
|
439
|
+
if sub == "list":
|
|
440
|
+
return asset_list(args.directory, args.kind)
|
|
441
|
+
if sub == "search":
|
|
442
|
+
return asset_search(args.directory, args.query, kind=args.kind, category=args.category,
|
|
443
|
+
tags=args.tag or [], rig=args.rig, limit=args.limit, offset=args.offset)
|
|
444
|
+
if sub == "show":
|
|
445
|
+
return asset_show(args.directory, args.asset_id, kind=args.kind)
|
|
446
|
+
return asset_import(args)
|
|
447
|
+
if command == "pose":
|
|
448
|
+
if sub == "catalog":
|
|
449
|
+
return pose_catalog(args.directory, category=args.category, tag=args.tag)
|
|
450
|
+
if sub == "batch":
|
|
451
|
+
return pose_batch(args.directory, load_spec(args.spec, "POSE_BATCH_INVALID"), dry_run=args.dry_run)
|
|
452
|
+
return pose_apply(args.directory, args.node_id, args.pose_id, dry_run=args.dry_run)
|
|
453
|
+
return render(args.directory, args.payload)
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def _fail(code: str, message: str, details: Any = None) -> int:
|
|
457
|
+
body = {"ok": False, "code": code, "error": message}
|
|
458
|
+
if details is not None:
|
|
459
|
+
body["details"] = details
|
|
460
|
+
print(json.dumps(body, ensure_ascii=False), file=sys.stderr)
|
|
461
|
+
return exit_code_for(code)
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
def _print_doctor(report: dict[str, Any]) -> None:
|
|
465
|
+
print(f"topview-3d-cli {report['cliVersion']} ({report.get('runtime', 'no')} runtime)")
|
|
466
|
+
for name, check in report["checks"].items():
|
|
467
|
+
detail = check.get("version") or check.get("mode") or check.get("executablePath") or check.get("error") or ""
|
|
468
|
+
print(f" [{'ok' if check['ok'] else 'FAIL'}] {name} {detail}".rstrip())
|
|
469
|
+
if not check["ok"] and check.get("hint"):
|
|
470
|
+
print(f" {check['hint']}")
|
|
471
|
+
print("ready" if report["ok"] else "not ready: fix the failing checks above")
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
def main(argv: list[str] | None = None) -> int:
|
|
475
|
+
try:
|
|
476
|
+
args = _parser().parse_args(argv)
|
|
477
|
+
if args.command == "doctor" and not args.json:
|
|
478
|
+
report = doctor()
|
|
479
|
+
_print_doctor(report)
|
|
480
|
+
return EXIT_OK if report["ok"] else exit_code_for("DOCTOR_FAILED")
|
|
481
|
+
result = _dispatch(args)
|
|
482
|
+
except SystemExit as exc:
|
|
483
|
+
return int(exc.code or 0) if not isinstance(exc.code, str) else EXIT_INVALID_INPUT
|
|
484
|
+
except LocalProjectError as exc:
|
|
485
|
+
return _fail(exc.code, str(exc), exc.details)
|
|
486
|
+
except Exception as exc: # noqa: BLE001 - every failure must stay machine-readable
|
|
487
|
+
return _fail("INTERNAL_ERROR", f"{type(exc).__name__}: {exc}")
|
|
488
|
+
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|
489
|
+
return EXIT_OK
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
if __name__ == "__main__":
|
|
493
|
+
raise SystemExit(main())
|