robopark 3.3.57 → 3.3.59

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.57",
3
+ "version": "3.3.59",
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": {
@@ -454,7 +454,10 @@
454
454
  // Without a deadline a stalled control request hung the join forever: no
455
455
  // error surfaced, so nothing ever retried.
456
456
  if(!request.signal&&window.AbortSignal&&AbortSignal.timeout)request.signal=AbortSignal.timeout(25000);
457
- return fetch(API_BASE+path,request).then(function(response){if(!response.ok)return response.text().then(function(body){var failure=new Error(body||('request '+response.status));failure.status=response.status;throw failure});return response.json()})}
457
+ return fetch(API_BASE+path,request).then(function(response){if(!response.ok)return response.text().then(function(body){// Name the request in the failure. "Method Not Allowed" with no URL sent an
458
+ // operator (and the tooling behind them) hunting through every endpoint the
459
+ // page touches; the failing call should identify itself.
460
+ var failure=new Error((options.method||'GET')+' '+path+' -> '+response.status+(body?': '+body.slice(0,200):''));failure.status=response.status;throw failure});return response.json()})}
458
461
  function append(role,message){message=String(message||'').trim();if(!message)return;var key=role+'|'+message;if(seen[key])return;seen[key]=1;var node=document.createElement('div');node.className='turn '+role;node.textContent=message;el('transcript').appendChild(node);el('transcript').scrollTop=el('transcript').scrollHeight}
459
462
  function management(path,body){if(!session||!eventToken)return Promise.resolve();return api('/api/sessions/'+encodeURIComponent(session)+path,{method:'POST',headers:headers(),body:JSON.stringify(body||{})}).catch(function(){})}
460
463
  function event(stageName,status,message,details){var row=document.createElement('div');row.className='event';row.innerHTML='<span>'+String(stageName||'event').replace(/[<>]/g,'')+'</span><span class="'+String(status||'ok').replace(/[<>]/g,'')+'">'+String(status||'ok').replace(/[<>]/g,'')+'</span><span>'+String(message||'').replace(/[<>]/g,'')+'</span>';var host=el('events');if(host){host.prepend(row);while(host.children.length>9)host.lastElementChild.remove()}return Promise.resolve()}
@@ -1 +1 @@
1
- yswW2wNJG-5o2FkKKTtei
1
+ cxjKMxl9mcTp7rbD2AE31
@@ -18,33 +18,33 @@
18
18
  "static/chunks/9880-53262785284464cc.js",
19
19
  "static/chunks/app/layout-fcce963da1d8220f.js"
20
20
  ],
