roborama 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1342 @@
1
+ // src/errors.ts
2
+ var RoboramaError = class extends Error {
3
+ constructor(message, opts) {
4
+ super(message);
5
+ this.name = "RoboramaError";
6
+ this.code = opts.code;
7
+ this.status = opts.status;
8
+ this.run_id = opts.run_id;
9
+ this.doc_url = opts.doc_url;
10
+ this.partial_result = opts.partial_result;
11
+ }
12
+ };
13
+ function errorFromEnvelope(status, payload) {
14
+ const err = payload?.error ?? { code: "error", message: "Roborama API error" };
15
+ return new RoboramaError(err.message ?? "Roborama API error", {
16
+ code: err.code ?? "error",
17
+ status,
18
+ run_id: err.run_id,
19
+ doc_url: err.doc_url,
20
+ partial_result: err.partial_result
21
+ });
22
+ }
23
+
24
+ // src/data.ts
25
+ var PRICING = { "currency": "USD", "meters": [{ "id": "robot_hours", "label": "Robot-hours", "unit": "robot-hour", "description": "Occupancy of an embodiment: metered per minute while your policy holds the cell, from first motor enable to final reset." }, { "id": "environment_hours", "label": "Environment-hours", "unit": "environment-hour", "description": "Occupancy of a versioned catalogue scene, metered per minute alongside the robot meter. Both meters appear in every quote and every result." }], "billing_note": "Metered per minute, displayed per hour. Hours in results are rounded to 0.1 h; billing uses exact minutes. A max_budget_usd on any run is a hard stop, metered live.", "robot_rates": [{ "id": "nori-a3", "class": "arm-pod", "tier": "soak", "usd_per_robot_hour": 7, "note": "Commodity soak pods, 24 cells. Iterate here; escalate when the numbers hold." }, { "id": "so-101", "class": "arm-pod", "tier": "soak", "usd_per_robot_hour": 9, "note": "Hobby-class 5-DoF pods, 16 cells. The cheapest smoke test for a policy." }, { "id": "stretch3", "class": "mobile-manipulator", "tier": "verify", "usd_per_robot_hour": 34, "note": "Mobile manipulation, firmware-pinned." }, { "id": "fr3-bench", "class": "fixed-arm", "tier": "verify", "usd_per_robot_hour": 38, "note": "Torque-controlled 7-DoF industrial arm on a fixed bench." }, { "id": "aloha-bimanual", "class": "bimanual", "tier": "verify", "usd_per_robot_hour": 52, "note": "Bimanual teleop-grade rig." }, { "id": "tiago-pro", "class": "mobile-manipulator", "tier": "verify", "usd_per_robot_hour": 61, "note": "Premium mobile manipulator; torso lift and force-torque wrists." }, { "id": "aloha2-pro", "class": "bimanual", "tier": "verify", "usd_per_robot_hour": 74, "note": "Second-generation bimanual rig, gravity-compensated, higher payload." }, { "id": "spot-arm", "class": "quadruped-manip", "tier": "verify", "usd_per_robot_hour": 89, "note": "Quadruped with a 6-DoF arm; uneven-floor manipulation cells." }, { "id": "g1-edu-pro", "class": "humanoid", "tier": "verify", "usd_per_robot_hour": 148, "note": "37-DoF humanoid with dex3-1 hands; the verification-tier default." }, { "id": "g1-edu-plus", "class": "humanoid", "tier": "verify", "usd_per_robot_hour": 176, "note": "43-DoF humanoid; limited cells." }], "environment_rates": [{ "adder_tier": "bare", "usd_per_env_hour": 12, "examples": ["cell-a@v1.0"], "note": "Bare bench cell, overhead camera." }, { "adder_tier": "standard", "usd_per_env_hour": 24, "examples": ["warehouse-std@v2.0"], "note": "Standard catalogue scene with mocap." }, { "adder_tier": "replica", "usd_per_env_hour": 39, "examples": ["kitchen-std@v1.2"], "note": "Full replica scene, mocap + force-torque." }, { "adder_tier": "instrumented-replica", "usd_per_env_hour": 85, "examples": ["surgical-replica@v3"], "note": "High-instrumentation replica, high-speed capture." }], "priority": [{ "id": "burst", "multiplier": 1.5, "note": "Expedited queue: next available cell, both meters at 1.5\xD7." }, { "id": "standard", "multiplier": 1, "note": "Default queue." }, { "id": "soak", "multiplier": 0.6, "note": "Off-peak, interruptible scheduling at 0.6\xD7." }], "adders": [{ "id": "teleop-fallback", "usd_per_robot_hour": 18, "note": "Human takeover on policy halt (Policy.endpoint fallback)." }, { "id": "extra-instrumentation", "usd_per_env_hour": 12, "note": "Additional mocap or high-speed capture beyond the environment's base kit." }], "packages": [{ "id": "suite-manip-core-v3", "label": "rbr-manip-core@v3 suite run", "usd": 9500, "includes": "5 tasks \xD7 300 episodes on g1-edu-pro@fw2.3; suite attestation (rbr:att: id) and the verification report." }, { "id": "threshold-contract", "label": "Outcome contract \u2014 threshold()", "usd": null, "pricing": "monthly_cap_usd you set", "includes": "Soak-tier iteration (e.g. nori-a3 pods) with automatic escalation to verification-tier hardware on threshold crossing; on_verified webhook; hard monthly cap." }], "worked_quote": { "label": "What do 600 humanoid episodes cost?", "robot": "g1-edu-pro", "environment": "kitchen-std@v1.2", "episodes": 600, "priority": "standard", "minutes_per_episode_est": 2, "robot_hours": 20, "env_hours": 20, "robot_usd_per_hour": 148, "env_usd_per_hour": 39, "robot_usd": 2960, "env_usd": 780, "usd": 3740, "queue_eta": "6h" } };
26
+ var ROBOTS = [{ "id": "g1-edu-pro", "class": "humanoid", "dof": 37, "hands": "dex3-1", "firmwares": ["2.3", "2.4"], "cells": 6, "duty_cycle_pct": 71, "tier": "verify" }, { "id": "g1-edu-plus", "class": "humanoid", "dof": 43, "hands": "dex3-1", "firmwares": ["2.3"], "cells": 2, "duty_cycle_pct": 64, "tier": "verify" }, { "id": "spot-arm", "class": "quadruped-manip", "dof": 19, "hands": "gripper-2f", "firmwares": ["4.1"], "cells": 2, "duty_cycle_pct": 60, "tier": "verify" }, { "id": "aloha2-pro", "class": "bimanual", "dof": 14, "hands": "gripper-2f", "firmwares": ["1.1"], "cells": 2, "duty_cycle_pct": 62, "tier": "verify" }, { "id": "aloha-bimanual", "class": "bimanual", "dof": 14, "hands": "gripper-2f", "firmwares": ["3.1"], "cells": 3, "duty_cycle_pct": 66, "tier": "verify" }, { "id": "tiago-pro", "class": "mobile-manipulator", "dof": 12, "hands": "gripper-2f", "firmwares": ["2.0"], "cells": 2, "duty_cycle_pct": 55, "tier": "verify" }, { "id": "stretch3", "class": "mobile-manipulator", "dof": 10, "hands": "gripper-2f", "firmwares": ["1.9"], "cells": 4, "duty_cycle_pct": 58, "tier": "verify" }, { "id": "fr3-bench", "class": "fixed-arm", "dof": 7, "hands": "gripper-2f", "firmwares": ["5.2"], "cells": 8, "duty_cycle_pct": 74, "tier": "verify" }, { "id": "nori-a3", "class": "arm-pod", "dof": 6, "hands": "gripper-2f", "firmwares": ["1.0"], "cells": 24, "duty_cycle_pct": 83, "tier": "soak" }, { "id": "so-101", "class": "arm-pod", "dof": 5, "hands": "gripper-2f", "firmwares": ["1.2"], "cells": 16, "duty_cycle_pct": 79, "tier": "soak" }];
27
+ var ENVIRONMENTS = [{ "id": "cell-a", "rev": "v1.0", "class": "bench", "instrumentation": ["overhead-cam"], "objects": 24, "reset": "scripted", "adder_tier": "bare" }, { "id": "warehouse-std", "rev": "v2.0", "class": "warehouse", "instrumentation": ["mocap"], "objects": 388, "reset": "scripted", "adder_tier": "standard" }, { "id": "kitchen-std", "rev": "v1.2", "class": "kitchen", "instrumentation": ["mocap", "ft"], "objects": 214, "reset": "scripted", "adder_tier": "replica" }, { "id": "kitchen-replica", "rev": "v2.0", "class": "kitchen", "instrumentation": ["mocap", "ft", "scale"], "objects": 178, "reset": "scripted", "adder_tier": "replica" }, { "id": "surgical-replica", "rev": "v3", "class": "surgical", "instrumentation": ["mocap", "ft", "high-speed-cam"], "objects": 61, "reset": "teleop-assisted", "adder_tier": "instrumented-replica" }];
28
+ var SUITES = [{ "id": "rbr-manip-core", "rev": "v3", "frozen": "2026-03-14", "scoring": "binary success, Wilson 95% interval", "episodes_per_task": 300, "tasks": ["pick_place@v1", "load_dishwasher@v2", "shelf_restock@v1", "fold_towel@v2", "bin_pick@v1"], "environments": ["kitchen-std@v1.2", "warehouse-std@v2.0", "cell-a@v1.0"], "perturbation_rev": "suite-core@v3.0", "sample_report": { "robot": "g1-edu-pro@fw2.3", "overall": { "n": 1500, "success_rate": 0.859, "ci95": [0.841, 0.876] }, "per_task": [{ "task": "pick_place@v1", "n": 300, "success_rate": 0.903, "ci95": [0.865, 0.932] }, { "task": "load_dishwasher@v2", "n": 300, "success_rate": 0.873, "ci95": [0.831, 0.906] }, { "task": "shelf_restock@v1", "n": 300, "success_rate": 0.813, "ci95": [0.765, 0.853] }, { "task": "fold_towel@v2", "n": 300, "success_rate": 0.93, "ci95": [0.895, 0.954] }, { "task": "bin_pick@v1", "n": 300, "success_rate": 0.777, "ci95": [0.726, 0.82] }], "attestation": "rbr:att:9f3c2ab7", "attestation_url": "https://api.roborama.com/v1/attestations/rbr:att:9f3c2ab7", "citation": "Roborama Manip-Core v3, run 2026-09-04, attestation rbr:att:9f3c2ab7" } }];
29
+ var TASKS = [{ "id": "task_espresso_01", "object": "task", "name": "espresso", "rev": "espresso@v1", "status": "frozen", "visibility": "private", "created": "2026-08-20T09:14:02Z", "frozen": "2026-08-24T16:40:11Z", "signatures": ["acct_acme", "roborama"], "initial_conditions": { "machine": { "object": "kit/acme-breville", "pose": "P1", "tol_mm": 20 }, "cup": { "object": "catalogue/cup-std-08", "randomize": "zone-A" } }, "success_predicates": [{ "cup_on_tray": "gt.pose(cup) within tray_zone" }, { "liquid_mass": "scale.delta between 22 and 42 g" }, { "no_spill": "vision.spill_area < 2 cm2" }, { "t_complete": "episode.duration < 180 s" }], "instructions": { "sampled_per_episode": ["make an espresso", "brew me a coffee", "fais un espresso"], "held_out": ["prepare a single shot"] }, "stages": [{ "grasp_cup": "gt.pose(cup) in gripper" }, { "cup_placed": "gt.pose(cup) within drip_zone" }, { "extraction": "scale.delta > 0 within 60s" }, { "served": "all success_predicates" }], "envelope": { "lighting": ["3000K", "5600K"], "distractors": "set-B", "out_of_scope": ["oat_milk"] }, "pilot": { "run_id": "run_9088", "robot": "g1-edu-pro@fw2.3", "episodes": 25, "amendments": 1 } }, { "id": "task_latte_art_01", "object": "task", "name": "latte_art", "rev": null, "status": "draft", "visibility": "private", "created": "2026-08-30T11:03:44Z", "frozen": null, "signatures": [], "initial_conditions": { "machine": { "object": "kit/acme-breville", "pose": "P1", "tol_mm": 20 }, "pitcher": { "object": "catalogue/pitcher-std-02", "randomize": "zone-A" } }, "success_predicates": [{ "pattern_visible": "vision.contrast_score > 0.6" }, { "no_spill": "vision.spill_area < 2 cm2" }, { "t_complete": "episode.duration < 240 s" }], "instructions": { "sampled_per_episode": ["pour a latte heart", "make latte art"], "held_out": ["draw a heart in the milk"] }, "stages": [{ "steam_milk": "audio.steam_event within 90s" }, { "poured": "scale.delta between 120 and 200 g" }, { "pattern": "all success_predicates" }], "envelope": { "lighting": ["3000K"], "distractors": "none", "out_of_scope": ["oat_milk", "tulip_pattern"] }, "pilot": null }];
30
+ var KITS = [{ "id": "kit_acme_breville", "object": "kit", "name": "acme-breville", "status": "available", "status_history": [{ "status": "registered", "at": "2026-08-12T10:02:00Z" }, { "status": "received", "at": "2026-08-15T18:30:00Z" }, { "status": "tracked", "at": "2026-08-17T09:12:00Z" }, { "status": "available", "at": "2026-08-17T14:45:00Z" }], "items": [{ "desc": "Breville BES870", "qty": 1 }], "tracking": { "mocap_markers": 6, "mass_g": 10480, "mesh_scan": true }, "environment_ref": "kit/acme-breville", "shipping_label_url": "https://api.roborama.com/v1/kits/kit_acme_breville/shipping-label" }, { "id": "kit_pi_breville", "object": "kit", "name": "pi-breville", "status": "tracked", "status_history": [{ "status": "registered", "at": "2026-08-28T08:11:00Z" }, { "status": "received", "at": "2026-08-31T17:05:00Z" }, { "status": "tracked", "at": "2026-09-01T10:22:00Z" }], "items": [{ "desc": "Breville BES870", "qty": 2 }], "tracking": { "mocap_markers": 6, "mass_g": 10480, "mesh_scan": true }, "environment_ref": null, "shipping_label_url": "https://api.roborama.com/v1/kits/kit_pi_breville/shipping-label" }];
31
+ var RUNS = [{ "id": "run_8842", "object": "run", "kind": "run", "status": "completed", "created": "2026-08-28T14:02:11Z", "completed": "2026-08-29T11:37:48Z", "robot": "g1-edu-pro@fw2.3", "environment": "kitchen-std@v1.2", "task": "load_dishwasher@v2", "policy": { "type": "container", "image": "ghcr.io/acme/skill:v4", "action_space": "joint_delta_50hz", "observation_contract": "droid-3cam" }, "episodes": "auto(ci=0.95, moe=0.03)", "priority": "standard", "perturbation": { "layout_jitter_mm": 25, "lighting": ["3000K", "5600K"], "distractors": "set-B", "seed": 42 }, "data": { "retention": "30d", "train_on_failures": false }, "max_budget_usd": 4e3, "resolved_pins": { "robot": "g1-edu-pro@fw2.3", "environment": "kitchen-std@v1.2", "task": "load_dishwasher@v2", "perturbation_rev": "set-B@v1.4" }, "result": { "n": 612, "success_rate": 0.874, "ci95": [0.846, 0.898], "interval_method": "wilson", "failure_clusters": { "grasp_slip": 41, "perception_miss": 22, "collision": 9 }, "robot_hours": 20.4, "environment_hours": 20.4, "cost_usd": 3812, "artifacts": { "mcap": "https://api.roborama.com/v1/runs/run_8842/episodes?artifact=mcap", "video": "https://api.roborama.com/v1/runs/run_8842/episodes?artifact=video", "ground_truth": "https://api.roborama.com/v1/runs/run_8842/episodes?artifact=ground_truth", "report_pdf": "https://api.roborama.com/v1/runs/run_8842/report?format=pdf" } } }, { "id": "run_9101", "object": "run", "kind": "run", "status": "completed", "created": "2026-08-26T08:15:33Z", "completed": "2026-08-27T04:02:19Z", "robot": "g1-edu-pro@fw2.3", "environment": "kitchen-std@v1.2", "task": "espresso@v1", "policy": { "type": "checkpoint", "hf": "pi/espresso-v7", "runtime": "openpi", "inference": { "action_horizon": 50, "chunk_size": 50, "temp": 0 }, "action_space": "joint_delta_50hz", "observation_contract": "droid-3cam" }, "episodes": 412, "priority": "standard", "interventions": "none", "perturbation": { "layout_jitter_mm": 25, "lighting": ["3000K", "5600K"], "distractors": "set-B", "seed": 7 }, "data": { "retention": "30d", "train_on_failures": false }, "max_budget_usd": 3e3, "resolved_pins": { "robot": "g1-edu-pro@fw2.3", "environment": "kitchen-std@v1.2", "task": "espresso@v1", "perturbation_rev": "set-B@v1.4" }, "result": { "n": 412, "success_rate": 0.85, "ci95": [0.812, 0.881], "interval_method": "wilson", "failure_clusters": { "cup_misplace": 24, "spill": 19, "timeout": 12 }, "stage_rates": [{ "stage": "grasp_cup", "n": 412, "success_rate": 0.966, "ci95": [0.944, 0.98] }, { "stage": "cup_placed", "n": 412, "success_rate": 0.932, "ci95": [0.904, 0.953] }, { "stage": "extraction", "n": 412, "success_rate": 0.893, "ci95": [0.86, 0.919] }, { "stage": "served", "n": 412, "success_rate": 0.85, "ci95": [0.812, 0.881] }], "instruction_breakdown": [{ "instruction": "make an espresso", "held_out": false, "n": 118, "success_rate": 0.873, "ci95": [0.801, 0.921] }, { "instruction": "brew me a coffee", "held_out": false, "n": 112, "success_rate": 0.866, "ci95": [0.791, 0.917] }, { "instruction": "fais un espresso", "held_out": false, "n": 99, "success_rate": 0.848, "ci95": [0.765, 0.906] }, { "instruction": "prepare a single shot", "held_out": true, "n": 83, "success_rate": 0.795, "ci95": [0.696, 0.868] }], "interventions": { "declared": "none", "count": 0, "timestamps": [] }, "robot_hours": 13.7, "environment_hours": 13.7, "cost_usd": 2562, "artifacts": { "mcap": "https://api.roborama.com/v1/runs/run_9101/episodes?artifact=mcap", "video": "https://api.roborama.com/v1/runs/run_9101/episodes?artifact=video", "ground_truth": "https://api.roborama.com/v1/runs/run_9101/episodes?artifact=ground_truth", "report_pdf": "https://api.roborama.com/v1/runs/run_9101/report?format=pdf" } } }];
32
+ var MATRIX = { "id": "run_8901", "object": "run", "kind": "matrix", "status": "completed", "task": "pick_place_class3", "episodes_per_cell": "auto(ci=0.95, moe=0.05)", "gate": { "metric": "ci95_lower", "min": 0.8 }, "robots": ["g1-edu-pro@fw2.3", "g1-edu-pro@fw2.4", "g1-edu-plus@fw2.3", "aloha2-pro@fw1.1", "spot-arm@fw4.1", "tiago-pro@fw2.0", "fr3-bench@fw5.2", "stretch3@fw1.9", "nori-a3@fw1.0", "so-101@fw1.2"], "environments": ["cell-a@v1.0", "kitchen-std@v1.2", "warehouse-std@v2.0"], "cells": [{ "robot": "g1-edu-pro@fw2.3", "environment": "cell-a@v1.0", "n": 190, "success_rate": 0.937, "ci95": [0.893, 0.964], "status": "pass" }, { "robot": "g1-edu-pro@fw2.3", "environment": "kitchen-std@v1.2", "n": 240, "success_rate": 0.892, "ci95": [0.846, 0.925], "status": "pass" }, { "robot": "g1-edu-pro@fw2.3", "environment": "warehouse-std@v2.0", "n": 210, "success_rate": 0.862, "ci95": [0.809, 0.902], "status": "pass" }, { "robot": "g1-edu-pro@fw2.4", "environment": "cell-a@v1.0", "n": 190, "success_rate": 0.921, "ci95": [0.874, 0.952], "status": "pass" }, { "robot": "g1-edu-pro@fw2.4", "environment": "kitchen-std@v1.2", "n": 240, "success_rate": 0.787, "ci95": [0.731, 0.835], "status": "fail" }, { "robot": "g1-edu-pro@fw2.4", "environment": "warehouse-std@v2.0", "n": 210, "success_rate": 0.838, "ci95": [0.782, 0.882], "status": "fail" }, { "robot": "g1-edu-plus@fw2.3", "environment": "cell-a@v1.0", "n": 190, "success_rate": 0.953, "ci95": [0.912, 0.975], "status": "pass" }, { "robot": "g1-edu-plus@fw2.3", "environment": "kitchen-std@v1.2", "n": 240, "success_rate": 0.921, "ci95": [0.88, 0.949], "status": "pass" }, { "robot": "g1-edu-plus@fw2.3", "environment": "warehouse-std@v2.0", "n": 210, "success_rate": 0.89, "ci95": [0.841, 0.926], "status": "pass" }, { "robot": "stretch3@fw1.9", "environment": "cell-a@v1.0", "n": 190, "success_rate": 0.879, "ci95": [0.825, 0.918], "status": "pass" }, { "robot": "stretch3@fw1.9", "environment": "kitchen-std@v1.2", "n": 240, "success_rate": 0.729, "ci95": [0.67, 0.781], "status": "fail" }, { "robot": "stretch3@fw1.9", "environment": "warehouse-std@v2.0", "n": 210, "success_rate": 0.629, "ci95": [0.561, 0.691], "status": "fail" }, { "robot": "nori-a3@fw1.0", "environment": "cell-a@v1.0", "n": 190, "success_rate": 0.8, "ci95": [0.737, 0.851], "status": "fail" }, { "robot": "nori-a3@fw1.0", "environment": "kitchen-std@v1.2", "n": 240, "success_rate": 0.537, "ci95": [0.474, 0.599], "status": "fail" }, { "robot": "nori-a3@fw1.0", "environment": "warehouse-std@v2.0", "n": 210, "success_rate": 0.467, "ci95": [0.4, 0.534], "status": "fail" }, { "robot": "aloha2-pro@fw1.1", "environment": "cell-a@v1.0", "n": 190, "success_rate": 0.937, "ci95": [0.893, 0.964], "status": "pass" }, { "robot": "aloha2-pro@fw1.1", "environment": "kitchen-std@v1.2", "n": 240, "success_rate": 0.871, "ci95": [0.823, 0.908], "status": "pass" }, { "robot": "aloha2-pro@fw1.1", "environment": "warehouse-std@v2.0", "n": 210, "success_rate": 0.843, "ci95": [0.788, 0.886], "status": "fail" }, { "robot": "spot-arm@fw4.1", "environment": "cell-a@v1.0", "n": 190, "success_rate": 0.884, "ci95": [0.831, 0.922], "status": "pass" }, { "robot": "spot-arm@fw4.1", "environment": "kitchen-std@v1.2", "n": 240, "success_rate": 0.746, "ci95": [0.687, 0.797], "status": "fail" }, { "robot": "spot-arm@fw4.1", "environment": "warehouse-std@v2.0", "n": 210, "success_rate": 0.79, "ci95": [0.73, 0.84], "status": "fail" }, { "robot": "tiago-pro@fw2.0", "environment": "cell-a@v1.0", "n": 190, "success_rate": 0.905, "ci95": [0.855, 0.939], "status": "pass" }, { "robot": "tiago-pro@fw2.0", "environment": "kitchen-std@v1.2", "n": 240, "success_rate": 0.808, "ci95": [0.753, 0.853], "status": "fail" }, { "robot": "tiago-pro@fw2.0", "environment": "warehouse-std@v2.0", "n": 210, "success_rate": 0.724, "ci95": [0.66, 0.78], "status": "fail" }, { "robot": "fr3-bench@fw5.2", "environment": "cell-a@v1.0", "n": 190, "success_rate": 0.947, "ci95": [0.905, 0.971], "status": "pass" }, { "robot": "fr3-bench@fw5.2", "environment": "kitchen-std@v1.2", "n": 240, "success_rate": 0.821, "ci95": [0.768, 0.864], "status": "fail" }, { "robot": "fr3-bench@fw5.2", "environment": "warehouse-std@v2.0", "n": 210, "success_rate": 0.79, "ci95": [0.73, 0.84], "status": "fail" }, { "robot": "so-101@fw1.2", "environment": "cell-a@v1.0", "n": 190, "success_rate": 0.742, "ci95": [0.675, 0.799], "status": "fail" }, { "robot": "so-101@fw1.2", "environment": "kitchen-std@v1.2", "n": 240, "success_rate": 0.463, "ci95": [0.401, 0.526], "status": "fail" }, { "robot": "so-101@fw1.2", "environment": "warehouse-std@v2.0", "n": 210, "success_rate": 0.41, "ci95": [0.346, 0.478], "status": "fail" }], "regressions_vs": "run_8821", "regressions": [{ "robot": "g1-edu-pro@fw2.4", "environment": "kitchen-std@v1.2", "delta_pts": -10.5, "note": "fw2.4 wrist controller change; grasp_slip cluster opened" }] };
33
+ var TRANSFER = { "object": "transfer", "task": "towel_fold@v1", "interval_method": "wilson", "source": { "robot": "aloha-bimanual@fw3.1", "success_rate": 0.942, "n": 380, "ci95": [0.914, 0.961] }, "target": { "robot": "g1-edu-pro@fw2.3", "success_rate": 0.715, "n": 340, "ci95": [0.665, 0.76] }, "delta_pts": -22.7, "new_failure_clusters": ["grasp_slip", "wrist_singularity"] };
34
+ var USAGE = { "object": "usage", "period": "2026-09", "robot_hours": { "verify": 141.2, "soak": 2210.5 }, "env_hours": { "bare": 96, "standard": 402.3, "replica": 118.9, "instrumented-replica": 6.2 }, "usd_total": 18940, "budget": { "monthly_usd": 25e3, "hard": true, "remaining_usd": 6060 } };
35
+ var STREAM = { "object": "stream", "run": "run_9101", "episode": 341, "of": 412, "cell": "cell-g1-04", "webrtc": "wss://streams.roborama.com/cells/cell-g1-04/webrtc", "mjpeg": "https://streams.roborama.com/cells/cell-g1-04/mjpeg", "viewer_token": "vt_7Kq2mHentXw4", "expires": "2026-09-01T20:00:00Z" };
36
+
37
+ // src/stats.ts
38
+ var Z_TABLE = {
39
+ "0.9": 1.6448536269514722,
40
+ "0.95": 1.959963984540054,
41
+ "0.99": 2.5758293035489004
42
+ };
43
+ var AUTO_RE = /^auto\(ci=(0\.[0-9]+), ?moe=(0\.[0-9]+)\)$/;
44
+ function zForCi(ci) {
45
+ const exact = Z_TABLE[String(ci)];
46
+ if (exact !== void 0) return exact;
47
+ const p = 1 - (1 - ci) / 2;
48
+ const a = [
49
+ -39.69683028665376,
50
+ 220.9460984245205,
51
+ -275.9285104469687,
52
+ 138.357751867269,
53
+ -30.66479806614716,
54
+ 2.506628277459239
55
+ ];
56
+ const b = [
57
+ -54.47609879822406,
58
+ 161.5858368580409,
59
+ -155.6989798598866,
60
+ 66.80131188771972,
61
+ -13.28068155288572
62
+ ];
63
+ const c = [
64
+ -0.007784894002430293,
65
+ -0.3223964580411365,
66
+ -2.400758277161838,
67
+ -2.549732539343734,
68
+ 4.374664141464968,
69
+ 2.938163982698783
70
+ ];
71
+ const d = [
72
+ 0.007784695709041462,
73
+ 0.3224671290700398,
74
+ 2.445134137142996,
75
+ 3.754408661907416
76
+ ];
77
+ const plow = 0.02425;
78
+ const phigh = 1 - plow;
79
+ if (p < plow) {
80
+ const q2 = Math.sqrt(-2 * Math.log(p));
81
+ return -(((((c[0] * q2 + c[1]) * q2 + c[2]) * q2 + c[3]) * q2 + c[4]) * q2 + c[5]) / ((((d[0] * q2 + d[1]) * q2 + d[2]) * q2 + d[3]) * q2 + 1);
82
+ }
83
+ if (p <= phigh) {
84
+ const q2 = p - 0.5;
85
+ const r = q2 * q2;
86
+ return (((((a[0] * r + a[1]) * r + a[2]) * r + a[3]) * r + a[4]) * r + a[5]) * q2 / (((((b[0] * r + b[1]) * r + b[2]) * r + b[3]) * r + b[4]) * r + 1);
87
+ }
88
+ const q = Math.sqrt(-2 * Math.log(1 - p));
89
+ return (((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) / ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);
90
+ }
91
+ var round1 = (x) => Math.round(x * 10) / 10;
92
+ var round3 = (x) => Math.round(x * 1e3) / 1e3;
93
+ function wilsonCi(successes, n, ci = 0.95) {
94
+ if (n <= 0) return [0, 0];
95
+ const z = zForCi(ci);
96
+ const p = successes / n;
97
+ const z2 = z * z;
98
+ const denom = 1 + z2 / n;
99
+ const center = (p + z2 / (2 * n)) / denom;
100
+ const half = z * Math.sqrt(p * (1 - p) / n + z2 / (4 * n * n)) / denom;
101
+ return [round3(center - half), round3(center + half)];
102
+ }
103
+ function parseAuto(spec) {
104
+ const m = AUTO_RE.exec(spec.trim());
105
+ if (!m) return null;
106
+ return { ci: Number(m[1]), moe: Number(m[2]) };
107
+ }
108
+ function validateEpisodes(spec) {
109
+ if (typeof spec === "number") {
110
+ if (!Number.isInteger(spec) || spec < 1) {
111
+ throw new RangeError("episodes must be a positive integer or 'auto(ci=..., moe=...)'");
112
+ }
113
+ return;
114
+ }
115
+ if (parseAuto(spec) === null) {
116
+ throw new RangeError(
117
+ "episodes is neither a positive int nor a well-formed 'auto(ci=..., moe=...)'"
118
+ );
119
+ }
120
+ }
121
+ function planningN(ci, moe) {
122
+ const z = zForCi(ci);
123
+ return Math.ceil(z * z * 0.25 / (moe * moe));
124
+ }
125
+ function realizedN(ci, moe) {
126
+ const z = zForCi(ci);
127
+ const z95 = zForCi(0.95);
128
+ return Math.max(1, Math.round(612 * (0.03 / moe) ** 2 * (z / z95) ** 2));
129
+ }
130
+
131
+ // src/mock.ts
132
+ var MOCK_NOW = "2026-09-08T00:00:00Z";
133
+ var MOCK_LATER = "2026-09-08T01:00:00Z";
134
+ var BASE_RATE = 535 / 612;
135
+ var CLUSTER_SPLIT = [
136
+ ["grasp_slip", 41 / 77],
137
+ ["perception_miss", 22 / 77],
138
+ ["collision", 9 / 77]
139
+ ];
140
+ var MINUTES_PER_EPISODE = 2;
141
+ var QUEUE_ETA = { burst: "1h", standard: "6h", soak: "24h" };
142
+ var PRIORITY_MULT = { burst: 1.5, standard: 1, soak: 0.6 };
143
+ var DEFAULT_ROBOT_RATE = 148;
144
+ var DEFAULT_ENV_TIER = "replica";
145
+ var baseId = (ref) => ref.split("@", 1)[0].split("@")[0];
146
+ var clone = (x) => JSON.parse(JSON.stringify(x));
147
+ function notFound(message, run_id) {
148
+ return new RoboramaError(message, { code: "not_found", status: 404, run_id });
149
+ }
150
+ var MockBackend = class {
151
+ constructor() {
152
+ this.runs = /* @__PURE__ */ new Map();
153
+ this.tasks = /* @__PURE__ */ new Map();
154
+ this.kits = /* @__PURE__ */ new Map();
155
+ this.counters = /* @__PURE__ */ new Map();
156
+ this.robotRates = /* @__PURE__ */ new Map();
157
+ this.envTier = /* @__PURE__ */ new Map();
158
+ this.tierRates = /* @__PURE__ */ new Map();
159
+ for (const r of RUNS) this.runs.set(r.id, clone(r));
160
+ this.runs.set(MATRIX.id, clone(MATRIX));
161
+ const demo = clone(this.runs.get("run_8842"));
162
+ demo.id = "run_demo";
163
+ this.runs.set("run_demo", demo);
164
+ for (const t of TASKS) this.tasks.set(t.id, clone(t));
165
+ for (const k of KITS) this.kits.set(k.id, clone(k));
166
+ for (const r of PRICING.robot_rates) {
167
+ this.robotRates.set(r.id, r.usd_per_robot_hour);
168
+ }
169
+ for (const e of ENVIRONMENTS) this.envTier.set(e.id, e.adder_tier);
170
+ for (const e of PRICING.environment_rates) {
171
+ this.tierRates.set(e.adder_tier, e.usd_per_env_hour);
172
+ }
173
+ }
174
+ next(prefix, start) {
175
+ const n = this.counters.get(prefix) ?? start;
176
+ this.counters.set(prefix, n + 1);
177
+ return `${prefix}${n}`;
178
+ }
179
+ request(method, path, body, params) {
180
+ body = body ?? {};
181
+ params = params ?? {};
182
+ const parts = path.split("/").filter(Boolean);
183
+ const key = `${method.toUpperCase()} ${path}`;
184
+ if (key === "POST /v1/quote") return this.quote(body);
185
+ if (key === "POST /v1/runs") return this.createRun(body);
186
+ if (key === "GET /v1/runs") return this.listRuns(params);
187
+ if (method === "GET" && parts.length === 3 && parts[1] === "runs") {
188
+ return this.getRun(parts[2]);
189
+ }
190
+ if (method === "GET" && parts.length === 4 && parts[1] === "runs" && parts[3] === "episodes") {
191
+ return this.listEpisodes(parts[2]);
192
+ }
193
+ if (method === "GET" && parts.length === 4 && parts[1] === "runs" && parts[3] === "report") {
194
+ return this.report(parts[2]);
195
+ }
196
+ if (method === "POST" && parts.length === 4 && parts[1] === "runs" && parts[3] === "export") {
197
+ return this.export(parts[2], body.format ?? "lerobot");
198
+ }
199
+ if (method === "POST" && parts.length === 4 && parts[1] === "runs" && parts[3] === "on-verified") {
200
+ const run = this.getRun(parts[2]);
201
+ run.on_verified = { webhook: body.webhook };
202
+ return run;
203
+ }
204
+ if (key === "GET /v1/robots") {
205
+ return { object: "list", url: "/v1/robots", data: clone(ROBOTS) };
206
+ }
207
+ if (key === "GET /v1/environments") {
208
+ let data = clone(ENVIRONMENTS);
209
+ if (params.cls) data = data.filter((e) => e.class === params.cls);
210
+ return { object: "list", url: "/v1/environments", data };
211
+ }
212
+ if (key === "GET /v1/suites") {
213
+ return { object: "list", url: "/v1/suites", data: clone(SUITES) };
214
+ }
215
+ if (key === "POST /v1/gates") {
216
+ return {
217
+ object: "gate",
218
+ id: this.next("gate_", 312),
219
+ status: "active",
220
+ on: body.on,
221
+ suite: body.suite,
222
+ robots: body.robots ?? [],
223
+ fail_if: body.fail_if ?? {}
224
+ };
225
+ }
226
+ if (method === "GET" && parts.length === 3 && parts[1] === "streams") {
227
+ const cell = parts[2];
228
+ return {
229
+ ...clone(STREAM),
230
+ cell,
231
+ webrtc: `wss://streams.roborama.com/cells/${cell}/webrtc`,
232
+ mjpeg: `https://streams.roborama.com/cells/${cell}/mjpeg`
233
+ };
234
+ }
235
+ if (key === "GET /v1/usage") {
236
+ const usage = clone(USAGE);
237
+ if (params.period) usage.period = params.period;
238
+ return usage;
239
+ }
240
+ if (key === "PUT /v1/budgets") {
241
+ const monthly = Number(body.monthly_usd ?? 0);
242
+ return {
243
+ monthly_usd: monthly,
244
+ hard: Boolean(body.hard ?? false),
245
+ remaining_usd: Math.round((monthly - USAGE.usd_total) * 100) / 100
246
+ };
247
+ }
248
+ if (key === "POST /v1/keys") {
249
+ return {
250
+ object: "key",
251
+ id: this.next("key_", 7001),
252
+ scope: body.scope ?? [],
253
+ secret: "rbr_live_mock_0000000000000000",
254
+ created: MOCK_NOW
255
+ };
256
+ }
257
+ if (key === "GET /v1/keys") return { object: "list", data: [] };
258
+ if (key === "POST /v1/calibration/exports") {
259
+ const id = this.next("exp_cal_", 1);
260
+ return {
261
+ object: "export",
262
+ id,
263
+ format: "mcap-bundle",
264
+ status: "ready",
265
+ download_url: `https://artifacts.roborama.com/calibration/${id}.tar.gz`
266
+ };
267
+ }
268
+ if (method === "DELETE" && parts.length === 3 && parts[1] === "data") {
269
+ return {
270
+ object: "purge_receipt",
271
+ id: this.next("purge_", 1),
272
+ run_id: parts[2],
273
+ status: "completed",
274
+ purged_at: MOCK_NOW
275
+ };
276
+ }
277
+ if (key === "POST /v1/tasks") return this.createTask(body);
278
+ if (key === "GET /v1/tasks") {
279
+ return { object: "list", url: "/v1/tasks", data: [...this.tasks.values()] };
280
+ }
281
+ if (method === "GET" && parts.length === 3 && parts[1] === "tasks") {
282
+ return this.getTask(parts[2]);
283
+ }
284
+ if (method === "POST" && parts.length === 4 && parts[1] === "tasks") {
285
+ return this.taskAction(parts[2], parts[3], body);
286
+ }
287
+ if (key === "POST /v1/kits") return this.registerKit(body);
288
+ if (method === "GET" && parts.length === 3 && parts[1] === "kits") {
289
+ return this.getKit(parts[2]);
290
+ }
291
+ if (method === "GET" && parts.length === 4 && parts[1] === "kits" && parts[3] === "shipping-label") {
292
+ const kit = this.getKit(parts[2]);
293
+ return {
294
+ url: `https://artifacts.roborama.com/labels/${kit.id}.pdf`,
295
+ expires: MOCK_LATER
296
+ };
297
+ }
298
+ throw notFound(`No mock route for ${method} ${path}`);
299
+ }
300
+ *events(runId) {
301
+ const run = this.getRun(runId);
302
+ const n = this.realizedEpisodes(run);
303
+ const cell = "cell-g1-04";
304
+ for (const i of [1, 2, 3]) {
305
+ yield { episode: i, of: n, outcome: "success", duration_s: 121, status: "running" };
306
+ }
307
+ yield {
308
+ status: "completed",
309
+ episode: n,
310
+ of: n,
311
+ cell,
312
+ webrtc: `wss://streams.roborama.com/cells/${cell}/webrtc`
313
+ };
314
+ }
315
+ // -- rate card --------------------------------------------------------------
316
+ robotRate(robot) {
317
+ return this.robotRates.get(baseId(robot)) ?? DEFAULT_ROBOT_RATE;
318
+ }
319
+ envRate(environment) {
320
+ const tier = this.envTier.get(baseId(environment)) ?? DEFAULT_ENV_TIER;
321
+ return this.tierRates.get(tier) ?? this.tierRates.get(DEFAULT_ENV_TIER);
322
+ }
323
+ episodeCount(spec, realized) {
324
+ validateEpisodes(spec);
325
+ if (typeof spec === "number") return spec;
326
+ const auto = parseAuto(spec);
327
+ return realized ? realizedN(auto.ci, auto.moe) : planningN(auto.ci, auto.moe);
328
+ }
329
+ quote(body) {
330
+ const robot = body.robot ?? "g1-edu-pro";
331
+ const environment = body.environment ?? "kitchen-std";
332
+ const priority = body.priority ?? "standard";
333
+ const episodes = body.episodes ?? 600;
334
+ const n = this.episodeCount(episodes, false);
335
+ const hours = round1(n * MINUTES_PER_EPISODE / 60);
336
+ const rr = this.robotRate(robot);
337
+ const er = this.envRate(environment);
338
+ const robotUsd = Math.round(hours * rr);
339
+ const envUsd = Math.round(hours * er);
340
+ const mult = PRIORITY_MULT[priority] ?? 1;
341
+ return {
342
+ object: "quote",
343
+ robot,
344
+ environment,
345
+ episodes,
346
+ priority,
347
+ robot_hours: hours,
348
+ env_hours: hours,
349
+ usd: Math.round((robotUsd + envUsd) * mult),
350
+ queue_eta: QUEUE_ETA[priority] ?? "6h",
351
+ breakdown: {
352
+ minutes_per_episode_est: MINUTES_PER_EPISODE,
353
+ robot_usd_per_hour: rr,
354
+ env_usd_per_hour: er,
355
+ robot_usd: robotUsd,
356
+ env_usd: envUsd
357
+ },
358
+ expires: MOCK_NOW
359
+ };
360
+ }
361
+ // -- runs ---------------------------------------------------------------------
362
+ canonicalResult(runId) {
363
+ return clone(this.runs.get(runId).result);
364
+ }
365
+ genericResult(n, robot, environment, priority, rate = BASE_RATE) {
366
+ const successes = Math.round(n * rate);
367
+ const failures = n - successes;
368
+ const clusters = {};
369
+ for (const [name, share] of CLUSTER_SPLIT) {
370
+ const count = Math.round(failures * share);
371
+ if (count) clusters[name] = count;
372
+ }
373
+ const hours = round1(n * MINUTES_PER_EPISODE / 60);
374
+ const mult = PRIORITY_MULT[priority] ?? 1;
375
+ const cost = Math.round(
376
+ n * MINUTES_PER_EPISODE / 60 * (this.robotRate(robot) + this.envRate(environment)) * mult
377
+ );
378
+ return {
379
+ n,
380
+ success_rate: round3(successes / Math.max(n, 1)),
381
+ ci95: wilsonCi(successes, n),
382
+ interval_method: "wilson",
383
+ failure_clusters: clusters,
384
+ robot_hours: hours,
385
+ environment_hours: hours,
386
+ cost_usd: cost
387
+ };
388
+ }
389
+ artifacts(runId) {
390
+ const base = `https://api.roborama.com/v1/runs/${runId}`;
391
+ return {
392
+ mcap: `${base}/episodes?artifact=mcap`,
393
+ video: `${base}/episodes?artifact=video`,
394
+ ground_truth: `${base}/episodes?artifact=ground_truth`,
395
+ report_pdf: `${base}/report?format=pdf`
396
+ };
397
+ }
398
+ createRun(body) {
399
+ const kind = body.kind ?? "run";
400
+ const id = this.next("run_", 9200);
401
+ const run = {
402
+ id,
403
+ object: "run",
404
+ kind,
405
+ status: "completed",
406
+ created: MOCK_NOW,
407
+ completed: MOCK_LATER,
408
+ priority: body.priority ?? "standard"
409
+ };
410
+ for (const field of [
411
+ "robot",
412
+ "robots",
413
+ "environment",
414
+ "environments",
415
+ "task",
416
+ "suite",
417
+ "policy",
418
+ "policies",
419
+ "policy_stream",
420
+ "episodes",
421
+ "episodes_per_cell",
422
+ "perturbation",
423
+ "data",
424
+ "max_budget_usd",
425
+ "paired",
426
+ "publish",
427
+ "audit_sample",
428
+ "return_ground_truth",
429
+ "source",
430
+ "target",
431
+ "iterate_on",
432
+ "escalate_to",
433
+ "monthly_cap_usd",
434
+ "on_verified",
435
+ "interventions",
436
+ "scenarios"
437
+ ]) {
438
+ if (body[field] !== void 0 && body[field] !== null) run[field] = body[field];
439
+ }
440
+ const pins = {};
441
+ for (const field of ["robot", "environment", "task"]) {
442
+ if (typeof run[field] === "string") pins[field] = run[field];
443
+ }
444
+ if (run.perturbation) pins.perturbation_rev = "set-B@v1.4";
445
+ if (Object.keys(pins).length) run.resolved_pins = pins;
446
+ switch (kind) {
447
+ case "eval":
448
+ this.buildEval(run);
449
+ break;
450
+ case "verify":
451
+ this.buildVerify(run);
452
+ break;
453
+ case "matrix":
454
+ this.buildMatrix(run);
455
+ break;
456
+ case "threshold":
457
+ run.status = "running";
458
+ break;
459
+ case "compare":
460
+ this.buildCompare(run);
461
+ break;
462
+ case "transfer":
463
+ this.buildTransfer(run);
464
+ break;
465
+ default:
466
+ this.buildRun(run);
467
+ }
468
+ this.runs.set(id, run);
469
+ return run;
470
+ }
471
+ buildRun(run) {
472
+ const robot = run.robot ?? "g1-edu-pro@fw2.3";
473
+ const environment = run.environment ?? "kitchen-std@v1.2";
474
+ const priority = run.priority ?? "standard";
475
+ const spec = run.episodes ?? "auto(ci=0.95, moe=0.03)";
476
+ const n = this.episodeCount(spec, true);
477
+ const budget = run.max_budget_usd;
478
+ const episodeUsd = MINUTES_PER_EPISODE / 60 * (this.robotRate(robot) + this.envRate(environment)) * (PRIORITY_MULT[priority] ?? 1);
479
+ if (budget !== void 0 && episodeUsd * n > Number(budget)) {
480
+ const affordable = Math.max(0, Math.floor(Number(budget) / episodeUsd));
481
+ run.status = "stopped";
482
+ run.error = {
483
+ code: "budget_exceeded",
484
+ message: `Run halted at max_budget_usd=${budget} after ${affordable} episodes.`,
485
+ run_id: run.id,
486
+ doc_url: "https://roborama.com/docs/errors/#budget_exceeded",
487
+ partial_result: affordable ? this.genericResult(affordable, robot, environment, priority) : void 0
488
+ };
489
+ return;
490
+ }
491
+ let result;
492
+ const task = String(run.task ?? "");
493
+ if (task.startsWith("espresso")) {
494
+ result = this.canonicalResult("run_9101");
495
+ } else if (spec === "auto(ci=0.95, moe=0.03)" || n === 612) {
496
+ result = this.canonicalResult("run_8842");
497
+ } else {
498
+ result = this.genericResult(n, robot, environment, priority);
499
+ }
500
+ result.artifacts = this.artifacts(run.id);
501
+ run.result = result;
502
+ }
503
+ buildEval(run) {
504
+ const sample = (SUITES[0] ?? {}).sample_report ?? {};
505
+ run.citation = sample.citation;
506
+ run.attestation = sample.attestation;
507
+ run.attestation_url = sample.attestation_url;
508
+ run.overall = sample.overall;
509
+ run.per_task = sample.per_task;
510
+ const overall = { ...sample.overall ?? {} };
511
+ overall.interval_method = overall.interval_method ?? "wilson";
512
+ overall.artifacts = this.artifacts(run.id);
513
+ run.result = overall;
514
+ }
515
+ buildVerify(run) {
516
+ run.sim_agreement = 0.93;
517
+ run.disagreements = ["scn_0042", "scn_0113", "scn_0197"];
518
+ if (run.return_ground_truth) {
519
+ run.ground_truth_url = `https://artifacts.roborama.com/verify/${run.id}/ground_truth.tar.gz`;
520
+ }
521
+ const robots = run.robots ?? ["g1-edu-pro@fw2.3"];
522
+ const result = this.genericResult(340, robots[0], "kitchen-std", run.priority ?? "standard");
523
+ result.artifacts = this.artifacts(run.id);
524
+ run.result = result;
525
+ }
526
+ matrixCell(robot, environment) {
527
+ const envBase = baseId(environment);
528
+ for (const cell of MATRIX.cells) {
529
+ if (cell.robot === robot && baseId(cell.environment) === envBase) {
530
+ return { ...clone(cell), environment };
531
+ }
532
+ }
533
+ const n = { "cell-a": 190, "kitchen-std": 240, "warehouse-std": 210 }[envBase] ?? 240;
534
+ let rate = 0.85;
535
+ for (const cell of MATRIX.cells) {
536
+ if (cell.robot === robot && baseId(cell.environment) === "kitchen-std") {
537
+ rate = cell.success_rate;
538
+ break;
539
+ }
540
+ }
541
+ const successes = Math.round(n * rate);
542
+ const [lo, hi] = wilsonCi(successes, n);
543
+ return {
544
+ robot,
545
+ environment,
546
+ n,
547
+ success_rate: round3(successes / n),
548
+ ci95: [lo, hi],
549
+ status: lo >= 0.8 ? "pass" : "fail"
550
+ };
551
+ }
552
+ buildMatrix(run) {
553
+ const robots = run.robots ?? [];
554
+ const environments = run.environments ?? [];
555
+ const cells = robots.flatMap((r) => environments.map((e) => this.matrixCell(r, e)));
556
+ run.cells = cells;
557
+ run.gate = { metric: "ci95_lower", min: 0.8 };
558
+ run.regressions = clone(MATRIX.regressions);
559
+ const nTotal = cells.reduce((acc, c) => acc + c.n, 0);
560
+ const successes = cells.reduce((acc, c) => acc + Math.round(c.n * c.success_rate), 0);
561
+ const result = this.genericResult(
562
+ Math.max(nTotal, 1),
563
+ robots[0] ?? "g1-edu-pro@fw2.3",
564
+ environments[0] ?? "kitchen-std",
565
+ run.priority ?? "standard",
566
+ nTotal ? successes / nTotal : BASE_RATE
567
+ );
568
+ if (String(run.task ?? "").startsWith("espresso")) {
569
+ const canonical = this.canonicalResult("run_9101");
570
+ result.stage_rates = canonical.stage_rates;
571
+ result.instruction_breakdown = canonical.instruction_breakdown;
572
+ }
573
+ result.artifacts = this.artifacts(run.id);
574
+ run.result = result;
575
+ }
576
+ buildCompare(run) {
577
+ const names = Object.keys(run.policies ?? {}).sort();
578
+ const winner = names.length ? names[names.length - 1] : void 0;
579
+ run.winner = winner;
580
+ run.p_value = 8e-3;
581
+ run.effect_pts = 4.1;
582
+ const results = {};
583
+ for (const name of names) {
584
+ const rate = name === winner ? BASE_RATE : BASE_RATE - 0.041;
585
+ const successes = Math.round(340 * rate);
586
+ results[name] = {
587
+ n: 340,
588
+ success_rate: round3(successes / 340),
589
+ ci95: wilsonCi(successes, 340),
590
+ interval_method: "wilson"
591
+ };
592
+ }
593
+ run.results = results;
594
+ }
595
+ buildTransfer(run) {
596
+ const report = clone(TRANSFER);
597
+ if (run.task) report.task = run.task;
598
+ if (run.source) report.source = { ...report.source, robot: run.source };
599
+ if (typeof run.target === "string") report.target = { ...report.target, robot: run.target };
600
+ run.transfer = report;
601
+ }
602
+ listRuns(params) {
603
+ let data = [...this.runs.values()];
604
+ if (params.kind) data = data.filter((r) => r.kind === params.kind);
605
+ if (params.status) data = data.filter((r) => r.status === params.status);
606
+ const limit = Number(params.limit ?? 20);
607
+ return { object: "list", url: "/v1/runs", data: data.slice(0, limit) };
608
+ }
609
+ getRun(runId) {
610
+ const run = this.runs.get(runId);
611
+ if (!run) throw notFound(`No such run: ${runId}`, runId);
612
+ return run;
613
+ }
614
+ realizedEpisodes(run) {
615
+ if (run.result?.n) return Number(run.result.n);
616
+ try {
617
+ return this.episodeCount(run.episodes ?? 612, true);
618
+ } catch {
619
+ return 612;
620
+ }
621
+ }
622
+ listEpisodes(runId) {
623
+ const run = this.getRun(runId);
624
+ const n = this.realizedEpisodes(run);
625
+ const rate = run.result?.success_rate ?? BASE_RATE;
626
+ const successes = Math.round(n * rate);
627
+ const failures = n - successes;
628
+ const clusters = [];
629
+ for (const [name, count] of Object.entries(run.result?.failure_clusters ?? {})) {
630
+ for (let i = 0; i < Number(count); i++) clusters.push(name);
631
+ }
632
+ const episodes = [];
633
+ let failSeen = 0;
634
+ for (let i = 0; i < n; i++) {
635
+ const isFailure = failures > 0 && Math.floor(i * failures / n) !== Math.floor((i + 1) * failures / n);
636
+ const base = `https://artifacts.roborama.com/${runId}/ep_${String(i).padStart(5, "0")}`;
637
+ const episode = {
638
+ id: `${runId}_ep_${String(i).padStart(5, "0")}`,
639
+ object: "episode",
640
+ index: i,
641
+ outcome: isFailure ? "failure" : "success",
642
+ duration_s: 100 + i * 37 % 80,
643
+ video_url: `${base}/video.mp4`,
644
+ mcap_url: `${base}/episode.mcap`,
645
+ replay_spec: {
646
+ episode: i,
647
+ seed: run.perturbation?.seed ?? 42,
648
+ initial_conditions: { layout_seed: 42 + i }
649
+ }
650
+ };
651
+ if (isFailure) {
652
+ if (failSeen < clusters.length) episode.cluster = clusters[failSeen];
653
+ failSeen += 1;
654
+ }
655
+ episodes.push(episode);
656
+ }
657
+ return { object: "list", url: `/v1/runs/${runId}/episodes`, data: episodes };
658
+ }
659
+ raiseIfStopped(run) {
660
+ if (run.status === "stopped" && run.error) {
661
+ throw errorFromEnvelope(402, { error: run.error });
662
+ }
663
+ }
664
+ report(runId) {
665
+ const run = this.getRun(runId);
666
+ this.raiseIfStopped(run);
667
+ const pdf = `https://api.roborama.com/v1/runs/${runId}/report?format=pdf`;
668
+ return {
669
+ object: "report",
670
+ run_id: runId,
671
+ kind: "verification",
672
+ result: run.result,
673
+ resolved_pins: run.resolved_pins ?? {},
674
+ citation: `Roborama verification report, ${runId}, task ${run.task ?? "n/a"}, ${MOCK_NOW.slice(0, 10)}`,
675
+ pdf_url: pdf,
676
+ url: pdf
677
+ };
678
+ }
679
+ export(runId, format) {
680
+ this.getRun(runId);
681
+ const id = this.next("exp_", 1);
682
+ return {
683
+ object: "export",
684
+ id,
685
+ run_id: runId,
686
+ format,
687
+ status: "ready",
688
+ download_url: `https://artifacts.roborama.com/exports/${runId}-${format}.tar.gz`
689
+ };
690
+ }
691
+ // -- tasks / kits -------------------------------------------------------------
692
+ createTask(body) {
693
+ const name = body.name ?? "task";
694
+ const num = this.next(`tasknum_${name}_`, 1).split("_").pop();
695
+ const task = {
696
+ id: `task_${name}_${num.padStart(2, "0")}`,
697
+ object: "task",
698
+ name,
699
+ rev: null,
700
+ status: "draft",
701
+ visibility: body.visibility ?? "private",
702
+ created: MOCK_NOW,
703
+ frozen: null,
704
+ signatures: [],
705
+ initial_conditions: body.initial_conditions ?? {},
706
+ success_predicates: body.success_predicates ?? [],
707
+ instructions: body.instructions ?? {},
708
+ stages: body.stages ?? [],
709
+ envelope: body.envelope ?? {},
710
+ baseline: body.baseline,
711
+ pilot: null
712
+ };
713
+ this.tasks.set(task.id, task);
714
+ return task;
715
+ }
716
+ getTask(taskId) {
717
+ const task = this.tasks.get(taskId);
718
+ if (!task) throw notFound(`No such task: ${taskId}`);
719
+ return task;
720
+ }
721
+ taskAction(taskId, action, body) {
722
+ const task = this.getTask(taskId);
723
+ if (action === "pilot") {
724
+ task.status = "piloting";
725
+ const pilot = {
726
+ run_id: "run_9088",
727
+ robot: body.robot,
728
+ episodes: body.episodes ?? 25,
729
+ stream_url: "wss://streams.roborama.com/cells/cell-g1-02/webrtc",
730
+ amendments: task.pilot?.amendments ?? 0
731
+ };
732
+ task.pilot = pilot;
733
+ return pilot;
734
+ }
735
+ if (action === "amend") {
736
+ for (const [key, value] of Object.entries(body)) {
737
+ if (value !== void 0 && value !== null) task[key] = value;
738
+ }
739
+ if (task.pilot) task.pilot.amendments = (task.pilot.amendments ?? 0) + 1;
740
+ return task;
741
+ }
742
+ if (action === "freeze") {
743
+ task.status = "frozen";
744
+ task.rev = `${task.name}@v1`;
745
+ task.frozen = MOCK_NOW;
746
+ task.signatures = ["acct_customer", "roborama"];
747
+ return task;
748
+ }
749
+ throw notFound(`No mock route for task action: ${action}`);
750
+ }
751
+ registerKit(body) {
752
+ const name = body.name ?? "kit";
753
+ const id = `kit_${String(name).replace(/-/g, "_")}`;
754
+ const kit = {
755
+ id,
756
+ object: "kit",
757
+ name,
758
+ status: "registered",
759
+ status_history: [{ status: "registered", at: MOCK_NOW }],
760
+ items: body.items ?? [],
761
+ tracking: null,
762
+ environment_ref: null,
763
+ shipping_label_url: `https://api.roborama.com/v1/kits/${id}/shipping-label`
764
+ };
765
+ this.kits.set(id, kit);
766
+ return kit;
767
+ }
768
+ getKit(kitId) {
769
+ const kit = this.kits.get(kitId);
770
+ if (!kit) throw notFound(`No such kit: ${kitId}`);
771
+ return kit;
772
+ }
773
+ };
774
+
775
+ // src/client.ts
776
+ var DEFAULT_BASE_URL = "https://api.roborama.com";
777
+ var USER_AGENT = "roborama-typescript/0.1.0";
778
+ function env(name) {
779
+ const proc = globalThis.process;
780
+ return proc?.env?.[name];
781
+ }
782
+ function envFlag(name) {
783
+ const value = (env(name) ?? "").trim().toLowerCase();
784
+ return value === "1" || value === "true" || value === "yes" || value === "on";
785
+ }
786
+ function idempotencyKey() {
787
+ const c = globalThis.crypto;
788
+ if (c?.randomUUID) return c.randomUUID();
789
+ return `idk-${Date.now()}-${Math.floor(Math.random() * 1e9)}`;
790
+ }
791
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
792
+ function cleanBody(params) {
793
+ const out = {};
794
+ for (const [key, value] of Object.entries(params)) {
795
+ if (value !== void 0 && value !== null) out[key] = value;
796
+ }
797
+ return out;
798
+ }
799
+ var Transport = class {
800
+ constructor(opts = {}) {
801
+ this.apiKey = opts.apiKey;
802
+ this.baseUrl = (opts.baseUrl ?? env("ROBORAMA_BASE_URL") ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
803
+ this.mockOpt = opts.mock;
804
+ this.fetchImpl = opts.fetch;
805
+ }
806
+ get mock() {
807
+ return this.mockOpt ?? envFlag("ROBORAMA_MOCK");
808
+ }
809
+ engine() {
810
+ if (!this.mockBackend) this.mockBackend = new MockBackend();
811
+ return this.mockBackend;
812
+ }
813
+ key() {
814
+ const key = this.apiKey ?? env("ROBORAMA_API_KEY");
815
+ if (!key) {
816
+ throw new RoboramaError(
817
+ "No API key. Export ROBORAMA_API_KEY (keys use the rbr_live_ prefix), or set ROBORAMA_MOCK=1 / new Roborama({ mock: true }) to integrate offline against the deterministic mock backend.",
818
+ { code: "invalid_api_key", status: 401 }
819
+ );
820
+ }
821
+ return key;
822
+ }
823
+ async request(method, path, opts = {}) {
824
+ if (this.mock) {
825
+ return this.engine().request(method, path, opts.body, opts.params);
826
+ }
827
+ const fetchFn = this.fetchImpl ?? globalThis.fetch;
828
+ const headers = {
829
+ Authorization: `Bearer ${this.key()}`,
830
+ Accept: "application/json",
831
+ "User-Agent": USER_AGENT
832
+ };
833
+ if (opts.body !== void 0) headers["Content-Type"] = "application/json";
834
+ if (method === "POST" && path === "/v1/runs") {
835
+ headers["Idempotency-Key"] = idempotencyKey();
836
+ }
837
+ const url = new URL(this.baseUrl + path);
838
+ for (const [key, value] of Object.entries(opts.params ?? {})) {
839
+ if (value !== void 0 && value !== null) url.searchParams.set(key, String(value));
840
+ }
841
+ for (let attempt = 1; ; attempt++) {
842
+ let resp;
843
+ try {
844
+ resp = await fetchFn(url, {
845
+ method,
846
+ headers,
847
+ body: opts.body !== void 0 ? JSON.stringify(opts.body) : void 0
848
+ });
849
+ } catch (cause) {
850
+ if (attempt <= 2) {
851
+ await sleep(500 * attempt);
852
+ continue;
853
+ }
854
+ throw new RoboramaError(`Network error talking to ${this.baseUrl}: ${cause}`, {
855
+ code: "transport_error"
856
+ });
857
+ }
858
+ if (resp.status === 429 && attempt <= 2) {
859
+ const retryAfter = Number(resp.headers.get("Retry-After") ?? "1");
860
+ await sleep(Math.min(Number.isFinite(retryAfter) ? retryAfter * 1e3 : 1e3, 3e4));
861
+ continue;
862
+ }
863
+ if (resp.ok) {
864
+ const text = await resp.text();
865
+ return text ? JSON.parse(text) : null;
866
+ }
867
+ let payload;
868
+ try {
869
+ payload = await resp.json();
870
+ } catch {
871
+ payload = void 0;
872
+ }
873
+ if (!payload) {
874
+ throw new RoboramaError(`HTTP ${resp.status} from ${path}`, {
875
+ code: `http_${resp.status}`,
876
+ status: resp.status
877
+ });
878
+ }
879
+ throw errorFromEnvelope(resp.status, payload);
880
+ }
881
+ }
882
+ async *events(runId) {
883
+ if (this.mock) {
884
+ yield* this.engine().events(runId);
885
+ return;
886
+ }
887
+ const fetchFn = this.fetchImpl ?? globalThis.fetch;
888
+ const resp = await fetchFn(`${this.baseUrl}/v1/runs/${runId}/events`, {
889
+ headers: {
890
+ Authorization: `Bearer ${this.key()}`,
891
+ Accept: "text/event-stream",
892
+ "User-Agent": USER_AGENT
893
+ }
894
+ });
895
+ if (!resp.ok || !resp.body) {
896
+ let payload;
897
+ try {
898
+ payload = await resp.json();
899
+ } catch {
900
+ payload = void 0;
901
+ }
902
+ throw errorFromEnvelope(resp.status, payload);
903
+ }
904
+ const reader = resp.body.getReader();
905
+ const decoder = new TextDecoder();
906
+ let buffer = "";
907
+ let eventType;
908
+ while (true) {
909
+ const { done, value } = await reader.read();
910
+ if (done) break;
911
+ buffer += decoder.decode(value, { stream: true });
912
+ let idx;
913
+ while ((idx = buffer.indexOf("\n")) >= 0) {
914
+ const line = buffer.slice(0, idx).trim();
915
+ buffer = buffer.slice(idx + 1);
916
+ if (!line) {
917
+ eventType = void 0;
918
+ continue;
919
+ }
920
+ if (line.startsWith("event:")) {
921
+ eventType = line.slice(6).trim();
922
+ } else if (line.startsWith("data:")) {
923
+ try {
924
+ const data = JSON.parse(line.slice(5).trim());
925
+ if (data.status === void 0) {
926
+ data.status = eventType === "episode" ? "running" : eventType ?? "running";
927
+ }
928
+ yield data;
929
+ } catch {
930
+ }
931
+ }
932
+ }
933
+ }
934
+ }
935
+ };
936
+ var Policy = {
937
+ /** A container image we run in-facility, GPU-adjacent. */
938
+ container(opts) {
939
+ return { type: "container", ...opts };
940
+ },
941
+ /** A checkpoint on a known runtime (`openpi`, `lerobot`). */
942
+ checkpoint(opts) {
943
+ return { type: "checkpoint", ...opts };
944
+ },
945
+ /** Customer-hosted inference; measured RTT is logged per step. */
946
+ endpoint(opts) {
947
+ return { type: "endpoint", latency_budget_ms: 80, fallback: "halt", ...opts };
948
+ }
949
+ };
950
+ var BaseRunHandle = class {
951
+ constructor(transport, data) {
952
+ this.transport = transport;
953
+ this.data = data;
954
+ }
955
+ get id() {
956
+ return this.data.id;
957
+ }
958
+ get kind() {
959
+ return this.data.kind;
960
+ }
961
+ get status() {
962
+ return this.data.status;
963
+ }
964
+ get resolved_pins() {
965
+ return this.data.resolved_pins ?? {};
966
+ }
967
+ /** The full wire payload, for forward compatibility. */
968
+ get raw() {
969
+ return this.data;
970
+ }
971
+ async refresh() {
972
+ this.data = await this.transport.request("GET", `/v1/runs/${this.id}`);
973
+ return this;
974
+ }
975
+ raiseIfStopped() {
976
+ const error = this.data.error;
977
+ if (this.data.status === "stopped" && error) {
978
+ throw errorFromEnvelope(402, { error });
979
+ }
980
+ }
981
+ };
982
+ var RunHandle = class extends BaseRunHandle {
983
+ /** `n`, `success_rate`, `ci95`, clusters, both meters, `cost_usd`. */
984
+ async result() {
985
+ await this.refresh();
986
+ this.raiseIfStopped();
987
+ return this.data.result ?? {};
988
+ }
989
+ /** Per-episode artifacts: `video_url`, `mcap_url`, `replay_spec`. */
990
+ async episodes() {
991
+ const payload = await this.transport.request("GET", `/v1/runs/${this.id}/episodes`);
992
+ return payload.data ?? [];
993
+ }
994
+ /** Live events: episode ticker plus WebRTC stream URLs. No polling. */
995
+ watch() {
996
+ return this.transport.events(this.id);
997
+ }
998
+ /** Dataset export: `lerobot`, `rlds`, or `mcap-bundle`. */
999
+ async export(opts) {
1000
+ return this.transport.request("POST", `/v1/runs/${this.id}/export`, {
1001
+ body: { format: opts.format }
1002
+ });
1003
+ }
1004
+ /** The deployment verification report, citable. */
1005
+ async report(opts = {}) {
1006
+ return this.transport.request("GET", `/v1/runs/${this.id}/report`, {
1007
+ params: { format: opts.format ?? "pdf" }
1008
+ });
1009
+ }
1010
+ };
1011
+ var EvalRunHandle = class extends RunHandle {
1012
+ get citation() {
1013
+ return this.data.citation;
1014
+ }
1015
+ get attestation() {
1016
+ return this.data.attestation;
1017
+ }
1018
+ get attestation_url() {
1019
+ return this.data.attestation_url;
1020
+ }
1021
+ get per_task() {
1022
+ return this.data.per_task ?? [];
1023
+ }
1024
+ };
1025
+ var CompareRunHandle = class extends RunHandle {
1026
+ get winner() {
1027
+ return this.data.winner;
1028
+ }
1029
+ get p_value() {
1030
+ return this.data.p_value;
1031
+ }
1032
+ get effect_pts() {
1033
+ return this.data.effect_pts;
1034
+ }
1035
+ get results() {
1036
+ return this.data.results ?? {};
1037
+ }
1038
+ };
1039
+ var VerifyRunHandle = class extends RunHandle {
1040
+ /** Where reality matched sim predictions. */
1041
+ get sim_agreement() {
1042
+ return this.data.sim_agreement;
1043
+ }
1044
+ /** Scenario ids where reality diverged. */
1045
+ get disagreements() {
1046
+ return this.data.disagreements ?? [];
1047
+ }
1048
+ get ground_truth_url() {
1049
+ return this.data.ground_truth_url;
1050
+ }
1051
+ };
1052
+ var ThresholdContractHandle = class extends RunHandle {
1053
+ /** Register the webhook fired after verification-tier confirmation. */
1054
+ async onVerified(opts) {
1055
+ this.data = await this.transport.request("POST", `/v1/runs/${this.id}/on-verified`, {
1056
+ body: { webhook: opts.webhook }
1057
+ });
1058
+ return this;
1059
+ }
1060
+ };
1061
+ var TransferRunHandle = class extends BaseRunHandle {
1062
+ /** Source vs. target statistics and the failure clusters transfer opened. */
1063
+ async report() {
1064
+ await this.refresh();
1065
+ this.raiseIfStopped();
1066
+ return this.data.transfer ?? {};
1067
+ }
1068
+ };
1069
+ var MatrixRunHandle = class extends RunHandle {
1070
+ get cells() {
1071
+ return this.data.cells ?? [];
1072
+ }
1073
+ /** Embodiment × environment grid of rate ± CI per cell. */
1074
+ async heatmap() {
1075
+ const grid = {};
1076
+ for (const cell of this.cells) {
1077
+ (grid[cell.robot] ??= {})[cell.environment] = {
1078
+ n: cell.n,
1079
+ success_rate: cell.success_rate,
1080
+ ci95: cell.ci95,
1081
+ status: cell.status
1082
+ };
1083
+ }
1084
+ return grid;
1085
+ }
1086
+ /** Cells that degraded vs. a prior release run. */
1087
+ async regressions(opts) {
1088
+ await this.refresh();
1089
+ return this.data.regressions ?? [];
1090
+ }
1091
+ /** The release-gate deliverable (PDF). */
1092
+ async verificationReport(opts = {}) {
1093
+ return this.report(opts);
1094
+ }
1095
+ /** Overall stats plus the grid and per-stage/per-instruction breakdowns. */
1096
+ async result() {
1097
+ await this.refresh();
1098
+ this.raiseIfStopped();
1099
+ return {
1100
+ ...this.data.result ?? {},
1101
+ heatmap: await this.heatmap(),
1102
+ cells: this.cells
1103
+ };
1104
+ }
1105
+ };
1106
+ function makeRunHandle(transport, data) {
1107
+ switch (data.kind) {
1108
+ case "eval":
1109
+ return new EvalRunHandle(transport, data);
1110
+ case "compare":
1111
+ return new CompareRunHandle(transport, data);
1112
+ case "verify":
1113
+ return new VerifyRunHandle(transport, data);
1114
+ case "threshold":
1115
+ return new ThresholdContractHandle(transport, data);
1116
+ case "transfer":
1117
+ return new TransferRunHandle(transport, data);
1118
+ case "matrix":
1119
+ return new MatrixRunHandle(transport, data);
1120
+ default:
1121
+ return new RunHandle(transport, data);
1122
+ }
1123
+ }
1124
+ var TaskHandle = class {
1125
+ constructor(transport, data) {
1126
+ this.transport = transport;
1127
+ this.data = data;
1128
+ }
1129
+ get id() {
1130
+ return this.data.id;
1131
+ }
1132
+ get name() {
1133
+ return this.data.name;
1134
+ }
1135
+ get status() {
1136
+ return this.data.status;
1137
+ }
1138
+ /** Frozen revision id (`espresso@v1`); null until frozen. */
1139
+ get rev() {
1140
+ return this.data.rev;
1141
+ }
1142
+ get visibility() {
1143
+ return this.data.visibility;
1144
+ }
1145
+ get raw() {
1146
+ return this.data;
1147
+ }
1148
+ /** Run the calibration session; the returned `stream_url` is live. */
1149
+ async pilot(opts) {
1150
+ const pilot = await this.transport.request("POST", `/v1/tasks/${this.id}/pilot`, {
1151
+ body: { robot: opts.robot, episodes: opts.episodes ?? 25 }
1152
+ });
1153
+ this.data.status = "piloting";
1154
+ return pilot;
1155
+ }
1156
+ /** Iterate on the method while piloting. Omitted fields are unchanged. */
1157
+ async amend(fields) {
1158
+ this.data = await this.transport.request("POST", `/v1/tasks/${this.id}/amend`, {
1159
+ body: cleanBody(fields)
1160
+ });
1161
+ return this;
1162
+ }
1163
+ /** Freeze to an immutable, co-signed revision (`name@vN`). */
1164
+ async freeze() {
1165
+ this.data = await this.transport.request("POST", `/v1/tasks/${this.id}/freeze`, {
1166
+ body: {}
1167
+ });
1168
+ return this;
1169
+ }
1170
+ };
1171
+ var KitHandle = class {
1172
+ constructor(transport, data) {
1173
+ this.transport = transport;
1174
+ this.data = data;
1175
+ }
1176
+ get id() {
1177
+ return this.data.id;
1178
+ }
1179
+ get name() {
1180
+ return this.data.name;
1181
+ }
1182
+ /** `registered → received → tracked → available` (as `kit/<name>`). */
1183
+ get status() {
1184
+ return this.data.status;
1185
+ }
1186
+ get environment_ref() {
1187
+ return this.data.environment_ref;
1188
+ }
1189
+ get raw() {
1190
+ return this.data;
1191
+ }
1192
+ /** The inbound shipping label to the facility (URL). */
1193
+ async shippingLabel() {
1194
+ const payload = await this.transport.request(
1195
+ "GET",
1196
+ `/v1/kits/${this.id}/shipping-label`
1197
+ );
1198
+ return payload.url ?? "";
1199
+ }
1200
+ };
1201
+ var Roborama = class {
1202
+ constructor(options = {}) {
1203
+ const transport = new Transport(options);
1204
+ this.transport = transport;
1205
+ this.runs = {
1206
+ create: (async (params) => {
1207
+ const body = cleanBody(params);
1208
+ body.kind = body.kind ?? "run";
1209
+ const data = await transport.request("POST", "/v1/runs", { body });
1210
+ return makeRunHandle(transport, data);
1211
+ }),
1212
+ get: async (runId) => {
1213
+ const data = await transport.request("GET", `/v1/runs/${runId}`);
1214
+ return makeRunHandle(transport, data);
1215
+ },
1216
+ list: async (params = {}) => {
1217
+ const payload = await transport.request("GET", "/v1/runs", { params });
1218
+ return (payload.data ?? []).map(
1219
+ (r) => makeRunHandle(transport, r)
1220
+ );
1221
+ }
1222
+ };
1223
+ this.verifications = {
1224
+ create: (params) => this.runs.create({ ...params, kind: "verify" })
1225
+ };
1226
+ this.matrices = {
1227
+ create: (params) => this.runs.create({ ...params, kind: "matrix" })
1228
+ };
1229
+ this.robots = {
1230
+ list: async () => (await transport.request("GET", "/v1/robots")).data ?? []
1231
+ };
1232
+ this.environments = {
1233
+ list: async (params = {}) => (await transport.request("GET", "/v1/environments", { params })).data ?? []
1234
+ };
1235
+ this.suites = {
1236
+ list: async () => (await transport.request("GET", "/v1/suites")).data ?? []
1237
+ };
1238
+ this.gates = {
1239
+ create: (params) => transport.request("POST", "/v1/gates", { body: cleanBody(params) })
1240
+ };
1241
+ this.streams = {
1242
+ get: (cell) => transport.request("GET", `/v1/streams/${cell}`)
1243
+ };
1244
+ this.keys = {
1245
+ create: (params) => transport.request("POST", "/v1/keys", { body: { scope: params.scope } }),
1246
+ list: async () => (await transport.request("GET", "/v1/keys")).data ?? []
1247
+ };
1248
+ this.budgets = {
1249
+ set: (params) => transport.request("PUT", "/v1/budgets", {
1250
+ body: { monthly_usd: params.monthly_usd, hard: params.hard ?? false }
1251
+ })
1252
+ };
1253
+ this.data = {
1254
+ purge: (runId) => transport.request("DELETE", `/v1/data/${runId}`)
1255
+ };
1256
+ this.calibration = {
1257
+ export: (params) => transport.request("POST", "/v1/calibration/exports", {
1258
+ body: cleanBody(params)
1259
+ })
1260
+ };
1261
+ this.tasks = {
1262
+ create: async (params) => {
1263
+ const data = await transport.request("POST", "/v1/tasks", {
1264
+ body: cleanBody(params)
1265
+ });
1266
+ return new TaskHandle(transport, data);
1267
+ },
1268
+ get: async (taskId) => {
1269
+ const data = await transport.request("GET", `/v1/tasks/${taskId}`);
1270
+ return new TaskHandle(transport, data);
1271
+ },
1272
+ list: async () => {
1273
+ const payload = await transport.request("GET", "/v1/tasks");
1274
+ return (payload.data ?? []).map((t) => new TaskHandle(transport, t));
1275
+ }
1276
+ };
1277
+ this.kits = {
1278
+ register: async (params) => {
1279
+ const data = await transport.request("POST", "/v1/kits", {
1280
+ body: cleanBody(params)
1281
+ });
1282
+ return new KitHandle(transport, data);
1283
+ },
1284
+ get: async (kitId) => {
1285
+ const data = await transport.request("GET", `/v1/kits/${kitId}`);
1286
+ return new KitHandle(transport, data);
1287
+ }
1288
+ };
1289
+ this.scenarios = {
1290
+ fromFile: async (path) => {
1291
+ const { readFile } = await import("fs/promises");
1292
+ const parsed = JSON.parse(await readFile(path, "utf8"));
1293
+ return Array.isArray(parsed) ? { scenarios: parsed } : parsed;
1294
+ }
1295
+ };
1296
+ }
1297
+ /** Whether this client is in mock mode. */
1298
+ get mock() {
1299
+ return this.transport.mock;
1300
+ }
1301
+ get baseUrl() {
1302
+ return this.transport.baseUrl;
1303
+ }
1304
+ /** Both meters, dollars, and a queue ETA — before you commit. */
1305
+ async quote(params) {
1306
+ return this.transport.request("POST", "/v1/quote", {
1307
+ body: cleanBody(params)
1308
+ });
1309
+ }
1310
+ /** Metered usage for a period (`YYYY-MM`): hours by tier, dollars, budget. */
1311
+ async usage(params = {}) {
1312
+ return this.transport.request("GET", "/v1/usage", {
1313
+ params: { period: params.period }
1314
+ });
1315
+ }
1316
+ };
1317
+ /** Policy constructors: `Roborama.Policy.checkpoint({...})`, etc. */
1318
+ Roborama.Policy = Policy;
1319
+
1320
+ // src/index.ts
1321
+ var index_default = Roborama;
1322
+ export {
1323
+ CompareRunHandle,
1324
+ DEFAULT_BASE_URL,
1325
+ EvalRunHandle,
1326
+ KitHandle,
1327
+ MatrixRunHandle,
1328
+ Policy,
1329
+ Roborama,
1330
+ RoboramaError,
1331
+ RunHandle,
1332
+ TaskHandle,
1333
+ ThresholdContractHandle,
1334
+ TransferRunHandle,
1335
+ VerifyRunHandle,
1336
+ index_default as default,
1337
+ parseAuto,
1338
+ planningN,
1339
+ wilsonCi,
1340
+ zForCi
1341
+ };
1342
+ //# sourceMappingURL=index.js.map