Spaces:
Sleeping
Sleeping
File size: 5,650 Bytes
cd53438 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | """SimReady asset packaging — entry point for the /simready-package skill.
Usage:
python package.py <target_dir> [--profile NAME] [--output DIR]
For each immediate subfolder of <target_dir> containing a USD file, picks
the interface USD and runs `simready ingest usd` on it, producing a
packaged tree under <output_dir>. Independent of /simready-report — runs
the packaging step alone, without validation or dashboard generation.
"""
from __future__ import annotations
import argparse
import os
import shutil
import subprocess
import sys
from pathlib import Path
USD_EXTS = (".usd", ".usda", ".usdc", ".usdz")
def _find_simready_cli() -> str | None:
"""Locate the `simready` CLI in a layout-agnostic way.
Order: (1) on PATH, (2) alongside `sys.executable` (covers any active
venv/conda regardless of OS).
"""
on_path = shutil.which("simready")
if on_path:
return on_path
py_bin = Path(sys.executable).parent
for candidate in (py_bin / "simready.exe", py_bin / "simready"):
if candidate.is_file():
return str(candidate)
return None
def _discover_interface_usds(target: Path) -> list[Path]:
"""Pick one interface USD per immediate subfolder of `target`.
Heuristic per subfolder:
- If exactly one USD at root, that's the interface.
- Else prefer the USD whose stem matches the folder name (case- and
hyphen/underscore-insensitive).
- Else fall back to packaging all of them.
Subfolders starting with "." or "_" are ignored.
"""
interface_files: list[Path] = []
for sub in sorted(target.iterdir()):
if not sub.is_dir() or sub.name.startswith((".", "_")):
continue
usds = [p for ext in USD_EXTS for p in sub.glob(f"*{ext}")]
if not usds:
continue
if len(usds) == 1:
interface_files.append(usds[0])
continue
named = [p for p in usds if p.stem.lower() == sub.name.lower().replace("-", "_")]
if named:
interface_files.append(named[0])
else:
interface_files.extend(usds)
return interface_files
def _default_output(target: Path) -> Path:
"""Pick a default output dir based on target location."""
target_str = str(target).replace("\\", "/")
if "/assets_to_validate/" in target_str:
# Workspace-style layout: <X>/assets_to_validate/<name>
# → output at sibling packages dir: <X>/packages/<name>
return target.parent.parent / "packages" / target.name
return target.parent / "packages" / target.name
def main() -> int:
ap = argparse.ArgumentParser(description="Package SimReady assets via `simready ingest usd`.")
ap.add_argument("target", nargs="?", default=None,
help="Directory containing one or more asset subfolders (default: cwd)")
ap.add_argument("--profile", default="Robot-Body-Runnable",
help="Profile passed to `simready ingest usd -p` (default: Robot-Body-Runnable)")
ap.add_argument("--output", default=None,
help="Output dir (default: <target>.parent/packages/<target.name>, "
"or sibling-of-assets_to_validate/packages/<name> when applicable)")
args = ap.parse_args()
target = Path(args.target).resolve() if args.target else Path.cwd().resolve()
if not target.is_dir():
print(f"ERROR: target is not a directory: {target}", flush=True)
return 2
simready_exe = _find_simready_cli()
if simready_exe is None:
print("ERROR: `simready` CLI not found on PATH or alongside the active Python.", flush=True)
print(" Install simready-oem-sdk-poc into the active Python, or run bootstrap.ps1.", flush=True)
return 2
interface_files = _discover_interface_usds(target)
if not interface_files:
print(f"ERROR: no USD files found in immediate subfolders of {target}.", flush=True)
print(" /simready-package expects each asset to live in its own subfolder "
"with an interface USD.", flush=True)
return 2
output = Path(args.output).resolve() if args.output else _default_output(target)
output.mkdir(parents=True, exist_ok=True)
print(f"Target: {target}", flush=True)
print(f"Output: {output}", flush=True)
print(f"Profile: {args.profile}", flush=True)
print(f"CLI: {simready_exe}", flush=True)
print(f"Discovered {len(interface_files)} interface USD(s).", flush=True)
env = {**os.environ, "PYTHONIOENCODING": "utf-8", "PYTHONUTF8": "1"}
packaged = 0
skipped = 0
failures: list[tuple[str, str]] = []
for usd in interface_files:
already = output / usd.stem / usd.name
if already.is_file():
print(f" skip (already packaged): {usd.name}", flush=True)
skipped += 1
continue
print(f" package: {usd.name}", flush=True)
cmd = [simready_exe, "ingest", "usd", str(usd),
"-o", str(output), "-p", args.profile, "--no-validate"]
try:
subprocess.run(cmd, env=env, check=True, capture_output=True, text=True, timeout=120)
packaged += 1
except subprocess.CalledProcessError as e:
err = (e.stderr or "")[-300:] or str(e)
failures.append((usd.name, err))
print(f" FAILED: {usd.name} - {err}", flush=True)
print(f"\nSUMMARY: packaged={packaged} skipped={skipped} failed={len(failures)}", flush=True)
print(f"OUTPUT: {output}", flush=True)
return 0 if not failures else 1
if __name__ == "__main__":
sys.exit(main())
|