robopark 3.3.6 → 3.3.7

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.6",
3
+ "version": "3.3.7",
4
4
  "description": "RoboPark fleet control CLI \u2014 scheduler, control UI, and character voice calls. Standalone: no infinicode runtime.",
5
5
  "type": "module",
6
6
  "bin": {
package/scheduler/main.py CHANGED
@@ -3393,6 +3393,75 @@ async def issue_livekit_token(payload: LiveKitTokenRequest,
3393
3393
  identity=payload.identity, expires_at=expires,
3394
3394
  )
3395
3395
 
3396
+ class CameraTokenRequest(BaseModel):
3397
+ robot_id: Optional[str] = None
3398
+ character_id: Optional[str] = None
3399
+
3400
+
3401
+ @app.post("/api/camera-token", response_model=LiveKitTokenResponse)
3402
+ async def issue_camera_token(payload: CameraTokenRequest):
3403
+ """Mint a PUBLISH-ONLY token so a browser can publish its camera.
3404
+
3405
+ The control center's own route signs this from LIVEKIT_* environment
3406
+ variables, which a standalone install does not have — it 503s on every
3407
+ attempt while the join page retries, which is the camera-token flood in the
3408
+ console. The keys live in this database, so sign it here instead.
3409
+ """
3410
+ stream_id = (payload.robot_id or payload.character_id or "").strip()
3411
+ if not re.match(r"^[A-Za-z0-9_\-]{1,64}$", stream_id):
3412
+ raise HTTPException(400, "A character or robot stream identity is required")
3413
+
3414
+ try:
3415
+ url, key, sec = await _lk_config_async()
3416
+ except HTTPException:
3417
+ async with aiosqlite.connect(DB_PATH) as db:
3418
+ async with db.execute(
3419
+ "SELECT url, api_key, api_secret FROM livekit_servers "
3420
+ "WHERE api_key IS NOT NULL AND api_secret IS NOT NULL "
3421
+ "ORDER BY (status = 'online') DESC, created_at ASC LIMIT 1"
3422
+ ) as c:
3423
+ row = await c.fetchone()
3424
+ if not row:
3425
+ raise HTTPException(503, "No LiveKit server is configured on the scheduler")
3426
+ url, key, sec = row[0], row[1], row[2]
3427
+
3428
+ try:
3429
+ from livekit.api import AccessToken, VideoGrants
3430
+ except ImportError:
3431
+ raise HTTPException(503, "livekit-api not installed on scheduler")
3432
+
3433
+ room = f"robopark-{stream_id}"
3434
+ identity = f"voice-vision:{stream_id}:{uuid.uuid4().hex[:8]}"
3435
+ at = (
3436
+ AccessToken(key, sec)
3437
+ .with_identity(identity)
3438
+ .with_name(f"{stream_id} voice + vision")
3439
+ # No character_id in metadata: this participant only publishes a camera,
3440
+ # and letting it advertise a character would allow one link to retag a
3441
+ # live robot so the wrong persona answers.
3442
+ .with_metadata(_json.dumps({
3443
+ "robot_id": payload.robot_id or None,
3444
+ "stream_id": stream_id,
3445
+ "participant_role": "voice_vision",
3446
+ }))
3447
+ .with_ttl(timedelta(hours=2))
3448
+ .with_grants(VideoGrants(
3449
+ room=room,
3450
+ room_join=True,
3451
+ can_publish=True,
3452
+ can_publish_data=True,
3453
+ can_subscribe=False,
3454
+ ))
3455
+ )
3456
+ return LiveKitTokenResponse(
3457
+ url=url,
3458
+ token=at.to_jwt(),
3459
+ room=room,
3460
+ identity=identity,
3461
+ expires_at=datetime.utcnow() + timedelta(hours=2),
3462
+ )
3463
+
3464
+
3396
3465
  class MonitorTokenRequest(BaseModel):
3397
3466
  room_name: str
3398
3467
 
@@ -98,25 +98,74 @@
98
98
  // active conversation, with the cooldown reserved before sending so a burst of
99
99
  // motion cannot queue duplicate greetings.
100
100
  var motionTimer=null,motionCanvas=null,motionPrev=null,motionLastFire=0,motionArmedAt=0,motionMode='listening',motionLastTurn=0,motionDirective='',motionSent={};
101
- var motionThreshold=Math.max(.005,Math.min(1,Number(q.get('motion_threshold'))||.06)),motionCooldown=Math.max(10,Number(q.get('motion_cooldown'))||10)*1000,motionQuiet=45000,motionSample=32;
101
+ // Presence, not just movement. Frame differencing only sees CHANGE, so a
102
+ // visitor standing still reads as zero motion; holding presence for a few
103
+ // seconds after the last movement is what stops a stationary person from
104
+ // being treated as gone and re-greeted the moment they shift their weight.
105
+ var motionLastSeen=0,motionPresent=false,motionAbsentSince=0,motionLastContext=0,motionLastSampleAt=0;
106
+ var motionThreshold=Math.max(.005,Math.min(1,Number(q.get('motion_threshold'))||.06)),
107
+ motionCooldown=Math.max(5,Number(q.get('motion_cooldown'))||20)*1000,
108
+ // Fast sampling so a greeting lands within a fraction of a second of
109
+ // someone arriving, rather than up to a full tick later.
110
+ motionInterval=Math.max(100,Math.min(2000,Number(q.get('motion_interval'))||250)),
111
+ motionHold=Math.max(1000,Number(q.get('motion_hold'))||8000),
112
+ // How long the frame must be empty before a return counts as a NEW
113
+ // visitor. This replaces the old blanket "stay quiet for 45s after any
114
+ // turn", which made motion useless on exactly the long always-on calls
115
+ // it was meant for.
116
+ motionAbsence=Math.max(2000,Number(q.get('motion_absence'))||25000),
117
+ motionContextGap=Math.max(5000,Number(q.get('motion_context_gap'))||20000),
118
+ motionSample=32;
102
119
  function motionLevel(){var source=visionSource();if(!source)return -1;var width=source.videoWidth||source.naturalWidth,height=source.videoHeight||source.naturalHeight;if(!width||!height)return -1;if(!motionCanvas)motionCanvas=document.createElement('canvas');var rows=Math.max(8,Math.round(motionSample*height/width));motionCanvas.width=motionSample;motionCanvas.height=rows;var ctx=motionCanvas.getContext('2d'),data;try{ctx.drawImage(source,0,0,motionSample,rows);data=ctx.getImageData(0,0,motionSample,rows).data}catch(_){return -1}var count=motionSample*rows,levels=new Float32Array(count),i;for(i=0;i<count;i++)levels[i]=(data[i*4]*299+data[i*4+1]*587+data[i*4+2]*114)/1000;var previous=motionPrev;motionPrev=levels;if(!previous||previous.length!==count)return -1;var total=0;for(i=0;i<count;i++)total+=Math.abs(levels[i]-previous[i]);return total/count/255}
120
+ // Silent channel: contextual updates never start a turn, so these are safe to
121
+ // send while the character is mid-sentence. Rate-limited because every one of
122
+ // them is re-sent as prompt context on each subsequent turn.
123
+ function motionContext(now,text){
124
+ if(now-motionLastContext<motionContextGap)return;
125
+ motionLastContext=now;
126
+ try{conversation.sendContextualUpdate(text)}catch(_){}
127
+ }
103
128
  function motionTick(myGeneration){
104
129
  // Checked per tick, not at start, so the always-on toggle takes effect live.
105
130
  if(ending||!conversation||myGeneration!==generation||!alwaysOn)return;
106
131
  var level=motionLevel(),now=Date.now();
107
132
  if(level<0||now<motionArmedAt)return;
108
- if(level<motionThreshold)return;
133
+ // Normalise to change-per-second. The raw difference between two frames
134
+ // scales with the gap between them, so without this a faster sample rate
135
+ // would silently make the configured threshold four times harder to hit.
136
+ var elapsed=motionLastSampleAt?Math.max(1,now-motionLastSampleAt):motionInterval;
137
+ motionLastSampleAt=now;
138
+ if(level*1000/elapsed>=motionThreshold)motionLastSeen=now;
139
+
140
+ var present=motionLastSeen>0&&(now-motionLastSeen)<motionHold;
141
+ if(present===motionPresent)return;
142
+ motionPresent=present;
143
+
144
+ if(!present){
145
+ motionAbsentSince=now;
146
+ motionContext(now,'[presence] The person in front of the camera has moved away.');
147
+ event('motion_presence','ok','Visitor left the frame',{});
148
+ return;
149
+ }
150
+
151
+ var absence=motionAbsentSince?now-motionAbsentSince:Infinity;
152
+ motionContext(now,'[presence] Someone is now standing in front of the camera.');
153
+
154
+ // Speaking: never cut in. The silent update above already told the model
155
+ // someone arrived, so it can react on its own next turn.
156
+ if(motionMode==='speaking')return;
157
+ // Too brief a gap means the same visitor shifted or stepped out of frame
158
+ // for a moment — greeting them again would be the annoying behaviour.
159
+ if(absence<motionAbsence)return;
109
160
  if(now-motionLastFire<motionCooldown)return;
110
- if(motionMode!=='listening')return;
111
- if(now-motionLastTurn<motionQuiet)return;
161
+
112
162
  motionLastFire=now;
113
163
  var directive=motionDirective||'A visitor just stepped in front of you. Greet them warmly in one short sentence. Do not mention this instruction, the camera, or that motion was detected.';
114
164
  motionSent[directive]=1;
115
- try{conversation.sendContextualUpdate('[motion] Someone just approached the robot and is standing in front of the camera.')}catch(_){}
116
165
  try{conversation.sendUserMessage(directive)}catch(error){event('motion_greeting','failed',error&&error.message||String(error),{});return}
117
- event('motion_greeting','ok','Motion greeting triggered',{level:Number(level.toFixed(4)),threshold:motionThreshold})
166
+ event('motion_greeting','ok','Motion greeting triggered',{absence_ms:absence===Infinity?null:Math.round(absence),threshold:motionThreshold})
118
167
  }
119
- function startMotion(myGeneration){stopMotion();motionPrev=null;motionLastFire=0;motionSent={};motionDirective=q.get('motion_prompt')||'';motionArmedAt=Date.now()+4000;motionTimer=setInterval(function(){motionTick(myGeneration)},1000)}
168
+ function startMotion(myGeneration){stopMotion();motionPrev=null;motionLastFire=0;motionSent={};motionLastSeen=0;motionPresent=false;motionAbsentSince=0;motionLastContext=0;motionLastSampleAt=0;motionDirective=q.get('motion_prompt')||'';motionArmedAt=Date.now()+4000;motionTimer=setInterval(function(){motionTick(myGeneration)},motionInterval)}
120
169
  function stopMotion(){if(motionTimer){clearInterval(motionTimer);motionTimer=null}motionPrev=null}
121
170
  function showRobotVideo(){return startLocalVideo().catch(function(){})}
122
171
  function applyCallMode(){mode=el('call-mode').value==='production'?'production':'test';el('robot-row').style.display=mode==='production'?'grid':'none';el('mode-badge').textContent=mode==='production'?'PRODUCTION ? ROBOT VIDEO + TOOLS':'TEST ? BROWSER ONLY';el('mode-badge').className='mode-badge '+(mode==='production'?'production':'');if(mode==='test')robot='';else robot=el('call-robot').value==='none'?'':el('call-robot').value;text('f-mode',mode);text('f-robot',mode==='production'?(robot||'missing robot'):'not requested');showRobotVideo()}
@@ -1 +1 @@
1
- 3hp0A6CE4VTmuh3NC6Bep
1
+ 7hosI6Tk4PpkcjmN3-s_F
@@ -39,12 +39,12 @@
39
39
  "static/chunks/main-app-f2b2a716b0f3924b.js",
40
40
  "static/chunks/app/api/models/route-0952b44bda5bfb6d.js"
41
41
  ],
