robopark 2.8.21 → 2.8.22
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/package.json
CHANGED
|
Binary file
|
package/scheduler/main.py
CHANGED
|
@@ -55,6 +55,12 @@ class Robot(BaseModel):
|
|
|
55
55
|
last_heartbeat: Optional[datetime] = None
|
|
56
56
|
total_sessions: int = 0
|
|
57
57
|
total_runtime_seconds: int = 0
|
|
58
|
+
# Added after the `robots` table's additive trigger_count/created_at
|
|
59
|
+
# migration — response_model=Robot on GET /api/robots(/{id}) was
|
|
60
|
+
# silently stripping both from every response since the DB row has
|
|
61
|
+
# them but this model didn't declare them.
|
|
62
|
+
trigger_count: int = 0
|
|
63
|
+
created_at: Optional[datetime] = None
|
|
58
64
|
|
|
59
65
|
class LiveKitServer(BaseModel):
|
|
60
66
|
id: str
|
|
@@ -1589,10 +1595,16 @@ async def update_device(device_id: str, payload: DeviceUpdate):
|
|
|
1589
1595
|
await _log_history("device", "device", device_id, "updated", "operator", f"fields={','.join(fields.keys())}")
|
|
1590
1596
|
return await get_device(device_id)
|
|
1591
1597
|
|
|
1592
|
-
@app.post("/api/devices"
|
|
1598
|
+
@app.post("/api/devices")
|
|
1593
1599
|
async def create_device(payload: DeviceCreate):
|
|
1594
1600
|
"""Manually register a device from the UI. Returns the device record and
|
|
1595
|
-
a one-time enrollment token (if one wasn't provided).
|
|
1601
|
+
a one-time enrollment token (if one wasn't provided).
|
|
1602
|
+
|
|
1603
|
+
BUG FIXED: this had response_model=Device, which made FastAPI silently
|
|
1604
|
+
strip the enrollment_token key from the response below (Device has no
|
|
1605
|
+
such field) — the docstring's promised token was never actually
|
|
1606
|
+
reaching the caller. Dropping response_model here since the return
|
|
1607
|
+
value is deliberately Device-plus-one-extra-field, not just Device."""
|
|
1596
1608
|
if not payload.name or not payload.name.strip():
|
|
1597
1609
|
raise HTTPException(400, "name is required")
|
|
1598
1610
|
|
|
@@ -1763,6 +1775,17 @@ async def enroll_device(payload: DeviceEnrollRequest):
|
|
|
1763
1775
|
)
|
|
1764
1776
|
else:
|
|
1765
1777
|
# First-boot enroll via the global default token -> create a new device.
|
|
1778
|
+
#
|
|
1779
|
+
# BUG FIXED: this used to persist enrollment_token_hash=token_hash
|
|
1780
|
+
# (the hash of the SHARED default token) onto the new device row.
|
|
1781
|
+
# Since the "existing" lookup above matches on enrollment_token_hash,
|
|
1782
|
+
# every subsequent enroll using that same shared default token would
|
|
1783
|
+
# match THIS row and take the "existing" reuse/rotate path instead of
|
|
1784
|
+
# minting an independent device — silently renaming and re-keying
|
|
1785
|
+
# whichever device happened to enroll first, and invalidating its
|
|
1786
|
+
# live device_token. Devices minted via the default token must keep
|
|
1787
|
+
# enrollment_token_hash NULL so the default token stays reusable
|
|
1788
|
+
# across an entire fleet, as its name implies.
|
|
1766
1789
|
device_id = f"dev_{secrets.token_hex(4)}"
|
|
1767
1790
|
new_token = secrets.token_urlsafe(32)
|
|
1768
1791
|
new_hash = _hash(new_token)
|
|
@@ -1779,7 +1802,7 @@ async def enroll_device(payload: DeviceEnrollRequest):
|
|
|
1779
1802
|
payload.tailscale_ip, payload.lan_ip,
|
|
1780
1803
|
payload.motor_server_url, payload.character_id,
|
|
1781
1804
|
payload.livekit_url, payload.video_device, payload.audio_device,
|
|
1782
|
-
new_hash,
|
|
1805
|
+
new_hash, None, now, now,
|
|
1783
1806
|
),
|
|
1784
1807
|
)
|
|
1785
1808
|
await db.execute(
|
|
@@ -190,10 +190,18 @@ class PreviewAgent:
|
|
|
190
190
|
logger.error("no DEVICE_TOKEN and no ENROLLMENT_TOKEN; cannot poll scheduler")
|
|
191
191
|
sys.exit(1)
|
|
192
192
|
|
|
193
|
+
self._session = httpx.AsyncClient()
|
|
194
|
+
|
|
195
|
+
# Must come after self._session exists — _resolve_device_id() guards
|
|
196
|
+
# on it and silently no-ops otherwise. On a fresh enroll (no cached
|
|
197
|
+
# device_id in ~/.robopark/preview_agent.json) that made this ALWAYS
|
|
198
|
+
# no-op, so device_id never resolved and preview/agent polling fell
|
|
199
|
+
# back to robot_id (the display name) instead of the real device_id
|
|
200
|
+
# for the entire session — the same id-mismatch bug fixed elsewhere,
|
|
201
|
+
# recurring here only on a first-ever enroll.
|
|
193
202
|
if not self.device_id:
|
|
194
203
|
await self._resolve_device_id()
|
|
195
204
|
|
|
196
|
-
self._session = httpx.AsyncClient()
|
|
197
205
|
self._loop = asyncio.get_running_loop()
|
|
198
206
|
self._start_vision_webhook_server()
|
|
199
207
|
|