robopark 3.3.61 → 3.3.62

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "robopark",
3
- "version": "3.3.61",
3
+ "version": "3.3.62",
4
4
  "description": "RoboPark fleet control CLI — scheduler, control UI, and character voice calls. Standalone: no infinicode runtime.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1 +1 @@
1
- 95wYdEKkTppMWNZn6Vr-N
1
+ Omr4BaSGuNd9w4zCHNwa5
@@ -7,13 +7,6 @@
7
7
  "static/chunks/main-app-a1f59168303c7f84.js",
8
8
  "static/chunks/app/_not-found/page-cfd7daa82d71540c.js"
9
9
  ],
10
- "/api/device-character/route": [
11
- "static/chunks/webpack-5252e5254da079b5.js",
12
- "static/chunks/4bd1b696-f785427dddbba9fb.js",
13
- "static/chunks/1255-404bb8de49c90395.js",
14
- "static/chunks/main-app-a1f59168303c7f84.js",
15
- "static/chunks/app/api/device-character/route-0952b44bda5bfb6d.js"
16
- ],
17
10
  "/layout": [
18
11
  "static/chunks/webpack-5252e5254da079b5.js",
19
12
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
@@ -25,6 +18,13 @@
25
18
  "static/chunks/9880-53262785284464cc.js",
26
19
  "static/chunks/app/layout-fcce963da1d8220f.js"
27
20
  ],
21
+ "/api/device-character/route": [
22
+ "static/chunks/webpack-5252e5254da079b5.js",
23
+ "static/chunks/4bd1b696-f785427dddbba9fb.js",
24
+ "static/chunks/1255-404bb8de49c90395.js",
25
+ "static/chunks/main-app-a1f59168303c7f84.js",
26
+ "static/chunks/app/api/device-character/route-0952b44bda5bfb6d.js"
27
+ ],
28
28
  "/api/download-piper-model/route": [
29
29
  "static/chunks/webpack-5252e5254da079b5.js",
30
30
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
@@ -144,12 +144,12 @@
144
144
  "static/chunks/main-app-a1f59168303c7f84.js",
145
145
  "static/chunks/app/api/voices/route-0952b44bda5bfb6d.js"
146
146
  ],