42
- "/api/prewarm/route": [
42
+ "/api/prompt/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-f2b2a716b0f3924b.js",
47
- "static/chunks/app/api/prewarm/route-0952b44bda5bfb6d.js"
47
+ "static/chunks/app/api/prompt/route-0952b44bda5bfb6d.js"
48
48
  ],
49
49
  "/api/reload-tools/route": [
50
50
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -53,12 +53,12 @@
53
53
  "static/chunks/main-app-f2b2a716b0f3924b.js",
54
54
  "static/chunks/app/api/reload-tools/route-0952b44bda5bfb6d.js"
55
55
  ],
56
- "/api/prompt/route": [
56
+ "/api/prewarm/route": [
57
57
  "static/chunks/webpack-5252e5254da079b5.js",
58
58
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
59
59
  "static/chunks/1255-404bb8de49c90395.js",
60
60
  "static/chunks/main-app-f2b2a716b0f3924b.js",
61
- "static/chunks/app/api/prompt/route-0952b44bda5bfb6d.js"
61
+ "static/chunks/app/api/prewarm/route-0952b44bda5bfb6d.js"
62
62
  ],
63
63
  "/api/robopark/devices/[deviceId]/control/route": [
64
64
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -67,19 +67,19 @@
67
67
  "static/chunks/main-app-f2b2a716b0f3924b.js",
68
68
  "static/chunks/app/api/robopark/devices/[deviceId]/control/route-0952b44bda5bfb6d.js"
69
69
  ],
70
- "/api/robopark/robots/[robotId]/operations/route": [
70
+ "/api/robopark/sessions/[sessionId]/end/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-f2b2a716b0f3924b.js",
75
- "static/chunks/app/api/robopark/robots/[robotId]/operations/route-0952b44bda5bfb6d.js"
75
+ "static/chunks/app/api/robopark/sessions/[sessionId]/end/route-0952b44bda5bfb6d.js"
76
76
  ],
77
- "/api/robopark/sessions/[sessionId]/end/route": [
77
+ "/api/robopark/robots/[robotId]/operations/route": [
78
78
  "static/chunks/webpack-5252e5254da079b5.js",
79
79
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
80
80
  "static/chunks/1255-404bb8de49c90395.js",
81
81
  "static/chunks/main-app-f2b2a716b0f3924b.js",
82
- "static/chunks/app/api/robopark/sessions/[sessionId]/end/route-0952b44bda5bfb6d.js"
82
+ "static/chunks/app/api/robopark/robots/[robotId]/operations/route-0952b44bda5bfb6d.js"
83
83
  ],
84
84
  "/api/robopark/transcripts/[sessionId]/route": [
85
85
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -88,47 +88,47 @@
88
88
  "static/chunks/main-app-f2b2a716b0f3924b.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-f2b2a716b0f3924b.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-n8n/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-f2b2a716b0f3924b.js",
103
- "static/chunks/app/api/setup/status/route-0952b44bda5bfb6d.js"
103
+ "static/chunks/app/api/setup/test-n8n/route-0952b44bda5bfb6d.js"
104
104
  ],
105
- "/api/setup/test-groq/route": [
105
+ "/api/setup/complete/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-f2b2a716b0f3924b.js",
110
- "static/chunks/app/api/setup/test-groq/route-0952b44bda5bfb6d.js"
110
+ "static/chunks/app/api/setup/complete/route-0952b44bda5bfb6d.js"
111
111
  ],
112
- "/api/setup/test-hass/route": [
112
+ "/api/setup/test-ollama/route": [
113
113
  "static/chunks/webpack-5252e5254da079b5.js",
114
114
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
115
115
  "static/chunks/1255-404bb8de49c90395.js",
116
116
  "static/chunks/main-app-f2b2a716b0f3924b.js",
117
- "static/chunks/app/api/setup/test-hass/route-0952b44bda5bfb6d.js"
117
+ "static/chunks/app/api/setup/test-ollama/route-0952b44bda5bfb6d.js"
118
118
  ],
119
- "/api/setup/test-ollama/route": [
119
+ "/api/setup/status/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-f2b2a716b0f3924b.js",
124
- "static/chunks/app/api/setup/test-ollama/route-0952b44bda5bfb6d.js"
124
+ "static/chunks/app/api/setup/status/route-0952b44bda5bfb6d.js"
125
125
  ],
126
- "/api/setup/test-n8n/route": [
126
+ "/api/setup/test-hass/route": [
127
127
  "static/chunks/webpack-5252e5254da079b5.js",
128
128
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
129
129
  "static/chunks/1255-404bb8de49c90395.js",
130
130
  "static/chunks/main-app-f2b2a716b0f3924b.js",
131
- "static/chunks/app/api/setup/test-n8n/route-0952b44bda5bfb6d.js"
131
+ "static/chunks/app/api/setup/test-hass/route-0952b44bda5bfb6d.js"
132
132
  ],
133
133
  "/api/test-tts/route": [
134
134
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -137,26 +137,26 @@
137
137
  "static/chunks/main-app-f2b2a716b0f3924b.js",
138
138
  "static/chunks/app/api/test-tts/route-0952b44bda5bfb6d.js"
139
139
  ],
140
- "/api/voices/route": [
140
+ "/api/wake-word/status/route": [
141
141
  "static/chunks/webpack-5252e5254da079b5.js",
142
142
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
143
143
  "static/chunks/1255-404bb8de49c90395.js",
144
144
  "static/chunks/main-app-f2b2a716b0f3924b.js",
145
- "static/chunks/app/api/voices/route-0952b44bda5bfb6d.js"
145
+ "static/chunks/app/api/wake-word/status/route-0952b44bda5bfb6d.js"
146
146
  ],
147
- "/api/wake-word/models/route": [
147
+ "/api/voices/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-f2b2a716b0f3924b.js",
152
- "static/chunks/app/api/wake-word/models/route-0952b44bda5bfb6d.js"
152
+ "static/chunks/app/api/voices/route-0952b44bda5bfb6d.js"
153
153
  ],
154
- "/api/wake-word/status/route": [
154
+ "/api/wake-word/models/route": [
155
155
  "static/chunks/webpack-5252e5254da079b5.js",
156
156
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
157
157
  "static/chunks/1255-404bb8de49c90395.js",
158
158
  "static/chunks/main-app-f2b2a716b0f3924b.js",
159
- "static/chunks/app/api/wake-word/status/route-0952b44bda5bfb6d.js"
159
+ "static/chunks/app/api/wake-word/models/route-0952b44bda5bfb6d.js"
160
160
  ],
161
161
  "/api/wake-word/upload/route": [
162
162
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -200,13 +200,6 @@
200
200
  "static/chunks/main-app-f2b2a716b0f3924b.js",
201
201
  "static/chunks/app/api/robopark/character-ai/route-0952b44bda5bfb6d.js"
202
202
  ],
203
- "/api/robopark/live-rooms/route": [
204
- "static/chunks/webpack-5252e5254da079b5.js",
205
- "static/chunks/4bd1b696-f785427dddbba9fb.js",
206
- "static/chunks/1255-404bb8de49c90395.js",
207
- "static/chunks/main-app-f2b2a716b0f3924b.js",
208
- "static/chunks/app/api/robopark/live-rooms/route-0952b44bda5bfb6d.js"
209
- ],
210
203
  "/api/robopark/device-access/route": [
211
204
  "static/chunks/webpack-5252e5254da079b5.js",
212
205
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
@@ -214,12 +207,12 @@
214
207
  "static/chunks/main-app-f2b2a716b0f3924b.js",
215
208
  "static/chunks/app/api/robopark/device-access/route-0952b44bda5bfb6d.js"
216
209
  ],
217
- "/api/robopark/monitor-token/route": [
210
+ "/api/robopark/live-rooms/route": [
218
211
  "static/chunks/webpack-5252e5254da079b5.js",
219
212
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
220
213
  "static/chunks/1255-404bb8de49c90395.js",
221
214
  "static/chunks/main-app-f2b2a716b0f3924b.js",
222
- "static/chunks/app/api/robopark/monitor-token/route-0952b44bda5bfb6d.js"
215
+ "static/chunks/app/api/robopark/live-rooms/route-0952b44bda5bfb6d.js"
223
216
  ],
224
217
  "/api/robopark/robots/route": [
225
218
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -228,26 +221,26 @@
228
221
  "static/chunks/main-app-f2b2a716b0f3924b.js",
229
222
  "static/chunks/app/api/robopark/robots/route-0952b44bda5bfb6d.js"
230
223
  ],
231
- "/api/robopark/servers/route": [
224
+ "/api/robopark/servers/[serverId]/metrics/route": [
232
225
  "static/chunks/webpack-5252e5254da079b5.js",
233
226
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
234
227
  "static/chunks/1255-404bb8de49c90395.js",
235
228
  "static/chunks/main-app-f2b2a716b0f3924b.js",
236
- "static/chunks/app/api/robopark/servers/route-0952b44bda5bfb6d.js"
229
+ "static/chunks/app/api/robopark/servers/[serverId]/metrics/route-0952b44bda5bfb6d.js"
237
230
  ],
238
- "/api/robopark/servers/[serverId]/route": [
231
+ "/api/robopark/monitor-token/route": [
239
232
  "static/chunks/webpack-5252e5254da079b5.js",
240
233
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
241
234
  "static/chunks/1255-404bb8de49c90395.js",
242
235
  "static/chunks/main-app-f2b2a716b0f3924b.js",
243
- "static/chunks/app/api/robopark/servers/[serverId]/route-0952b44bda5bfb6d.js"
236
+ "static/chunks/app/api/robopark/monitor-token/route-0952b44bda5bfb6d.js"
244
237
  ],
245
- "/api/robopark/servers/[serverId]/metrics/route": [
238
+ "/api/robopark/servers/[serverId]/route": [
246
239
  "static/chunks/webpack-5252e5254da079b5.js",
247
240
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
248
241
  "static/chunks/1255-404bb8de49c90395.js",
249
242
  "static/chunks/main-app-f2b2a716b0f3924b.js",
250
- "static/chunks/app/api/robopark/servers/[serverId]/metrics/route-0952b44bda5bfb6d.js"
243
+ "static/chunks/app/api/robopark/servers/[serverId]/route-0952b44bda5bfb6d.js"
251
244
  ],
252
245
  "/api/robopark/sessions/route": [
253
246
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -256,12 +249,12 @@
256
249
  "static/chunks/main-app-f2b2a716b0f3924b.js",
257
250
  "static/chunks/app/api/robopark/sessions/route-0952b44bda5bfb6d.js"
258
251
  ],
259
- "/api/robopark/vision-caption/route": [
252
+ "/api/robopark/servers/route": [
260
253
  "static/chunks/webpack-5252e5254da079b5.js",
261
254
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
262
255
  "static/chunks/1255-404bb8de49c90395.js",
263
256
  "static/chunks/main-app-f2b2a716b0f3924b.js",
264
- "static/chunks/app/api/robopark/vision-caption/route-0952b44bda5bfb6d.js"
257
+ "static/chunks/app/api/robopark/servers/route-0952b44bda5bfb6d.js"
265
258
  ],
266
259
  "/api/robovoice/robots/[robotId]/media-health/route": [
267
260
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -277,19 +270,19 @@
277
270
  "static/chunks/main-app-f2b2a716b0f3924b.js",
278
271
  "static/chunks/app/api/robopark/robots/[robotId]/voice-engine/route-0952b44bda5bfb6d.js"
279
272
  ],
280
- "/api/settings/route": [
273
+ "/api/robopark/vision-caption/route": [
281
274
  "static/chunks/webpack-5252e5254da079b5.js",
282
275
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
283
276
  "static/chunks/1255-404bb8de49c90395.js",
284
277
  "static/chunks/main-app-f2b2a716b0f3924b.js",
285
- "static/chunks/app/api/settings/route-0952b44bda5bfb6d.js"
278
+ "static/chunks/app/api/robopark/vision-caption/route-0952b44bda5bfb6d.js"
286
279
  ],
287
- "/api/robopark/transcripts/route": [
280
+ "/api/settings/route": [
288
281
  "static/chunks/webpack-5252e5254da079b5.js",
289
282
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
290
283
  "static/chunks/1255-404bb8de49c90395.js",
291
284
  "static/chunks/main-app-f2b2a716b0f3924b.js",
292
- "static/chunks/app/api/robopark/transcripts/route-0952b44bda5bfb6d.js"
285
+ "static/chunks/app/api/settings/route-0952b44bda5bfb6d.js"
293
286
  ],
294
287
  "/api/turn-credentials/route": [
295
288
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -298,13 +291,6 @@
298
291
  "static/chunks/main-app-f2b2a716b0f3924b.js",
299
292
  "static/chunks/app/api/turn-credentials/route-0952b44bda5bfb6d.js"
300
293
  ],
301
- "/camera-grid/route": [
302
- "static/chunks/webpack-5252e5254da079b5.js",
303
- "static/chunks/4bd1b696-f785427dddbba9fb.js",
304
- "static/chunks/1255-404bb8de49c90395.js",
305
- "static/chunks/main-app-f2b2a716b0f3924b.js",
306
- "static/chunks/app/camera-grid/route-0952b44bda5bfb6d.js"
307
- ],
308
294
  "/api/wake/route": [
309
295
  "static/chunks/webpack-5252e5254da079b5.js",
310
296
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
@@ -312,12 +298,12 @@
312
298
  "static/chunks/main-app-f2b2a716b0f3924b.js",
313
299
  "static/chunks/app/api/wake/route-0952b44bda5bfb6d.js"
314
300
  ],
315
- "/tailnet/route": [
301
+ "/camera-grid/route": [
316
302
  "static/chunks/webpack-5252e5254da079b5.js",
317
303
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
318
304
  "static/chunks/1255-404bb8de49c90395.js",
319
305
  "static/chunks/main-app-f2b2a716b0f3924b.js",
320
- "static/chunks/app/tailnet/route-0952b44bda5bfb6d.js"
306
+ "static/chunks/app/camera-grid/route-0952b44bda5bfb6d.js"
321
307
  ],
322
308
  "/fed/media/robots/[robotId]/video_feed/route": [
323
309
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -333,6 +319,13 @@
333
319
  "static/chunks/main-app-f2b2a716b0f3924b.js",
334
320
  "static/chunks/app/robopark/[...path]/route-0952b44bda5bfb6d.js"
335
321
  ],
322
+ "/api/robopark/transcripts/route": [
323
+ "static/chunks/webpack-5252e5254da079b5.js",
324
+ "static/chunks/4bd1b696-f785427dddbba9fb.js",
325
+ "static/chunks/1255-404bb8de49c90395.js",
326
+ "static/chunks/main-app-f2b2a716b0f3924b.js",
327
+ "static/chunks/app/api/robopark/transcripts/route-0952b44bda5bfb6d.js"
328
+ ],
336
329
  "/fed/[...path]/route": [
337
330
  "static/chunks/webpack-5252e5254da079b5.js",
338
331
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
@@ -340,12 +333,19 @@
340
333
  "static/chunks/main-app-f2b2a716b0f3924b.js",
341
334
  "static/chunks/app/fed/[...path]/route-0952b44bda5bfb6d.js"
342
335
  ],
343
- "/vendor/livekit-client.umd.js/route": [
336
+ "/tailnet/route": [
344
337
  "static/chunks/webpack-5252e5254da079b5.js",
345
338
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
346
339
  "static/chunks/1255-404bb8de49c90395.js",
347
340
  "static/chunks/main-app-f2b2a716b0f3924b.js",
348
- "static/chunks/app/vendor/livekit-client.umd.js/route-0952b44bda5bfb6d.js"
341
+ "static/chunks/app/tailnet/route-0952b44bda5bfb6d.js"
342
+ ],
343
+ "/(app)/opengraph-image-xg4ifa/route": [
344
+ "static/chunks/webpack-5252e5254da079b5.js",
345
+ "static/chunks/4bd1b696-f785427dddbba9fb.js",
346
+ "static/chunks/1255-404bb8de49c90395.js",
347
+ "static/chunks/main-app-f2b2a716b0f3924b.js",
348
+ "static/chunks/app/(app)/opengraph-image-xg4ifa/route-0952b44bda5bfb6d.js"
349
349
  ],
350
350
  "/robopark/api/robots/[robotId]/stream/route": [
351
351
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -354,12 +354,12 @@
354
354
  "static/chunks/main-app-f2b2a716b0f3924b.js",
355
355
  "static/chunks/app/robopark/api/robots/[robotId]/stream/route-0952b44bda5bfb6d.js"
356
356
  ],
357
- "/(app)/opengraph-image-xg4ifa/route": [
357
+ "/vendor/livekit-client.umd.js/route": [
358
358
  "static/chunks/webpack-5252e5254da079b5.js",
359
359
  "static/chunks/4bd1b696-f785427dddbba9fb.js",
360
360
  "static/chunks/1255-404bb8de49c90395.js",
361
361
  "static/chunks/main-app-f2b2a716b0f3924b.js",
362
- "static/chunks/app/(app)/opengraph-image-xg4ifa/route-0952b44bda5bfb6d.js"
362
+ "static/chunks/app/vendor/livekit-client.umd.js/route-0952b44bda5bfb6d.js"
363
363
  ],
364
364
  "/robopark/api/[...path]/route": [
365
365
  "static/chunks/webpack-5252e5254da079b5.js",
@@ -3,23 +3,23 @@
3
3
  "/api/download-piper-model/route": "/api/download-piper-model",
4
4
  "/api/device-character/route": "/api/device-character",
5
5
  "/api/models/route": "/api/models",
6
- "/api/prewarm/route": "/api/prewarm",
7
- "/api/reload-tools/route": "/api/reload-tools",
8
6
  "/api/prompt/route": "/api/prompt",
7
+ "/api/reload-tools/route": "/api/reload-tools",
8
+ "/api/prewarm/route": "/api/prewarm",
9
9
  "/api/robopark/devices/[deviceId]/control/route": "/api/robopark/devices/[deviceId]/control",
10
- "/api/robopark/robots/[robotId]/operations/route": "/api/robopark/robots/[robotId]/operations",
11
10
  "/api/robopark/sessions/[sessionId]/end/route": "/api/robopark/sessions/[sessionId]/end",
11
+ "/api/robopark/robots/[robotId]/operations/route": "/api/robopark/robots/[robotId]/operations",
12
12
  "/api/robopark/transcripts/[sessionId]/route": "/api/robopark/transcripts/[sessionId]",
13
+ "/api/setup/test-groq/route": "/api/setup/test-groq",
14
+ "/api/setup/test-n8n/route": "/api/setup/test-n8n",
13
15
  "/api/setup/complete/route": "/api/setup/complete",
16
+ "/api/setup/test-ollama/route": "/api/setup/test-ollama",
14
17
  "/api/setup/status/route": "/api/setup/status",
15
- "/api/setup/test-groq/route": "/api/setup/test-groq",
16
18
  "/api/setup/test-hass/route": "/api/setup/test-hass",
17
- "/api/setup/test-ollama/route": "/api/setup/test-ollama",
18
- "/api/setup/test-n8n/route": "/api/setup/test-n8n",
19
19
  "/api/test-tts/route": "/api/test-tts",
20
+ "/api/wake-word/status/route": "/api/wake-word/status",
20
21
  "/api/voices/route": "/api/voices",
21
22
  "/api/wake-word/models/route": "/api/wake-word/models",
22
- "/api/wake-word/status/route": "/api/wake-word/status",
23
23
  "/api/wake-word/upload/route": "/api/wake-word/upload",
24
24
  "/favicon.ico/route": "/favicon.ico",
25
25
  "/api/connection-details/route": "/api/connection-details",
@@ -27,29 +27,29 @@
27
27
  "/api/model-status/route": "/api/model-status",
28
28
  "/api/robopark/camera-token/route": "/api/robopark/camera-token",
29
29
  "/api/robopark/character-ai/route": "/api/robopark/character-ai",
30
- "/api/robopark/live-rooms/route": "/api/robopark/live-rooms",
31
30
  "/api/robopark/device-access/route": "/api/robopark/device-access",
32
- "/api/robopark/monitor-token/route": "/api/robopark/monitor-token",
31
+ "/api/robopark/live-rooms/route": "/api/robopark/live-rooms",
33
32
  "/api/robopark/robots/route": "/api/robopark/robots",
34
- "/api/robopark/servers/route": "/api/robopark/servers",
35
- "/api/robopark/servers/[serverId]/route": "/api/robopark/servers/[serverId]",
36
33
  "/api/robopark/servers/[serverId]/metrics/route": "/api/robopark/servers/[serverId]/metrics",
34
+ "/api/robopark/monitor-token/route": "/api/robopark/monitor-token",
35
+ "/api/robopark/servers/[serverId]/route": "/api/robopark/servers/[serverId]",
37
36
  "/api/robopark/sessions/route": "/api/robopark/sessions",
38
- "/api/robopark/vision-caption/route": "/api/robopark/vision-caption",
37
+ "/api/robopark/servers/route": "/api/robopark/servers",
39
38
  "/api/robovoice/robots/[robotId]/media-health/route": "/api/robovoice/robots/[robotId]/media-health",
40
39
  "/api/robopark/robots/[robotId]/voice-engine/route": "/api/robopark/robots/[robotId]/voice-engine",
40
+ "/api/robopark/vision-caption/route": "/api/robopark/vision-caption",
41
41
  "/api/settings/route": "/api/settings",
42
- "/api/robopark/transcripts/route": "/api/robopark/transcripts",
43
42
  "/api/turn-credentials/route": "/api/turn-credentials",
44
- "/camera-grid/route": "/camera-grid",
45
43
  "/api/wake/route": "/api/wake",
46
- "/tailnet/route": "/tailnet",
44
+ "/camera-grid/route": "/camera-grid",
47
45
  "/fed/media/robots/[robotId]/video_feed/route": "/fed/media/robots/[robotId]/video_feed",
48
46
  "/robopark/[...path]/route": "/robopark/[...path]",
47
+ "/api/robopark/transcripts/route": "/api/robopark/transcripts",
49
48
  "/fed/[...path]/route": "/fed/[...path]",
50
- "/vendor/livekit-client.umd.js/route": "/vendor/livekit-client.umd.js",
51
- "/robopark/api/robots/[robotId]/stream/route": "/robopark/api/robots/[robotId]/stream",
49
+ "/tailnet/route": "/tailnet",
52
50
  "/(app)/opengraph-image-xg4ifa/route": "/opengraph-image-xg4ifa",
51
+ "/robopark/api/robots/[robotId]/stream/route": "/robopark/api/robots/[robotId]/stream",
52
+ "/vendor/livekit-client.umd.js/route": "/vendor/livekit-client.umd.js",
53
53
  "/robopark/api/[...path]/route": "/robopark/api/[...path]",
54
54
  "/robopark/page": "/robopark",
55
55
  "/voice-tester/page": "/voice-tester",
@@ -5,8 +5,8 @@
5
5
  "devFiles": [],
6
6
  "ampDevFiles": [],
7
7
  "lowPriorityFiles": [
8
- "static/3hp0A6CE4VTmuh3NC6Bep/_buildManifest.js",
9
- "static/3hp0A6CE4VTmuh3NC6Bep/_ssgManifest.js"
8
+ "static/7hosI6Tk4PpkcjmN3-s_F/_buildManifest.js",
9
+ "static/7hosI6Tk4PpkcjmN3-s_F/_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": "c25b75a645b28445866a623413e3943d",
38
- "previewModeSigningKey": "e017ef9b86df311d1b60c6f0e3b0d37c1daa5f1bf2992a44ea0fb32d7330516f",
39
- "previewModeEncryptionKey": "1b287e85dce803ece9794e4beb47944cf34b24bd72862376b3ca9da453db1ba9"
37
+ "previewModeId": "8ef7ac94feff5fe2c565e9c8f257ccb8",
38
+ "previewModeSigningKey": "2314133b4228543e7a1922ffbe261bf4583fa957fb4bf228a4a99be1960ec055",
39
+ "previewModeEncryptionKey": "d820aa2dc678f79fb2b28d92d0bd07846f88b6fb326fb4fc1afd2d873552154a"
40
40
  }
41
41
  }
@@ -1 +1 @@
1
- (()=>{var a={};a.id=6848,a.ids=[6848],a.modules={261:a=>{"use strict";a.exports=require("next/dist/shared/lib/router/utils/app-paths")},3295:a=>{"use strict";a.exports=require("next/dist/server/app-render/after-task-async-storage.external.js")},4573:a=>{"use strict";a.exports=require("node:buffer")},10846:a=>{"use strict";a.exports=require("next/dist/compiled/next-server/app-page.runtime.prod.js")},19121:a=>{"use strict";a.exports=require("next/dist/server/app-render/action-async-storage.external.js")},24008:(a,b,c)=>{"use strict";function d(a,b){let c=a.device_registry||{},d=String(b.robotId||"").trim(),g=String(b.characterId||"").trim();for(let a of[d,g])if(a&&c[a])return f(a);for(let a of[d,g]){let b=e(a);if(!b)continue;let d=Object.entries(c).find(([,a])=>e(a?.character_id)===b);if(d)return f(d[0])}return f(d||g)}function e(a){return String(a||"").trim().toLowerCase().replace(/_heb$/,"")}function f(a){return a.trim().toLowerCase().replace(/[^a-z0-9_-]+/g,"-").replace(/^-+|-+$/g,"")}c.d(b,{_1:()=>d})},29294:a=>{"use strict";a.exports=require("next/dist/server/app-render/work-async-storage.external.js")},44870:a=>{"use strict";a.exports=require("next/dist/compiled/next-server/app-route.runtime.prod.js")},51455:a=>{"use strict";a.exports=require("node:fs/promises")},57975:a=>{"use strict";a.exports=require("node:util")},63033:a=>{"use strict";a.exports=require("next/dist/server/app-render/work-unit-async-storage.external.js")},76760:a=>{"use strict";a.exports=require("node:path")},77598:a=>{"use strict";a.exports=require("node:crypto")},78335:()=>{},85086:(a,b,c)=>{"use strict";c.r(b),c.d(b,{handler:()=>F,patchFetch:()=>E,routeModule:()=>A,serverHooks:()=>D,workAsyncStorage:()=>B,workUnitAsyncStorage:()=>C});var d={};c.r(d),c.d(d,{POST:()=>z,dynamic:()=>y});var e=c(95736),f=c(9117),g=c(4044),h=c(39326),i=c(32324),j=c(261),k=c(54290),l=c(85328),m=c(38928),n=c(46595),o=c(3421),p=c(17679),q=c(41681),r=c(63446),s=c(86439),t=c(51356),u=c(58755),v=c(10641),w=c(99435),x=c(24008);let y="force-dynamic";async function z(a){let b=await a.json().catch(()=>({})),c=await (0,w.U)(),d=(0,x._1)(c,{robotId:b.robot_id,characterId:b.character_id});if(!d)return v.NextResponse.json({error:"A character or robot stream identity is required"},{status:400});let e=process.env.LIVEKIT_API_KEY,f=process.env.LIVEKIT_API_SECRET;if(!e||!f)return v.NextResponse.json({error:"LiveKit credentials are unavailable"},{status:503});let g=`robopark-${d}`,h=new u.mk(e,f,{identity:`voice-vision:${d}:${crypto.randomUUID().slice(0,8)}`,name:`${String(b.character_id||d)} voice + vision PC`,metadata:JSON.stringify({robot_id:b.robot_id||null,stream_id:d,participant_role:"voice_vision"}),ttl:"2h"});return h.addGrant({room:g,roomJoin:!0,canPublish:!0,canPublishData:!0,canSubscribe:!1}),v.NextResponse.json({url:function(a){let b=new URL(a.url),c=(a.headers.get("x-forwarded-host")||a.headers.get("host")||b.host).split(",")[0].trim().split(":")[0];if(c.endsWith(".ts.net"))return`wss://${c}:3443`;let d=process.env.LIVEKIT_PUBLIC_URL||process.env.NEXT_PUBLIC_LIVEKIT_URL;return d&&"auto"!==d?d:`${"https:"===b.protocol?"wss":"ws"}://${b.host}`}(a),token:await h.toJwt(),room:g,stream_id:d},{headers:{"cache-control":"no-store"}})}let A=new e.AppRouteRouteModule({definition:{kind:f.RouteKind.APP_ROUTE,page:"/api/robopark/camera-token/route",pathname:"/api/robopark/camera-token",filename:"route",bundlePath:"app/api/robopark/camera-token/route"},distDir:".next",relativeProjectDir:"",resolvedPagePath:"C:\\Users\\yunge\\AppData\\Local\\Temp\\rp-uibuild\\frontend\\app\\api\\robopark\\camera-token\\route.ts",nextConfigOutput:"standalone",userland:d}),{workAsyncStorage:B,workUnitAsyncStorage:C,serverHooks:D}=A;function E(){return(0,g.patchFetch)({workAsyncStorage:B,workUnitAsyncStorage:C})}async function F(a,b,c){var d;let e="/api/robopark/camera-token/route";"/index"===e&&(e="/");let g=await A.prepare(a,b,{srcPage:e,multiZoneDraftMode:!1});if(!g)return b.statusCode=400,b.end("Bad Request"),null==c.waitUntil||c.waitUntil.call(c,Promise.resolve()),null;let{buildId:u,params:v,nextConfig:w,isDraftMode:x,prerenderManifest:y,routerServerContext:z,isOnDemandRevalidate:B,revalidateOnlyGenerated:C,resolvedPathname:D}=g,E=(0,j.normalizeAppPath)(e),F=!!(y.dynamicRoutes[E]||y.routes[D]);if(F&&!x){let a=!!y.routes[D],b=y.dynamicRoutes[E];if(b&&!1===b.fallback&&!a)throw new s.NoFallbackError}let G=null;!F||A.isDev||x||(G="/index"===(G=D)?"/":G);let H=!0===A.isDev||!F,I=F&&!H,J=a.method||"GET",K=(0,i.getTracer)(),L=K.getActiveScopeSpan(),M={params:v,prerenderManifest:y,renderOpts:{experimental:{cacheComponents:!!w.experimental.cacheComponents,authInterrupts:!!w.experimental.authInterrupts},supportsDynamicResponse:H,incrementalCache:(0,h.getRequestMeta)(a,"incrementalCache"),cacheLifeProfiles:null==(d=w.experimental)?void 0:d.cacheLife,isRevalidate:I,waitUntil:c.waitUntil,onClose:a=>{b.on("close",a)},onAfterTaskError:void 0,onInstrumentationRequestError:(b,c,d)=>A.onRequestError(a,b,d,z)},sharedContext:{buildId:u}},N=new k.NodeNextRequest(a),O=new k.NodeNextResponse(b),P=l.NextRequestAdapter.fromNodeNextRequest(N,(0,l.signalFromNodeResponse)(b));try{let d=async c=>A.handle(P,M).finally(()=>{if(!c)return;c.setAttributes({"http.status_code":b.statusCode,"next.rsc":!1});let d=K.getRootSpanAttributes();if(!d)return;if(d.get("next.span_type")!==m.BaseServerSpan.handleRequest)return void console.warn(`Unexpected root span type '${d.get("next.span_type")}'. Please report this Next.js issue https://github.com/vercel/next.js`);let e=d.get("next.route");if(e){let a=`${J} ${e}`;c.setAttributes({"next.route":e,"http.route":e,"next.span_name":a}),c.updateName(a)}else c.updateName(`${J} ${a.url}`)}),g=async g=>{var i,j;let k=async({previousCacheEntry:f})=>{try{if(!(0,h.getRequestMeta)(a,"minimalMode")&&B&&C&&!f)return b.statusCode=404,b.setHeader("x-nextjs-cache","REVALIDATED"),b.end("This page could not be found"),null;let e=await d(g);a.fetchMetrics=M.renderOpts.fetchMetrics;let i=M.renderOpts.pendingWaitUntil;i&&c.waitUntil&&(c.waitUntil(i),i=void 0);let j=M.renderOpts.collectedTags;if(!F)return await (0,o.I)(N,O,e,M.renderOpts.pendingWaitUntil),null;{let a=await e.blob(),b=(0,p.toNodeOutgoingHttpHeaders)(e.headers);j&&(b[r.NEXT_CACHE_TAGS_HEADER]=j),!b["content-type"]&&a.type&&(b["content-type"]=a.type);let c=void 0!==M.renderOpts.collectedRevalidate&&!(M.renderOpts.collectedRevalidate>=r.INFINITE_CACHE)&&M.renderOpts.collectedRevalidate,d=void 0===M.renderOpts.collectedExpire||M.renderOpts.collectedExpire>=r.INFINITE_CACHE?void 0:M.renderOpts.collectedExpire;return{value:{kind:t.CachedRouteKind.APP_ROUTE,status:e.status,body:Buffer.from(await a.arrayBuffer()),headers:b},cacheControl:{revalidate:c,expire:d}}}}catch(b){throw(null==f?void 0:f.isStale)&&await A.onRequestError(a,b,{routerKind:"App Router",routePath:e,routeType:"route",revalidateReason:(0,n.c)({isRevalidate:I,isOnDemandRevalidate:B})},z),b}},l=await A.handleResponse({req:a,nextConfig:w,cacheKey:G,routeKind:f.RouteKind.APP_ROUTE,isFallback:!1,prerenderManifest:y,isRoutePPREnabled:!1,isOnDemandRevalidate:B,revalidateOnlyGenerated:C,responseGenerator:k,waitUntil:c.waitUntil});if(!F)return null;if((null==l||null==(i=l.value)?void 0:i.kind)!==t.CachedRouteKind.APP_ROUTE)throw Object.defineProperty(Error(`Invariant: app-route received invalid cache entry ${null==l||null==(j=l.value)?void 0:j.kind}`),"__NEXT_ERROR_CODE",{value:"E701",enumerable:!1,configurable:!0});(0,h.getRequestMeta)(a,"minimalMode")||b.setHeader("x-nextjs-cache",B?"REVALIDATED":l.isMiss?"MISS":l.isStale?"STALE":"HIT"),x&&b.setHeader("Cache-Control","private, no-cache, no-store, max-age=0, must-revalidate");let m=(0,p.fromNodeOutgoingHttpHeaders)(l.value.headers);return(0,h.getRequestMeta)(a,"minimalMode")&&F||m.delete(r.NEXT_CACHE_TAGS_HEADER),!l.cacheControl||b.getHeader("Cache-Control")||m.get("Cache-Control")||m.set("Cache-Control",(0,q.getCacheControlHeader)(l.cacheControl)),await (0,o.I)(N,O,new Response(l.value.body,{headers:m,status:l.value.status||200})),null};L?await g(L):await K.withPropagatedContext(a.headers,()=>K.trace(m.BaseServerSpan.handleRequest,{spanName:`${J} ${a.url}`,kind:i.SpanKind.SERVER,attributes:{"http.method":J,"http.target":a.url}},g))}catch(b){if(b instanceof s.NoFallbackError||await A.onRequestError(a,b,{routerKind:"App Router",routePath:E,routeType:"route",revalidateReason:(0,n.c)({isRevalidate:I,isOnDemandRevalidate:B})}),F)throw b;return await (0,o.I)(N,O,new Response(null,{status:500})),null}}},86439:a=>{"use strict";a.exports=require("next/dist/shared/lib/no-fallback-error.external")},96487:()=>{},99435:(a,b,c)=>{"use strict";c.d(b,{U:()=>l});var d=c(51455),e=c(76760);let f=(process.env.WEBHOOK_URL||"http://agent:8889").replace(/\/+$/,""),g=null,h=0,i=null;function j(a,b){return{...a,characters:Array.isArray(a.characters)?a.characters:[],device_registry:a.device_registry&&"object"==typeof a.device_registry?a.device_registry:{},registry_source:b}}async function k(){for(let a of[process.env.CAAL_SETTINGS_PATH,process.env.ROBOVOICE_SETTINGS_PATH,(0,e.resolve)(process.cwd(),"..","settings.json"),(0,e.resolve)(process.cwd(),"settings.json")].filter(a=>!!a))try{let b=JSON.parse(await (0,d.readFile)(a,"utf8"));return j(b,"persisted-snapshot")}catch{}return j({},"empty")}async function l(){if(g&&Date.now()<h)return g;if(i)return i;i=(async()=>{try{let a=await fetch(`${f}/settings`,{cache:"no-store",signal:AbortSignal.timeout(1500)});if(!a.ok)throw Error(`Settings registry returned ${a.status}`);let b=await a.json();return g=null,h=0,j(b.settings||b,"live-agent")}catch{return g=await k(),h=Date.now()+15e3,g}})();try{return await i}finally{i=null}}}};var b=require("../../../../webpack-runtime.js");b.C(a);var c=b.X(0,[5873,1428,1692,8755],()=>b(b.s=85086));module.exports=c})();
1
+ (()=>{var a={};a.id=6848,a.ids=[6848],a.modules={261:a=>{"use strict";a.exports=require("next/dist/shared/lib/router/utils/app-paths")},3295:a=>{"use strict";a.exports=require("next/dist/server/app-render/after-task-async-storage.external.js")},4573:a=>{"use strict";a.exports=require("node:buffer")},10846:a=>{"use strict";a.exports=require("next/dist/compiled/next-server/app-page.runtime.prod.js")},19121:a=>{"use strict";a.exports=require("next/dist/server/app-render/action-async-storage.external.js")},24008:(a,b,c)=>{"use strict";function d(a,b){let c=a.device_registry||{},d=String(b.robotId||"").trim(),g=String(b.characterId||"").trim();for(let a of[d,g])if(a&&c[a])return f(a);for(let a of[d,g]){let b=e(a);if(!b)continue;let d=Object.entries(c).find(([,a])=>e(a?.character_id)===b);if(d)return f(d[0])}return f(d||g)}function e(a){return String(a||"").trim().toLowerCase().replace(/_heb$/,"")}function f(a){return a.trim().toLowerCase().replace(/[^a-z0-9_-]+/g,"-").replace(/^-+|-+$/g,"")}c.d(b,{_1:()=>d})},29294:a=>{"use strict";a.exports=require("next/dist/server/app-render/work-async-storage.external.js")},44870:a=>{"use strict";a.exports=require("next/dist/compiled/next-server/app-route.runtime.prod.js")},51455:a=>{"use strict";a.exports=require("node:fs/promises")},57975:a=>{"use strict";a.exports=require("node:util")},63033:a=>{"use strict";a.exports=require("next/dist/server/app-render/work-unit-async-storage.external.js")},76760:a=>{"use strict";a.exports=require("node:path")},77598:a=>{"use strict";a.exports=require("node:crypto")},78335:()=>{},85086:(a,b,c)=>{"use strict";c.r(b),c.d(b,{handler:()=>H,patchFetch:()=>G,routeModule:()=>C,serverHooks:()=>F,workAsyncStorage:()=>D,workUnitAsyncStorage:()=>E});var d={};c.r(d),c.d(d,{POST:()=>B,dynamic:()=>y});var e=c(95736),f=c(9117),g=c(4044),h=c(39326),i=c(32324),j=c(261),k=c(54290),l=c(85328),m=c(38928),n=c(46595),o=c(3421),p=c(17679),q=c(41681),r=c(63446),s=c(86439),t=c(51356),u=c(58755),v=c(10641),w=c(99435),x=c(24008);let y="force-dynamic",z=(process.env.ROBOPARK_SCHEDULER_URL||"http://host.docker.internal:8080").replace(/\/+$/,"");async function A(a){try{let b=await fetch(`${z}/api/camera-token`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({robot_id:a.robot_id??null,character_id:a.character_id??null}),cache:"no-store",signal:AbortSignal.timeout(8e3)}),c=await b.json().catch(()=>null);if(!b.ok||!c?.token)return v.NextResponse.json({error:c?.detail||`Scheduler returned ${b.status}`},{status:200===b.status?502:b.status});return v.NextResponse.json({url:c.url,token:c.token,room:c.room,stream_id:c.room?.replace(/^robopark-/,"")},{headers:{"cache-control":"no-store"}})}catch(b){let a=b instanceof Error?b.message:"unknown error";return v.NextResponse.json({error:`Scheduler at ${z} is unreachable (${a}).`},{status:503})}}async function B(a){let b=await a.json().catch(()=>({})),c=await (0,w.U)(),d=(0,x._1)(c,{robotId:b.robot_id,characterId:b.character_id});if(!d)return v.NextResponse.json({error:"A character or robot stream identity is required"},{status:400});let e=process.env.LIVEKIT_API_KEY,f=process.env.LIVEKIT_API_SECRET;if(!e||!f)return A({robot_id:b.robot_id,character_id:b.character_id});let g=`robopark-${d}`,h=new u.mk(e,f,{identity:`voice-vision:${d}:${crypto.randomUUID().slice(0,8)}`,name:`${String(b.character_id||d)} voice + vision PC`,metadata:JSON.stringify({robot_id:b.robot_id||null,stream_id:d,participant_role:"voice_vision"}),ttl:"2h"});return h.addGrant({room:g,roomJoin:!0,canPublish:!0,canPublishData:!0,canSubscribe:!1}),v.NextResponse.json({url:function(a){let b=new URL(a.url),c=(a.headers.get("x-forwarded-host")||a.headers.get("host")||b.host).split(",")[0].trim().split(":")[0];if(c.endsWith(".ts.net"))return`wss://${c}:3443`;let d=process.env.LIVEKIT_PUBLIC_URL||process.env.NEXT_PUBLIC_LIVEKIT_URL;return d&&"auto"!==d?d:`${"https:"===b.protocol?"wss":"ws"}://${b.host}`}(a),token:await h.toJwt(),room:g,stream_id:d},{headers:{"cache-control":"no-store"}})}let C=new e.AppRouteRouteModule({definition:{kind:f.RouteKind.APP_ROUTE,page:"/api/robopark/camera-token/route",pathname:"/api/robopark/camera-token",filename:"route",bundlePath:"app/api/robopark/camera-token/route"},distDir:".next",relativeProjectDir:"",resolvedPagePath:"C:\\Users\\yunge\\AppData\\Local\\Temp\\rp-uibuild\\frontend\\app\\api\\robopark\\camera-token\\route.ts",nextConfigOutput:"standalone",userland:d}),{workAsyncStorage:D,workUnitAsyncStorage:E,serverHooks:F}=C;function G(){return(0,g.patchFetch)({workAsyncStorage:D,workUnitAsyncStorage:E})}async function H(a,b,c){var d;let e="/api/robopark/camera-token/route";"/index"===e&&(e="/");let g=await C.prepare(a,b,{srcPage:e,multiZoneDraftMode:!1});if(!g)return b.statusCode=400,b.end("Bad Request"),null==c.waitUntil||c.waitUntil.call(c,Promise.resolve()),null;let{buildId:u,params:v,nextConfig:w,isDraftMode:x,prerenderManifest:y,routerServerContext:z,isOnDemandRevalidate:A,revalidateOnlyGenerated:B,resolvedPathname:D}=g,E=(0,j.normalizeAppPath)(e),F=!!(y.dynamicRoutes[E]||y.routes[D]);if(F&&!x){let a=!!y.routes[D],b=y.dynamicRoutes[E];if(b&&!1===b.fallback&&!a)throw new s.NoFallbackError}let G=null;!F||C.isDev||x||(G="/index"===(G=D)?"/":G);let H=!0===C.isDev||!F,I=F&&!H,J=a.method||"GET",K=(0,i.getTracer)(),L=K.getActiveScopeSpan(),M={params:v,prerenderManifest:y,renderOpts:{experimental:{cacheComponents:!!w.experimental.cacheComponents,authInterrupts:!!w.experimental.authInterrupts},supportsDynamicResponse:H,incrementalCache:(0,h.getRequestMeta)(a,"incrementalCache"),cacheLifeProfiles:null==(d=w.experimental)?void 0:d.cacheLife,isRevalidate:I,waitUntil:c.waitUntil,onClose:a=>{b.on("close",a)},onAfterTaskError:void 0,onInstrumentationRequestError:(b,c,d)=>C.onRequestError(a,b,d,z)},sharedContext:{buildId:u}},N=new k.NodeNextRequest(a),O=new k.NodeNextResponse(b),P=l.NextRequestAdapter.fromNodeNextRequest(N,(0,l.signalFromNodeResponse)(b));try{let d=async c=>C.handle(P,M).finally(()=>{if(!c)return;c.setAttributes({"http.status_code":b.statusCode,"next.rsc":!1});let d=K.getRootSpanAttributes();if(!d)return;if(d.get("next.span_type")!==m.BaseServerSpan.handleRequest)return void console.warn(`Unexpected root span type '${d.get("next.span_type")}'. Please report this Next.js issue https://github.com/vercel/next.js`);let e=d.get("next.route");if(e){let a=`${J} ${e}`;c.setAttributes({"next.route":e,"http.route":e,"next.span_name":a}),c.updateName(a)}else c.updateName(`${J} ${a.url}`)}),g=async g=>{var i,j;let k=async({previousCacheEntry:f})=>{try{if(!(0,h.getRequestMeta)(a,"minimalMode")&&A&&B&&!f)return b.statusCode=404,b.setHeader("x-nextjs-cache","REVALIDATED"),b.end("This page could not be found"),null;let e=await d(g);a.fetchMetrics=M.renderOpts.fetchMetrics;let i=M.renderOpts.pendingWaitUntil;i&&c.waitUntil&&(c.waitUntil(i),i=void 0);let j=M.renderOpts.collectedTags;if(!F)return await (0,o.I)(N,O,e,M.renderOpts.pendingWaitUntil),null;{let a=await e.blob(),b=(0,p.toNodeOutgoingHttpHeaders)(e.headers);j&&(b[r.NEXT_CACHE_TAGS_HEADER]=j),!b["content-type"]&&a.type&&(b["content-type"]=a.type);let c=void 0!==M.renderOpts.collectedRevalidate&&!(M.renderOpts.collectedRevalidate>=r.INFINITE_CACHE)&&M.renderOpts.collectedRevalidate,d=void 0===M.renderOpts.collectedExpire||M.renderOpts.collectedExpire>=r.INFINITE_CACHE?void 0:M.renderOpts.collectedExpire;return{value:{kind:t.CachedRouteKind.APP_ROUTE,status:e.status,body:Buffer.from(await a.arrayBuffer()),headers:b},cacheControl:{revalidate:c,expire:d}}}}catch(b){throw(null==f?void 0:f.isStale)&&await C.onRequestError(a,b,{routerKind:"App Router",routePath:e,routeType:"route",revalidateReason:(0,n.c)({isRevalidate:I,isOnDemandRevalidate:A})},z),b}},l=await C.handleResponse({req:a,nextConfig:w,cacheKey:G,routeKind:f.RouteKind.APP_ROUTE,isFallback:!1,prerenderManifest:y,isRoutePPREnabled:!1,isOnDemandRevalidate:A,revalidateOnlyGenerated:B,responseGenerator:k,waitUntil:c.waitUntil});if(!F)return null;if((null==l||null==(i=l.value)?void 0:i.kind)!==t.CachedRouteKind.APP_ROUTE)throw Object.defineProperty(Error(`Invariant: app-route received invalid cache entry ${null==l||null==(j=l.value)?void 0:j.kind}`),"__NEXT_ERROR_CODE",{value:"E701",enumerable:!1,configurable:!0});(0,h.getRequestMeta)(a,"minimalMode")||b.setHeader("x-nextjs-cache",A?"REVALIDATED":l.isMiss?"MISS":l.isStale?"STALE":"HIT"),x&&b.setHeader("Cache-Control","private, no-cache, no-store, max-age=0, must-revalidate");let m=(0,p.fromNodeOutgoingHttpHeaders)(l.value.headers);return(0,h.getRequestMeta)(a,"minimalMode")&&F||m.delete(r.NEXT_CACHE_TAGS_HEADER),!l.cacheControl||b.getHeader("Cache-Control")||m.get("Cache-Control")||m.set("Cache-Control",(0,q.getCacheControlHeader)(l.cacheControl)),await (0,o.I)(N,O,new Response(l.value.body,{headers:m,status:l.value.status||200})),null};L?await g(L):await K.withPropagatedContext(a.headers,()=>K.trace(m.BaseServerSpan.handleRequest,{spanName:`${J} ${a.url}`,kind:i.SpanKind.SERVER,attributes:{"http.method":J,"http.target":a.url}},g))}catch(b){if(b instanceof s.NoFallbackError||await C.onRequestError(a,b,{routerKind:"App Router",routePath:E,routeType:"route",revalidateReason:(0,n.c)({isRevalidate:I,isOnDemandRevalidate:A})}),F)throw b;return await (0,o.I)(N,O,new Response(null,{status:500})),null}}},86439:a=>{"use strict";a.exports=require("next/dist/shared/lib/no-fallback-error.external")},96487:()=>{},99435:(a,b,c)=>{"use strict";c.d(b,{U:()=>l});var d=c(51455),e=c(76760);let f=(process.env.WEBHOOK_URL||"http://agent:8889").replace(/\/+$/,""),g=null,h=0,i=null;function j(a,b){return{...a,characters:Array.isArray(a.characters)?a.characters:[],device_registry:a.device_registry&&"object"==typeof a.device_registry?a.device_registry:{},registry_source:b}}async function k(){for(let a of[process.env.CAAL_SETTINGS_PATH,process.env.ROBOVOICE_SETTINGS_PATH,(0,e.resolve)(process.cwd(),"..","settings.json"),(0,e.resolve)(process.cwd(),"settings.json")].filter(a=>!!a))try{let b=JSON.parse(await (0,d.readFile)(a,"utf8"));return j(b,"persisted-snapshot")}catch{}return j({},"empty")}async function l(){if(g&&Date.now()<h)return g;if(i)return i;i=(async()=>{try{let a=await fetch(`${f}/settings`,{cache:"no-store",signal:AbortSignal.timeout(1500)});if(!a.ok)throw Error(`Settings registry returned ${a.status}`);let b=await a.json();return g=null,h=0,j(b.settings||b,"live-agent")}catch{return g=await k(),h=Date.now()+15e3,g}})();try{return await i}finally{i=null}}}};var b=require("../../../../webpack-runtime.js");b.C(a);var c=b.X(0,[5873,1428,1692,8755],()=>b(b.s=85086));module.exports=c})();
@@ -3,23 +3,23 @@
3
3
  "/api/download-piper-model/route": "app/api/download-piper-model/route.js",
4
4
  "/api/device-character/route": "app/api/device-character/route.js",
5
5
  "/api/models/route": "app/api/models/route.js",
6
- "/api/prewarm/route": "app/api/prewarm/route.js",
7
- "/api/reload-tools/route": "app/api/reload-tools/route.js",
8
6
  "/api/prompt/route": "app/api/prompt/route.js",
7
+ "/api/reload-tools/route": "app/api/reload-tools/route.js",
8
+ "/api/prewarm/route": "app/api/prewarm/route.js",
9
9
  "/api/robopark/devices/[deviceId]/control/route": "app/api/robopark/devices/[deviceId]/control/route.js",
10
- "/api/robopark/robots/[robotId]/operations/route": "app/api/robopark/robots/[robotId]/operations/route.js",
11
10
  "/api/robopark/sessions/[sessionId]/end/route": "app/api/robopark/sessions/[sessionId]/end/route.js",
11
+ "/api/robopark/robots/[robotId]/operations/route": "app/api/robopark/robots/[robotId]/operations/route.js",
12
12
  "/api/robopark/transcripts/[sessionId]/route": "app/api/robopark/transcripts/[sessionId]/route.js",
13
+ "/api/setup/test-groq/route": "app/api/setup/test-groq/route.js",
14
+ "/api/setup/test-n8n/route": "app/api/setup/test-n8n/route.js",
13
15
  "/api/setup/complete/route": "app/api/setup/complete/route.js",
16
+ "/api/setup/test-ollama/route": "app/api/setup/test-ollama/route.js",
14
17
  "/api/setup/status/route": "app/api/setup/status/route.js",
15
- "/api/setup/test-groq/route": "app/api/setup/test-groq/route.js",
16
18
  "/api/setup/test-hass/route": "app/api/setup/test-hass/route.js",
17
- "/api/setup/test-ollama/route": "app/api/setup/test-ollama/route.js",
18
- "/api/setup/test-n8n/route": "app/api/setup/test-n8n/route.js",
19
19
  "/api/test-tts/route": "app/api/test-tts/route.js",
20
+ "/api/wake-word/status/route": "app/api/wake-word/status/route.js",
20
21
  "/api/voices/route": "app/api/voices/route.js",
21
22
  "/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
23
  "/api/wake-word/upload/route": "app/api/wake-word/upload/route.js",
24
24
  "/favicon.ico/route": "app/favicon.ico/route.js",
25
25
  "/api/connection-details/route": "app/api/connection-details/route.js",
@@ -27,29 +27,29 @@
27
27
  "/api/model-status/route": "app/api/model-status/route.js",
28
28
  "/api/robopark/camera-token/route": "app/api/robopark/camera-token/route.js",
29
29
  "/api/robopark/character-ai/route": "app/api/robopark/character-ai/route.js",
30
- "/api/robopark/live-rooms/route": "app/api/robopark/live-rooms/route.js",
31
30
  "/api/robopark/device-access/route": "app/api/robopark/device-access/route.js",
32
- "/api/robopark/monitor-token/route": "app/api/robopark/monitor-token/route.js",
31
+ "/api/robopark/live-rooms/route": "app/api/robopark/live-rooms/route.js",
33
32
  "/api/robopark/robots/route": "app/api/robopark/robots/route.js",
34
- "/api/robopark/servers/route": "app/api/robopark/servers/route.js",
35
- "/api/robopark/servers/[serverId]/route": "app/api/robopark/servers/[serverId]/route.js",
36
33
  "/api/robopark/servers/[serverId]/metrics/route": "app/api/robopark/servers/[serverId]/metrics/route.js",
34
+ "/api/robopark/monitor-token/route": "app/api/robopark/monitor-token/route.js",
35
+ "/api/robopark/servers/[serverId]/route": "app/api/robopark/servers/[serverId]/route.js",
37
36
  "/api/robopark/sessions/route": "app/api/robopark/sessions/route.js",
38
- "/api/robopark/vision-caption/route": "app/api/robopark/vision-caption/route.js",
37
+ "/api/robopark/servers/route": "app/api/robopark/servers/route.js",
39
38
  "/api/robovoice/robots/[robotId]/media-health/route": "app/api/robovoice/robots/[robotId]/media-health/route.js",
40
39
  "/api/robopark/robots/[robotId]/voice-engine/route": "app/api/robopark/robots/[robotId]/voice-engine/route.js",
40
+ "/api/robopark/vision-caption/route": "app/api/robopark/vision-caption/route.js",
41
41
  "/api/settings/route": "app/api/settings/route.js",
42
- "/api/robopark/transcripts/route": "app/api/robopark/transcripts/route.js",
43
42
  "/api/turn-credentials/route": "app/api/turn-credentials/route.js",
44
- "/camera-grid/route": "app/camera-grid/route.js",
45
43
  "/api/wake/route": "app/api/wake/route.js",
46
- "/tailnet/route": "app/tailnet/route.js",
44
+ "/camera-grid/route": "app/camera-grid/route.js",
47
45
  "/fed/media/robots/[robotId]/video_feed/route": "app/fed/media/robots/[robotId]/video_feed/route.js",
48
46
  "/robopark/[...path]/route": "app/robopark/[...path]/route.js",
47
+ "/api/robopark/transcripts/route": "app/api/robopark/transcripts/route.js",
49
48
  "/fed/[...path]/route": "app/fed/[...path]/route.js",
50
- "/vendor/livekit-client.umd.js/route": "app/vendor/livekit-client.umd.js/route.js",
51
- "/robopark/api/robots/[robotId]/stream/route": "app/robopark/api/robots/[robotId]/stream/route.js",
49
+ "/tailnet/route": "app/tailnet/route.js",
52
50
  "/(app)/opengraph-image-xg4ifa/route": "app/(app)/opengraph-image-xg4ifa/route.js",
51
+ "/robopark/api/robots/[robotId]/stream/route": "app/robopark/api/robots/[robotId]/stream/route.js",
52
+ "/vendor/livekit-client.umd.js/route": "app/vendor/livekit-client.umd.js/route.js",
53
53
  "/robopark/api/[...path]/route": "app/robopark/api/[...path]/route.js",
54
54
  "/robopark/page": "app/robopark/page.js",
55
55
  "/voice-tester/page": "app/voice-tester/page.js",
@@ -3,8 +3,8 @@
3
3
  "functions": {
4
4
  "/api/kiosk-hardware": {},
5
5
  "/api/robovoice/robots/[robotId]/media-health": {},
6
- "/fed/media/robots/[robotId]/video_feed": {},
7
6
  "/robopark/[...path]": {},
7
+ "/fed/media/robots/[robotId]/video_feed": {},
8
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/3hp0A6CE4VTmuh3NC6Bep/_buildManifest.js" defer=""></script><script src="/_next/static/3hp0A6CE4VTmuh3NC6Bep/_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":"3hp0A6CE4VTmuh3NC6Bep","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/7hosI6Tk4PpkcjmN3-s_F/_buildManifest.js" defer=""></script><script src="/_next/static/7hosI6Tk4PpkcjmN3-s_F/_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":"7hosI6Tk4PpkcjmN3-s_F","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}</script></body></html>
@@ -1 +1 @@
1
- {"node":{},"edge":{},"encryptionKey":"3+k2CZUYOQqorUdwyE4Pz5/F0l3devnJl7ZUqeJEngE="}
1
+ {"node":{},"edge":{},"encryptionKey":"oMzmrICjd1miwxs/vfayAYdzjnbywYaPFjZAUCXe3Eo="}
@@ -97,25 +97,74 @@
97
97
  // active conversation, with the cooldown reserved before sending so a burst of
98
98
  // motion cannot queue duplicate greetings.
99
99
  var motionTimer=null,motionCanvas=null,motionPrev=null,motionLastFire=0,motionArmedAt=0,motionMode='listening',motionLastTurn=0,motionDirective='',motionSent={};
100
- var motionThreshold=Math.max(.005,Math.min(1,Number(q.get('motion_threshold'))||.06)),motionCooldown=Math.max(10,Number(q.get('motion_cooldown'))||10)*1000,motionQuiet=45000,motionSample=32;
100
+ // Presence, not just movement. Frame differencing only sees CHANGE, so a
101
+ // visitor standing still reads as zero motion; holding presence for a few
102
+ // seconds after the last movement is what stops a stationary person from
103
+ // being treated as gone and re-greeted the moment they shift their weight.
104
+ var motionLastSeen=0,motionPresent=false,motionAbsentSince=0,motionLastContext=0,motionLastSampleAt=0;
105
+ var motionThreshold=Math.max(.005,Math.min(1,Number(q.get('motion_threshold'))||.06)),
106
+ motionCooldown=Math.max(5,Number(q.get('motion_cooldown'))||20)*1000,
107
+ // Fast sampling so a greeting lands within a fraction of a second of
108
+ // someone arriving, rather than up to a full tick later.
109
+ motionInterval=Math.max(100,Math.min(2000,Number(q.get('motion_interval'))||250)),
110
+ motionHold=Math.max(1000,Number(q.get('motion_hold'))||8000),
111
+ // How long the frame must be empty before a return counts as a NEW
112
+ // visitor. This replaces the old blanket "stay quiet for 45s after any
113
+ // turn", which made motion useless on exactly the long always-on calls
114
+ // it was meant for.
115
+ motionAbsence=Math.max(2000,Number(q.get('motion_absence'))||25000),
116
+ motionContextGap=Math.max(5000,Number(q.get('motion_context_gap'))||20000),
117
+ motionSample=32;
101
118
  function motionLevel(){var source=visionSource();if(!source)return -1;var width=source.videoWidth||source.naturalWidth,height=source.videoHeight||source.naturalHeight;if(!width||!height)return -1;if(!motionCanvas)motionCanvas=document.createElement('canvas');var rows=Math.max(8,Math.round(motionSample*height/width));motionCanvas.width=motionSample;motionCanvas.height=rows;var ctx=motionCanvas.getContext('2d'),data;try{ctx.drawImage(source,0,0,motionSample,rows);data=ctx.getImageData(0,0,motionSample,rows).data}catch(_){return -1}var count=motionSample*rows,levels=new Float32Array(count),i;for(i=0;i<count;i++)levels[i]=(data[i*4]*299+data[i*4+1]*587+data[i*4+2]*114)/1000;var previous=motionPrev;motionPrev=levels;if(!previous||previous.length!==count)return -1;var total=0;for(i=0;i<count;i++)total+=Math.abs(levels[i]-previous[i]);return total/count/255}
119
+ // Silent channel: contextual updates never start a turn, so these are safe to
120
+ // send while the character is mid-sentence. Rate-limited because every one of
121
+ // them is re-sent as prompt context on each subsequent turn.
122
+ function motionContext(now,text){
123
+ if(now-motionLastContext<motionContextGap)return;
124
+ motionLastContext=now;
125
+ try{conversation.sendContextualUpdate(text)}catch(_){}
126
+ }
102
127
  function motionTick(myGeneration){
103
128
  // Checked per tick, not at start, so the always-on toggle takes effect live.
104
129
  if(ending||!conversation||myGeneration!==generation||!alwaysOn)return;
105
130
  var level=motionLevel(),now=Date.now();
106
131
  if(level<0||now<motionArmedAt)return;
107
- if(level<motionThreshold)return;
132
+ // Normalise to change-per-second. The raw difference between two frames
133
+ // scales with the gap between them, so without this a faster sample rate
134
+ // would silently make the configured threshold four times harder to hit.
135
+ var elapsed=motionLastSampleAt?Math.max(1,now-motionLastSampleAt):motionInterval;
136
+ motionLastSampleAt=now;
137
+ if(level*1000/elapsed>=motionThreshold)motionLastSeen=now;
138
+
139
+ var present=motionLastSeen>0&&(now-motionLastSeen)<motionHold;
140
+ if(present===motionPresent)return;
141
+ motionPresent=present;
142
+
143
+ if(!present){
144
+ motionAbsentSince=now;
145
+ motionContext(now,'[presence] The person in front of the camera has moved away.');
146
+ event('motion_presence','ok','Visitor left the frame',{});
147
+ return;
148
+ }
149
+
150
+ var absence=motionAbsentSince?now-motionAbsentSince:Infinity;
151
+ motionContext(now,'[presence] Someone is now standing in front of the camera.');
152
+
153
+ // Speaking: never cut in. The silent update above already told the model
154
+ // someone arrived, so it can react on its own next turn.
155
+ if(motionMode==='speaking')return;
156
+ // Too brief a gap means the same visitor shifted or stepped out of frame
157
+ // for a moment — greeting them again would be the annoying behaviour.
158
+ if(absence<motionAbsence)return;
108
159
  if(now-motionLastFire<motionCooldown)return;
109
- if(motionMode!=='listening')return;
110
- if(now-motionLastTurn<motionQuiet)return;
160
+
111
161
  motionLastFire=now;
112
162
  var directive=motionDirective||'A visitor just stepped in front of you. Greet them warmly in one short sentence. Do not mention this instruction, the camera, or that motion was detected.';
113
163
  motionSent[directive]=1;
114
- try{conversation.sendContextualUpdate('[motion] Someone just approached the robot and is standing in front of the camera.')}catch(_){}
115
164
  try{conversation.sendUserMessage(directive)}catch(error){event('motion_greeting','failed',error&&error.message||String(error),{});return}
116
- event('motion_greeting','ok','Motion greeting triggered',{level:Number(level.toFixed(4)),threshold:motionThreshold})
165
+ event('motion_greeting','ok','Motion greeting triggered',{absence_ms:absence===Infinity?null:Math.round(absence),threshold:motionThreshold})
117
166
  }
118
- function startMotion(myGeneration){stopMotion();motionPrev=null;motionLastFire=0;motionSent={};motionDirective=q.get('motion_prompt')||'';motionArmedAt=Date.now()+4000;motionTimer=setInterval(function(){motionTick(myGeneration)},1000)}
167
+ function startMotion(myGeneration){stopMotion();motionPrev=null;motionLastFire=0;motionSent={};motionLastSeen=0;motionPresent=false;motionAbsentSince=0;motionLastContext=0;motionLastSampleAt=0;motionDirective=q.get('motion_prompt')||'';motionArmedAt=Date.now()+4000;motionTimer=setInterval(function(){motionTick(myGeneration)},motionInterval)}
119
168
  function stopMotion(){if(motionTimer){clearInterval(motionTimer);motionTimer=null}motionPrev=null}
120
169
  function showRobotVideo(){return startLocalVideo().catch(function(){})}
121
170
  function applyCallMode(){mode=el('call-mode').value==='production'?'production':'test';el('robot-row').style.display=mode==='production'?'grid':'none';el('mode-badge').textContent=mode==='production'?'PRODUCTION ? ROBOT VIDEO + TOOLS':'TEST ? BROWSER ONLY';el('mode-badge').className='mode-badge '+(mode==='production'?'production':'');if(mode==='test')robot='';else robot=el('call-robot').value==='none'?'':el('call-robot').value;text('f-mode',mode);text('f-robot',mode==='production'?(robot||'missing robot'):'not requested');showRobotVideo()}