CLI
Part of the OpenRAL public-symbol inventory. Hand-curated;
(LNN)markers are refreshed bytools/refresh_methods_linenos.py.
python/cli/src/openral_cli/main.py
openral CLI entry point — openral command (typer-based).
_root(ctx)— Top-level callback: configures observability with the mode-derived trace sample ratio and opens acli.commandroot span that closes when the subcommand returns. (L392)_RUN_MODE_BY_SUBCOMMAND: dict[str, str]— Subcommand →openral.run.modemapping consumed by_root. (L373)_SAMPLE_RATIO_BY_MODE: dict[str, float]—openral.run.mode→ trace sample ratio; hardware samples at 0.1. (L386)class CheckResult(NamedTuple)— One row inopenral doctoroutput. (L423) fields:check, status, details_check_python() -> CheckResult(L440)_check_platform() -> CheckResult(L445)_check_openral_core() -> CheckResult(L449)_check_ros2() -> list[CheckResult]— ROS 2 binary, distro, RMW. A missingros2binary isabsent(non-fatal), notmissing: Tier-0 ships no ROS 2 by design, soopenral doctormust still exit 0 there. (L457)_check_colcon() -> CheckResult—absent(non-fatal) when unavailable, for the same Tier-0 reason as_check_ros2. (L505)_check_gpu(result, warnings) -> list[CheckResult]— One row per GPU. (L514)_check_compute_spec(result) -> list[CheckResult]— BuildsComputeSpecrows from_check_gpu'sGpuProbeResult(no second probe), mirroring exactly whatopenral detectwould write into the manifest so doctor and detect stay in sync. (L551)_check_usb() -> list[CheckResult]— Candidate robot USB serial devices. (L622)_check_just() -> CheckResult(L640)_check_reasoner_llm() -> list[CheckResult]— Model-first doctor dispatcher: readsOPENRAL_REASONER_MODEL, resolves it againstREASONER_MODELS, and reports dialect/hosting/endpoint/key status. An uncurated model fails untilOPENRAL_REASONER_ENDPOINTnames an endpoint or dialect; never prints the API key value. (L864)_check_reasoner_model(model_key) -> list[CheckResult]— Resolve one curated/uncurated model into doctor rows; delegates loopback checks to_reasoner_endpoint_probe_row. Resolves a namedOPENRAL_REASONER_ENDPOINTthrough the sameopenral_core.REASONER_ENDPOINT_PRESETStable the factory uses, without importing the optional reasoner package. (L736)_reasoner_endpoint_probe_row(label, base_url, *, managed, autostart) -> CheckResult— Generic loopback probe. Down managed-local + autostart isinfo; managed with autostart disabled and every BYO-local endpoint arewarn. (L699)_cosmos_autostart_enabled() -> bool— mirrors the reasoner client'sOPENRAL_COSMOS3_AUTOSTARTparsing (falsy spellings0/false/no/off); kept local so doctor never imports the optionally-installed reasoner package. (L655)_is_local_base_url(url) -> bool— True when host resolves to a loopback name. (L666)_probe_tcp(host, port, *, timeout_s=0.2) -> bool— Fast non-blocking TCP probe used to diagnose the Ollama daemon. (L672)_gather_checks() -> list[CheckResult](L886)_YELLOW_STATUSES: frozenset[str]—{"absent", "info", "warn"};CheckResult.statusvalues rendered yellow (advisory, non-fatal) rather than red/green indoctor's table. (L420)doctor(--json)— Diagnose host: Python, OS, ROS 2, GPU, USB. Delegates GPU enumeration toprobe_gpus. (L919)detect(--output, --robot/--as, --report, --dds-timeout, --include, --no-write, --deployment, --yes)(L954) — Always-interactiverobot.yamlbuilder;--no-writeshort-circuits to non-interactive probe-only inspection (CI-safe),--report <path>dumps the rawDetectionReportJSON alongside either mode.--robot/--asforces a canonical base manifest as the template over USB/DDS inference. Prompts a rig name, walks detected cameras (V4L2/RealSense/Orbbec) into robot or workcell sensors, optionally overrides joint/safety limits, and relocatesfile:asset refs;--deployment <path>additionally scaffolds a self-containedDeployScene._prompt_robot_name(default) -> str—typer.promptfor the custom rig name; blank input keepsdefault._run_camera_binding_wizard(canonical, detection) -> tuple[list[SensorSpec], list[SensorSpec]]—(robot_sensors, workcell_sensors); the routing prompt loop above over_iter_wizard_cameras(V4L2 + RealSense + Orbbec). Enter skips a device, dropping any canonical sensor never bound. Any canonical camera (rgb/depth/stereo/ir/point_cloud) can be reused by name; only an RGB one gets a/dev/video(or serial) reader binding, while a depth-type camera is kept as declared, to be bound to its vendor driver's topic (ros2_image) in the host's unit overlay._iter_wizard_cameras(detection) -> list[tuple[str, str, str]]—(device_path, label, serial)per bindable camera; V4L2 keys ondevice_path, RealSense/Orbbec key onserial(emptydevice_path)._grab_camera_thumbnail(device_path, out_dir) -> Path | None— Best-effort one-frame JPEG grab (opencv) so the operator can see which physical camera a/dev/video*node is._CV2_WARNED: set[str](L1391) — Print-once guard for the "opencv missing" hint; a mutated set avoids aglobalstatement while staying a single-process latch. (Used by_grab_camera_thumbnail.)_maybe_customize_limits(description) -> RobotDescription— Opt-in gate"Customize joint limits & safety envelope?"(default No); declining inherits the canonicalJointSpec/SafetySpecfields verbatim, accepting walks each present (non-None) joint field and the four safety scalars with Enter-to-keep-default prompts._relocate_file_assets(description, *, canonical_dir, output_path) -> RobotDescription— Rewritesassets.urdf.ref/mjcf/srdffile:<rel>refs to repo-root-relative whenoutput_pathis outsidecanonical_dir, so the custom manifest's URDF/SRDF still resolve after relocation._write_deploy_scene_scaffold(path, description, sensor_specs, *, detection, assume_yes) -> None— Validates throughDeployScenethen writes the scaffold + review banner.sensor_specshere are always workcell (non-robot) cameras from the wizard. Overrides the seeded HALportwith the one probed indetection.usb.matches(when present) instead of the manifest's stale default._print_safety_fitting_reminders(description, output) -> None— Right afterdetectwritesrobot.yaml: nudgesopenral collision lower --writewhen an MJCF/URDF asset exists, and, per robot-mounted depth camera (is_depth_camera+parent_frame), to calibrate its mount and declare it inrobots/<id>/units/<unit>.yaml(OpenRAL does not measure it;deploy runenforces the declaration)._ROBOT_NAME_RE: re.Pattern[str](L1156) —[a-z0-9_]+; validates a custom rig name typed at the_prompt_robot_nameprompt._render_detection_summary(detection)— Print per-probe Rich table foropenral detect.connect(--robot, --port)(L1624) — Open HAL, read state, disconnect.--robotacceptsso100/so101(both drive the sharedSO100FollowerHAL)._connect_so_follower(label: str, port: str) -> None— Connect an SO-100/SO-101 follower arm viaSO100FollowerHAL, read one state, disconnect.calibrate_camera(--sensor, --topic, --chessboard-size, --square-size, --dry-run)(L1690) — Run ROS 2camera_calibration.prompt_command(TEXT, --topic, --wait-s, --discovery-wait-s, --new-goal)(python/cli/src/openral_cli/prompt.pyL31) —openral prompt: publishes a one-shotPromptStampedonto/openral/prompt_in/cli, whichprompt_router_nodefans out to the reasoner; spins up to--discovery-wait-sfor a subscriber before publishing anyway.--new-goaltells the reasoner to rebuild its mission queue instead of treating the prompt as conversational context.estop_reset_command(--service-timeout-s, --discovery-wait-s)(python/cli/src/openral_cli/estop.pyL41) —openral estop reset: the dashboard'sPOST /api/estop_resetsequence from a shell. Calls the kernel's/openral/estop_reset(std_srvs/Trigger) first; only onsuccess=truebroadcasts/openral/estop_cleared(RELIABLE/VOLATILE/depth 10, three copies) so every HAL and the rSkill runner un-latch. Exit 0 all cleared, 1 kernel refused (nothing broadcast), 2 no rclpy, 3 service absent/unanswered (nothing broadcast).estop_app: typer.Typer(estop.pyL33) — theopenral estopsub-app.RESET_SERVICE: str(estop.pyL30) —"/openral/estop_reset", the kernel'sstd_srvs/Triggerreset.CLEARED_TOPIC: str(estop.pyL31) —"/openral/estop_cleared", the HAL/runner un-latch broadcast.robot_vendor_urdf(robot_id, --upstream, --out, --rename, --raw-text)—openral robot vendor-urdftyper command; defers therobot_descriptions/xacrodoc/yourdfpyimport and delegates torobot.py'svendor_urdf. (L3757)rskill_install(HUB_ID, --revision, --force, --non-commercial, --yes)(L1804) —openral rskill install. Download rSkill, validate, register. An org-lessHUB_ID(no/) fails fast with anOpenRAL/<name>suggestion +rskill searchhint instead of a raw Hub 404; a 404 on a qualified id appends the same search hint._DEFAULT_RSKILL_ORG: Final[str](L1800) —"OpenRAL"; suggested org prefix for an org-lessHUB_IDpassed torskill_install.rskill_search(QUERY?..., --kind, --role, --embodiment, --license, --family, --limit, --json)(L1968) — Thin renderer overopenral_rskill.hub_search.search_hub_rskills(seedocs/methods/04-rskill.md).QUERYis variadic and matched locally (case-insensitive substring match against id/name/description/tags), not sent to the Hub assearch=. Table gains alocalcolumn (in-tree/installed/—)._render_rskill_search_results(result: HubRSkillSearchResult, query) -> None— Print therskill searchtable or the no-results notice, including thelocalcolumn and a skipped-count notice that names every skipped id (N OpenRAL repo(s) skipped — no valid rskill.yaml: <id>, <id>)._RSKILL_SEARCH_DESC_MAX: int(L1920) —60; character caprskill search's table truncates a manifestdescriptionto.rskill_list(--json)(L2056) —openral rskill list. List installed rSkills.rskill_check(rskill_id?, --robot, --rskills-dir, --json)(L2240) —openral rskill check. Two modes. With a positional id, resolves it viaload_rskill_manifestand renders a per-section breakdown viacheck_single_rskill. Without an id, falls back to the legacy walk-all path (check_installed_rskills).--rskills-dirdefaults torskills/and is silently skipped when the directory does not exist. Exits 1 on any blocking failure._SECTION_DISPLAY: dict[str, str](L2161) — Manifest section key → display label forrskill_check's per-section breakdown table.rskill_new(ID, --out-dir, --owner, --license, --embodiment-tag, --family, --from-hf, --yes, --overwrite)— Scaffolds a new local rSkill fromrskills/template/. Three modes:--from-hf <repo>introspects the Hub config to auto-fill manifest fields;--family <...>overlays family-aware defaults; otherwise interactive prompts fill any missing flag (skipped under--yes).--embodiment-tagis checked againstopenral_rskill.loader.intree_embodiment_tags()(robot-declared tags + any/custom/multi), not a closed schema Literal. (L2332)_resolve_or_prompt(value, *, prompt, default, skip_prompt) -> str— Drives the owner / license / embodiment prompts only when the flag was not provided and--yesis off._DEFAULT_OWNER: str(L2326) —"your-org"; default forrskill new --ownerwhen unset._DEFAULT_LICENSE: str(L2327) —"apache-2.0"; default forrskill new --licensewhen unset._DEFAULT_EMBODIMENT: str(L2328) —"franka_panda"; default forrskill new --embodiment-tagwhen unset._resolve_family_and_patch(*, family, from_hf, yes) -> tuple[RSkillFamily | None, RSkillPatch | None]— Resolves--family/--from-hfinto a family + manifest patch forscaffold_rskill. Prompts for family in interactive mode; bails non-zero with a clear message when--from-hfintrospection fails or--familyis unrecognized._display_license_banner(name, license_value, version, con) -> Nonesensor_list(--vendor, --modality, --kind, --json)— List entries in sensor catalog. (L2623)sensor_show(SENSOR_ID, --name, --parent-frame, --json)— Resolve catalog entry to aSensorSpec/SensorBundle. (L2714)benchmark_list(--benchmarks-dir)— List every benchmark suite id underbenchmarks/*.yaml, one paste-able--suitevalue per line. (L2783)benchmark_report(--rskills-dir, --json)— Aggregaterskills/*/eval/*.jsonbenchmark blocks into a rich-table or JSON dump. Validates every JSON againstRSkillEvalResult.--rskills-dirdefaults torskills/. (L3523)benchmark_run(--suite, --rskill, --out, --device, --save-dir, --benchmarks-dir, --task, --n-episodes, --dry-run, --update-manifest/--no-update-manifest, --video/--no-video, --video-dir, --dashboard, --dashboard-port)— Resolves--suiteto a list ofBenchmarkScenes, parses the rSkill reference, dispatches toopenral_sim.run_benchmark, and writes a validatedRSkillEvalResultJSON.--video(default on) records one world MP4 per episode;--taskruns a single suite task, otherwise the run auto-filters to the rSkill'sevaluated_tasks.--update-manifest(default on) writesavg_success_rateback into the manifest. (L2807)benchmark_scene(--config, --rskill, --out, --device, --save-dir, --save-video, --video-size, --n-episodes, --view/--no-view, --dry-run, --update-manifest/--no-update-manifest, --dashboard, --dashboard-port)— Single-scene sibling ofbenchmark_run.--viewopens a livemujoco.viewerper episode;--save-video DIRwrites a clean single-view world MP4 per episode plus avideos.jsonmanifest, for website hero clips. Strictly accepts aBenchmarkSceneYAML (rejects DeployScene/SimScene) and writes results torskills/<dir>/eval/scene_<scene_id>.json. (L3189)_default_benchmark_scene_out_path(vla_spec, scene) -> Path— Mirrors_default_benchmark_out_pathbut for single-scene JSONs; thescene_prefix distinguishes per-scene outputs from multi-task suite outputs under the same rSkill directory. (L3506)deploy sim(--config, --robot, --dashboard-port, --foxglove/--no-foxglove, --foxglove-port, --reset-to-pose-service, --hal, --memory-dir, --initial-task, --dry-run)— Boots the full ROS graph (dashboard, safety kernel, reasoner, prompt router, runtime, HAL) against a digital-twin HAL via one genericdeploy_e2e.launch.pylaunch. No envelope YAML is written: the launch computes the safety envelope intersection and forwards it as kernel ROS parameters. No--rskillflag — the reasoner picks the active rSkill dynamically from the in-tree palette.--memory-dirwires in the deploy's self-maintained memory, 3D scene graph, and occupancy grid when present. Defined inopenral_cli.deploy_sim.deploy_list()—openral deploy list; list every deploy scene underscenes/deploy/*.yaml, one paste-able--configpath per line; anything under alocal/directory (the gitignoredscenes/deploy/local/operator copies) is skipped. (L3911)dashboard(--host, --port, --log-level, --inprocess)(L3841) —openral dashboard. Boots a live debug pane that doubles as an OTLP/HTTP receiver on the same port (default127.0.0.1:4318), so it works without Jaeger/Tempo.--inprocessspawns a shell-quoted child workload with the OTLP env vars pre-set, so a demo needs no second shell. Inverse path:openral sim run --dashboard.replay(BAG, --trace, --frame, --dataset-root, --dashboard, --out)(L4318) — Reads a.mcapfile or rosbag2 directory, joins with OTel spans from--dashboard, and emits a chronological JSON timeline keyed bytrace_id.--frame <repo_id>/<episode>/<frame>pivots from a written LeRobotDataset frame instead of an explicit--trace._resolve_frame_trace_id(frame_spec, dataset_root) -> str— Parse a<repo_id>/<episode>/<frame>spec (rsplit('/', 2); repo_id keeps its own slash) and return that frame's storedtrace_idviaread_frame_trace.typer.Exit(2)on a malformed spec, missing frame, or a frame with no trace.record(--out, --profile, --storage, --extra-topic, --extra-regex, --dry-run)(L4394) — Spawnros2 bag recordwithslim(default) orfullprofile presets;--dry-runprints the composed argv without forking.profile_session(ACTION, --output, --name)(L4471) —openral profile session (start | stop | view). Drive an LTTng session for the realtime hot path; surfacesLttngSessionErrorcleanly whenlttngis missing on PATH. SetOPENRAL_ROS2_TRACING=1on the agent process to emit tracepoints; without the gate every tracepoint is a no-op._print_benchmark_run_plan(scenes, *, suite_id, vla_spec) -> None—benchmark run --dry-run's plan printer. Applies the samefilter_scenes_for_skill/_manifest_for_filterpassrun_benchmarkapplies, so the printed plan is the plan that would execute: exits 1 when the rSkill'sevaluated_taskscover no suite task (previously dry-ran clean, then raisedROSCapabilityMismatchon the real run), and prints a skip note for partial coverage._resolve_benchmark_suite(suite: str, benchmarks_dir: Path) -> tuple[list[BenchmarkScene], str]— Accept either a built-in id (looked up atbenchmarks/<id>.yaml) or a direct YAML path; raisetyper.BadParameterlisting catalogue entries on a typo._parse_rskill_cli_arg(raw)— Parse--rskill <ref>into aVLASpec. Accepts bare names (smolvla-libero), paths (rskills/smolvla-libero), or HF repo ids; validates viaopenral_rskill.validate_skill_refsoVLASpec.weights_urirejects explicit URI schemes. The adapter id is read from the manifest'smodel_family. Raisestyper.BadParameteron an invalid scheme or empty input.behavior_serve(--rskill, --task, --instruction, --host, --port, --device, --state-dim, --action-dim)(L3660) — Serve one rSkill through the official BEHAVIOR Challenge WebSocket policy protocol. Defaults to the R1Pro observation/action contract; OmniGibson remains out-of-process and owns task loading, metrics, and videos._summarize_results(results: dict[str, object]) -> str— Headline-line picker for free-formresultsblocks (*_avg→ numeric → status fallback). (L3613)_path_completer(text: str, state: int) -> str | None— Stdlibreadline-shaped Tab completer for_run_repl: globstext*(with~expansion), adds trailing/to directories, and rewrites$HOMEback to~. Lets the REPL complete filesystem paths after flags like--config/--rskill.render_banner(version_str: str, *, width: int | None = None) -> RenderableType(L180) — Builds the interactive-REPL welcome box as a content-sized richPanel, choosing a two-column or stacked layout to fitwidth. Returns a renderable (not a print), so it exports to plain text in tests independent of TTY/colour state._logo_wordmark(*, stacked: bool) -> RenderableType— Logo mark + OPENRAL wordmark side by side (vertical-middle grid) or stacked._identity(*, stacked: bool) -> RenderableType—_logo_wordmarkabove the tagline + capability strip._kv_grid(rows: tuple[tuple[str, str], ...], key_style: str) -> Table— Borderless two-columnkey valuegrid (styled key, dim value) used for the links and commands cells._LOGO_ART: str(L101) — White (single-weight, no gradient) OpenRAL logo mark — a 6-row block icon (horns flaring out and down into a rounded head, eyes below) matching the wordmark height._WORDMARK_ART: str(L112) — The OPENRAL block-letter wordmark._TAGLINE_TAIL: Final[str](L122) —" — Open Robot Agentic Layer (harness) for embodied AI", appended to the banner identity line._CAPABILITIES: Final[str](L123) —"fast policies · slow reasoning · rewards · perception · control"capability strip shown under the tagline._LINKS(L125) — Community links (Discord / GitHub / Hugging Face / Website) shown in the right cell._COMMANDS: tuple[tuple[str, str], ...](L132) — Quick-start commands (doctor,rskill search,help,exit) shown in the right cell._WIDE_MIN(L143) — Minimum terminal columns (127) for the two-column layout, measured from the rendered box so the richest layout that fits is chosen and the box never overflows._SIDE_BY_SIDE_MIN: int(L144) — Minimum terminal columns (82) below which the logo stacks above the wordmark instead of sharing a line._cli_version() -> str— Best-effortopenral-clipackage version for the banner title; suppressesPackageNotFoundErrorand falls back to"0.0.0"._print_banner() -> None— Printrender_banner(_cli_version(), width=console.width)to the REPLconsoleat_run_replstartup, sizing to the live terminal.
python/cli/src/openral_cli/_dds_scope.py
Keeps a simulation and a real robot off each other's ROS graph.
Two controls, covering different directions: confinement stops a sim reaching a robot, and the occupancy guard stops a launch in either direction joining a graph a robot is already on — something confinement alone cannot do, since a real robot's graph legitimately spans machines and is never confined. The guard's signature is a foreign /joint_states publisher, because every robot has exactly one whether real or simulated.
confine_sim_scope(env: dict[str, str]) -> None— Pin a sim toROS_AUTOMATIC_DISCOVERY_RANGE=LOCALHOST+ROS_DOMAIN_ID=77, in place.LOCALHOSTnotOFF: measured against the live robot,LOCALHOSThid its nodes while same-host discovery was unaffected, whereasOFFhides the sim's own nodes from each other.setdefault, so an explicitly-exported scope wins and attaching a dashboard from another host stays possible. (L101)assert_graph_unoccupied(env, *, hal_mode: str) -> None— Refuse to launch onto a graph that already carries a robot. Symmetric: same rule whichever side starts first,hal_modeonly shapes the wording. RaisesROSConfigErrornaming the occupying node, the scope scanned, and the ros2_control signature when the other side looks like real hardware. An unreadable graph also raises — a guard that passes when its instrument is broken is worse than no guard. (L187)ALLOW_UNVERIFIED_GRAPH_ENV: Final[str]—OPENRAL_ALLOW_UNVERIFIED_GRAPH=1. Waives only the unreadable-graph refusal (rclpywon't import, the probe crashed) — a broken instrument, not a detected hazard. The occupied-graph refusal is unwaivable: no env var starts a robot onto a graph that already carries one. (L65)SIM_DOMAIN_ID: Final[str]—"77". Any non-zero value would do; 0 is where an unconfigured robot host lands. (L69)_PROBE_SPIN_S: Final[float]— Seconds the graph-scope probe spins before deciding the graph is empty (3.0); too short passes the guard on an occupied graph. (L75)_PROBE_NODE_NAME: Final[str]—"openral_graph_scope_probe", the probe's own node name (excluded from the occupancy count). (L77)_NO_ROS: Final[str]— Sentinel_scan_graphreturns whenrclpyis not importable; distinct from a probe failure — a non-ROS host has no graph to refuse. (L85)_MAX_NODES_LISTED: Final[int]—12; how many occupying node names the refusal message lists before eliding. (L89)_HARDWARE_SIGNATURES: Final[tuple[str, ...]]—ros2_controlnode-name substrings (controller_manager,joint_state_broadcaster,hardware_interface) that flag an occupying graph as "looks like real hardware" in the refusal wording only. (L94)
The
ros2CLI daemon is unusable for this check: it answers from the environment it was started with, soros2 node listunderLOCALHOSTstill reported the remote robot's nodes. The probe runs in a subprocess under the exact launch env and talks torclpydirectly.
python/cli/src/openral_cli/_hf_publish.py
Shared HF Hub publishing helpers, de-duped from tools/rskill_publisher.py.
resolve_token(token_arg: str | None = None) -> str— Resolve the HF token from arg →HF_TOKEN→HUGGINGFACE_HUB_TOKEN. RaisesROSConfigErrorwith actionable hint when missing. (L47)ensure_private(api: HfApi, repo_id: str, *, repo_type: str = "model") -> None— Re-fetch repo metadata and raiseROSConfigErrorif the repo is public. Critical safety gate;repo_typesupports"model"/"dataset"/"space". (L80)IGNORE_PATTERNS: Final[list[str]]— Glob patterns excluded from every upload (.env,__pycache__,*.key, etc.). (L35)
python/cli/src/openral_cli/_rskill_doc_validator.py
rSkill README + manifest publish-readiness validator (the publish gate).
Hard gate consumed by tools/rskill_publisher.py: refuses publish when the README is missing/too short/missing sections/has template sentinels, or the manifest still has template defaults. Supports single-hop delegation via an HTML marker so sibling stubs can share one canonical README.
class DocValidationIssue(BaseModel)— one problem record:severity("error"/"warning"),field(readme.section.License/manifest.description/ …),message. (L147)class DocValidationReport(BaseModel)—skill_dir+manifest_name+issues;.is_valid/.errors/.warningsderived properties. (L164)is_valid() -> bool— True iff noerror-severity issue was found. (L184)errors() -> list[DocValidationIssue]— Theissuessubset withseverity == "error". (L189)warnings() -> list[DocValidationIssue]— Theissuessubset withseverity == "warning". (L194)validate_rskill_docs(skill_dir: Path, manifest: RSkillManifest) -> DocValidationReport— Public entry point; composes README and manifest checks into one report. (L202)_validate_readme(skill_dir) -> list[DocValidationIssue]— Presence, min-length, required-sections-via-heading, placeholder-sentinel scan. Honors single-hop delegation._resolve_delegation(skill_dir, body) -> (list[str] | None, list[DocValidationIssue])— Resolve aopenral:rskill-readme-delegates-tomarker; rejects double-hop and missing-target._validate_manifest_content(manifest) -> list[DocValidationIssue]— Description / provenance (paper_url∨source_repo) /name/weights_uri/source_repochecks beyond what the Pydantic schema catches.format_report(report) -> str— Human-readable summary used by the publisher dry-run. (L508)README_REQUIRED_SECTIONS: Final[tuple[tuple[str, tuple[str, ...]], ...]]— Canonical README section coverage (display label → acceptable case-insensitive heading substrings). (L69)PLACEHOLDER_SENTINELS: Final[tuple[str, ...]]— Strings (plusRSKILL_TEMPLATE_SENTINELS) that must not survive into a published README or manifest. (L102)PLACEHOLDER_MANIFEST_DESCRIPTION_MARKERS: Final[tuple[str, ...]]— Substrings that flag a manifestdescriptionleft at the template default. (L118)DELEGATION_MARKER_NAME: Final[str]—"openral:rskill-readme-delegates-to", the HTML-comment marker name letting a stub README delegate section checks to a sibling. (L124)_README_FILENAME: Final[str]—"README.md". (L54)_MIN_README_BODY_LENGTH: Final[int]—200; minimum stripped-body length before the README is flagged too short. (L55)_MIN_DESCRIPTION_LENGTH: Final[int]—30; minimum manifestdescriptionlength. (L56)_MEDIA_PATTERN: Final[re.Pattern[str]]— Matches a markdown image,<img>, or<video>tag; missing preview media is a warning, not a publish-blocking error. (L58)_DELEGATION_MARKER_PATTERN: Final[re.Pattern[str]]— Compiled regex parsing theDELEGATION_MARKER_NAMEHTML comment into its target path. (L138)
python/cli/src/openral_cli/_rskill_readme.py
Derive an HF model-card README from an rSkill manifest, the single source of truth.
The manifest-derived front-matter is emitted uniformly so every published repo, private or public, carries a consistent card; tools/rskill_publisher.py rebuilds it at publish time while preserving the human-written prose body verbatim. Hand-curated extras already in the in-tree README are unioned in; derived fields win on conflict.
build_rskill_frontmatter(manifest: RSkillManifest) -> dict[str, Any]— Pure, deterministic derivation of the model-card front-matter dict (no network).base_modelcomes fromsource_repo(NF4 repos self-host weights viaweights_uri);base_model_relationisquantizedwhen a quantization block is present, elsefinetune. (L113)render_frontmatter(fm: dict[str, Any]) -> str— Render the dict as a----fenced YAML block, stable field order. (L172)build_rskill_readme(manifest: RSkillManifest, body: str) -> str— Full README = merged front-matter + prose body; strips any existing front-matter frombodyand merges its curated extras. Idempotent. (L178)_LEROBOT_FAMILIES: frozenset[str]—model_familyvalues that load through lerobot'sPolicyProcessorPipeline(solibrary_nameshould point there); gr00t/rldx (out-of-process) and molmoact2 (transformers custom-code) are excluded. (L38)_LICENSE_MAP: dict[RSkillLicensePosture, tuple[str, str | None]]— License posture →(HF license id, optional license_name); non-standard postures map to"other"+ a descriptivelicense_name. (L43)_NON_QUANTIZED_DTYPES: frozenset[str]— dtype strings (fp32/bf16/fp16/float32/float16) that do NOT imply a quantizedbase_model_relation. (L55)_KIND_PIPELINE_TAG: dict[str, str]— rSkillkind→ HFpipeline_tag(detector→object-detection,segmenter→mask-generation); other kinds fall back torobotics. (L61)_KIND_EXTRA_TAGS: dict[str, list[str]]— rSkillkind→ extra model-card tags (vla,detector,segmenter). (L65)_FRONTMATTER_RE: re.Pattern[str]— Matches a leading----fenced YAML front-matter block to strip from an existing README body. (L82)
python/cli/src/openral_cli/deploy_sim.py
openral deploy sim — boot the full ROS graph against a digital-twin HAL via ros2 launch openral_rskill_ros deploy_e2e.launch.py (one generic launch, no --rskill).
deploy_sim_command(--config, --robot, --dashboard-port, --foxglove/--no-foxglove, --foxglove-port, --reset-to-pose-service, --hal, --memory-dir, --initial-task, --dry-run, …)(L3127) — Typer callback fordeploy sim. Resolves the launch invocation, runs preflight checks (package discoverability, orphan-process reap, palette dependency + VRAM fit), writes the ephemeral envelope + HAL params, and shellsros2 launchvia_run_launch(spawned by_spawn_launchin its own session with SIGINT reset to its default disposition — an ignored SIGINT survives exec, and a CLI started with it ignored, e.g.cmd &in a script, otherwise handed the launch a SIGINT it ignored, so graceful teardown never reached a node), which tears the launch's process group down in escalating stages on exit (OPENRAL_SKIP_ORPHAN_REAP=1opts a parallel harness out of both the startup and the exit sweep so concurrent workers don't kill each other's graphs;resolve_launch_invocationrefuses it withROSConfigErrorunderhal_mode="real", no override — Safety-WG HZ-0390-5).assert_ros2_packages_discoverable(packages, *, prefix_lookup=_ros2_pkg_prefix) -> None(L2739) — RaisesROSConfigErrorlisting everypkgros2 pkg prefixcannot resolve — catches an un-sourced workspace overlay or a stalejust ros2-buildbeforeros2 launch's terse failure.prefix_lookupis injectable for a deterministic test fake._repo_root_from(start) -> Path(L306) — Resolves the repo root holdingrobots/+rskills/:$OPENRAL_REPO_ROOToverride, then walking up fromstart(editable install), then from the cwd (wheel install run from inside a checkout) — the cwd step is what makesdeploy simwork off a published wheel at all, since the manifest trees are repo data with no site-packages ancestor._preflight_scene_assets(config) -> None(L2772) — Provisions the scene's sim backend beforeros2 launch, backend-agnostic via each scene's ownSCENES.provisionhook, so a multi-GB asset pull or license prompt happens on a reachable TTY instead of timing out inside the HAL's boundedon_configure. Advisory — a failure warns and continues, since the backend retries and raises there. Called afterassert_ros2_packages_discoverableso a missing overlay fails fast, not after a multi-GB download._preflight_palette_deps(*, repo_root, robot_yaml, commercial_deployment=False) -> None— Advisory (not a gate) policy-extras preflight mirroringReasonerNode._maybe_seed_palette_from_search_paths: probes each capability-matched rSkill's importability. When any are blocked it either auto-installs (OPENRAL_AUTO_INSTALL_DEPS=1), prompts on a TTY, or drops them from the palette and proceeds — failing hard only if that would leave the palette empty. Also calls_apply_palette_head_camonce the palette is known non-empty._apply_palette_head_cam(matched) -> bool— SetsOPENRAL_ROBOCASA_HEAD_CAM=1when any capability-matched rSkill declaresobservation.images.headinsensors_required, so RoboCasa's forward nav camera renders without per-scene config. An operator-set value always wins. Called from_preflight_palette_deps; defined inopenral_cli.deploy_sim.run_launch_invocation(invocation, *, run_preflight=True) -> int(L1959) — Shared shelling path fordeploy sim+deploy run: underrun_preflight(the default) runs the same preflight sequence asdeploy sim(package discoverability, orphan reap, palette deps, reward VRAM fit), then writes the ephemeral HAL params and shells the resolved argv via_run_launch, returning its exit code.run_preflight=Falseis a full bypass. Defined inopenral_cli.deploy_sim.DDS_TRANSPORT_READY_MARKER: Final[str] = "dds_transport_ready:"(L2402) — Printed once the orphan reap and stale Fast-DDS shm purge (/dev/shm/fastrtps_*for Fast-DDS 2.x,fastdds_*for 3.x) have run and beforeros2 launchspawns, asdds_transport_ready: rmw=<rmw> shm_purged=<N> shm_kept_live=<M> fastdds_profile=<path>(n/aon Cyclone/Zenoh); waiters gate on this line. The purge only unlinks segment/port groups no live process has open, mapped orflocked (per/proc/<pid>/{fd,maps}and/proc/locks), so a participant already running keeps its shared memory. Outside the initial PID namespace (inside a container) liveness cannot be proven — a driver in another namespace sharing/dev/shmis invisible — so nothing is unlinked. Defined inopenral_cli.deploy_sim.FASTDDS_SHM_CLEAN_ENV: Final[str] = "OPENRAL_FASTDDS_SHM_CLEAN"(L2466) — Set to0to skip the Fast-DDS shm purge entirely (counts print asn/a). Defined inopenral_cli.deploy_sim.FASTDDS_PROFILES_ENV: Final[str] = "FASTRTPS_DEFAULT_PROFILES_FILE"(L2469) — The env var Fast DDS 2.x (Jazzy) reads its default XML profile from, once per process. Defined inopenral_cli.deploy_sim.FASTDDS_LARGE_DATA_PROFILE: Final[Path](L2472) —openral_cli/fastdds_large_data.xml: the builtin transports (SHM + UDPv4) with a 16 MiB shared-memory segment instead of 512 KiB, so same-host best-effort samples above ~512 KB (depth clouds, depth/colour images) are delivered (docs/reference/dds-large-messages.md). Defined inopenral_cli.deploy_sim.apply_fastdds_large_data_profile(env: dict[str, str]) -> str(L2475) — ExportsFASTDDS_LARGE_DATA_PROFILEasFASTRTPS_DEFAULT_PROFILES_FILEinto the launch env unless the operator set a non-empty one; returns the profile the graph loads. Called by_apply_rmw_defaulton Fast DDS (not Cyclone/Zenoh), which names it on thedds_transport_ready:line. Every graph process, scenedrivers:included, inherits it; only the publisher's segment matters. Defined inopenral_cli.deploy_sim._preflight_depth_extrinsics(description, overlays, unit, scene_sensors=(), *, estimated_camera=None) -> None(L2674) — Real-deploy gate (theruntime.estimated_depth_cameracounts as a cloud source) called fromresolve_launch_invocationwhenhal_mode == "real", octomap is on and the kernel check is on:openral_core.depth_extrinsic.depth_extrinsic_problemsover the unit-overlaid description, its overlays and the scene's sensors; raisesROSConfigErrornaming every cloud source whose mount is undeclared (a robot-mounted one must come from the unit overlay). No override flag;--no-enable-octomap-kernel-checkis the only way past._detect_gpu_free_vram_gb() -> float—openral_core.gpu.detect_gpu_vram_gb("memory.free"); budgets against free, not total, VRAM, since a desktop compositor or sibling worktree can already hold GBs the VLA+reward pair never sees. Returns0.0on any failure, which makes the preflight skip and defer to the reasoner's runtime check. Defined inopenral_cli.deploy_sim._capability_matched_manifests(repo_root, description, *, commercial_deployment=False) -> list[RSkillManifest]— Loads everyrskills/*/rskill.yamland returns those the reasoner palette would admit, sincedeploy simdoesn't preselect a VLA and reward resolution + the VRAM preflight reason over this set instead. Defined inopenral_cli.deploy_sim._resolve_reward_monitor_manifest(*, repo_root, description, explicit_manifest) -> str— Reward-model resolution: an explicit--reward-monitor-manifestwins, else derives it from the capability-matched VLA palette'sreward_rskill_nameconsensus, defaulting torobometer-4bwhen none names one or they disagree. Defined inopenral_cli.deploy_sim._preflight_reward_vram_fit(*, repo_root, description, reward_manifest_path, gpu_budget_gb, commercial_deployment=False) -> None— Pre-launch VLA↔reward VRAM gate: checks each capability-matched VLA against the reward model viaassert_vla_reward_fits, advisory per-VLA but a hardtyper.Exit(1)only when none can co-reside with it. Defined inopenral_cli.deploy_sim._ros2_argv_head() -> list[str]— Argv prefix for theros2 launchshell-out:[sys.executable, <abs path to ros2>]when theros2script's shebang names a Python, else bare["ros2"]. Running the launch parser undersys.executablekeeps the venv'sinclude-system-site-packages = falsein effect, so apt-installed system packages (e.g.python3-pandas) can't shadow the venv's own compiled extensions and crash the launch. Defined inopenral_cli.deploy_sim.- Tegra allocator gate (in
_prepare_launch_env) —openral_core.is_tegra_host()skips theexpandable_segments:Trueallocator default on an L4T host: the iGPU shares system RAM (no discrete-card fragmentation to recover) and torch's expandable path queries NVML GPU-fabric info the iGPU cannot answer — on the lab AGX Orin (torch 2.13+cu130, 2026-09-22) the runtime_node's first CUDA allocation raisedExpected NVML_SUCCESS == …nvmlDeviceGetGpuFabricInfoV_…363 s into a policy load. DGX Spark (GB10) keeps the default: expandable segments are reported to work and help there (vllm#55569; unsloth-zoo#1235 carves GB10 out of the same Tegra exclusion); not verified on our hosts. An operator's explicit setting still passes through. -
resolve_launch_invocation(*, config=None, robot_override, dashboard_port, reset_to_pose_service, approach_skill_id=None, hal_param_overrides=None, hal_mode="sim", enable_slam=None, enable_nav2=None, enable_octomap=None, enable_dashboard=True, initial_task_prompt=None, …) -> LaunchInvocation(L908) — Pure resolver shared bydeploy sim/deploy run: loads theDeployScene, merges its committedruntime:block under the CLI flags (precedence CLI > scene > auto), and merges anyhal:binding defaults intohal_paramsbelow a--haloverride. Forwardsworkcell_json:=...when the scene declares safety/ACM overrides, so the launch applies tighten-only safety before configuring the kernel. Always forwards the scene YAML asdeploy_config:=(sim and real) so the launch derives the HAL autostart budget frombackend_options.boot_timeout_sand merges scenesensors:; the real-only camera leg is gated onhal_modeinside the launch.LaunchInvocationalso carriespreload_rskill_id/preload_rskill_revision/preload_prompt(scene-only, fromDeployRuntime), forwarded aspreload_rskill_id:=/preload_rskill_revision:=/preload_prompt:=only when the scene sets them.runtime.workspace_contact_enabled(issue #349) forwardsworkspace_contact_enabled:=true+workspace_contact_max_speed_mps:=+workspace_contact_max_lead_m:=only when on, andhal_mode="real"(deploy run) refuses it withROSConfigError(sim-only until Safety-WG review). -
deploy_run(--config, --robot, --hal, --dashboard/--no-dashboard, --dashboard-port, --foxglove/--no-foxglove, --foxglove-port, --initial-task, --enable-reward-monitor/--no-enable-reward-monitor, --reward-monitor-manifest, --dataset-out, --dataset-repo-id, --dataset-license, --dry-run)(python/cli/src/openral_cli/main.pyL3942) — Typer callback fordeploy run: resolves theDeploySceneand shells the same launch graph asdeploy simwithhal_mode:=real.--enable-reward-monitorbrings up the reward monitor parallel to the VLA;--dataset-out/--dataset-repo-id/--dataset-licensemirrordeploy sim's recording args, since a real-hardware session is exactly the kind of run worth recording. Defined inopenral_cli.main. deploy_validate(--config, --robot, --hal)(python/cli/src/openral_cli/main.pyL4115) — Pre-run readiness check foropenral deploy run, touching no hardware. Loads and resolves theDeployScene, then checks real-run-only requirements that otherwise fail late: a declared serialport, calibration files present whencalibrate_on_connect=false, and every deploy sensor'sdeploy_binding(the manifest's robot cameras with the host'sRobotUnitoverlay, plus the scene's workcell cameras, viamerge_deploy_sensors); a scene naming a robot camera is refused. Missing committed data errors (exit non-zero); a device merely not attached now only warns._parse_hal_overrides(raw: list[str] | None) -> dict[str, object]— Parse repeated--hal key=valueflags. Values are JSON-decoded where possible (so--hal viewer_enabled=falseparses as bool); fall back to raw string for--hal port=/dev/ttyUSB0.class LaunchInvocation(L133) — Frozen dataclass carrying the resolved launch config forros2 launch deploy_e2e.launch.py: robot/HAL identity and mode, the optional MoveItapproach_skill_id, and the object-detector, reward-monitor, scene-VLM, and critic-producer legs. Each optional field forwards asfield:=…only when set, sinceros2 launchrejects an emptyname:=._vision_attachment_hal_params(leg: VisionAttachmentRuntime) -> dict[str, object](L446) — Maps an enabledDeployRuntime.vision_attachmentto the HAL node'svision_attachment_*params (enabled,camera,depth_topic,camera_info_topic= the DEPTH camera_info,deadline_s,evidence_timeout_s,grasp_target_enabled,grasp_target_approach_m(None→0.0= off),grasp_target_margin_m(always forwarded;0.0= unbloated),grasp_target_premeasure,place_target_enabled,release_timeout_s,tf_framesaslink=frameentries, omitted when empty).resolve_launch_invocationmerges them withsetdefault(a--haloverride wins), forwardsenable_vision_attachment:=true+vision_attachment_camera/_rgb_topic/_rgb_camera_info_topic/_segmenter_manifest(scene-relative, else repo-relative, resolved absolute) /_segmenter_deviceonly when enabled, plusvision_attachment_tf_frames:=(the HAL's finalvision_attachment_tf_frameslist, comma-joined, for the octomap bridge'sattach_link_tf_frames) when that list is non-empty, and runs_preflight_depth_extrinsicsondeploy runwith the leg on.deploy runwithgrasp_allowance_enabledbut no enabled leg withgrasp_target_enabledis aROSConfigError, judged on the EFFECTIVE HAL params (--hal vision_attachment_enabled=false/vision_attachment_grasp_target_enabled=falsecannot defeat it). rclpy silently drops a name the HAL does not declare, sotests/integration/test_vision_attachment_hal_params_live.pyloads the written params file into the realManifestHALLifecycleNodeand requires every forwardedvision_attachment_*name declared with the scene's value._omdet_runtime_available() -> bool— Probe (importlib.util.find_specfortransformers+timm) deciding whetherresolve_launch_invocation's default object detector is the open-vocabomdet-turbo-indoorcontinuous backend or the in-tree RT-DETR COCO ONNX fallback. Patched in unit tests to exercise both branches deterministically. Defined inopenral_cli.deploy_sim.class _HalSpec(L82) — Frozen dataclasspackage/executable/node_name/bare_twin_sim, derived per deploy (no per-robot table).packageis always_GENERIC_HAL_PACKAGE("openral_hal_node", the one manifest-driven HAL lifecycle node package);node_nameis the ROS node NAMEopenral_hal_<robot_id>(the launch's__node:=remap — not a package name). A robot's vendor real bringup comes from the manifest'shal.real_bringup, not from the package._resolve_clock_origin(*, hal_mode, config, pinned=None, cloud_topic="") -> str(L525) — The graph's clock authority: a scene'sruntime.clock_originwins; elsehost_wallfordeploy run, and fordeploy simsimulationwhen the scene's backend exposes a clock (every scene-configured bare twin, and scene-attached backends registered withsim_clock=True),host_wallotherwise — including a scene-less--robot-only twin, and a twin whose octomapcloud_topiclies outside/openral/cameras/(a real driver stamping on wall-clock). RaisesROSConfigErrorfor asimulationpin with no clock or with such a real-driver cloud.resolve_launch_invocationalso refuses (ROSConfigError) octomap enabled — explicitly, or auto-enabled by a cloud source (openral_core.deploy_cloud_topicnon-empty, or ondeploy runanySensorSpec.is_cloud_sourcein manifest ∪ scene) — with no cloud to map, and forwardsDeployRuntime.voxel_freshness_sasworld_voxel_deadline_s:=/max_octree_age_s:=._scene_builds_bare_twin(deploy_scene: DeployScene | None) -> bool(L104) — The SCENE decides sim HAL shape: bare MuJoCo twin iff there is no scene, the DeployScene declares its owncomposition, or itsscene.idis not inopenral_sim.SCENES; otherwise scene-attach (SimAttachedHALviasim_env_yaml)._derive_hal_spec(robot_id: str, deploy_scene: DeployScene | None) -> _HalSpec(L122) — Builds the_HalSpecabove. Adding a robot todeploy sim|runneeds onlyrobots/<id>/robot.yaml(or$OPENRAL_ROBOTS_DIR/<id>/robot.yaml);resolve_launch_invocationresolves it viaopenral_sim.policies.robots.resolve_robot_manifest, rejects sim mode early when the robot has nohal.sim, nosim:block and a bare-twin scene, and forwards the merged DeployScenehal.defaults+--halkwargs as thehal_transport_jsonnode param.
python/cli/src/openral_cli/dataset.py
openral dataset Typer app + push subcommand.
dataset_app: typer.Typer— Public Typer group mounted underopenralatname="dataset". (L41)push_command(root, *, repo_id, yes, dry_run, token, commit_message) -> None—openral dataset push <root>. Readsmeta/info.json, resolves the repo_id, runs the PII consent prompt (skippable via--yesorOPENRAL_DATASET_CONSENT=1), thenHfApi.create_repo(private=True) → ensure_private → upload_folder. (L294)from_bag_command(bag_path, *, robot, output, repo_id, license, fps) -> None—openral dataset from-bag <bag.mcap> --robot robots/<x>/robot.yaml --output <ds-root>. CallsRosbag2ToLeRobotConverter.from_bag; produces a v3 dataset ready foropenral dataset push. (L59)_read_info_json(root: Path) -> dict[str, object]— Parsemeta/info.json; raisesROSConfigErroron missing / malformed file. (L185)_camera_keys_from_info(info) -> list[str]— Extractobservation.images.*feature keys for the consent prompt's camera disclosure. (L212)_confirm_consent(repo_id, root, info, yes) -> None— Render the PII / regulatory disclosure Panel, accept--yes/ env-var overrides, refuse non-TTY without override. (L220)_resolve_repo_id(root, info, cli_repo_id) -> str— CLI override → info.json → error. Validates<org>/<name>format. (L266)_CONSENT_ENV_VAR: Final[str]—"OPENRAL_DATASET_CONSENT"; unattended-consent override for the PII disclosure prompt (mirrors--yes). (L38)_CONSENT_PROMPT_BODY: str— Rendered body text of the PII / regulatory disclosure Panel shown by_confirm_consent. (L158)
python/cli/src/openral_cli/collision.py
openral collision lower|check Typer app: offline URDF/SRDF → manifest self-collision model. Defers openral_safety.urdf_lowering.lower_robot (yourdfpy/trimesh) so openral --help stays fast.
collision_app: typer.Typer— Public Typer group mounted underopenralatname="collision".parse_tight_links(raw) -> dict[str, int]—--tight-link NAME[=K]values as{link: pieces}: a bare name is one box + hull,NAME=Kcuts the link intoKslabs (urdf_lowering._split_tight_pieces); a repeated name keeps the largerK. Raisestyper.BadParameterfor a non-positive or non-integerK. (L485)lower(robot, *, write, acm_only, geometry_only, emit_cumotion=None, tight_link=[]) -> None—openral collision lower --robot <yaml>. Prints a unified diff of the regenerated collision blocks and mutates the manifest only with--write; MJCF-lowered robots fit their geometry from the MJCF like URDF robots do from the URDF.--tight-link NAME[=K](repeatable) lowers that link as a box plus its exact hull (tight_geometry), inKslabs when given; links the manifest already refines stay refined, in as many pieces as they carry. Refused together with--acm-only(which keeps the manifest geometry). Refuses (exit 3, untouched) when the lowered geometry is looser than what's already committed, with no override flag — deleting the tighter entries is the only way past it.--emit-cumotion <path>additionally renders a cuRobo robot-config from the manifest's own collision geometry. (L594)class GeometryLoosening— Frozen dataclass report record:link_name,shipped,lowered,volume_ratio,circumradius_ratio.lowered == ""with infinite ratios means the lowering emits no primitive for a link the manifest covers. (L120)collision_primitive_envelope(shape) -> tuple[float, float]—(volume_m³, circumradius_m)for oneCollisionShape, closed-form for all three kinds — the two numbers a "did this get looser?" comparison needs, independent oforigin_xyz_rpy. RaisesROSConfigErroron an unknown discriminator rather than silently scoring it zero. (L147)_BLOCK_FLOAT_DP: int—4; decimal places bothrender_blocksandgeometry_loosening's quantization round to, so a re-rendered manifest never reads as looser than itself. (L200)_render_tight(tight) -> list[str]— A box'stight_geometryblock forrender_blocks, at full float precision (the refinement is written at 1 nm by the lowering and has no headroom, so it is never rounded to 4 dp). (L409)geometry_loosening(shipped, lowered) -> list[GeometryLoosening]— Links where re-lowering would claim more space than the committed manifest, worst volume ratio first, reported when volume or circumradius grows (they can fail independently). Both sides are quantized torender_blocks's 4 dp first, so a re-rendered manifest never reads as looser than itself; a dropped link gets infinite ratios, an added link is not reported. (L239)_JOINT_NAME_RE: re.Pattern[str]— Matches a manifest joint-list- name: "<joint>"line, used byinject_joint_fkto locate the block to patch. (L346)_FK_ZERO_SNAP_M: float—1e-9; magnitudes below this snap to0.0wheninject_joint_fkrenders FK floats, avoiding-0.0/near-zero noise in the diff. (L351)_lower(robot_path, *, acm_only, geometry_only, tight_links=()) -> tuple[RobotDescription, LoweredCollisionModel]— Shared loader: parse the manifest and lower vialower_robot_auto(the provenance dispatcher). Used by_lowered_textand the--emit-cumotionpath. (L520)_lowered_text(robot_path, *, acm_only, geometry_only, tight_links=()) -> tuple[str, str, list[GeometryLoosening]]—(current_text, spliced_text, loosening)— the one place holding the committed manifest and its replacement at once, which is why the loosening comparison lives here. The third element is empty whenever the geometry block isn't rewritten at all, so a caller can't mistake "not compared" for "compared and clean". (L549)check(robot, *, all_robots, acm_only, geometry_only) -> None—openral collision check (--robot <yaml> | --all). Exits 1 if any manifest drifts from its lowered model (the fleet-wide ACM drift guard). A drifting manifest whose re-lower would loosen it also says so, rather than sending the reader into alower --writethat will refuse. (L694)splice_collision_blocks(text, *, geometry_block=None, acm_block=None) -> str— Replace only the two collision blocks in a manifest's text, preserving every other line + comment (absorbs the block's own header comment so repeated lowers stay idempotent). (L101)render_blocks(model) -> tuple[str, str]— Render aLoweredCollisionModelto(geometry_block, acm_block)YAML text with a generated-provenance header; floats rounded to 4 dp for a stable diff. (L434)inject_joint_fk(text, joint_fk) -> str— Injectorigin_xyz/origin_rpy/axis_xyzinto the named manifest joint blocks (matched by name), dropping any pre-existing FK lines. Used when onboarding a robot onto self-collision (the kernel needs joint FK to place capsules). Idempotent; preserves all other lines/comments. (L364)
python/cli/src/openral_cli/behavior.py
BEHAVIOR Challenge WebSocket policy server backing openral behavior serve (behavior_serve in main.py).
_R1PRO_CAMERA_SENSORS— Re-export ofopenral_sim._behavior_wire.CAMERA_SENSORS, the R1Pro observation contract's camera sensor keys. (L25)_R1PRO_STATE_KEY— Re-export ofopenral_sim._behavior_wire.STATE_KEY. (L26)_R1PRO_STATE_DIM— Re-export ofopenral_sim._behavior_wire.STATE_DIM. (L27)_R1PRO_ACTION_DIM— Re-export ofopenral_sim._behavior_wire.ACTION_DIM. (L28)_RGB_RANK: int—3; expectedndarray.ndimfor an RGB observation frame. (L29)_RGB_CHANNELS: int—3; expected channel count for an RGB observation frame. (L30)
python/cli/src/openral_cli/check.py
openral check: static, host-independent validation of the declarative robot/skill/scene set. Imports only openral_core (no hardware probe); complements the host-specific openral rskill check. Manifest JSON-Schema emission lives in tools/schema_export.py (CI-gated), not here.
class CheckFinding(BaseModel)— One problem:rule(robot_parse/rskill_parse/scene_parse/asset_ref/scene_robot_id/scene_sensor_geometry/robot_unit/scene_robot_unit/embodiment_reach/frames),severity(error/warning),target,message. (L84)class GraphCheckReport(BaseModel)— Typedopenral check --jsonpayload:schema_version("0.1"),generated_at,n_robots/n_rskills/n_scenes,findings;.errors/.warnings/.okproperties. (L95)errors() -> list[CheckFinding]— Findings that fail the check. (L108)warnings() -> list[CheckFinding]— Advisory findings (fail only under--strict). (L113)ok() -> bool— True when there are no error-severity findings. (L118)check_description_graph(repo_root, *, resolve_remote_assets=False) -> GraphCheckReport— Parses every robot/rskill/scene manifest, resolvesfile:/ros2://asset refs, and checks that scenerobot_ids resolve, no scene sensor reuses a robot-manifest sensor's name (check_scene_sensor_overrides), everyrobots/<id>/units/*.yamland scenerobot_unitresolves and applies (load_robot_unit,apply_sensor_overlays), rSkill embodiment tags reach an in-repo robot, and sensorparent_frames are declared — reusingRobotDescription.from_yaml/resolve_assetrather than parallel validation logic. (L330)check_command(--repo-root, --strict, --resolve-remote-assets, --json)— Theopenral checkleaf command; exit 1 on any error (and on warnings under--strict). Registered inmain.pyviaapp.command("check"). (L399)_REMOTE_ASSET_PREFIXES: tuple[str, ...]— Asset-ref prefixes (rd:/gym_aloha:/openarm:/menagerie:) that download a package or need a sim-only dep; skipped unlessresolve_remote_assets. (L72)_SCENE_TIERS: dict[str, type[DeployScene]]— Scene subdirectory name (deploy/sim/benchmark) → its schema class. (L74)_LOAD_ERRORS: tuple[type[Exception], ...]— Exceptions a manifestfrom_yaml/model_validatemay raise for a bad file, caught to produce aCheckFindinginstead of crashing the whole check. (L81)_URDF_LINK_RE: re.Pattern[str]— Matches a URDF<link name="...">tag, used to enumerate link names for theframesrule. (L152)_SEVERITY_STYLE: dict[str, str]—CheckFinding.severity→ Rich console style (error→ red,warning→ yellow). (L367)
python/cli/src/openral_cli/install.py
openral install <group> — post-install escape hatch for the Tier-0 curl-bash installer; each command wraps _install_group for one uv sync --group extras group (sim physics, LIBERO, MetaWorld, ManiSkill3, SimplerEnv, RoboCasa, RLDX sidecar) or the sudo+apt ROS 2 bootstrap.
install_sim(--force)—openral install sim; installs thesimgroup (gym-aloha, gym-pusht, MuJoCo, bitsandbytes). (L296)install_libero(--force)—openral install libero; installs LIBERO (mutually exclusive withrobocasa). (L304)install_metaworld(--force)—openral install metaworld; installs the MetaWorld MT50 task suite (Sawyer scenes). (L312)install_maniskill3(--force)—openral install maniskill3; installs ManiSkill3 (SAPIEN GPU physics). (L320)install_simpler_env(--force)—openral install simpler-env; installs the SimplerEnv real-to-sim correlator backend. (L328)install_robocasa(--force)—openral install robocasa; installs RoboCasa (mutually exclusive withlibero). (L336)install_rldx(--force)—openral install rldx; installs the RLDX-1 sidecar client (pyzmq + msgpack). (L344)install_ros()—openral install ros; re-runs the packagedbootstrap_<os>.sh(sudo + apt, Linux/macOS only); raisesROSConfigErroron an unsupported platform or non-zero bootstrap exit. (L352)install_list()—openral install list; table of every known group with package counts + conflicts. (L390)_GROUPS: Final[dict[str, list[str]]]—uv sync --groupextras group name → its package list; source of truth forinstall_listand the per-group commands. (L50)_CONFLICTS: Final[tuple[frozenset[str], ...]]— Mutually-exclusive group pairs ({"libero", "robocasa"}); checked before_install_groupruns. (L116)
python/cli/src/openral_cli/robot.py
openral robot vendor-urdf <id>: expand an upstream xacro to a flat, committed URDF so end users need no xacro tooling at runtime. Defers robot_descriptions/xacrodoc/yourdfpy inside the command so openral --help stays fast.
vendor_urdf(robot_id, *, upstream, out_dir, rename=None, raw_text=False) -> Path(L137) — Loadsupstream(rd:xacro expanded via xacrodoc, orfile:already-flat URDF), applies the per-robotrenamepattern(s), and writes<robot_id>.urdfwith a provenance header. Forrd:upstreams, mesh paths are rewritten to portablerd:<module>:<path>refs rather than machine-specific absolute ones.raw_text=Truecopies an already-flat URDF's text verbatim (no yourdfpy round-trip), preservingpackage://paths and CRLF byte-for-byte, for joint-name-only patches._PROVENANCE: str— HTML-comment provenance header (openral robot vendor-urdf {id}+ source) written after the XML declaration of every vendored URDF. (L35)_SO_ARM_JOINT_NAMES: tuple[str, ...]— SO-ARM semantic joint names (shoulder_pan…gripper) keyed by the upstream so100/so101 URDFs' numeric joint order; shared by both follower arms'_RAW_RENAMES. (L42)_RAW_RENAMES: dict[str, list[tuple[str, str]]]— Per-robot raw-text(regex, replacement)rename pairs applied in order underraw_text=True(so100/so101 numeric→semantic; gr1/h1_joint-suffix strip; gr1 elbow-pitch collapse). (L60)_RENAME: dict[str, tuple[str, str]]— Per-robot default rename applied to ard:/file:upstream viayourdfpy. Empty today — openarm'sopenarm_-prefix strip was removed once the wire names were standardised to match the arm's own ros2_control graph. (L88)_PAIR_LEN: int—2; length of a(pattern, repl)rename tuple, named to satisfy the magic-value lint. (L91)
python/cli/src/openral_cli/_rskill_scaffolder.py
Scaffolder helper backing openral rskill new and tools/rskill_scaffolder.py.
Copies rskills/template/ into a target directory, rewrites manifest sentinels (name / license / embodiment_tags / weights_uri / source_repo) plus README sentinels, then re-validates the result through RSkillManifest.from_yaml + rSkill.from_yaml so a malformed scaffold fails at scaffold-time. Partial scaffolds are cleaned up on validation failure.
_TEMPLATE_DIR_NAME: str—"template"; the directory name underrskills/copied by_default_template_dir. (L36)_MANIFEST_FILENAME: str—"rskill.yaml". (L37)_README_FILENAME: str—"README.md". (L38)_default_template_dir() -> Path— Walks parents of this file until arskills/template/directory is found. (L41)scaffold_rskill(rskill_id, *, out_dir, owner, license_, embodiment_tag, family=None, patch=None, template_dir=None, overwrite=False) -> Path— Public entry point; copies the template, applies family defaults + introspection patch, rewrites placeholders, re-validates the manifest. (L60)_rewrite_manifest(manifest_path, *, rskill_id, owner, license_, embodiment_tag, family, patch) -> None— Layered rewrite: (1) template baseline → (2) family defaults from_rskill_intel.family_defaults→ (3) explicitpatch→ (4) CLI rename/license/embodiment_tags. (L173)_apply_patch(raw, patch) -> None— Overlay patch keys onto the raw manifest dict; aNonevalue removes the key (so e.g. ACT family clearsmin_vram_gb)._rewrite_readme(readme_path, *, rskill_id, owner) -> None— ReplaceTEMPLATE_ORG/TEMPLATE_IDsentinels inREADME.md._validate_scaffold(scaffold_dir) -> None— Round-trip throughRSkillManifest.from_yaml+rSkill.from_yaml.
python/cli/src/openral_cli/_rskill_intel.py
Per-family scaffold defaults + HF Hub config introspection for openral rskill new.
RSkillFamily—Literal["act", "smolvla", "pi05", "xvla", "diffusion"]; mirrors the keys ofopenral_sim.registry.POLICIESminus the mock entries. (L26)_CHW_DIMS: int—2; minimumshapelength for HxW extraction from aninput_featuresentry, else the template default is used. (L30)RSKILL_FAMILIES: tuple[RSkillFamily, ...]— Tuple form of the above for menu rendering / validation. (L36)class RSkillPatch(TypedDict, total=False)— Subset of manifest fields the scaffolder overlays:model_family,chunk_size,quantization,latency_budget,min_vram_gb,n_action_steps,image_preprocessing,state_contract,action_contract,sensors_required,weights_uri,source_repo,description. (L39)family_defaults(family: RSkillFamily) -> RSkillPatch— Per-family manifest baseline mirroring the in-tree reference manifests (act-aloha,smolvla-libero,pi05-libero-int8,xvla-libero,diffusion-pusht). (L67)_CONFIG_TYPE_TO_FAMILY: dict[str, RSkillFamily]— Maps a HFconfig.jsonpolicytypestring to anRSkillFamily, including thediffusion_policyalias fordiffusion. (L158)introspect_hf(repo_id, *, default_family=None) -> tuple[RSkillFamily, RSkillPatch]— Fetchesconfig.jsonfrom a HF Hub repo, infers the policy family fromtype, and derives chunk_size / sensors / state_contract / image_preprocessing.aliases / weights_uri frominput_features. (L168)_fetch_hf_json(repo_id, filename) -> Any—huggingface_hub.hf_hub_download+json.load; raisesValueErroron network / parse error._sensors_from_input_features(input_features) -> list[dict]— OneSensorRequirement-shaped dict perobservation.images.*feature, with min_width / min_height pulled off the CHW shape._state_dim_from_input_features(input_features) -> int | None— Readsobservation.state.shape[0]._aliases_from_input_features(input_features) -> dict[str, str]— Pairscamera<N>source keys with the checkpoint's image-feature names; empty when names already match.
python/cli/src/openral_cli/autodetect.py
USB VID/PID enumeration and DDS topic discovery for openral detect.
class UsbDevice(NamedTuple)— A USB serial device on the host. (L66) fields:port, vid, pid, descriptionclass KnownDevice(NamedTuple)— A known USB adapter/controller from the VID/PID table. (L82) fields:chip, driver_hint, embodiment_tag, bh_robot_typeclass UsbMatch(NamedTuple)— A detected device matched against the table. (L100) fields:device, knownclass DdsTopic(NamedTuple)— A ROS 2 topic observed during DDS scan. (L112) fields:name, type_nameclass CanInterface(NamedTuple)— One SocketCAN interface on the host. Defined inopenral_core.canand re-exported here; the enumeration is transport mechanism shared with the HAL layer's bus preflight, while the robot matching below is this module's own concern. fields:name, is_up, fd_enabled, bitrate, data_bitrate, state, driver, mtu, vid, pid, descriptionclass CanMatch(NamedTuple)— CAN interfaces that together identify one robot; a bimanual arm contributes one per side. (L496) fields:interfaces, known_enumerate_linux_pyudev() -> list[UsbDevice](L249)_enumerate_macos_system_profiler() -> list[UsbDevice](L285)_enumerate_glob_fallback() -> list[UsbDevice]—/dev/tty*glob. (L330)enumerate_usb_devices() -> list[UsbDevice]— OS-routed enumerator. (L353)match_known_devices(devices) -> list[UsbMatch](L385)_VID_PID_TABLE: dict[tuple[int, int], KnownDevice]— USB(VID, PID)→KnownDevicelookup drivingmatch_known_devices. (L128)scan_dds_topics(timeout_s=5.0) -> list[DdsTopic]—ros2 topic list -t. (L412)_TOPIC_ROBOT_MAP: dict[str, str]— DDS topic name substring → robot slug, drivinginfer_robot_from_topics. (L233)infer_robot_from_topics(topics) -> str | None(L460)enumerate_can_interfaces(*, sysfs_net=None) -> list[CanInterface]— Re-exported fromopenral_core.can(see Layer 0); Linux-only, dependency-free SocketCAN enumeration via/sys/class/netandip -details -json link show. Needs no root and opens no socket, so it never perturbs a running robot.match_can_interfaces(interfaces) -> list[CanMatch]— Groups up interfaces by the robot their names declare, since a CAN bus carries no vendor descriptor — only a udev-pinned interface name is a durable signal. A barecan0stays unmatched by design. (L586)_CAN_NAME_ROBOT_TABLE: dict[str, KnownDevice]— Interface-name substring →KnownDevice, drivingmatch_can_interfaces. (L522)infer_robot_from_can(interfaces) -> str | None— First matched robot slug, elseNone. (L629)can_adapter_name(iface) -> str— Catalogued adapter name (_CAN_ADAPTER_TABLE), else the USB product string, else a vendor-only label, else"SocketCAN"for an on-SoC controller. Descriptive only — never used to infer a robot, since the same dongle drives any CAN machine. (L546)_CAN_ADAPTER_TABLE: dict[tuple[int, int], str]— USB(VID, PID)→ catalogued CAN adapter name, used only for the descriptive label incan_adapter_name. (L536)_CAN_VENDOR_TABLE: dict[int, str]— USB VID → vendor-only fallback label forcan_adapter_namewhen the PID is not catalogued. (L541)