147
- "/api/wake-word/models/route": [
147
+ "/api/wake-word/upload/route": [
148
148
  "static/chunks/webpack-5252e5254da079b5.js",
149
149
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
150
150
  "static/chunks/1255-404bb8de49c90395.js",
151
151
  "static/chunks/main-app-a1f59168303c7f84.js",
152
- "static/chunks/app/api/wake-word/models/route-0952b44bda5bfb6d.js"
152
+ "static/chunks/app/api/wake-word/upload/route-0952b44bda5bfb6d.js"
153
153
  ],
154
154
  "/api/wake-word/status/route": [
155
155
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -158,12 +158,12 @@
158
158
  "static/chunks/main-app-a1f59168303c7f84.js",
159
159
  "static/chunks/app/api/wake-word/status/route-0952b44bda5bfb6d.js"
160
160
  ],
161
- "/api/wake-word/upload/route": [
161
+ "/api/wake-word/models/route": [
162
162
  "static/chunks/webpack-5252e5254da079b5.js",
163
163
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
164
164
  "static/chunks/1255-404bb8de49c90395.js",
165
165
  "static/chunks/main-app-a1f59168303c7f84.js",
166
- "static/chunks/app/api/wake-word/upload/route-0952b44bda5bfb6d.js"
166
+ "static/chunks/app/api/wake-word/models/route-0952b44bda5bfb6d.js"
167
167
  ],
168
168
  "/api/kiosk-hardware/route": [
169
169
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -172,26 +172,26 @@
172
172
  "static/chunks/main-app-a1f59168303c7f84.js",
173
173
  "static/chunks/app/api/kiosk-hardware/route-0952b44bda5bfb6d.js"
174
174
  ],
175
- "/api/connection-details/route": [
175
+ "/api/model-status/route": [
176
176
  "static/chunks/webpack-5252e5254da079b5.js",
177
177
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
178
178
  "static/chunks/1255-404bb8de49c90395.js",
179
179
  "static/chunks/main-app-a1f59168303c7f84.js",
180
- "static/chunks/app/api/connection-details/route-0952b44bda5bfb6d.js"
180
+ "static/chunks/app/api/model-status/route-0952b44bda5bfb6d.js"
181
181
  ],
182
- "/api/model-status/route": [
182
+ "/api/connection-details/route": [
183
183
  "static/chunks/webpack-5252e5254da079b5.js",
184
184
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
185
185
  "static/chunks/1255-404bb8de49c90395.js",
186
186
  "static/chunks/main-app-a1f59168303c7f84.js",
187
- "static/chunks/app/api/model-status/route-0952b44bda5bfb6d.js"
187
+ "static/chunks/app/api/connection-details/route-0952b44bda5bfb6d.js"
188
188
  ],
189
- "/api/robopark/live-rooms/route": [
189
+ "/api/robopark/character-ai/route": [
190
190
  "static/chunks/webpack-5252e5254da079b5.js",
191
191
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
192
192
  "static/chunks/1255-404bb8de49c90395.js",
193
193
  "static/chunks/main-app-a1f59168303c7f84.js",
194
- "static/chunks/app/api/robopark/live-rooms/route-0952b44bda5bfb6d.js"
194
+ "static/chunks/app/api/robopark/character-ai/route-0952b44bda5bfb6d.js"
195
195
  ],
196
196
  "/api/robopark/camera-token/route": [
197
197
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -200,26 +200,26 @@
200
200
  "static/chunks/main-app-a1f59168303c7f84.js",
201
201
  "static/chunks/app/api/robopark/camera-token/route-0952b44bda5bfb6d.js"
202
202
  ],
203
- "/api/robopark/monitor-token/route": [
203
+ "/api/robopark/device-access/route": [
204
204
  "static/chunks/webpack-5252e5254da079b5.js",
205
205
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
206
206
  "static/chunks/1255-404bb8de49c90395.js",
207
207
  "static/chunks/main-app-a1f59168303c7f84.js",
208
- "static/chunks/app/api/robopark/monitor-token/route-0952b44bda5bfb6d.js"
208
+ "static/chunks/app/api/robopark/device-access/route-0952b44bda5bfb6d.js"
209
209
  ],
210
- "/api/robopark/character-ai/route": [
210
+ "/api/robopark/servers/[serverId]/metrics/route": [
211
211
  "static/chunks/webpack-5252e5254da079b5.js",
212
212
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
213
213
  "static/chunks/1255-404bb8de49c90395.js",
214
214
  "static/chunks/main-app-a1f59168303c7f84.js",
215
- "static/chunks/app/api/robopark/character-ai/route-0952b44bda5bfb6d.js"
215
+ "static/chunks/app/api/robopark/servers/[serverId]/metrics/route-0952b44bda5bfb6d.js"
216
216
  ],
217
- "/api/robopark/device-access/route": [
217
+ "/api/robopark/live-rooms/route": [
218
218
  "static/chunks/webpack-5252e5254da079b5.js",
219
219
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
220
220
  "static/chunks/1255-404bb8de49c90395.js",
221
221
  "static/chunks/main-app-a1f59168303c7f84.js",
222
- "static/chunks/app/api/robopark/device-access/route-0952b44bda5bfb6d.js"
222
+ "static/chunks/app/api/robopark/live-rooms/route-0952b44bda5bfb6d.js"
223
223
  ],
224
224
  "/api/robopark/robots/route": [
225
225
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -228,12 +228,12 @@
228
228
  "static/chunks/main-app-a1f59168303c7f84.js",
229
229
  "static/chunks/app/api/robopark/robots/route-0952b44bda5bfb6d.js"
230
230
  ],
231
- "/api/robopark/servers/[serverId]/metrics/route": [
231
+ "/api/robopark/monitor-token/route": [
232
232
  "static/chunks/webpack-5252e5254da079b5.js",
233
233
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
234
234
  "static/chunks/1255-404bb8de49c90395.js",
235
235
  "static/chunks/main-app-a1f59168303c7f84.js",
236
- "static/chunks/app/api/robopark/servers/[serverId]/metrics/route-0952b44bda5bfb6d.js"
236
+ "static/chunks/app/api/robopark/monitor-token/route-0952b44bda5bfb6d.js"
237
237
  ],
238
238
  "/api/robopark/servers/[serverId]/route": [
239
239
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -242,33 +242,33 @@
242
242
  "static/chunks/main-app-a1f59168303c7f84.js",
243
243
  "static/chunks/app/api/robopark/servers/[serverId]/route-0952b44bda5bfb6d.js"
244
244
  ],
245
- "/api/robopark/vision-caption/route": [
245
+ "/api/robopark/servers/route": [
246
246
  "static/chunks/webpack-5252e5254da079b5.js",
247
247
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
248
248
  "static/chunks/1255-404bb8de49c90395.js",
249
249
  "static/chunks/main-app-a1f59168303c7f84.js",
250
- "static/chunks/app/api/robopark/vision-caption/route-0952b44bda5bfb6d.js"
250
+ "static/chunks/app/api/robopark/servers/route-0952b44bda5bfb6d.js"
251
251
  ],
252
- "/api/robopark/sessions/route": [
252
+ "/api/robovoice/robots/[robotId]/media-health/route": [
253
253
  "static/chunks/webpack-5252e5254da079b5.js",
254
254
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
255
255
  "static/chunks/1255-404bb8de49c90395.js",
256
256
  "static/chunks/main-app-a1f59168303c7f84.js",
257
- "static/chunks/app/api/robopark/sessions/route-0952b44bda5bfb6d.js"
257
+ "static/chunks/app/api/robovoice/robots/[robotId]/media-health/route-0952b44bda5bfb6d.js"
258
258
  ],
259
- "/api/robopark/servers/route": [
259
+ "/api/robopark/vision-caption/route": [
260
260
  "static/chunks/webpack-5252e5254da079b5.js",
261
261
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
262
262
  "static/chunks/1255-404bb8de49c90395.js",
263
263
  "static/chunks/main-app-a1f59168303c7f84.js",
264
- "static/chunks/app/api/robopark/servers/route-0952b44bda5bfb6d.js"
264
+ "static/chunks/app/api/robopark/vision-caption/route-0952b44bda5bfb6d.js"
265
265
  ],
266
- "/api/robovoice/robots/[robotId]/media-health/route": [
266
+ "/api/robopark/robots/[robotId]/voice-engine/route": [
267
267
  "static/chunks/webpack-5252e5254da079b5.js",
268
268
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
269
269
  "static/chunks/1255-404bb8de49c90395.js",
270
270
  "static/chunks/main-app-a1f59168303c7f84.js",
271
- "static/chunks/app/api/robovoice/robots/[robotId]/media-health/route-0952b44bda5bfb6d.js"
271
+ "static/chunks/app/api/robopark/robots/[robotId]/voice-engine/route-0952b44bda5bfb6d.js"
272
272
  ],
273
273
  "/api/settings/route": [
274
274
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -277,12 +277,12 @@
277
277
  "static/chunks/main-app-a1f59168303c7f84.js",
278
278
  "static/chunks/app/api/settings/route-0952b44bda5bfb6d.js"
279
279
  ],
280
- "/api/robopark/robots/[robotId]/voice-engine/route": [
280
+ "/api/robopark/sessions/route": [
281
281
  "static/chunks/webpack-5252e5254da079b5.js",
282
282
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
283
283
  "static/chunks/1255-404bb8de49c90395.js",
284
284
  "static/chunks/main-app-a1f59168303c7f84.js",
285
- "static/chunks/app/api/robopark/robots/[robotId]/voice-engine/route-0952b44bda5bfb6d.js"
285
+ "static/chunks/app/api/robopark/sessions/route-0952b44bda5bfb6d.js"
286
286
  ],
287
287
  "/api/turn-credentials/route": [
288
288
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -298,12 +298,12 @@
298
298
  "static/chunks/main-app-a1f59168303c7f84.js",
299
299
  "static/chunks/app/api/robopark/transcripts/route-0952b44bda5bfb6d.js"
300
300
  ],
301
- "/fed/media/robots/[robotId]/video_feed/route": [
301
+ "/camera-grid/route": [
302
302
  "static/chunks/webpack-5252e5254da079b5.js",
303
303
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
304
304
  "static/chunks/1255-404bb8de49c90395.js",
305
305
  "static/chunks/main-app-a1f59168303c7f84.js",
306
- "static/chunks/app/fed/media/robots/[robotId]/video_feed/route-0952b44bda5bfb6d.js"
306
+ "static/chunks/app/camera-grid/route-0952b44bda5bfb6d.js"
307
307
  ],
308
308
  "/api/wake/route": [
309
309
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -312,26 +312,26 @@
312
312
  "static/chunks/main-app-a1f59168303c7f84.js",
313
313
  "static/chunks/app/api/wake/route-0952b44bda5bfb6d.js"
314
314
  ],
315
- "/camera-grid/route": [
315
+ "/fed/media/robots/[robotId]/video_feed/route": [
316
316
  "static/chunks/webpack-5252e5254da079b5.js",
317
317
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
318
318
  "static/chunks/1255-404bb8de49c90395.js",
319
319
  "static/chunks/main-app-a1f59168303c7f84.js",
320
- "static/chunks/app/camera-grid/route-0952b44bda5bfb6d.js"
320
+ "static/chunks/app/fed/media/robots/[robotId]/video_feed/route-0952b44bda5bfb6d.js"
321
321
  ],
322
- "/robopark/api/robots/[robotId]/stream/route": [
322
+ "/robopark/[...path]/route": [
323
323
  "static/chunks/webpack-5252e5254da079b5.js",
324
324
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
325
325
  "static/chunks/1255-404bb8de49c90395.js",
326
326
  "static/chunks/main-app-a1f59168303c7f84.js",
327
- "static/chunks/app/robopark/api/robots/[robotId]/stream/route-0952b44bda5bfb6d.js"
327
+ "static/chunks/app/robopark/[...path]/route-0952b44bda5bfb6d.js"
328
328
  ],
329
- "/fed/[...path]/route": [
329
+ "/tailnet/route": [
330
330
  "static/chunks/webpack-5252e5254da079b5.js",
331
331
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
332
332
  "static/chunks/1255-404bb8de49c90395.js",
333
333
  "static/chunks/main-app-a1f59168303c7f84.js",
334
- "static/chunks/app/fed/[...path]/route-0952b44bda5bfb6d.js"
334
+ "static/chunks/app/tailnet/route-0952b44bda5bfb6d.js"
335
335
  ],
336
336
  "/vendor/livekit-client.umd.js/route": [
337
337
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -340,19 +340,19 @@
340
340
  "static/chunks/main-app-a1f59168303c7f84.js",
341
341
  "static/chunks/app/vendor/livekit-client.umd.js/route-0952b44bda5bfb6d.js"
342
342
  ],
343
- "/robopark/[...path]/route": [
343
+ "/fed/[...path]/route": [
344
344
  "static/chunks/webpack-5252e5254da079b5.js",
345
345
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
346
346
  "static/chunks/1255-404bb8de49c90395.js",
347
347
  "static/chunks/main-app-a1f59168303c7f84.js",
348
- "static/chunks/app/robopark/[...path]/route-0952b44bda5bfb6d.js"
348
+ "static/chunks/app/fed/[...path]/route-0952b44bda5bfb6d.js"
349
349
  ],
350
- "/tailnet/route": [
350
+ "/robopark/api/robots/[robotId]/stream/route": [
351
351
  "static/chunks/webpack-5252e5254da079b5.js",
352
352
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
353
353
  "static/chunks/1255-404bb8de49c90395.js",
354
354
  "static/chunks/main-app-a1f59168303c7f84.js",
355
- "static/chunks/app/tailnet/route-0952b44bda5bfb6d.js"
355
+ "static/chunks/app/robopark/api/robots/[robotId]/stream/route-0952b44bda5bfb6d.js"
356
356
  ],
357
357
  "/(app)/opengraph-image-xg4ifa/route": [
358
358
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -368,19 +368,19 @@
368
368
  "static/chunks/main-app-a1f59168303c7f84.js",
369
369
  "static/chunks/app/robopark/api/[...path]/route-0952b44bda5bfb6d.js"
370
370
  ],
371
- "/voice-tester/page": [
371
+ "/robopark/page": [
372
372
  "static/chunks/webpack-5252e5254da079b5.js",
373
373
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
374
374
  "static/chunks/1255-404bb8de49c90395.js",
375
375
  "static/chunks/main-app-a1f59168303c7f84.js",
376
- "static/chunks/app/voice-tester/page-ae699e048e0eb834.js"
376
+ "static/chunks/app/robopark/page-0952b44bda5bfb6d.js"
377
377
  ],
378
- "/robopark/page": [
378
+ "/voice-tester/page": [
379
379
  "static/chunks/webpack-5252e5254da079b5.js",
380
380
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
381
381
  "static/chunks/1255-404bb8de49c90395.js",
382
382
  "static/chunks/main-app-a1f59168303c7f84.js",
383
- "static/chunks/app/robopark/page-0952b44bda5bfb6d.js"
383
+ "static/chunks/app/voice-tester/page-ae699e048e0eb834.js"
384
384
  ],
385
385
  "/(app)/layout": [
386
386
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -1,6 +1,6 @@
1
1
  {
2
- "/api/device-character/route": "/api/device-character",
3
2
  "/_not-found/page": "/_not-found",
3
+ "/api/device-character/route": "/api/device-character",
4
4
  "/api/download-piper-model/route": "/api/download-piper-model",
5
5
  "/api/prewarm/route": "/api/prewarm",
6
6
  "/api/models/route": "/api/models",
@@ -18,41 +18,41 @@
18
18
  "/api/setup/test-ollama/route": "/api/setup/test-ollama",
19
19
  "/api/test-tts/route": "/api/test-tts",
20
20
  "/api/voices/route": "/api/voices",
21
- "/api/wake-word/models/route": "/api/wake-word/models",
22
- "/api/wake-word/status/route": "/api/wake-word/status",
23
21
  "/api/wake-word/upload/route": "/api/wake-word/upload",
22
+ "/api/wake-word/status/route": "/api/wake-word/status",
23
+ "/api/wake-word/models/route": "/api/wake-word/models",
24
24
  "/favicon.ico/route": "/favicon.ico",
25
25
  "/api/kiosk-hardware/route": "/api/kiosk-hardware",
26
- "/api/connection-details/route": "/api/connection-details",
27
26
  "/api/model-status/route": "/api/model-status",
28
- "/api/robopark/live-rooms/route": "/api/robopark/live-rooms",
29
- "/api/robopark/camera-token/route": "/api/robopark/camera-token",
30
- "/api/robopark/monitor-token/route": "/api/robopark/monitor-token",
27
+ "/api/connection-details/route": "/api/connection-details",
31
28
  "/api/robopark/character-ai/route": "/api/robopark/character-ai",
29
+ "/api/robopark/camera-token/route": "/api/robopark/camera-token",
32
30
  "/api/robopark/device-access/route": "/api/robopark/device-access",
33
- "/api/robopark/robots/route": "/api/robopark/robots",
34
31
  "/api/robopark/servers/[serverId]/metrics/route": "/api/robopark/servers/[serverId]/metrics",
32
+ "/api/robopark/live-rooms/route": "/api/robopark/live-rooms",
33
+ "/api/robopark/robots/route": "/api/robopark/robots",
34
+ "/api/robopark/monitor-token/route": "/api/robopark/monitor-token",
35
35
  "/api/robopark/servers/[serverId]/route": "/api/robopark/servers/[serverId]",
36
- "/api/robopark/vision-caption/route": "/api/robopark/vision-caption",
37
- "/api/robopark/sessions/route": "/api/robopark/sessions",
38
36
  "/api/robopark/servers/route": "/api/robopark/servers",
39
37
  "/api/robovoice/robots/[robotId]/media-health/route": "/api/robovoice/robots/[robotId]/media-health",
40
- "/api/settings/route": "/api/settings",
38
+ "/api/robopark/vision-caption/route": "/api/robopark/vision-caption",
41
39
  "/api/robopark/robots/[robotId]/voice-engine/route": "/api/robopark/robots/[robotId]/voice-engine",
40
+ "/api/settings/route": "/api/settings",
41
+ "/api/robopark/sessions/route": "/api/robopark/sessions",
42
42
  "/api/turn-credentials/route": "/api/turn-credentials",
43
43
  "/api/robopark/transcripts/route": "/api/robopark/transcripts",
44
- "/fed/media/robots/[robotId]/video_feed/route": "/fed/media/robots/[robotId]/video_feed",
45
- "/api/wake/route": "/api/wake",
46
44
  "/camera-grid/route": "/camera-grid",
47
- "/robopark/api/robots/[robotId]/stream/route": "/robopark/api/robots/[robotId]/stream",
48
- "/fed/[...path]/route": "/fed/[...path]",
49
- "/vendor/livekit-client.umd.js/route": "/vendor/livekit-client.umd.js",
45
+ "/api/wake/route": "/api/wake",
46
+ "/fed/media/robots/[robotId]/video_feed/route": "/fed/media/robots/[robotId]/video_feed",
50
47
  "/robopark/[...path]/route": "/robopark/[...path]",
51
48
  "/tailnet/route": "/tailnet",
49
+ "/vendor/livekit-client.umd.js/route": "/vendor/livekit-client.umd.js",
50
+ "/fed/[...path]/route": "/fed/[...path]",
51
+ "/robopark/api/robots/[robotId]/stream/route": "/robopark/api/robots/[robotId]/stream",
52
52
  "/(app)/opengraph-image-xg4ifa/route": "/opengraph-image-xg4ifa",
53
53
  "/robopark/api/[...path]/route": "/robopark/api/[...path]",
54
- "/voice-tester/page": "/voice-tester",
55
54
  "/robopark/page": "/robopark",
55
+ "/voice-tester/page": "/voice-tester",
56
56
  "/(app)/page": "/",
57
57
  "/(app)/voice/page": "/voice"
58
58
  }
@@ -5,8 +5,8 @@
5
5
  "devFiles": [],
6
6
  "ampDevFiles": [],
7
7
  "lowPriorityFiles": [
8
- "static/95wYdEKkTppMWNZn6Vr-N/_buildManifest.js",
9
- "static/95wYdEKkTppMWNZn6Vr-N/_ssgManifest.js"
8
+ "static/Omr4BaSGuNd9w4zCHNwa5/_buildManifest.js",
9
+ "static/Omr4BaSGuNd9w4zCHNwa5/_ssgManifest.js"
10
10
  ],
11
11
  "rootMainFiles": [
12
12
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -34,8 +34,8 @@
34
34
  "dynamicRoutes": {},
35
35
  "notFoundRoutes": [],
36
36
  "preview": {
37
- "previewModeId": "e46ba0ff266edc530143151a59c32f3b",
38
- "previewModeSigningKey": "4b0a605999a0372ef849785e10e12ef37b7983b5f6facefdba6884fcbf7a41fc",
39
- "previewModeEncryptionKey": "30aeae8a6ca4d1ee490d3f911a3c862f2ba26858bf42673238593480dc236648"
37
+ "previewModeId": "c38e1344b99b1c742a36d6c24fd3a618",
38
+ "previewModeSigningKey": "bb7f74585af2b67c95f333ea93a95a78e0bfb18bdfdece05c0d4b2315f9d7b7a",
39
+ "previewModeEncryptionKey": "72b2c4e8c2ce1269ca7d3e8707ccf19ca03f0e038228c18984dc4316dc49b351"
40
40
  }
41
41
  }
@@ -1,6 +1,6 @@
1
1
  {
2
- "/api/device-character/route": "app/api/device-character/route.js",
3
2
  "/_not-found/page": "app/_not-found/page.js",
3
+ "/api/device-character/route": "app/api/device-character/route.js",
4
4
  "/api/download-piper-model/route": "app/api/download-piper-model/route.js",
5
5
  "/api/prewarm/route": "app/api/prewarm/route.js",
6
6
  "/api/models/route": "app/api/models/route.js",
@@ -18,41 +18,41 @@
18
18
  "/api/setup/test-ollama/route": "app/api/setup/test-ollama/route.js",
19
19
  "/api/test-tts/route": "app/api/test-tts/route.js",
20
20
  "/api/voices/route": "app/api/voices/route.js",
21
- "/api/wake-word/models/route": "app/api/wake-word/models/route.js",
22
- "/api/wake-word/status/route": "app/api/wake-word/status/route.js",
23
21
  "/api/wake-word/upload/route": "app/api/wake-word/upload/route.js",
22
+ "/api/wake-word/status/route": "app/api/wake-word/status/route.js",
23
+ "/api/wake-word/models/route": "app/api/wake-word/models/route.js",
24
24
  "/favicon.ico/route": "app/favicon.ico/route.js",
25
25
  "/api/kiosk-hardware/route": "app/api/kiosk-hardware/route.js",
26
- "/api/connection-details/route": "app/api/connection-details/route.js",
27
26
  "/api/model-status/route": "app/api/model-status/route.js",
28
- "/api/robopark/live-rooms/route": "app/api/robopark/live-rooms/route.js",
29
- "/api/robopark/camera-token/route": "app/api/robopark/camera-token/route.js",
30
- "/api/robopark/monitor-token/route": "app/api/robopark/monitor-token/route.js",
27
+ "/api/connection-details/route": "app/api/connection-details/route.js",
31
28
  "/api/robopark/character-ai/route": "app/api/robopark/character-ai/route.js",
29
+ "/api/robopark/camera-token/route": "app/api/robopark/camera-token/route.js",
32
30
  "/api/robopark/device-access/route": "app/api/robopark/device-access/route.js",
33
- "/api/robopark/robots/route": "app/api/robopark/robots/route.js",
34
31
  "/api/robopark/servers/[serverId]/metrics/route": "app/api/robopark/servers/[serverId]/metrics/route.js",
32
+ "/api/robopark/live-rooms/route": "app/api/robopark/live-rooms/route.js",
33
+ "/api/robopark/robots/route": "app/api/robopark/robots/route.js",
34
+ "/api/robopark/monitor-token/route": "app/api/robopark/monitor-token/route.js",
35
35
  "/api/robopark/servers/[serverId]/route": "app/api/robopark/servers/[serverId]/route.js",
36
- "/api/robopark/vision-caption/route": "app/api/robopark/vision-caption/route.js",
37
- "/api/robopark/sessions/route": "app/api/robopark/sessions/route.js",
38
36
  "/api/robopark/servers/route": "app/api/robopark/servers/route.js",
39
37
  "/api/robovoice/robots/[robotId]/media-health/route": "app/api/robovoice/robots/[robotId]/media-health/route.js",
40
- "/api/settings/route": "app/api/settings/route.js",
38
+ "/api/robopark/vision-caption/route": "app/api/robopark/vision-caption/route.js",
41
39
  "/api/robopark/robots/[robotId]/voice-engine/route": "app/api/robopark/robots/[robotId]/voice-engine/route.js",
40
+ "/api/settings/route": "app/api/settings/route.js",
41
+ "/api/robopark/sessions/route": "app/api/robopark/sessions/route.js",
42
42
  "/api/turn-credentials/route": "app/api/turn-credentials/route.js",
43
43
  "/api/robopark/transcripts/route": "app/api/robopark/transcripts/route.js",
44
- "/fed/media/robots/[robotId]/video_feed/route": "app/fed/media/robots/[robotId]/video_feed/route.js",
45
- "/api/wake/route": "app/api/wake/route.js",
46
44
  "/camera-grid/route": "app/camera-grid/route.js",
47
- "/robopark/api/robots/[robotId]/stream/route": "app/robopark/api/robots/[robotId]/stream/route.js",
48
- "/fed/[...path]/route": "app/fed/[...path]/route.js",
49
- "/vendor/livekit-client.umd.js/route": "app/vendor/livekit-client.umd.js/route.js",
45
+ "/api/wake/route": "app/api/wake/route.js",
46
+ "/fed/media/robots/[robotId]/video_feed/route": "app/fed/media/robots/[robotId]/video_feed/route.js",
50
47
  "/robopark/[...path]/route": "app/robopark/[...path]/route.js",
51
48
  "/tailnet/route": "app/tailnet/route.js",
49
+ "/vendor/livekit-client.umd.js/route": "app/vendor/livekit-client.umd.js/route.js",
50
+ "/fed/[...path]/route": "app/fed/[...path]/route.js",
51
+ "/robopark/api/robots/[robotId]/stream/route": "app/robopark/api/robots/[robotId]/stream/route.js",
52
52
  "/(app)/opengraph-image-xg4ifa/route": "app/(app)/opengraph-image-xg4ifa/route.js",
53
53
  "/robopark/api/[...path]/route": "app/robopark/api/[...path]/route.js",
54
- "/voice-tester/page": "app/voice-tester/page.js",
55
54
  "/robopark/page": "app/robopark/page.js",
55
+ "/voice-tester/page": "app/voice-tester/page.js",
56
56
  "/(app)/page": "app/(app)/page.js",
57
57
  "/(app)/voice/page": "app/(app)/voice/page.js"
58
58
  }
@@ -1 +1 @@
1
- <!DOCTYPE html><html><head><meta charSet="utf-8" data-next-head=""/><meta name="viewport" content="width=device-width" data-next-head=""/><title data-next-head="">500: Internal Server Error</title><noscript data-n-css=""></noscript><script defer="" noModule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-5252e5254da079b5.js" defer=""></script><script src="/_next/static/chunks/framework-e60c938074ff7136.js" defer=""></script><script src="/_next/static/chunks/main-9e24e8708ff075fc.js" defer=""></script><script src="/_next/static/chunks/pages/_app-131c90850aef965b.js" defer=""></script><script src="/_next/static/chunks/pages/_error-e4ba546eb376bdf4.js" defer=""></script><script src="/_next/static/95wYdEKkTppMWNZn6Vr-N/_buildManifest.js" defer=""></script><script src="/_next/static/95wYdEKkTppMWNZn6Vr-N/_ssgManifest.js" defer=""></script></head><body><div id="__next"><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div style="line-height:48px"><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding-right:23px;font-size:24px;font-weight:500;vertical-align:top">500</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:28px">Internal Server Error<!-- -->.</h2></div></div></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"statusCode":500}},"page":"/_error","query":{},"buildId":"95wYdEKkTppMWNZn6Vr-N","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}</script></body></html>
1
+ <!DOCTYPE html><html><head><meta charSet="utf-8" data-next-head=""/><meta name="viewport" content="width=device-width" data-next-head=""/><title data-next-head="">500: Internal Server Error</title><noscript data-n-css=""></noscript><script defer="" noModule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-5252e5254da079b5.js" defer=""></script><script src="/_next/static/chunks/framework-e60c938074ff7136.js" defer=""></script><script src="/_next/static/chunks/main-9e24e8708ff075fc.js" defer=""></script><script src="/_next/static/chunks/pages/_app-131c90850aef965b.js" defer=""></script><script src="/_next/static/chunks/pages/_error-e4ba546eb376bdf4.js" defer=""></script><script src="/_next/static/Omr4BaSGuNd9w4zCHNwa5/_buildManifest.js" defer=""></script><script src="/_next/static/Omr4BaSGuNd9w4zCHNwa5/_ssgManifest.js" defer=""></script></head><body><div id="__next"><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div style="line-height:48px"><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding-right:23px;font-size:24px;font-weight:500;vertical-align:top">500</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:28px">Internal Server Error<!-- -->.</h2></div></div></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"statusCode":500}},"page":"/_error","query":{},"buildId":"Omr4BaSGuNd9w4zCHNwa5","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}</script></body></html>
@@ -1,5 +1,5 @@
1
1
  {
2
- "/_error": "pages/_error.js",
3
2
  "/_app": "pages/_app.js",
3
+ "/_error": "pages/_error.js",
4
4
  "/_document": "pages/_document.js"
5
5
  }
@@ -1 +1 @@
1
- {"node":{},"edge":{},"encryptionKey":"4BR88lq4iqpHk/8KKT2cL+dncVsnltA0ZuBJwvAdjnw="}
1
+ {"node":{},"edge":{},"encryptionKey":"sJTotsE+lm1BGwIDO9xwXe4GVqC5RCZujpDcR19GDRI="}
@@ -149,7 +149,7 @@ section h2 .count{color:var(--muted);}
149
149
  .fleet-services{margin:.5rem 0;border:1px solid var(--glass-border);border-radius:11px;overflow:hidden;background:rgba(0,0,0,.2)}.fleet-services-head{display:flex;justify-content:space-between;align-items:center;padding:.42rem .52rem;border-bottom:1px solid var(--glass-border);font:700 .51rem var(--mono);letter-spacing:.08em;text-transform:uppercase;color:var(--gold-pale)}.fleet-service{display:grid;grid-template-columns:8px minmax(72px,.7fr) minmax(90px,1fr) auto;gap:.42rem;align-items:center;padding:.4rem .5rem;border-bottom:1px solid rgba(255,255,255,.035);font:.51rem var(--mono)}.fleet-service:last-child{border-bottom:0}.fleet-service i{width:7px;height:7px;border-radius:50%;background:var(--bad)}.fleet-service.up i{background:var(--ok);box-shadow:0 0 7px rgba(111,224,166,.45)}.fleet-service.missing i{background:var(--muted)}.fleet-service b{color:var(--ink);white-space:nowrap}.fleet-service span{color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fleet-service-actions{display:flex;gap:.22rem}.fleet-service-actions button{padding:.22rem .34rem;font-size:.47rem}.fleet-service-fix{padding:.38rem .5rem;border-top:1px solid rgba(246,215,123,.08);background:rgba(246,215,123,.025)}.fleet-service-fix code{display:block;overflow:auto;white-space:nowrap;color:var(--gold-pale);font:.48rem var(--mono)}.fleet-service-fix button{margin-top:.3rem}
150
150
  .fleet-group-title{grid-column:1/-1;display:flex;align-items:center;justify-content:space-between;margin-top:.35rem;padding:.55rem .15rem .25rem;border-bottom:1px solid rgba(246,215,123,.18);font-family:var(--display);color:var(--gold-pale);font-size:.82rem;letter-spacing:.04em}.fleet-group-title span{font:500 .54rem var(--mono);color:var(--muted);text-transform:uppercase;letter-spacing:.1em}
151
151
  .fleet-live-camera{position:relative;aspect-ratio:16/9;margin:.55rem 0;border:1px solid var(--glass-border);border-radius:12px;overflow:hidden;background:radial-gradient(circle at 50% 35%,rgba(246,215,123,.08),transparent 44%),#050504;box-shadow:inset 0 1px 0 var(--glass-hi)}
152
- .fleet-live-camera img{width:100%;height:100%;display:block;object-fit:contain;background:#050504}.fleet-live-camera .state{position:absolute;left:.5rem;bottom:.45rem;padding:.18rem .4rem;border-radius:100px;background:rgba(3,3,2,.78);border:1px solid var(--glass-border);font-family:var(--mono);font-size:.5rem;color:var(--warn);backdrop-filter:blur(8px)}.fleet-live-camera.live .state{color:var(--ok)}.fleet-live-camera.failed .state{color:var(--bad)}
152
+ .fleet-live-camera img,.fleet-live-camera video{width:100%;height:100%;display:block;object-fit:contain;background:#050504}.fleet-live-camera .state{position:absolute;left:.5rem;bottom:.45rem;padding:.18rem .4rem;border-radius:100px;background:rgba(3,3,2,.78);border:1px solid var(--glass-border);font-family:var(--mono);font-size:.5rem;color:var(--warn);backdrop-filter:blur(8px)}.fleet-live-camera.live .state{color:var(--ok)}.fleet-live-camera.failed .state{color:var(--bad)}
153
153
  @media(max-width:1150px){.fleet-kpis{grid-template-columns:repeat(3,1fr)}.fleet-insights{grid-template-columns:1fr 1fr}.fleet-insights .fleet-panel:last-of-type{grid-column:1/-1}}
154
154
  @media(max-width:720px){.fleet-hero{align-items:flex-start;flex-direction:column}.fleet-hero-actions{justify-content:flex-start}.fleet-kpis{grid-template-columns:repeat(2,1fr)}.fleet-insights{display:block}.fleet-panel{margin-bottom:.65rem}.fleet-readiness{grid-template-columns:90px 1fr}.fleet-donut{width:86px;height:86px}}
155
155
 
@@ -1702,6 +1702,210 @@ footer.cmd{left:var(--global-nav-width);transition:left .24s cubic-bezier(.2,.8,
1702
1702
  }
1703
1703
  window.addEventListener('online',rpRecoverVisibleCameras);
1704
1704
  document.addEventListener('visibilitychange',function(){if(document.visibilityState==='visible')rpRecoverVisibleCameras();});
1705
+ // ── Fleet LiveKit cameras ──
1706
+ // Robot kiosks/character call pages publish the camera into LiveKit room
1707
+ // robopark-<stream id>, where the stream id is the claim/device id (e.g.
1708
+ // claim_63a36d48…). The fleet tiles used to hit only the MJPEG relay, which
1709
+ // 502s for claim devices, so tiles spun on "connecting camera" forever.
1710
+ // Subscribe each tile through /api/robopark/monitor-token instead and keep
1711
+ // the MJPEG relay strictly as a grace-period fallback.
1712
+ var fleetLiveKitRooms = {};
1713
+ var rpCameraStreamIds = {}; // tile key (display name) -> claim/device stream id
1714
+ var fleetLiveKitSdkPromise = null;
1715
+ function ensureFleetLiveKit(){
1716
+ if(window.LivekitClient)return Promise.resolve(window.LivekitClient);
1717
+ if(fleetLiveKitSdkPromise)return fleetLiveKitSdkPromise;
1718
+ fleetLiveKitSdkPromise=new Promise(function(resolve,reject){var s=document.createElement('script');s.src='/vendor/livekit-client.umd.js'+QS;s.onload=function(){resolve(window.LivekitClient);};s.onerror=function(){fleetLiveKitSdkPromise=null;reject(new Error('LiveKit SDK failed to load'));};document.head.appendChild(s);});
1719
+ return fleetLiveKitSdkPromise;
1720
+ }
1721
+ function pruneRoomTargets(entry){
1722
+ entry.videos=(entry.videos||[]).filter(function(node){return node&&node.isConnected;});
1723
+ entry.states=(entry.states||[]).filter(function(node){return node&&node.isConnected;});
1724
+ return entry;
1725
+ }
1726
+ // MJPEG fallback: when a LiveKit room connects but nobody publishes video
1727
+ // (no character call open), the tile fell into 'waiting for camera track'
1728
+ // forever. After a grace period we overlay the vision-server MJPEG stream in
1729
+ // the tile while keeping the room joined; a real LiveKit track always wins
1730
+ // and tears the overlay down. Every open MJPEG <img> holds an HTTP
1731
+ // connection, so teardown must be exhaustive (close, sweep, pagehide, error).
1732
+ var FLEET_FALLBACK_DELAY=12000;
1733
+ function removeFleetFallbackImgs(entry){
1734
+ (entry.fallbackImgs||[]).forEach(function(img){
1735
+ try{img.onload=null;img.onerror=null;img.src='';}catch(e){}
1736
+ var host=img._rpHost;
1737
+ if(img.parentNode)img.parentNode.removeChild(img);
1738
+ if(host&&!entry.hasTrack)host.classList.remove('live');
1739
+ });
1740
+ entry.fallbackImgs=[];entry.fallbackActive=false;
1741
+ }
1742
+ function clearFleetFallback(entry){
1743
+ if(!entry)return;
1744
+ if(entry.fallbackTimer){clearTimeout(entry.fallbackTimer);entry.fallbackTimer=null;}
1745
+ removeFleetFallbackImgs(entry);
1746
+ }
1747
+ function tryFleetFallback(cameraKey,entry){
1748
+ if(fleetLiveKitRooms[cameraKey]!==entry||entry.hasTrack)return;
1749
+ pruneRoomTargets(entry);
1750
+ if(!entry.videos.length)return;
1751
+ entry.fallbackImgs=(entry.fallbackImgs||[]).filter(function(img){return img&&img.isConnected;});
1752
+ entry.videos.forEach(function(video){
1753
+ var host=(video.closest&&video.closest('[data-fleet-camera-host]'))||video.parentNode;
1754
+ if(!host||host.querySelector('img[data-fleet-fallback]'))return;
1755
+ var img=document.createElement('img');
1756
+ img.setAttribute('data-fleet-fallback',cameraKey);
1757
+ img.alt='fallback camera stream';
1758
+ img.style.position='absolute';img.style.inset='0';img.style.zIndex='2';
1759
+ img.style.width='100%';img.style.height='100%';img.style.objectFit='contain';
1760
+ img._rpHost=host;
1761
+ img.onload=function(){
1762
+ if(fleetLiveKitRooms[cameraKey]!==entry||entry.hasTrack)return;
1763
+ entry.fallbackActive=true;entry.fallbackFailures=0;
1764
+ host.classList.add('live');host.classList.remove('failed');
1765
+ setRoomStatus(entry,'MJPEG camera live (fallback)');
1766
+ };
1767
+ img.onerror=function(){
1768
+ if(fleetLiveKitRooms[cameraKey]!==entry)return;
1769
+ removeFleetFallbackImgs(entry);
1770
+ if(entry.hasTrack)return;
1771
+ setRoomStatus(entry,'connected · waiting for camera track');
1772
+ armFleetFallback(cameraKey,entry,true);
1773
+ };
1774
+ // The MJPEG relay is keyed by the robot display name (the legacy tile
1775
+ // identity), not the LiveKit stream id.
1776
+ img.src=rpCameraFeedUrl(cameraKey);
1777
+ entry.fallbackImgs.push(img);
1778
+ host.appendChild(img);
1779
+ });
1780
+ }
1781
+ function armFleetFallback(cameraKey,entry,backoff){
1782
+ if(!entry||entry.fallbackTimer||entry.hasTrack)return;
1783
+ var delay=FLEET_FALLBACK_DELAY;
1784
+ if(backoff){
1785
+ entry.fallbackFailures=(entry.fallbackFailures||0)+1;
1786
+ delay=Math.min(15000,Math.round(1500*Math.pow(1.6,Math.min(entry.fallbackFailures-1,6))+Math.random()*500));
1787
+ }
1788
+ entry.fallbackTimer=setTimeout(function(){
1789
+ entry.fallbackTimer=null;
1790
+ if(fleetLiveKitRooms[cameraKey]!==entry)return;
1791
+ tryFleetFallback(cameraKey,entry);
1792
+ },delay);
1793
+ }
1794
+ // Always disconnect before dropping a room reference. Abandoning the object
1795
+ // leaves the monitor joined server-side, so every refresh or re-render would
1796
+ // stack another monitor participant in the room.
1797
+ function closeFleetCameraRoom(cameraKey,entry){
1798
+ entry=entry||fleetLiveKitRooms[cameraKey];
1799
+ if(!entry)return;
1800
+ if(entry.retry){clearTimeout(entry.retry);entry.retry=null;}
1801
+ clearFleetFallback(entry);
1802
+ entry.hasTrack=false;
1803
+ var room=entry.room;entry.room=null;
1804
+ if(room){try{room.disconnect(true);}catch(e){}}
1805
+ if(fleetLiveKitRooms[cameraKey]===entry)delete fleetLiveKitRooms[cameraKey];
1806
+ }
1807
+ // Sweep rooms whose tiles are gone. Without this a room stayed joined for as
1808
+ // long as the tab lived, even after its tile was re-rendered away.
1809
+ function sweepFleetCameraRooms(){
1810
+ Object.keys(fleetLiveKitRooms||{}).forEach(function(cameraKey){
1811
+ var entry=fleetLiveKitRooms[cameraKey];if(!entry)return;
1812
+ pruneRoomTargets(entry);
1813
+ if(!entry.videos.length&&!entry.connecting)closeFleetCameraRoom(cameraKey,entry);
1814
+ });
1815
+ }
1816
+ if(typeof window!=='undefined'&&!window.__rpFleetRoomTeardown){
1817
+ window.__rpFleetRoomTeardown=true;
1818
+ setInterval(sweepFleetCameraRooms,15000);
1819
+ // pagehide covers refresh, navigation and tab close; without it the monitor
1820
+ // lingered until LiveKit's own participant timeout.
1821
+ window.addEventListener('pagehide',function(){
1822
+ Object.keys(fleetLiveKitRooms||{}).forEach(function(cameraKey){closeFleetCameraRoom(cameraKey);});
1823
+ });
1824
+ }
1825
+ function setRoomStatus(entry,message){
1826
+ entry.states.forEach(function(label){if(label&&label.isConnected)label.textContent=message;});
1827
+ }
1828
+ function attachRoomTrack(entry,track){
1829
+ entry.videos.forEach(function(target){
1830
+ if(!target||!target.isConnected)return;
1831
+ track.attach(target);target.muted=true;target.autoplay=true;target.playsInline=true;
1832
+ var host=target.closest('[data-fleet-camera-host]');if(host){host.classList.add('live');host.classList.remove('failed');}
1833
+ });
1834
+ }
1835
+ // Retries must never capture a specific <video>. renderFleet() rebuilds its
1836
+ // tiles, so a captured element can already be detached when the timer fires.
1837
+ // Retry against the device entry instead; later renders register their fresh
1838
+ // elements on it.
1839
+ function scheduleFleetCameraRetry(cameraKey){
1840
+ var entry=fleetLiveKitRooms[cameraKey];if(!entry||entry.retry)return;
1841
+ entry.failures=(entry.failures||0)+1;
1842
+ var delay=Math.min(15000,Math.round(1500*Math.pow(1.6,Math.min(entry.failures-1,6))+Math.random()*500));
1843
+ entry.retry=setTimeout(function(){
1844
+ entry.retry=null;
1845
+ if(fleetLiveKitRooms[cameraKey]!==entry)return;
1846
+ pruneRoomTargets(entry);
1847
+ // Nothing on screen is waiting for this room any more.
1848
+ if(!entry.videos.length){closeFleetCameraRoom(cameraKey,entry);return;}
1849
+ openFleetCameraRoom(cameraKey);
1850
+ },delay);
1851
+ }
1852
+ function openFleetCameraRoom(cameraKey){
1853
+ var entry=fleetLiveKitRooms[cameraKey];if(!entry||entry.connecting||entry.room)return;
1854
+ var roomId=String(rpCameraStreamIds[cameraKey]||cameraKey);
1855
+ entry.roomId=roomId;
1856
+ if(!/^[a-zA-Z0-9_-]{1,64}$/.test(roomId)){
1857
+ // Not a valid robopark-<id> room name; skip LiveKit and rely on the
1858
+ // MJPEG relay so the tile is never a terminal 'connecting' state.
1859
+ if(!entry.fallbackActive&&!entry.fallbackTimer)armFleetFallback(cameraKey,entry,true);
1860
+ return;
1861
+ }
1862
+ entry.connecting=true;
1863
+ Promise.all([ensureFleetLiveKit(),fetch('/api/robopark/monitor-token',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({room_name:'robopark-'+roomId})}).then(function(r){if(!r.ok)throw new Error('monitor token '+r.status);return r.json();})]).then(function(parts){
1864
+ var LK=parts[0],info=parts[1];
1865
+ entry.connecting=false;
1866
+ if(fleetLiveKitRooms[cameraKey]!==entry)return null;
1867
+ var room=new LK.Room();entry.room=room;
1868
+ room.on(LK.RoomEvent.TrackSubscribed,function(track){if(track.kind==='video'){entry.hasTrack=true;clearFleetFallback(entry);pruneRoomTargets(entry);attachRoomTrack(entry,track);setRoomStatus(entry,'LiveKit camera live');}});
1869
+ room.on(LK.RoomEvent.Disconnected,function(){if(fleetLiveKitRooms[cameraKey]!==entry)return;entry.room=null;entry.hasTrack=false;if(!entry.fallbackActive)setRoomStatus(entry,'camera disconnected · retrying');scheduleFleetCameraRetry(cameraKey);});
1870
+ return room.connect(info.serverUrl,info.token);
1871
+ }).then(function(result){if(result===null||fleetLiveKitRooms[cameraKey]!==entry)return;entry.failures=0;if(entry.fallbackActive){setRoomStatus(entry,'MJPEG camera live (fallback)');}else{setRoomStatus(entry,'connected · waiting for camera track');armFleetFallback(cameraKey,entry);}}).catch(function(error){
1872
+ entry.connecting=false;entry.room=null;entry.hasTrack=false;
1873
+ if(fleetLiveKitRooms[cameraKey]!==entry)return;
1874
+ if(!entry.fallbackActive)setRoomStatus(entry,'camera unavailable · '+error.message+' · retrying');
1875
+ scheduleFleetCameraRetry(cameraKey);
1876
+ // LiveKit is down entirely; don't sit dark for the full grace period.
1877
+ if(!entry.fallbackActive&&!entry.fallbackTimer)armFleetFallback(cameraKey,entry,true);
1878
+ });
1879
+ }
1880
+ function connectFleetLiveCamera(video,cameraKey,state){
1881
+ if(!cameraKey||!video)return;
1882
+ var desiredRoomId=String(rpCameraStreamIds[cameraKey]||cameraKey);
1883
+ var entry=fleetLiveKitRooms[cameraKey];
1884
+ // The session/live-room census can discover that the camera publishes into
1885
+ // a claim room (robopark-claim_<hash>) after we already joined a stale
1886
+ // room. Re-key: drop the stale subscription and rejoin the room that
1887
+ // actually has a publisher. Only when not mid-connect and the target
1888
+ // genuinely changed.
1889
+ if(entry&&!entry.connecting&&entry.roomId&&entry.roomId!==desiredRoomId){closeFleetCameraRoom(cameraKey,entry);entry=null;}
1890
+ if(!entry){entry={connecting:false,room:null,roomId:null,videos:[],states:[],retry:null,failures:0,hasTrack:false,fallbackTimer:null,fallbackImgs:[],fallbackActive:false,fallbackFailures:0};fleetLiveKitRooms[cameraKey]=entry;}
1891
+ pruneRoomTargets(entry);
1892
+ if(entry.videos.indexOf(video)<0)entry.videos.push(video);
1893
+ if(state&&entry.states.indexOf(state)<0)entry.states.push(state);
1894
+ if(entry.room){
1895
+ entry.room.remoteParticipants.forEach(function(p){p.trackPublications.forEach(function(pub){if(pub.track&&pub.track.kind==='video'){entry.hasTrack=true;clearFleetFallback(entry);attachRoomTrack(entry,pub.track);setRoomStatus(entry,'LiveKit camera live');}});});
1896
+ if(!entry.hasTrack){
1897
+ // Freshly rendered tile joined an already-open but publisher-less room:
1898
+ // re-cover it with the active fallback, or make sure the timer is armed
1899
+ // so 'waiting for camera track' can never be a terminal state.
1900
+ if(entry.fallbackActive)tryFleetFallback(cameraKey,entry);
1901
+ else if(!entry.fallbackTimer)armFleetFallback(cameraKey,entry);
1902
+ }
1903
+ return;
1904
+ }
1905
+ // A connect or retry is already in flight; it will pick up this element.
1906
+ if(entry.connecting||entry.retry)return;
1907
+ openFleetCameraRoom(cameraKey);
1908
+ }
1705
1909
  function renderFleetOperations(nodes, prefix, telemetryRows){
1706
1910
  prefix=prefix||'fleet'; nodes=Array.isArray(nodes)?nodes:[]; telemetryRows=Array.isArray(telemetryRows)?telemetryRows:null;
1707
1911
  var rows=[];
@@ -1764,11 +1968,12 @@ footer.cmd{left:var(--global-nav-width);transition:left .24s cubic-bezier(.2,.8,
1764
1968
  }
1765
1969
  function renderFleet(nodes){
1766
1970
  var el = $('fleet');
1767
- // Keep existing MJPEG elements alive while telemetry cards are rebuilt.
1768
- // Replacing an <img> tears down its long-lived stream and made cameras
1769
- // appear to reconnect on every fleet poll.
1971
+ // Keep existing camera hosts alive while telemetry cards are rebuilt.
1972
+ // Replacing the host tears down the attached LiveKit <video> and any
1973
+ // long-lived MJPEG fallback <img>, making cameras appear to reconnect on
1974
+ // every fleet poll.
1770
1975
  var preservedCameras={};
1771
- if(el){Array.prototype.forEach.call(el.querySelectorAll('[data-fleet-camera]'),function(img){var key=img.getAttribute('data-fleet-camera');if(key)preservedCameras[key]=img;});}
1976
+ if(el){Array.prototype.forEach.call(el.querySelectorAll('[data-fleet-camera-host]'),function(host){var key=host.getAttribute('data-fleet-camera-host');if(key&&host.querySelector('[data-fleet-camera]'))preservedCameras[key]=host;});}
1772
1977
  renderFleetOperations(nodes||[],'fleet');
1773
1978
  if(!nodes || !nodes.length){ el.innerHTML = '<div class="empty">No nodes reporting yet. Start peers with <span class="mono">infinicode serve</span> or <span class="mono">robopark setup</span>.</div>'; return; }
1774
1979
  nodes=nodes.slice();
@@ -1871,7 +2076,7 @@ footer.cmd{left:var(--global-nav-width);transition:left .24s cubic-bezier(.2,.8,
1871
2076
  if(tele){var fleetStage=tele.current_stage||{},fleetStageStatus=fleetStage.status||tele.current_stage_status||'waiting',fleetStageLabel=fleetStage.label||tele.current_stage_label||'Waiting for pipeline telemetry';html+='<div class="fleet-stage-focus '+esc(fleetStageStatus)+'"><span>Live production stage</span><b>'+esc(fleetStageLabel)+'</b></div>';}
1872
2077
 
1873
2078
  if(isRegisteredRobot&&cameraRef){
1874
- html += '<div class="fleet-live-camera" data-fleet-camera-host="'+esc(cameraRef)+'">'+((up||partialOnline)?'<img alt="Live camera for '+esc(n.displayName)+'" data-fleet-camera="'+esc(cameraRef)+'">':'')+'<span class="state">'+(up?'connecting camera':(partialOnline?'connecting camera / voice active':'offline / camera paused'))+'</span></div>';
2079
+ html += '<div class="fleet-live-camera" data-fleet-camera-host="'+esc(cameraRef)+'">'+((up||partialOnline)?'<video muted autoplay playsinline aria-label="Live camera for '+esc(n.displayName)+'" data-fleet-camera="'+esc(cameraRef)+'"></video>':'')+'<span class="state">'+(up?'connecting camera':(partialOnline?'connecting camera / voice active':'offline / camera paused'))+'</span></div>';
1875
2080
  }
1876
2081
 
1877
2082
  if(up){
@@ -1896,16 +2101,15 @@ footer.cmd{left:var(--global-nav-width);transition:left .24s cubic-bezier(.2,.8,
1896
2101
  + (isRegisteredRobot?('<div class="fleet-card-actions"><button class="rp-btn primary" data-fleet-action="camera" data-robot="'+esc(n.displayName)+'"'+(up?'':' disabled')+'>Live camera</button><button class="rp-btn" data-fleet-action="motors" data-robot="'+esc(n.displayName)+'">Motors</button><button class="rp-btn" data-fleet-action="sessions" data-robot="'+esc(n.displayName)+'">Sessions</button><button class="rp-btn" data-fleet-action="incidents" data-robot="'+esc(n.displayName)+'">Health</button>'+(tele&&(tele.character_id||tele.character_preset_id)?'<button class="rp-btn" data-config-edit-character="'+esc(tele.character_id||tele.character_preset_id)+'">Edit character</button>':'')+(tele&&tele.voice_stack_id?'<button class="rp-btn" data-config-edit-stack="'+esc(tele.voice_stack_id)+'">Edit voice</button>':'')+'</div>'):'')+'</div>';
1897
2102
  }
1898
2103
  el.innerHTML = html;
1899
- Array.prototype.forEach.call(el.querySelectorAll('[data-fleet-camera]'),function(placeholder){
1900
- var key=placeholder.getAttribute('data-fleet-camera'),old=preservedCameras[key];
1901
- if(old){placeholder.parentNode.replaceChild(old,placeholder);placeholder=old;}
1902
- var host=placeholder.closest('[data-fleet-camera-host]'),state=host&&host.querySelector('.state');
1903
- rpReliableCamera(placeholder,key,{
1904
- connecting:function(){if(host){host.classList.remove('failed');}if(state)state.textContent='connecting camera';},
1905
- live:function(){if(host){host.classList.add('live');host.classList.remove('failed');}if(state)state.textContent='always-on live';},
1906
- failed:function(){if(host){host.classList.remove('live');host.classList.add('failed');}if(state)state.textContent='reconnecting camera';}
1907
- });
2104
+ Array.prototype.forEach.call(el.querySelectorAll('[data-fleet-camera-host]'),function(host){
2105
+ var key=host.getAttribute('data-fleet-camera-host'),video=host.querySelector('[data-fleet-camera]'),old=preservedCameras[key];
2106
+ if(!video)return; // offline card: no live media requested
2107
+ if(old&&host.parentNode){host.parentNode.replaceChild(old,host);host=old;video=host.querySelector('[data-fleet-camera]');}
2108
+ var state=host.querySelector('.state');
2109
+ connectFleetLiveCamera(video,key,state);
1908
2110
  });
2111
+ // Close rooms whose tiles just went offline or were rendered away.
2112
+ sweepFleetCameraRooms();
1909
2113
  var cards = el.querySelectorAll('.node');
1910
2114
  for(var j=0;j<cards.length;j++){
1911
2115
  (function(card){
@@ -2057,8 +2261,8 @@ footer.cmd{left:var(--global-nav-width);transition:left .24s cubic-bezier(.2,.8,
2057
2261
  });
2058
2262
  }
2059
2263
  function mergeSchedulerRobots(nodes){
2060
- return Promise.all([getJson('/robopark/api/robots'), getJson('/robopark/api/devices'), getJson('/robopark/api/character-presets'), getJson('/api/robopark/live-rooms').catch(function(){return [];})]).then(function(data){
2061
- var rows = data[0], devices = data[1], liveRooms=Array.isArray(data[3])?data[3]:[];
2264
+ return Promise.all([getJson('/robopark/api/robots'), getJson('/robopark/api/devices'), getJson('/robopark/api/character-presets'), getJson('/api/robopark/live-rooms').catch(function(){return [];}), getJson('/robopark/api/sessions?active_only=true').catch(function(){return [];})]).then(function(data){
2265
+ var rows = data[0], devices = data[1], liveRooms=Array.isArray(data[3])?data[3]:[], activeSessions=Array.isArray(data[4])?data[4]:[];
2062
2266
  characterPresets=Array.isArray(data[2])?data[2]:(data[2]&&data[2].presets)||[];
2063
2267
  var deviceById = {}, deviceByName = {};
2064
2268
  function norm(v){ return String(v||'').trim().toLowerCase(); }
@@ -2117,6 +2321,18 @@ footer.cmd{left:var(--global-nav-width);transition:left .24s cubic-bezier(.2,.8,
2117
2321
  var id=device.id||activeRow.id||activeRow.robot_id||(liveRoom&&liveRoom.robot_id)||'';
2118
2322
  var row=activeRow.id||activeRow.robot_id?activeRow:(rowsById[norm(id)]||{});
2119
2323
  var name=String(preset.name||characterId).trim();
2324
+ // Camera publishers join robopark-<claim/device id> (a character call
2325
+ // page claims a device id like claim_63a36d48…), so an active
2326
+ // session's robot_id or the live-room census is the authoritative
2327
+ // camera stream identity; the bound device id is only a fallback.
2328
+ var activeSession=activeSessions.find(function(sess){
2329
+ var sid=norm(sess.robot_id||sess.device_id);if(!sid)return false;
2330
+ if(sid===characterId||norm(sess.character_id||sess.character_preset_id)===characterId)return true;
2331
+ return matchingDevices.some(function(candidate){return norm(candidate.id)===sid;});
2332
+ })||null;
2333
+ var liveRoomName=liveRoom?String(liveRoom.room_name||liveRoom.room||'').trim():'';
2334
+ var cameraStreamId=String((activeSession&&(activeSession.robot_id||activeSession.device_id))||(liveRoomName?liveRoomName.replace(/^robopark-/,''):'')||(liveRoom&&liveRoom.robot_id)||id||'').trim();
2335
+ if(name&&cameraStreamId)rpCameraStreamIds[name]=cameraStreamId;
2120
2336
  var status = String(row.connection_status || row.status || device.connection_status || device.status || '').toLowerCase();
2121
2337
  var heartbeat = row.last_heartbeat || device.last_heartbeat;
2122
2338
  var heartbeatAt = heartbeat ? Date.parse(heartbeat) : NaN;