chisel/step-service/runner.py
maverick b2a52525d6 feat: unify on build123d kernel (preview == export)
Single source of truth: build123d drives BOTH the STL preview and the STEP
export from the same script + params, so what you see is what you download.
Verified live: generate → build123d → /api/build STL + /api/step STEP.

- prompts: SYSTEM_PROMPT teaches build123d (Python) with # @param sliders
- claude.ts: parse python fences
- step.ts → generic geometry-service client (/tessellate + /step), GeometryError
  carries the Python traceback for the repair loop
- worker: /api/generate returns code; /api/build tessellates; /api/step runs the
  same code+params (no separate Claude re-gen)
- client engine: runJscad → buildModel (server STL); parseParams handles # @param
- App: debounced param rebuilds + rebuilding indicator; STEP sends script+params
- Viewer: Z-up→Y-up rotation; retire client-side JSCAD worker
- VM service.py/runner.py: /tessellate + /step with params

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 16:02:18 +10:00

39 lines
1.2 KiB
Python

"""Isolated executor for Claude-authored build123d code.
python runner.py <code_file> <out_file> <fmt: stl|step> <params_json>
The model code must assign the final solid to `result`. A `params` dict (parsed
from <params_json>) is injected into its namespace so parametric sliders work —
the same code powers both the STL preview and the STEP export, guaranteeing the
preview matches the download.
"""
import json
import sys
from build123d import * # noqa: F401,F403 — runner's own export_* helpers
def main() -> int:
code_file, out_file, fmt = sys.argv[1], sys.argv[2], sys.argv[3]
params = json.loads(sys.argv[4]) if len(sys.argv) > 4 and sys.argv[4] else {}
with open(code_file) as f:
code = f.read()
ns: dict = {"params": params}
exec(compile(code, "<model>", "exec"), ns, ns) # noqa: S102 — sandboxed subprocess
result = ns.get("result")
if result is None:
print("ERROR: script did not assign `result`", file=sys.stderr)
return 2
obj = getattr(result, "part", result)
if fmt == "step":
export_step(obj, out_file) # noqa: F405
else:
export_stl(obj, out_file) # noqa: F405
return 0
if __name__ == "__main__":
sys.exit(main())