21
- "/api/download-piper-model/route": [
21
+ "/api/device-character/route": [
22
22
  "static/chunks/webpack-5252e5254da079b5.js",
23
23
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
24
24
  "static/chunks/1255-404bb8de49c90395.js",
25
25
  "static/chunks/main-app-a1f59168303c7f84.js",
26
- "static/chunks/app/api/download-piper-model/route-0952b44bda5bfb6d.js"
26
+ "static/chunks/app/api/device-character/route-0952b44bda5bfb6d.js"
27
27
  ],
28
- "/api/device-character/route": [
28
+ "/api/download-piper-model/route": [
29
29
  "static/chunks/webpack-5252e5254da079b5.js",
30
30
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
31
31
  "static/chunks/1255-404bb8de49c90395.js",
32
32
  "static/chunks/main-app-a1f59168303c7f84.js",
33
- "static/chunks/app/api/device-character/route-0952b44bda5bfb6d.js"
33
+ "static/chunks/app/api/download-piper-model/route-0952b44bda5bfb6d.js"
34
34
  ],
35
- "/api/models/route": [
35
+ "/api/prewarm/route": [
36
36
  "static/chunks/webpack-5252e5254da079b5.js",
37
37
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
38
38
  "static/chunks/1255-404bb8de49c90395.js",
39
39
  "static/chunks/main-app-a1f59168303c7f84.js",
40
- "static/chunks/app/api/models/route-0952b44bda5bfb6d.js"
40
+ "static/chunks/app/api/prewarm/route-0952b44bda5bfb6d.js"
41
41
  ],
42
- "/api/prewarm/route": [
42
+ "/api/models/route": [
43
43
  "static/chunks/webpack-5252e5254da079b5.js",
44
44
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
45
45
  "static/chunks/1255-404bb8de49c90395.js",
46
46
  "static/chunks/main-app-a1f59168303c7f84.js",
47
- "static/chunks/app/api/prewarm/route-0952b44bda5bfb6d.js"
47
+ "static/chunks/app/api/models/route-0952b44bda5bfb6d.js"
48
48
  ],
49
49
  "/api/prompt/route": [
50
50
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -60,19 +60,19 @@
60
60
  "static/chunks/main-app-a1f59168303c7f84.js",
61
61
  "static/chunks/app/api/reload-tools/route-0952b44bda5bfb6d.js"
62
62
  ],
63
- "/api/robopark/devices/[deviceId]/control/route": [
63
+ "/api/robopark/robots/[robotId]/operations/route": [
64
64
  "static/chunks/webpack-5252e5254da079b5.js",
65
65
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
66
66
  "static/chunks/1255-404bb8de49c90395.js",
67
67
  "static/chunks/main-app-a1f59168303c7f84.js",
68
- "static/chunks/app/api/robopark/devices/[deviceId]/control/route-0952b44bda5bfb6d.js"
68
+ "static/chunks/app/api/robopark/robots/[robotId]/operations/route-0952b44bda5bfb6d.js"
69
69
  ],
70
- "/api/robopark/robots/[robotId]/operations/route": [
70
+ "/api/robopark/devices/[deviceId]/control/route": [
71
71
  "static/chunks/webpack-5252e5254da079b5.js",
72
72
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
73
73
  "static/chunks/1255-404bb8de49c90395.js",
74
74
  "static/chunks/main-app-a1f59168303c7f84.js",
75
- "static/chunks/app/api/robopark/robots/[robotId]/operations/route-0952b44bda5bfb6d.js"
75
+ "static/chunks/app/api/robopark/devices/[deviceId]/control/route-0952b44bda5bfb6d.js"
76
76
  ],
77
77
  "/api/robopark/sessions/[sessionId]/end/route": [
78
78
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -88,26 +88,26 @@
88
88
  "static/chunks/main-app-a1f59168303c7f84.js",
89
89
  "static/chunks/app/api/robopark/transcripts/[sessionId]/route-0952b44bda5bfb6d.js"
90
90
  ],
91
- "/api/setup/complete/route": [
91
+ "/api/setup/test-groq/route": [
92
92
  "static/chunks/webpack-5252e5254da079b5.js",
93
93
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
94
94
  "static/chunks/1255-404bb8de49c90395.js",
95
95
  "static/chunks/main-app-a1f59168303c7f84.js",
96
- "static/chunks/app/api/setup/complete/route-0952b44bda5bfb6d.js"
96
+ "static/chunks/app/api/setup/test-groq/route-0952b44bda5bfb6d.js"
97
97
  ],
98
- "/api/setup/status/route": [
98
+ "/api/setup/test-hass/route": [
99
99
  "static/chunks/webpack-5252e5254da079b5.js",
100
100
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
101
101
  "static/chunks/1255-404bb8de49c90395.js",
102
102
  "static/chunks/main-app-a1f59168303c7f84.js",
103
- "static/chunks/app/api/setup/status/route-0952b44bda5bfb6d.js"
103
+ "static/chunks/app/api/setup/test-hass/route-0952b44bda5bfb6d.js"
104
104
  ],
105
- "/api/setup/test-groq/route": [
105
+ "/api/setup/status/route": [
106
106
  "static/chunks/webpack-5252e5254da079b5.js",
107
107
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
108
108
  "static/chunks/1255-404bb8de49c90395.js",
109
109
  "static/chunks/main-app-a1f59168303c7f84.js",
110
- "static/chunks/app/api/setup/test-groq/route-0952b44bda5bfb6d.js"
110
+ "static/chunks/app/api/setup/status/route-0952b44bda5bfb6d.js"
111
111
  ],
112
112
  "/api/setup/test-n8n/route": [
113
113
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -116,12 +116,26 @@
116
116
  "static/chunks/main-app-a1f59168303c7f84.js",
117
117
  "static/chunks/app/api/setup/test-n8n/route-0952b44bda5bfb6d.js"
118
118
  ],
119
- "/api/setup/test-hass/route": [
119
+ "/api/setup/test-ollama/route": [
120
120
  "static/chunks/webpack-5252e5254da079b5.js",
121
121
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
122
122
  "static/chunks/1255-404bb8de49c90395.js",
123
123
  "static/chunks/main-app-a1f59168303c7f84.js",
124
- "static/chunks/app/api/setup/test-hass/route-0952b44bda5bfb6d.js"
124
+ "static/chunks/app/api/setup/test-ollama/route-0952b44bda5bfb6d.js"
125
+ ],
126
+ "/api/test-tts/route": [
127
+ "static/chunks/webpack-5252e5254da079b5.js",
128
+ "static/chunks/4bd1b696-f785427dddbba9fb.js",
129
+ "static/chunks/1255-404bb8de49c90395.js",
130
+ "static/chunks/main-app-a1f59168303c7f84.js",
131
+ "static/chunks/app/api/test-tts/route-0952b44bda5bfb6d.js"
132
+ ],
133
+ "/api/setup/complete/route": [
134
+ "static/chunks/webpack-5252e5254da079b5.js",
135
+ "static/chunks/4bd1b696-f785427dddbba9fb.js",
136
+ "static/chunks/1255-404bb8de49c90395.js",
137
+ "static/chunks/main-app-a1f59168303c7f84.js",
138
+ "static/chunks/app/api/setup/complete/route-0952b44bda5bfb6d.js"
125
139
  ],
126
140
  "/api/voices/route": [
127
141
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -151,47 +165,47 @@
151
165
  "static/chunks/main-app-a1f59168303c7f84.js",
152
166
  "static/chunks/app/api/wake-word/status/route-0952b44bda5bfb6d.js"
153
167
  ],
154
- "/api/test-tts/route": [
168
+ "/api/model-status/route": [
155
169
  "static/chunks/webpack-5252e5254da079b5.js",
156
170
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
157
171
  "static/chunks/1255-404bb8de49c90395.js",
158
172
  "static/chunks/main-app-a1f59168303c7f84.js",
159
- "static/chunks/app/api/test-tts/route-0952b44bda5bfb6d.js"
173
+ "static/chunks/app/api/model-status/route-0952b44bda5bfb6d.js"
160
174
  ],
161
- "/api/setup/test-ollama/route": [
175
+ "/api/connection-details/route": [
162
176
  "static/chunks/webpack-5252e5254da079b5.js",
163
177
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
164
178
  "static/chunks/1255-404bb8de49c90395.js",
165
179
  "static/chunks/main-app-a1f59168303c7f84.js",
166
- "static/chunks/app/api/setup/test-ollama/route-0952b44bda5bfb6d.js"
180
+ "static/chunks/app/api/connection-details/route-0952b44bda5bfb6d.js"
167
181
  ],
168
- "/api/model-status/route": [
182
+ "/api/kiosk-hardware/route": [
169
183
  "static/chunks/webpack-5252e5254da079b5.js",
170
184
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
171
185
  "static/chunks/1255-404bb8de49c90395.js",
172
186
  "static/chunks/main-app-a1f59168303c7f84.js",
173
- "static/chunks/app/api/model-status/route-0952b44bda5bfb6d.js"
187
+ "static/chunks/app/api/kiosk-hardware/route-0952b44bda5bfb6d.js"
174
188
  ],
175
- "/api/kiosk-hardware/route": [
189
+ "/api/robopark/character-ai/route": [
176
190
  "static/chunks/webpack-5252e5254da079b5.js",
177
191
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
178
192
  "static/chunks/1255-404bb8de49c90395.js",
179
193
  "static/chunks/main-app-a1f59168303c7f84.js",
180
- "static/chunks/app/api/kiosk-hardware/route-0952b44bda5bfb6d.js"
194
+ "static/chunks/app/api/robopark/character-ai/route-0952b44bda5bfb6d.js"
181
195
  ],
182
- "/api/connection-details/route": [
196
+ "/api/robopark/live-rooms/route": [
183
197
  "static/chunks/webpack-5252e5254da079b5.js",
184
198
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
185
199
  "static/chunks/1255-404bb8de49c90395.js",
186
200
  "static/chunks/main-app-a1f59168303c7f84.js",
187
- "static/chunks/app/api/connection-details/route-0952b44bda5bfb6d.js"
201
+ "static/chunks/app/api/robopark/live-rooms/route-0952b44bda5bfb6d.js"
188
202
  ],
189
- "/api/robopark/character-ai/route": [
203
+ "/api/robopark/robots/route": [
190
204
  "static/chunks/webpack-5252e5254da079b5.js",
191
205
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
192
206
  "static/chunks/1255-404bb8de49c90395.js",
193
207
  "static/chunks/main-app-a1f59168303c7f84.js",
194
- "static/chunks/app/api/robopark/character-ai/route-0952b44bda5bfb6d.js"
208
+ "static/chunks/app/api/robopark/robots/route-0952b44bda5bfb6d.js"
195
209
  ],
196
210
  "/api/robopark/camera-token/route": [
197
211
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -200,33 +214,33 @@
200
214
  "static/chunks/main-app-a1f59168303c7f84.js",
201
215
  "static/chunks/app/api/robopark/camera-token/route-0952b44bda5bfb6d.js"
202
216
  ],
203
- "/api/robopark/monitor-token/route": [
217
+ "/api/robopark/device-access/route": [
204
218
  "static/chunks/webpack-5252e5254da079b5.js",
205
219
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
206
220
  "static/chunks/1255-404bb8de49c90395.js",
207
221
  "static/chunks/main-app-a1f59168303c7f84.js",
208
- "static/chunks/app/api/robopark/monitor-token/route-0952b44bda5bfb6d.js"
222
+ "static/chunks/app/api/robopark/device-access/route-0952b44bda5bfb6d.js"
209
223
  ],
210
- "/api/robopark/device-access/route": [
224
+ "/api/robopark/servers/[serverId]/route": [
211
225
  "static/chunks/webpack-5252e5254da079b5.js",
212
226
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
213
227
  "static/chunks/1255-404bb8de49c90395.js",
214
228
  "static/chunks/main-app-a1f59168303c7f84.js",
215
- "static/chunks/app/api/robopark/device-access/route-0952b44bda5bfb6d.js"
229
+ "static/chunks/app/api/robopark/servers/[serverId]/route-0952b44bda5bfb6d.js"
216
230
  ],
217
- "/api/robopark/robots/route": [
231
+ "/api/robopark/monitor-token/route": [
218
232
  "static/chunks/webpack-5252e5254da079b5.js",
219
233
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
220
234
  "static/chunks/1255-404bb8de49c90395.js",
221
235
  "static/chunks/main-app-a1f59168303c7f84.js",
222
- "static/chunks/app/api/robopark/robots/route-0952b44bda5bfb6d.js"
236
+ "static/chunks/app/api/robopark/monitor-token/route-0952b44bda5bfb6d.js"
223
237
  ],
224
- "/api/robopark/live-rooms/route": [
238
+ "/api/robopark/servers/[serverId]/metrics/route": [
225
239
  "static/chunks/webpack-5252e5254da079b5.js",
226
240
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
227
241
  "static/chunks/1255-404bb8de49c90395.js",
228
242
  "static/chunks/main-app-a1f59168303c7f84.js",
229
- "static/chunks/app/api/robopark/live-rooms/route-0952b44bda5bfb6d.js"
243
+ "static/chunks/app/api/robopark/servers/[serverId]/metrics/route-0952b44bda5bfb6d.js"
230
244
  ],
231
245
  "/api/robopark/servers/route": [
232
246
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -235,19 +249,19 @@
235
249
  "static/chunks/main-app-a1f59168303c7f84.js",
236
250
  "static/chunks/app/api/robopark/servers/route-0952b44bda5bfb6d.js"
237
251
  ],
238
- "/api/robopark/servers/[serverId]/metrics/route": [
252
+ "/api/robopark/sessions/route": [
239
253
  "static/chunks/webpack-5252e5254da079b5.js",
240
254
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
241
255
  "static/chunks/1255-404bb8de49c90395.js",
242
256
  "static/chunks/main-app-a1f59168303c7f84.js",
243
- "static/chunks/app/api/robopark/servers/[serverId]/metrics/route-0952b44bda5bfb6d.js"
257
+ "static/chunks/app/api/robopark/sessions/route-0952b44bda5bfb6d.js"
244
258
  ],
245
- "/api/robopark/servers/[serverId]/route": [
259
+ "/api/robopark/vision-caption/route": [
246
260
  "static/chunks/webpack-5252e5254da079b5.js",
247
261
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
248
262
  "static/chunks/1255-404bb8de49c90395.js",
249
263
  "static/chunks/main-app-a1f59168303c7f84.js",
250
- "static/chunks/app/api/robopark/servers/[serverId]/route-0952b44bda5bfb6d.js"
264
+ "static/chunks/app/api/robopark/vision-caption/route-0952b44bda5bfb6d.js"
251
265
  ],
252
266
  "/api/robopark/robots/[robotId]/voice-engine/route": [
253
267
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -256,13 +270,6 @@
256
270
  "static/chunks/main-app-a1f59168303c7f84.js",
257
271
  "static/chunks/app/api/robopark/robots/[robotId]/voice-engine/route-0952b44bda5bfb6d.js"
258
272
  ],
259
- "/api/robopark/vision-caption/route": [
260
- "static/chunks/webpack-5252e5254da079b5.js",
261
- "static/chunks/4bd1b696-f785427dddbba9fb.js",
262
- "static/chunks/1255-404bb8de49c90395.js",
263
- "static/chunks/main-app-a1f59168303c7f84.js",
264
- "static/chunks/app/api/robopark/vision-caption/route-0952b44bda5bfb6d.js"
265
- ],
266
273
  "/api/settings/route": [
267
274
  "static/chunks/webpack-5252e5254da079b5.js",
268
275
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
@@ -270,12 +277,12 @@
270
277
  "static/chunks/main-app-a1f59168303c7f84.js",
271
278
  "static/chunks/app/api/settings/route-0952b44bda5bfb6d.js"
272
279
  ],
273
- "/api/robopark/sessions/route": [
280
+ "/api/robovoice/robots/[robotId]/media-health/route": [
274
281
  "static/chunks/webpack-5252e5254da079b5.js",
275
282
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
276
283
  "static/chunks/1255-404bb8de49c90395.js",
277
284
  "static/chunks/main-app-a1f59168303c7f84.js",
278
- "static/chunks/app/api/robopark/sessions/route-0952b44bda5bfb6d.js"
285
+ "static/chunks/app/api/robovoice/robots/[robotId]/media-health/route-0952b44bda5bfb6d.js"
279
286
  ],
280
287
  "/api/turn-credentials/route": [
281
288
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -291,12 +298,12 @@
291
298
  "static/chunks/main-app-a1f59168303c7f84.js",
292
299
  "static/chunks/app/api/robopark/transcripts/route-0952b44bda5bfb6d.js"
293
300
  ],
294
- "/api/robovoice/robots/[robotId]/media-health/route": [
301
+ "/camera-grid/route": [
295
302
  "static/chunks/webpack-5252e5254da079b5.js",
296
303
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
297
304
  "static/chunks/1255-404bb8de49c90395.js",
298
305
  "static/chunks/main-app-a1f59168303c7f84.js",
299
- "static/chunks/app/api/robovoice/robots/[robotId]/media-health/route-0952b44bda5bfb6d.js"
306
+ "static/chunks/app/camera-grid/route-0952b44bda5bfb6d.js"
300
307
  ],
301
308
  "/api/wake/route": [
302
309
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -305,19 +312,19 @@
305
312
  "static/chunks/main-app-a1f59168303c7f84.js",
306
313
  "static/chunks/app/api/wake/route-0952b44bda5bfb6d.js"
307
314
  ],
308
- "/camera-grid/route": [
315
+ "/robopark/[...path]/route": [
309
316
  "static/chunks/webpack-5252e5254da079b5.js",
310
317
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
311
318
  "static/chunks/1255-404bb8de49c90395.js",
312
319
  "static/chunks/main-app-a1f59168303c7f84.js",
313
- "static/chunks/app/camera-grid/route-0952b44bda5bfb6d.js"
320
+ "static/chunks/app/robopark/[...path]/route-0952b44bda5bfb6d.js"
314
321
  ],
315
- "/robopark/api/robots/[robotId]/stream/route": [
322
+ "/fed/media/robots/[robotId]/video_feed/route": [
316
323
  "static/chunks/webpack-5252e5254da079b5.js",
317
324
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
318
325
  "static/chunks/1255-404bb8de49c90395.js",
319
326
  "static/chunks/main-app-a1f59168303c7f84.js",
320
- "static/chunks/app/robopark/api/robots/[robotId]/stream/route-0952b44bda5bfb6d.js"
327
+ "static/chunks/app/fed/media/robots/[robotId]/video_feed/route-0952b44bda5bfb6d.js"
321
328
  ],
322
329
  "/fed/[...path]/route": [
323
330
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -326,33 +333,26 @@
326
333
  "static/chunks/main-app-a1f59168303c7f84.js",
327
334
  "static/chunks/app/fed/[...path]/route-0952b44bda5bfb6d.js"
328
335
  ],
329
- "/tailnet/route": [
330
- "static/chunks/webpack-5252e5254da079b5.js",
331
- "static/chunks/4bd1b696-f785427dddbba9fb.js",
332
- "static/chunks/1255-404bb8de49c90395.js",
333
- "static/chunks/main-app-a1f59168303c7f84.js",
334
- "static/chunks/app/tailnet/route-0952b44bda5bfb6d.js"
335
- ],
336
- "/robopark/[...path]/route": [
336
+ "/vendor/livekit-client.umd.js/route": [
337
337
  "static/chunks/webpack-5252e5254da079b5.js",
338
338
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
339
339
  "static/chunks/1255-404bb8de49c90395.js",
340
340
  "static/chunks/main-app-a1f59168303c7f84.js",
341
- "static/chunks/app/robopark/[...path]/route-0952b44bda5bfb6d.js"
341
+ "static/chunks/app/vendor/livekit-client.umd.js/route-0952b44bda5bfb6d.js"
342
342
  ],
343
- "/vendor/livekit-client.umd.js/route": [
343
+ "/robopark/api/robots/[robotId]/stream/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/vendor/livekit-client.umd.js/route-0952b44bda5bfb6d.js"
348
+ "static/chunks/app/robopark/api/robots/[robotId]/stream/route-0952b44bda5bfb6d.js"
349
349
  ],
350
- "/fed/media/robots/[robotId]/video_feed/route": [
350
+ "/tailnet/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/fed/media/robots/[robotId]/video_feed/route-0952b44bda5bfb6d.js"
355
+ "static/chunks/app/tailnet/route-0952b44bda5bfb6d.js"
356
356
  ],
357
357
  "/(app)/opengraph-image-xg4ifa/route": [
358
358
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -1,54 +1,54 @@
1
1
  {
2
2
  "/_not-found/page": "/_not-found",
3
- "/api/download-piper-model/route": "/api/download-piper-model",
4
3
  "/api/device-character/route": "/api/device-character",
5
- "/api/models/route": "/api/models",
4
+ "/api/download-piper-model/route": "/api/download-piper-model",
6
5
  "/api/prewarm/route": "/api/prewarm",
6
+ "/api/models/route": "/api/models",
7
7
  "/api/prompt/route": "/api/prompt",
8
8
  "/api/reload-tools/route": "/api/reload-tools",
9
- "/api/robopark/devices/[deviceId]/control/route": "/api/robopark/devices/[deviceId]/control",
10
9
  "/api/robopark/robots/[robotId]/operations/route": "/api/robopark/robots/[robotId]/operations",
10
+ "/api/robopark/devices/[deviceId]/control/route": "/api/robopark/devices/[deviceId]/control",
11
11
  "/api/robopark/sessions/[sessionId]/end/route": "/api/robopark/sessions/[sessionId]/end",
12
12
  "/api/robopark/transcripts/[sessionId]/route": "/api/robopark/transcripts/[sessionId]",
13
- "/api/setup/complete/route": "/api/setup/complete",
14
- "/api/setup/status/route": "/api/setup/status",
15
13
  "/api/setup/test-groq/route": "/api/setup/test-groq",
16
- "/api/setup/test-n8n/route": "/api/setup/test-n8n",
17
14
  "/api/setup/test-hass/route": "/api/setup/test-hass",
15
+ "/api/setup/status/route": "/api/setup/status",
16
+ "/api/setup/test-n8n/route": "/api/setup/test-n8n",
17
+ "/api/setup/test-ollama/route": "/api/setup/test-ollama",
18
+ "/api/test-tts/route": "/api/test-tts",
19
+ "/api/setup/complete/route": "/api/setup/complete",
18
20
  "/api/voices/route": "/api/voices",
19
21
  "/api/wake-word/models/route": "/api/wake-word/models",
20
22
  "/api/wake-word/upload/route": "/api/wake-word/upload",
21
23
  "/api/wake-word/status/route": "/api/wake-word/status",
22
- "/api/test-tts/route": "/api/test-tts",
23
- "/api/setup/test-ollama/route": "/api/setup/test-ollama",
24
24
  "/favicon.ico/route": "/favicon.ico",
25
25
  "/api/model-status/route": "/api/model-status",
26
- "/api/kiosk-hardware/route": "/api/kiosk-hardware",
27
26
  "/api/connection-details/route": "/api/connection-details",
27
+ "/api/kiosk-hardware/route": "/api/kiosk-hardware",
28
28
  "/api/robopark/character-ai/route": "/api/robopark/character-ai",
29
+ "/api/robopark/live-rooms/route": "/api/robopark/live-rooms",
30
+ "/api/robopark/robots/route": "/api/robopark/robots",
29
31
  "/api/robopark/camera-token/route": "/api/robopark/camera-token",
30
- "/api/robopark/monitor-token/route": "/api/robopark/monitor-token",
31
32
  "/api/robopark/device-access/route": "/api/robopark/device-access",
32
- "/api/robopark/robots/route": "/api/robopark/robots",
33
- "/api/robopark/live-rooms/route": "/api/robopark/live-rooms",
34
- "/api/robopark/servers/route": "/api/robopark/servers",
35
- "/api/robopark/servers/[serverId]/metrics/route": "/api/robopark/servers/[serverId]/metrics",
36
33
  "/api/robopark/servers/[serverId]/route": "/api/robopark/servers/[serverId]",
37
- "/api/robopark/robots/[robotId]/voice-engine/route": "/api/robopark/robots/[robotId]/voice-engine",
34
+ "/api/robopark/monitor-token/route": "/api/robopark/monitor-token",
35
+ "/api/robopark/servers/[serverId]/metrics/route": "/api/robopark/servers/[serverId]/metrics",
36
+ "/api/robopark/servers/route": "/api/robopark/servers",
37
+ "/api/robopark/sessions/route": "/api/robopark/sessions",
38
38
  "/api/robopark/vision-caption/route": "/api/robopark/vision-caption",
39
+ "/api/robopark/robots/[robotId]/voice-engine/route": "/api/robopark/robots/[robotId]/voice-engine",
39
40
  "/api/settings/route": "/api/settings",
40
- "/api/robopark/sessions/route": "/api/robopark/sessions",
41
+ "/api/robovoice/robots/[robotId]/media-health/route": "/api/robovoice/robots/[robotId]/media-health",
41
42
  "/api/turn-credentials/route": "/api/turn-credentials",
42
43
  "/api/robopark/transcripts/route": "/api/robopark/transcripts",
43
- "/api/robovoice/robots/[robotId]/media-health/route": "/api/robovoice/robots/[robotId]/media-health",
44
- "/api/wake/route": "/api/wake",
45
44
  "/camera-grid/route": "/camera-grid",
46
- "/robopark/api/robots/[robotId]/stream/route": "/robopark/api/robots/[robotId]/stream",
47
- "/fed/[...path]/route": "/fed/[...path]",
48
- "/tailnet/route": "/tailnet",
45
+ "/api/wake/route": "/api/wake",
49
46
  "/robopark/[...path]/route": "/robopark/[...path]",
50
- "/vendor/livekit-client.umd.js/route": "/vendor/livekit-client.umd.js",
51
47
  "/fed/media/robots/[robotId]/video_feed/route": "/fed/media/robots/[robotId]/video_feed",
48
+ "/fed/[...path]/route": "/fed/[...path]",
49
+ "/vendor/livekit-client.umd.js/route": "/vendor/livekit-client.umd.js",
50
+ "/robopark/api/robots/[robotId]/stream/route": "/robopark/api/robots/[robotId]/stream",
51
+ "/tailnet/route": "/tailnet",
52
52
  "/(app)/opengraph-image-xg4ifa/route": "/opengraph-image-xg4ifa",
53
53
  "/robopark/api/[...path]/route": "/robopark/api/[...path]",
54
54
  "/robopark/page": "/robopark",
@@ -5,8 +5,8 @@
5
5
  "devFiles": [],
6
6
  "ampDevFiles": [],
7
7
  "lowPriorityFiles": [
8
- "static/yswW2wNJG-5o2FkKKTtei/_buildManifest.js",
9
- "static/yswW2wNJG-5o2FkKKTtei/_ssgManifest.js"
8
+ "static/cxjKMxl9mcTp7rbD2AE31/_buildManifest.js",
9
+ "static/cxjKMxl9mcTp7rbD2AE31/_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": "33702872480a5f639e2763fad1b625f6",
38
- "previewModeSigningKey": "2d70db468970bf5e4978c149028e92477b58ee44393dfb1a5e6ade29971cbc63",
39
- "previewModeEncryptionKey": "833f8f6d1a31a2c61645dd20e505e08b88c8c3c2b07b1a121691f152eba97b46"
37
+ "previewModeId": "efc58b5ea7688ede69c860e874aeca45",
38
+ "previewModeSigningKey": "43b12050e3bb8d0f7b321c93eb05b258d9952651348672b51bc0e72499edac5d",
39
+ "previewModeEncryptionKey": "846837c972415de4fdc9c1d4bda4b053f7d375b69eab62309e34e4544a4af3cb"
40
40
  }
41
41
  }
@@ -1,54 +1,54 @@
1
1
  {
2
2
  "/_not-found/page": "app/_not-found/page.js",
3
- "/api/download-piper-model/route": "app/api/download-piper-model/route.js",
4
3
  "/api/device-character/route": "app/api/device-character/route.js",
5
- "/api/models/route": "app/api/models/route.js",
4
+ "/api/download-piper-model/route": "app/api/download-piper-model/route.js",
6
5
  "/api/prewarm/route": "app/api/prewarm/route.js",
6
+ "/api/models/route": "app/api/models/route.js",
7
7
  "/api/prompt/route": "app/api/prompt/route.js",
8
8
  "/api/reload-tools/route": "app/api/reload-tools/route.js",
9
- "/api/robopark/devices/[deviceId]/control/route": "app/api/robopark/devices/[deviceId]/control/route.js",
10
9
  "/api/robopark/robots/[robotId]/operations/route": "app/api/robopark/robots/[robotId]/operations/route.js",
10
+ "/api/robopark/devices/[deviceId]/control/route": "app/api/robopark/devices/[deviceId]/control/route.js",
11
11
  "/api/robopark/sessions/[sessionId]/end/route": "app/api/robopark/sessions/[sessionId]/end/route.js",
12
12
  "/api/robopark/transcripts/[sessionId]/route": "app/api/robopark/transcripts/[sessionId]/route.js",
13
- "/api/setup/complete/route": "app/api/setup/complete/route.js",
14
- "/api/setup/status/route": "app/api/setup/status/route.js",
15
13
  "/api/setup/test-groq/route": "app/api/setup/test-groq/route.js",
16
- "/api/setup/test-n8n/route": "app/api/setup/test-n8n/route.js",
17
14
  "/api/setup/test-hass/route": "app/api/setup/test-hass/route.js",
15
+ "/api/setup/status/route": "app/api/setup/status/route.js",
16
+ "/api/setup/test-n8n/route": "app/api/setup/test-n8n/route.js",
17
+ "/api/setup/test-ollama/route": "app/api/setup/test-ollama/route.js",
18
+ "/api/test-tts/route": "app/api/test-tts/route.js",
19
+ "/api/setup/complete/route": "app/api/setup/complete/route.js",
18
20
  "/api/voices/route": "app/api/voices/route.js",
19
21
  "/api/wake-word/models/route": "app/api/wake-word/models/route.js",
20
22
  "/api/wake-word/upload/route": "app/api/wake-word/upload/route.js",
21
23
  "/api/wake-word/status/route": "app/api/wake-word/status/route.js",
22
- "/api/test-tts/route": "app/api/test-tts/route.js",
23
- "/api/setup/test-ollama/route": "app/api/setup/test-ollama/route.js",
24
24
  "/favicon.ico/route": "app/favicon.ico/route.js",
25
25
  "/api/model-status/route": "app/api/model-status/route.js",
26
- "/api/kiosk-hardware/route": "app/api/kiosk-hardware/route.js",
27
26
  "/api/connection-details/route": "app/api/connection-details/route.js",
27
+ "/api/kiosk-hardware/route": "app/api/kiosk-hardware/route.js",
28
28
  "/api/robopark/character-ai/route": "app/api/robopark/character-ai/route.js",
29
+ "/api/robopark/live-rooms/route": "app/api/robopark/live-rooms/route.js",
30
+ "/api/robopark/robots/route": "app/api/robopark/robots/route.js",
29
31
  "/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",
31
32
  "/api/robopark/device-access/route": "app/api/robopark/device-access/route.js",
32
- "/api/robopark/robots/route": "app/api/robopark/robots/route.js",
33
- "/api/robopark/live-rooms/route": "app/api/robopark/live-rooms/route.js",
34
- "/api/robopark/servers/route": "app/api/robopark/servers/route.js",
35
- "/api/robopark/servers/[serverId]/metrics/route": "app/api/robopark/servers/[serverId]/metrics/route.js",
36
33
  "/api/robopark/servers/[serverId]/route": "app/api/robopark/servers/[serverId]/route.js",
37
- "/api/robopark/robots/[robotId]/voice-engine/route": "app/api/robopark/robots/[robotId]/voice-engine/route.js",
34
+ "/api/robopark/monitor-token/route": "app/api/robopark/monitor-token/route.js",
35
+ "/api/robopark/servers/[serverId]/metrics/route": "app/api/robopark/servers/[serverId]/metrics/route.js",
36
+ "/api/robopark/servers/route": "app/api/robopark/servers/route.js",
37
+ "/api/robopark/sessions/route": "app/api/robopark/sessions/route.js",
38
38
  "/api/robopark/vision-caption/route": "app/api/robopark/vision-caption/route.js",
39
+ "/api/robopark/robots/[robotId]/voice-engine/route": "app/api/robopark/robots/[robotId]/voice-engine/route.js",
39
40
  "/api/settings/route": "app/api/settings/route.js",
40
- "/api/robopark/sessions/route": "app/api/robopark/sessions/route.js",
41
+ "/api/robovoice/robots/[robotId]/media-health/route": "app/api/robovoice/robots/[robotId]/media-health/route.js",
41
42
  "/api/turn-credentials/route": "app/api/turn-credentials/route.js",
42
43
  "/api/robopark/transcripts/route": "app/api/robopark/transcripts/route.js",
43
- "/api/robovoice/robots/[robotId]/media-health/route": "app/api/robovoice/robots/[robotId]/media-health/route.js",
44
- "/api/wake/route": "app/api/wake/route.js",
45
44
  "/camera-grid/route": "app/camera-grid/route.js",
46
- "/robopark/api/robots/[robotId]/stream/route": "app/robopark/api/robots/[robotId]/stream/route.js",
47
- "/fed/[...path]/route": "app/fed/[...path]/route.js",
48
- "/tailnet/route": "app/tailnet/route.js",
45
+ "/api/wake/route": "app/api/wake/route.js",
49
46
  "/robopark/[...path]/route": "app/robopark/[...path]/route.js",
50
- "/vendor/livekit-client.umd.js/route": "app/vendor/livekit-client.umd.js/route.js",
51
47
  "/fed/media/robots/[robotId]/video_feed/route": "app/fed/media/robots/[robotId]/video_feed/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",
50
+ "/robopark/api/robots/[robotId]/stream/route": "app/robopark/api/robots/[robotId]/stream/route.js",
51
+ "/tailnet/route": "app/tailnet/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
54
  "/robopark/page": "app/robopark/page.js",
@@ -4,7 +4,7 @@
4
4
  "/api/kiosk-hardware": {},
5
5
  "/api/robovoice/robots/[robotId]/media-health": {},
6
6
  "/fed/media/robots/[robotId]/video_feed": {},
7
- "/robopark/api/robots/[robotId]/stream": {},
8
- "/robopark/[...path]": {}
7
+ "/robopark/[...path]": {},
8
+ "/robopark/api/robots/[robotId]/stream": {}
9
9
  }
10
10
  }
@@ -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/yswW2wNJG-5o2FkKKTtei/_buildManifest.js" defer=""></script><script src="/_next/static/yswW2wNJG-5o2FkKKTtei/_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":"yswW2wNJG-5o2FkKKTtei","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/cxjKMxl9mcTp7rbD2AE31/_buildManifest.js" defer=""></script><script src="/_next/static/cxjKMxl9mcTp7rbD2AE31/_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":"cxjKMxl9mcTp7rbD2AE31","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}</script></body></html>
@@ -1 +1 @@
1
- {"node":{},"edge":{},"encryptionKey":"vp2cM5NGWFNKyrkaTwm4nAQcKmJtMWAWyhjM08yE2kI="}
1
+ {"node":{},"edge":{},"encryptionKey":"RD5FrWKStMYSmHSO3LNWFSPIQC9Qn3me1diUTTqrZSc="}
@@ -1453,6 +1453,71 @@ footer.cmd{left:var(--global-nav-width);transition:left .24s cubic-bezier(.2,.8,
1453
1453
  entry.dataHandlers=(entry.dataHandlers||[]).filter(function(handler){return !handler.node||handler.node.isConnected;});
1454
1454
  return entry;
1455
1455
  }
1456
+ // MJPEG fallback: when a LiveKit room connects but nobody publishes video
1457
+ // (no character call open), the tile fell into 'waiting for camera track'
1458
+ // forever. After a grace period we overlay the vision-server MJPEG stream in
1459
+ // the tile while keeping the room joined; a real LiveKit track always wins
1460
+ // and tears the overlay down. Every open MJPEG <img> holds an HTTP
1461
+ // connection, so teardown must be exhaustive (close, sweep, pagehide, error).
1462
+ var FLEET_FALLBACK_DELAY=12000;
1463
+ function removeFleetFallbackImgs(entry){
1464
+ (entry.fallbackImgs||[]).forEach(function(img){
1465
+ try{img.onload=null;img.onerror=null;img.src='';}catch(e){}
1466
+ var host=img._rpHost;
1467
+ if(img.parentNode)img.parentNode.removeChild(img);
1468
+ if(host&&!entry.hasTrack)host.classList.remove('live');
1469
+ });
1470
+ entry.fallbackImgs=[];entry.fallbackActive=false;
1471
+ }
1472
+ function clearFleetFallback(entry){
1473
+ if(!entry)return;
1474
+ if(entry.fallbackTimer){clearTimeout(entry.fallbackTimer);entry.fallbackTimer=null;}
1475
+ removeFleetFallbackImgs(entry);
1476
+ }
1477
+ function tryFleetFallback(deviceId,entry){
1478
+ if(fleetLiveKitRooms[deviceId]!==entry||entry.hasTrack)return;
1479
+ pruneRoomTargets(entry);
1480
+ if(!entry.videos.length)return;
1481
+ entry.fallbackImgs=(entry.fallbackImgs||[]).filter(function(img){return img&&img.isConnected;});
1482
+ entry.videos.forEach(function(video){
1483
+ var host=(video.closest&&video.closest('[data-fleet-camera-host]'))||video.parentNode;
1484
+ if(!host||host.querySelector('img[data-fleet-fallback]'))return;
1485
+ var img=document.createElement('img');
1486
+ img.setAttribute('data-fleet-fallback',deviceId);
1487
+ img.alt='fallback camera stream';
1488
+ img.style.position='absolute';img.style.inset='0';img.style.zIndex='2';
1489
+ img._rpHost=host;
1490
+ img.onload=function(){
1491
+ if(fleetLiveKitRooms[deviceId]!==entry||entry.hasTrack)return;
1492
+ entry.fallbackActive=true;entry.fallbackFailures=0;
1493
+ host.classList.add('live');
1494
+ setRoomStatus(entry,'MJPEG camera live (fallback)');
1495
+ };
1496
+ img.onerror=function(){
1497
+ if(fleetLiveKitRooms[deviceId]!==entry)return;
1498
+ removeFleetFallbackImgs(entry);
1499
+ if(entry.hasTrack)return;
1500
+ setRoomStatus(entry,'connected · waiting for camera track');
1501
+ armFleetFallback(deviceId,entry,true);
1502
+ };
1503
+ img.src='/fed/media/robots/'+encodeURIComponent(deviceId)+'/video_feed'+QS+(QS?'&':'?')+'_='+Date.now();
1504
+ entry.fallbackImgs.push(img);
1505
+ host.appendChild(img);
1506
+ });
1507
+ }
1508
+ function armFleetFallback(deviceId,entry,backoff){
1509
+ if(!entry||entry.fallbackTimer||entry.hasTrack)return;
1510
+ var delay=FLEET_FALLBACK_DELAY;
1511
+ if(backoff){
1512
+ entry.fallbackFailures=(entry.fallbackFailures||0)+1;
1513
+ delay=Math.min(15000,Math.round(1500*Math.pow(1.6,Math.min(entry.fallbackFailures-1,6))+Math.random()*500));
1514
+ }
1515
+ entry.fallbackTimer=setTimeout(function(){
1516
+ entry.fallbackTimer=null;
1517
+ if(fleetLiveKitRooms[deviceId]!==entry)return;
1518
+ tryFleetFallback(deviceId,entry);
1519
+ },delay);
1520
+ }
1456
1521
  // Always disconnect before dropping a room reference. Abandoning the object
1457
1522
  // left the monitor joined server-side, so every refresh or re-render stacked
1458
1523
  // another "RoboPark Control monitor" in the room -- rooms were carrying 3-4
@@ -1461,6 +1526,8 @@ footer.cmd{left:var(--global-nav-width);transition:left .24s cubic-bezier(.2,.8,
1461
1526
  entry=entry||fleetLiveKitRooms[deviceId];
1462
1527
  if(!entry)return;
1463
1528
  if(entry.retry){clearTimeout(entry.retry);entry.retry=null;}
1529
+ clearFleetFallback(entry);
1530
+ entry.hasTrack=false;
1464
1531
  var room=entry.room;entry.room=null;
1465
1532
  if(room){try{room.disconnect(true);}catch(e){}}
1466
1533
  if(fleetLiveKitRooms[deviceId]===entry)delete fleetLiveKitRooms[deviceId];
@@ -1517,32 +1584,48 @@ footer.cmd{left:var(--global-nav-width);transition:left .24s cubic-bezier(.2,.8,
1517
1584
  function openFleetCameraRoom(deviceId){
1518
1585
  var entry=fleetLiveKitRooms[deviceId];if(!entry||entry.connecting||entry.room)return;
1519
1586
  var roomId=rpCameraStreamIds[deviceId]||deviceId;
1587
+ entry.roomId=String(roomId);
1520
1588
  entry.connecting=true;
1521
1589
  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){
1522
1590
  var LK=parts[0],info=parts[1];
1523
1591
  entry.connecting=false;
1524
1592
  if(fleetLiveKitRooms[deviceId]!==entry)return null;
1525
1593
  var room=new LK.Room();entry.room=room;
1526
- room.on(LK.RoomEvent.TrackSubscribed,function(track){if(track.kind==='video'){pruneRoomTargets(entry);attachRoomTrack(entry,track);setRoomStatus(entry,'LiveKit camera live');}});
1594
+ 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');}});
1527
1595
  room.on(LK.RoomEvent.DataReceived,function(payload,participant,kind,topic){try{var data=JSON.parse(new TextDecoder().decode(payload));if(topic==='robopark_monitor'&&(data.type==='pipeline'||data.type==='stage'||data.type==='snapshot')){var canonical=data.stage||data.canonical_stage,snapshot={stage:canonical,state:data.state||data.stage,status:data.status||'active',stages:data.stages||data.pipeline||[],timestamp:data.timestamp||Date.now()};fleetPipelineByDevice[deviceId]=snapshot;renderParkMonitorPipeline(deviceId,snapshot);Array.prototype.forEach.call(document.querySelectorAll('[data-fleet-camera="'+deviceId+'"]'),function(camera){var card=camera.closest('.node'),focus=card&&card.querySelector('.fleet-stage-focus');if(focus&&canonical){focus.className='fleet-stage-focus active';focus.innerHTML='<span>Live production stage</span><b>'+esc(String(canonical).replace(/_/g,' '))+'</b>';}});}entry.dataHandlers.slice().forEach(function(handler){handler(data,topic);});}catch(e){}});
1528
- room.on(LK.RoomEvent.Disconnected,function(){if(fleetLiveKitRooms[deviceId]!==entry)return;entry.room=null;setRoomStatus(entry,'camera disconnected · retrying');scheduleFleetCameraRetry(deviceId);});
1596
+ room.on(LK.RoomEvent.Disconnected,function(){if(fleetLiveKitRooms[deviceId]!==entry)return;entry.room=null;entry.hasTrack=false;if(!entry.fallbackActive)setRoomStatus(entry,'camera disconnected · retrying');scheduleFleetCameraRetry(deviceId);});
1529
1597
  return room.connect(info.serverUrl,info.token);
1530
- }).then(function(result){if(result===null||fleetLiveKitRooms[deviceId]!==entry)return;entry.failures=0;setRoomStatus(entry,'connected · waiting for camera track');}).catch(function(error){
1531
- entry.connecting=false;entry.room=null;
1598
+ }).then(function(result){if(result===null||fleetLiveKitRooms[deviceId]!==entry)return;entry.failures=0;if(entry.fallbackActive){setRoomStatus(entry,'MJPEG camera live (fallback)');}else{setRoomStatus(entry,'connected · waiting for camera track');armFleetFallback(deviceId,entry);}}).catch(function(error){
1599
+ entry.connecting=false;entry.room=null;entry.hasTrack=false;
1532
1600
  if(fleetLiveKitRooms[deviceId]!==entry)return;
1533
- setRoomStatus(entry,'camera unavailable · '+error.message+' · retrying');
1601
+ if(!entry.fallbackActive)setRoomStatus(entry,'camera unavailable · '+error.message+' · retrying');
1534
1602
  scheduleFleetCameraRetry(deviceId);
1603
+ // LiveKit is down entirely; don't sit dark for the full grace period.
1604
+ if(!entry.fallbackActive&&!entry.fallbackTimer)armFleetFallback(deviceId,entry,true);
1535
1605
  });
1536
1606
  }
1537
1607
  function connectFleetLiveCamera(video,deviceId,state){
1538
1608
  if(!deviceId||!video)return;
1609
+ var desiredRoomId=String(rpCameraStreamIds[deviceId]||deviceId);
1539
1610
  var entry=fleetLiveKitRooms[deviceId];
1540
- if(!entry){entry={connecting:false,room:null,videos:[],states:[],dataHandlers:[],retry:null,failures:0};fleetLiveKitRooms[deviceId]=entry;}
1611
+ // The live-room census can discover that the camera publishes into a claim
1612
+ // room (robopark-claim_<hash>) after we already joined robopark-<device_id>.
1613
+ // Re-key: drop the stale subscription and rejoin the room that actually has
1614
+ // a publisher. Only when not mid-connect and the target genuinely changed.
1615
+ if(entry&&!entry.connecting&&entry.roomId&&entry.roomId!==desiredRoomId){closeFleetCameraRoom(deviceId,entry);entry=null;}
1616
+ if(!entry){entry={connecting:false,room:null,roomId:null,videos:[],states:[],dataHandlers:[],retry:null,failures:0,hasTrack:false,fallbackTimer:null,fallbackImgs:[],fallbackActive:false,fallbackFailures:0};fleetLiveKitRooms[deviceId]=entry;}
1541
1617
  pruneRoomTargets(entry);
1542
1618
  if(entry.videos.indexOf(video)<0)entry.videos.push(video);
1543
1619
  if(state&&entry.states.indexOf(state)<0)entry.states.push(state);
1544
1620
  if(entry.room){
1545
- entry.room.remoteParticipants.forEach(function(p){p.trackPublications.forEach(function(pub){if(pub.track&&pub.track.kind==='video'){attachRoomTrack(entry,pub.track);setRoomStatus(entry,'LIVE · secure camera relay');}});});
1621
+ 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,'LIVE · secure camera relay');}});});
1622
+ if(!entry.hasTrack){
1623
+ // Freshly rendered tile joined an already-open but publisher-less room:
1624
+ // re-cover it with the active fallback, or make sure the timer is armed
1625
+ // so 'waiting for camera track' can never be a terminal state.
1626
+ if(entry.fallbackActive)tryFleetFallback(deviceId,entry);
1627
+ else if(!entry.fallbackTimer)armFleetFallback(deviceId,entry);
1628
+ }
1546
1629
  return;
1547
1630
  }
1548
1631
  // A connect or retry is already in flight; it will pick up this element.
@@ -3373,7 +3456,7 @@ footer.cmd{left:var(--global-nav-width);transition:left .24s cubic-bezier(.2,.8,
3373
3456
  // reads the device id back out of the room name, so the device id is the
3374
3457
  // canonical stream identity. Deriving it from the character instead made
3375
3458
  // this grid watch a different room than the one cameras publish into.
3376
- rpCameraStreamIds={};devices=devices.map(function(d){var robot=robots.find(function(r){return String(r.robot_id||r.id)===String(d.id)||String(r.character_id||r.character_preset_id||'').toLowerCase()===String(d.character_id||'').toLowerCase();})||{},streamId=String(d.id).trim().toLowerCase();rpCameraStreamIds[d.id]=streamId;if(!robot.id&&!robot.robot_id)return d;var effectiveStatus=d.active_live_room?'online':(robot.connection_status||robot.status||d.connection_status||d.status);return Object.assign({},d,{status:effectiveStatus,connection_status:effectiveStatus,canonical_robot:robot});});
3459
+ rpCameraStreamIds={};devices=devices.map(function(d){var robot=robots.find(function(r){return String(r.robot_id||r.id)===String(d.id)||String(r.character_id||r.character_preset_id||'').toLowerCase()===String(d.character_id||'').toLowerCase();})||{},liveRoomName=d.active_live_room&&d.active_live_room.room_name?String(d.active_live_room.room_name).trim():'',streamId=liveRoomName?liveRoomName.replace(/^robopark-/,''):String(d.id).trim().toLowerCase();rpCameraStreamIds[d.id]=streamId;if(!robot.id&&!robot.robot_id)return d;var effectiveStatus=d.active_live_room?'online':(robot.connection_status||robot.status||d.connection_status||d.status);return Object.assign({},d,{status:effectiveStatus,connection_status:effectiveStatus,canonical_robot:robot});});
3377
3460
  var preferredDevices={};devices.forEach(function(d){var character=String(d.character_id||d.character_preset_id||'').replace(/_heb$/i,'').trim().toLowerCase(),name=String(d.name||'').replace(/_heb$/i,'').trim().toLowerCase(),key=character||name||String(d.id);var current=preferredDevices[key];if(!current||schedulerDeviceScore(d)>schedulerDeviceScore(current))preferredDevices[key]=d;});
3378
3461
  var cameraDevices=showOffline?devices:Object.keys(preferredDevices).map(function(key){return preferredDevices[key];}).filter(function(d){return String(d.connection_status||d.status||'').toLowerCase()==='online'||schedulerHeartbeatFresh(d);});
3379
3462
  var items=cameraDevices.map(function(d){var r=d.canonical_robot||robots.find(function(x){return String(x.robot_id||x.id)===String(d.id);})||{},s=sessions.find(function(x){return String(x.robot_id||x.device_id)===String(d.id);})||null,mh=d.device_inventory&&d.device_inventory.media_health||{},effectiveStatus=r.connection_status||r.status||d.connection_status||d.status||'enrolled';return {id:d.id,name:d.name||r.name||d.id,status:effectiveStatus,robot:r,device:d,session:s,camera:mh.camera_access!==false,mic:mh.audio_access!==false};});
@@ -453,7 +453,10 @@
453
453
  // Without a deadline a stalled control request hung the join forever: no
454
454
  // error surfaced, so nothing ever retried.
455
455
  if(!request.signal&&window.AbortSignal&&AbortSignal.timeout)request.signal=AbortSignal.timeout(25000);
456
- return fetch('/robopark'+path,request).then(function(response){if(!response.ok)return response.text().then(function(body){var failure=new Error(body||('request '+response.status));failure.status=response.status;throw failure});return response.json()})}
456
+ return fetch('/robopark'+path,request).then(function(response){if(!response.ok)return response.text().then(function(body){// Name the request in the failure. "Method Not Allowed" with no URL sent an
457
+ // operator (and the tooling behind them) hunting through every endpoint the
458
+ // page touches; the failing call should identify itself.
459
+ var failure=new Error((options.method||'GET')+' '+path+' -> '+response.status+(body?': '+body.slice(0,200):''));failure.status=response.status;throw failure});return response.json()})}
457
460
  function append(role,message){message=String(message||'').trim();if(!message)return;var key=role+'|'+message;if(seen[key])return;seen[key]=1;var node=document.createElement('div');node.className='turn '+role;node.textContent=message;el('transcript').appendChild(node);el('transcript').scrollTop=el('transcript').scrollHeight}
458
461
  function management(path,body){if(!session||!eventToken)return Promise.resolve();return api('/api/sessions/'+encodeURIComponent(session)+path,{method:'POST',headers:headers(),body:JSON.stringify(body||{})}).catch(function(){})}
459
462
  function event(stageName,status,message,details){var row=document.createElement('div');row.className='event';row.innerHTML='<span>'+String(stageName||'event').replace(/[<>]/g,'')+'</span><span class="'+String(status||'ok').replace(/[<>]/g,'')+'">'+String(status||'ok').replace(/[<>]/g,'')+'</span><span>'+String(message||'').replace(/[<>]/g,'')+'</span>';var host=el('events');if(host){host.prepend(row);while(host.children.length>9)host.lastElementChild.remove()}return Promise.resolve()}