robopark 3.2.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -105
- package/bin/robopark.js +9 -2
- package/conversation/elevenlabs_agent.py +1617 -1617
- package/conversation/supervisor_store.py +189 -189
- package/dist/{kernel/config-schema.js → config-schema.js} +1 -0
- package/dist/config-schema.js.map +1 -0
- package/dist/robopark/add-robot.js +1 -0
- package/dist/robopark/add-robot.js.map +1 -0
- package/dist/robopark/agent-ctl.js +1 -0
- package/dist/robopark/agent-ctl.js.map +1 -0
- package/dist/robopark/auto-start.js +51 -73
- package/dist/robopark/auto-start.js.map +1 -0
- package/dist/robopark/conversation.js +1 -0
- package/dist/robopark/conversation.js.map +1 -0
- package/dist/robopark/discovery.js +1 -0
- package/dist/robopark/discovery.js.map +1 -0
- package/dist/robopark/doctor.js +1 -0
- package/dist/robopark/doctor.js.map +1 -0
- package/dist/robopark/enroll.js +1 -0
- package/dist/robopark/enroll.js.map +1 -0
- package/dist/robopark/llm-set.js +1 -0
- package/dist/robopark/llm-set.js.map +1 -0
- package/dist/robopark/motor-control.js +1 -0
- package/dist/robopark/motor-control.js.map +1 -0
- package/dist/robopark/preview-agent-launcher.js +1 -0
- package/dist/robopark/preview-agent-launcher.js.map +1 -0
- package/dist/robopark/probe.js +16 -15
- package/dist/robopark/probe.js.map +1 -0
- package/dist/robopark/profile.js +1 -0
- package/dist/robopark/profile.js.map +1 -0
- package/dist/robopark/python-env.js +1 -7
- package/dist/robopark/python-env.js.map +1 -0
- package/dist/robopark/robot-runtime.js +1 -0
- package/dist/robopark/robot-runtime.js.map +1 -0
- package/dist/robopark/scan.js +1 -0
- package/dist/robopark/scan.js.map +1 -0
- package/dist/robopark/screen-control.js +1 -0
- package/dist/robopark/screen-control.js.map +1 -0
- package/dist/robopark/secrets.js +1 -0
- package/dist/robopark/secrets.js.map +1 -0
- package/dist/robopark/serve.js +6 -8
- package/dist/robopark/serve.js.map +1 -0
- package/dist/robopark/server-add.js +1 -0
- package/dist/robopark/server-add.js.map +1 -0
- package/dist/robopark/setup-livekit.js +53 -52
- package/dist/robopark/setup-livekit.js.map +1 -0
- package/dist/robopark/setup.js +1 -0
- package/dist/robopark/setup.js.map +1 -0
- package/dist/robopark/stop-all.js +1 -0
- package/dist/robopark/stop-all.js.map +1 -0
- package/dist/robopark/verify.js +1 -0
- package/dist/robopark/verify.js.map +1 -0
- package/dist/robopark/vision-agent-launcher.js +1 -0
- package/dist/robopark/vision-agent-launcher.js.map +1 -0
- package/dist/robopark/vision-control.js +1 -0
- package/dist/robopark/vision-control.js.map +1 -0
- package/dist/robopark-cli.js +7 -125
- package/dist/robopark-cli.js.map +1 -0
- package/package.json +16 -56
- package/pi-client/_install_steps.sh +29 -29
- package/pi-client/client.py +76 -76
- package/pi-client/install.sh +40 -40
- package/pi-client/join_convo.sh +54 -54
- package/pi-client/livekit_bridge.py +24 -24
- package/pi-client/motor_bridge.py +10 -10
- package/scheduler/__pycache__/main.cpython-312.pyc +0 -0
- package/scheduler/main.py +196 -17
- package/scheduler/media_lock.py +57 -57
- package/scheduler/preview_agent.py +1122 -1122
- package/scheduler/production_config.json +73 -113
- package/scheduler/scripts/install-robot-supervisor-linux.sh +33 -33
- package/{ui/standalone/public → scheduler/static}/voice-join.html +101 -13
- package/screen/screen_runtime.py +75 -75
- package/vision/app_pi_clean.py +297 -297
- package/vision/audio_server_pi.py +24 -24
- package/vision/install.sh +34 -34
- package/vision/motor_server.py +275 -275
- package/vision/requirements_camera.txt +6 -6
- package/vision/requirements_motor.txt +4 -4
- package/vision/requirements_pi_unified.txt +2 -2
- package/vision/requirements_vision_agent.txt +19 -19
- package/vision/run.sh +244 -244
- package/vision/services/services.sh +12 -12
- package/dist/kernel/types.js +0 -7
- package/dist/robopark/access.js +0 -99
- package/dist/robopark/control-center-html.js +0 -9191
- package/dist/robopark/control-server.js +0 -148
- package/dist/robopark/deployment-commands.js +0 -47
- package/dist/robopark/frontend-service.js +0 -80
- package/dist/robopark/standalone.js +0 -584
- package/dist/robopark/voice-join-html.js +0 -57
- package/scripts/windows-shim-fix.cjs +0 -58
- package/static/robopark/barracuda.png +0 -0
- package/static/robopark/bmw.png +0 -0
- package/static/robopark/jaguar.png +0 -0
- package/static/robopark/magnus.jpg +0 -0
- package/static/robopark/panda.png +0 -0
- package/static/robopark/tesla.png +0 -0
- package/static/robopark/titan.jpg +0 -0
- package/static/robopark/volt.png +0 -0
- package/static/vendor/elevenlabs-client.LICENSE +0 -21
- package/static/vendor/elevenlabs-client.iife.js +0 -24200
- package/ui/ROBOVOICE_BUILD.json +0 -9
- package/ui/standalone/.next/BUILD_ID +0 -1
- package/ui/standalone/.next/app-build-manifest.json +0 -391
- package/ui/standalone/.next/app-path-routes-manifest.json +0 -55
- package/ui/standalone/.next/build-manifest.json +0 -33
- package/ui/standalone/.next/package.json +0 -1
- package/ui/standalone/.next/prerender-manifest.json +0 -41
- package/ui/standalone/.next/react-loadable-manifest.json +0 -14
- package/ui/standalone/.next/required-server-files.json +0 -362
- package/ui/standalone/.next/routes-manifest.json +0 -246
- package/ui/standalone/.next/server/app/(app)/opengraph-image-xg4ifa/route.js +0 -1
- package/ui/standalone/.next/server/app/(app)/opengraph-image-xg4ifa/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/(app)/opengraph-image-xg4ifa/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/(app)/page.js +0 -2
- package/ui/standalone/.next/server/app/(app)/page.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/(app)/page_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/(app)/voice/page.js +0 -12
- package/ui/standalone/.next/server/app/(app)/voice/page.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/(app)/voice/page_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/_not-found/page.js +0 -12
- package/ui/standalone/.next/server/app/_not-found/page.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/connection-details/route.js +0 -1
- package/ui/standalone/.next/server/app/api/connection-details/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/connection-details/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/device-character/route.js +0 -1
- package/ui/standalone/.next/server/app/api/device-character/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/device-character/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/download-piper-model/route.js +0 -1
- package/ui/standalone/.next/server/app/api/download-piper-model/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/download-piper-model/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/model-status/route.js +0 -1
- package/ui/standalone/.next/server/app/api/model-status/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/model-status/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/models/route.js +0 -1
- package/ui/standalone/.next/server/app/api/models/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/models/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/prewarm/route.js +0 -1
- package/ui/standalone/.next/server/app/api/prewarm/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/prewarm/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/prompt/route.js +0 -1
- package/ui/standalone/.next/server/app/api/prompt/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/prompt/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/reload-tools/route.js +0 -1
- package/ui/standalone/.next/server/app/api/reload-tools/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/reload-tools/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/camera-token/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/camera-token/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/camera-token/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/character-ai/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/character-ai/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/character-ai/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/device-access/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/device-access/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/device-access/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/devices/[deviceId]/control/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/devices/[deviceId]/control/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/devices/[deviceId]/control/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/live-rooms/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/live-rooms/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/live-rooms/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/monitor-token/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/monitor-token/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/monitor-token/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/robots/[robotId]/operations/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/robots/[robotId]/operations/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/robots/[robotId]/operations/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/robots/[robotId]/voice-engine/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/robots/[robotId]/voice-engine/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/robots/[robotId]/voice-engine/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/robots/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/robots/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/robots/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/servers/[serverId]/metrics/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/servers/[serverId]/metrics/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/servers/[serverId]/metrics/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/servers/[serverId]/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/servers/[serverId]/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/servers/[serverId]/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/servers/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/servers/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/servers/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/sessions/[sessionId]/end/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/sessions/[sessionId]/end/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/sessions/[sessionId]/end/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/sessions/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/sessions/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/sessions/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/transcripts/[sessionId]/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/transcripts/[sessionId]/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/transcripts/[sessionId]/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/transcripts/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robopark/transcripts/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robopark/transcripts/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/robovoice/robots/[robotId]/media-health/route.js +0 -1
- package/ui/standalone/.next/server/app/api/robovoice/robots/[robotId]/media-health/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/robovoice/robots/[robotId]/media-health/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/settings/route.js +0 -1
- package/ui/standalone/.next/server/app/api/settings/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/settings/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/setup/complete/route.js +0 -1
- package/ui/standalone/.next/server/app/api/setup/complete/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/setup/complete/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/setup/status/route.js +0 -1
- package/ui/standalone/.next/server/app/api/setup/status/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/setup/status/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/setup/test-groq/route.js +0 -1
- package/ui/standalone/.next/server/app/api/setup/test-groq/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/setup/test-groq/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/setup/test-hass/route.js +0 -1
- package/ui/standalone/.next/server/app/api/setup/test-hass/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/setup/test-hass/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/setup/test-n8n/route.js +0 -1
- package/ui/standalone/.next/server/app/api/setup/test-n8n/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/setup/test-n8n/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/setup/test-ollama/route.js +0 -1
- package/ui/standalone/.next/server/app/api/setup/test-ollama/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/setup/test-ollama/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/test-tts/route.js +0 -1
- package/ui/standalone/.next/server/app/api/test-tts/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/test-tts/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/turn-credentials/route.js +0 -1
- package/ui/standalone/.next/server/app/api/turn-credentials/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/turn-credentials/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/voices/route.js +0 -1
- package/ui/standalone/.next/server/app/api/voices/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/voices/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/wake/route.js +0 -1
- package/ui/standalone/.next/server/app/api/wake/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/wake/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/wake-word/models/route.js +0 -1
- package/ui/standalone/.next/server/app/api/wake-word/models/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/wake-word/models/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/wake-word/status/route.js +0 -1
- package/ui/standalone/.next/server/app/api/wake-word/status/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/wake-word/status/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/api/wake-word/upload/route.js +0 -1
- package/ui/standalone/.next/server/app/api/wake-word/upload/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/api/wake-word/upload/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/favicon.ico/route.js +0 -1
- package/ui/standalone/.next/server/app/favicon.ico/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/favicon.ico.body +0 -0
- package/ui/standalone/.next/server/app/favicon.ico.meta +0 -1
- package/ui/standalone/.next/server/app/fed/[...path]/route.js +0 -4
- package/ui/standalone/.next/server/app/fed/[...path]/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/fed/[...path]/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/fed/media/robots/[robotId]/video_feed/route.js +0 -1
- package/ui/standalone/.next/server/app/fed/media/robots/[robotId]/video_feed/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/fed/media/robots/[robotId]/video_feed/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/robopark/[...path]/route.js +0 -1
- package/ui/standalone/.next/server/app/robopark/[...path]/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/robopark/[...path]/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/robopark/api/[...path]/route.js +0 -1
- package/ui/standalone/.next/server/app/robopark/api/[...path]/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/robopark/api/[...path]/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/robopark/api/robots/[robotId]/stream/route.js +0 -1
- package/ui/standalone/.next/server/app/robopark/api/robots/[robotId]/stream/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/robopark/api/robots/[robotId]/stream/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/robopark/page.js +0 -2
- package/ui/standalone/.next/server/app/robopark/page.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/robopark/page_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/tailnet/route.js +0 -1
- package/ui/standalone/.next/server/app/tailnet/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/tailnet/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/vendor/livekit-client.umd.js/route.js +0 -1
- package/ui/standalone/.next/server/app/vendor/livekit-client.umd.js/route.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/vendor/livekit-client.umd.js/route_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app/voice-tester/page.js +0 -2
- package/ui/standalone/.next/server/app/voice-tester/page.js.nft.json +0 -1
- package/ui/standalone/.next/server/app/voice-tester/page_client-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/app-paths-manifest.json +0 -55
- package/ui/standalone/.next/server/chunks/1428.js +0 -1
- package/ui/standalone/.next/server/chunks/1692.js +0 -1
- package/ui/standalone/.next/server/chunks/37.js +0 -1
- package/ui/standalone/.next/server/chunks/4876.js +0 -1
- package/ui/standalone/.next/server/chunks/5232.js +0 -9
- package/ui/standalone/.next/server/chunks/5455.js +0 -11
- package/ui/standalone/.next/server/chunks/5611.js +0 -6
- package/ui/standalone/.next/server/chunks/5873.js +0 -22
- package/ui/standalone/.next/server/chunks/6018.js +0 -1
- package/ui/standalone/.next/server/chunks/8755.js +0 -1
- package/ui/standalone/.next/server/chunks/9077.js +0 -11
- package/ui/standalone/.next/server/functions-config-manifest.json +0 -9
- package/ui/standalone/.next/server/middleware-build-manifest.js +0 -1
- package/ui/standalone/.next/server/middleware-manifest.json +0 -6
- package/ui/standalone/.next/server/middleware-react-loadable-manifest.js +0 -1
- package/ui/standalone/.next/server/next-font-manifest.js +0 -1
- package/ui/standalone/.next/server/next-font-manifest.json +0 -1
- package/ui/standalone/.next/server/pages/500.html +0 -1
- package/ui/standalone/.next/server/pages/_app.js +0 -1
- package/ui/standalone/.next/server/pages/_app.js.nft.json +0 -1
- package/ui/standalone/.next/server/pages/_document.js +0 -1
- package/ui/standalone/.next/server/pages/_document.js.nft.json +0 -1
- package/ui/standalone/.next/server/pages/_error.js +0 -19
- package/ui/standalone/.next/server/pages/_error.js.nft.json +0 -1
- package/ui/standalone/.next/server/pages-manifest.json +0 -5
- package/ui/standalone/.next/server/server-reference-manifest.js +0 -1
- package/ui/standalone/.next/server/server-reference-manifest.json +0 -1
- package/ui/standalone/.next/server/webpack-runtime.js +0 -1
- package/ui/standalone/.next/static/T3eGaQUuZNb6rhEStcouc/_buildManifest.js +0 -1
- package/ui/standalone/.next/static/T3eGaQUuZNb6rhEStcouc/_ssgManifest.js +0 -1
- package/ui/standalone/.next/static/chunks/1255-404bb8de49c90395.js +0 -1
- package/ui/standalone/.next/static/chunks/144d3bae-bd5d150c0cab972e.js +0 -1
- package/ui/standalone/.next/static/chunks/1646.9123ee47220ed70b.js +0 -1
- package/ui/standalone/.next/static/chunks/2333-43c39acb531f7d4e.js +0 -3
- package/ui/standalone/.next/static/chunks/4bd1b696-f785427dddbba9fb.js +0 -1
- package/ui/standalone/.next/static/chunks/5139.c5e46d26064a85db.js +0 -1
- package/ui/standalone/.next/static/chunks/76e8f37e-2ae0e3fe7b44d874.js +0 -1
- package/ui/standalone/.next/static/chunks/9880-53262785284464cc.js +0 -1
- package/ui/standalone/.next/static/chunks/adfb71a7-625c1b5ebd039b6a.js +0 -1
- package/ui/standalone/.next/static/chunks/app/(app)/layout-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/(app)/opengraph-image-xg4ifa/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/(app)/page-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/(app)/voice/page-72d72d9a70d9c1d4.js +0 -1
- package/ui/standalone/.next/static/chunks/app/_not-found/page-cfd7daa82d71540c.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/connection-details/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/device-character/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/download-piper-model/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/model-status/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/models/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/prewarm/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/prompt/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/reload-tools/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/camera-token/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/character-ai/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/device-access/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/devices/[deviceId]/control/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/live-rooms/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/monitor-token/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/robots/[robotId]/operations/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/robots/[robotId]/voice-engine/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/robots/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/servers/[serverId]/metrics/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/servers/[serverId]/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/servers/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/sessions/[sessionId]/end/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/sessions/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/transcripts/[sessionId]/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robopark/transcripts/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/robovoice/robots/[robotId]/media-health/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/settings/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/setup/complete/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/setup/status/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/setup/test-groq/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/setup/test-hass/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/setup/test-n8n/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/setup/test-ollama/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/test-tts/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/turn-credentials/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/voices/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/wake/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/wake-word/models/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/wake-word/status/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/api/wake-word/upload/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/fed/[...path]/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/fed/media/robots/[robotId]/video_feed/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/layout-fcce963da1d8220f.js +0 -1
- package/ui/standalone/.next/static/chunks/app/robopark/[...path]/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/robopark/api/[...path]/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/robopark/api/robots/[robotId]/stream/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/robopark/page-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/tailnet/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/vendor/livekit-client.umd.js/route-5f312b128a0caf75.js +0 -1
- package/ui/standalone/.next/static/chunks/app/voice-tester/page-ae699e048e0eb834.js +0 -1
- package/ui/standalone/.next/static/chunks/framework-e60c938074ff7136.js +0 -1
- package/ui/standalone/.next/static/chunks/main-9e24e8708ff075fc.js +0 -1
- package/ui/standalone/.next/static/chunks/main-app-a1f59168303c7f84.js +0 -1
- package/ui/standalone/.next/static/chunks/pages/_app-131c90850aef965b.js +0 -1
- package/ui/standalone/.next/static/chunks/pages/_error-e4ba546eb376bdf4.js +0 -1
- package/ui/standalone/.next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
- package/ui/standalone/.next/static/chunks/webpack-5252e5254da079b5.js +0 -1
- package/ui/standalone/.next/static/css/3f478a573f9be82e.css +0 -1
- package/ui/standalone/.next/static/css/51560c4af1d5a21f.css +0 -1
- package/ui/standalone/.next/static/media/27fe93087559d480-s.p.otf +0 -0
- package/ui/standalone/.next/static/media/4d3869dd8a56345a-s.p.otf +0 -0
- package/ui/standalone/.next/static/media/63f3bb9ce29e22d5-s.p.otf +0 -0
- package/ui/standalone/.next/static/media/79e1c5d0f2fe9f68-s.p.otf +0 -0
- package/ui/standalone/.next/static/media/8bf7b2ceda89477b-s.p.woff2 +0 -0
- package/ui/standalone/.next/static/media/98baa4a7809fbf72-s.woff2 +0 -0
- package/ui/standalone/.next/static/media/f7650dfaa438c37c-s.woff2 +0 -0
- package/ui/standalone/package.json +0 -57
- package/ui/standalone/public/caal-logo.svg +0 -3
- package/ui/standalone/public/commit-mono-400-regular.woff +0 -0
- package/ui/standalone/public/cwl-logo-round.png +0 -0
- package/ui/standalone/public/everett-light.woff +0 -0
- package/ui/standalone/public/hey_cal.ppn +0 -0
- package/ui/standalone/public/install-robot-kiosk.sh +0 -242
- package/ui/standalone/public/lk-logo-dark.svg +0 -18
- package/ui/standalone/public/lk-logo.svg +0 -18
- package/ui/standalone/public/lk-wordmark.svg +0 -12
- package/ui/standalone/public/opengraph-image-bg.png +0 -0
- package/ui/standalone/public/porcupine_params.pv +0 -0
- package/ui/standalone/public/robopark-control.html +0 -9200
- package/ui/standalone/public/static/robopark/barracuda.png +0 -0
- package/ui/standalone/public/static/robopark/bmw.png +0 -0
- package/ui/standalone/public/static/robopark/jaguar.png +0 -0
- package/ui/standalone/public/static/robopark/magnus.jpg +0 -0
- package/ui/standalone/public/static/robopark/panda.png +0 -0
- package/ui/standalone/public/static/robopark/tesla.png +0 -0
- package/ui/standalone/public/static/robopark/titan.jpg +0 -0
- package/ui/standalone/public/static/robopark/volt.png +0 -0
- package/ui/standalone/public/tailnet/index.html +0 -8995
- package/ui/standalone/public/transcript-analytics.html +0 -72
- package/ui/standalone/public/vendor/livekit-client.umd.js +0 -2
- package/ui/standalone/server.js +0 -38
- /package/{ui/standalone/public → scheduler/static}/vendor/elevenlabs-client.iife.js +0 -0
|
@@ -1,139 +1,99 @@
|
|
|
1
|
-
{
|
|
2
|
-
"schema_version": 1,
|
|
3
|
-
"revision":
|
|
4
|
-
"global": {
|
|
5
|
-
"voice_engine_default": "elevenlabs",
|
|
6
|
-
"robovoice_enabled": true,
|
|
7
|
-
"allow_session_override": true,
|
|
8
|
-
"apply_changes_when_idle": true
|
|
9
|
-
},
|
|
10
|
-
"aliases": {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"name": "BARRACUDA",
|
|
15
|
-
"description": "The Star Guardians speed navigator and tactical pilot.",
|
|
16
|
-
"voice_stack_id": "english-default",
|
|
17
|
-
"default_engine": "elevenlabs",
|
|
18
|
-
"elevenlabs_agent_id": "agent_3001ky0z8wq0fht8xyb0tnkdksdz",
|
|
19
|
-
"elevenlabs_branch_id": null,
|
|
20
|
-
"robovoice_profile_id": "barracuda",
|
|
21
|
-
"system_prompt_revision": 1,
|
|
22
|
-
"nx": 0.18,
|
|
23
|
-
"ny": 0.28,
|
|
24
|
-
"img": "/static/robopark/barracuda.png"
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
"id": "jaguar",
|
|
28
|
-
"name": "JAGUAR",
|
|
29
|
-
"description": "A confident and agile Star Guardians scout.",
|
|
30
|
-
"voice_stack_id": "english-default",
|
|
31
|
-
"default_engine": "elevenlabs",
|
|
32
|
-
"elevenlabs_agent_id": "agent_4201ky0zhwn4fz7bwy9767wt6g3t",
|
|
33
|
-
"elevenlabs_branch_id": null,
|
|
34
|
-
"robovoice_profile_id": "jaguar",
|
|
35
|
-
"system_prompt_revision": 1,
|
|
36
|
-
"nx": 0.34,
|
|
37
|
-
"ny": 0.34,
|
|
38
|
-
"img": "/static/robopark/jaguar.png"
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
"id": "magnus",
|
|
42
|
-
"name": "MAGNUS",
|
|
43
|
-
"description": "The calm leader of the Star Guardians.",
|
|
44
|
-
"voice_stack_id": "english-default",
|
|
45
|
-
"default_engine": "elevenlabs",
|
|
46
|
-
"elevenlabs_agent_id": "agent_5601ky0ymahreyft2xz9xht0dj4v",
|
|
47
|
-
"elevenlabs_branch_id": null,
|
|
48
|
-
"robovoice_profile_id": "magnus",
|
|
49
|
-
"system_prompt_revision": 1,
|
|
50
|
-
"nx": 0.5,
|
|
51
|
-
"ny": 0.24,
|
|
52
|
-
"img": "/static/robopark/magnus.jpg"
|
|
53
|
-
},
|
|
1
|
+
{
|
|
2
|
+
"schema_version": 1,
|
|
3
|
+
"revision": 1,
|
|
4
|
+
"global": {
|
|
5
|
+
"voice_engine_default": "elevenlabs",
|
|
6
|
+
"robovoice_enabled": true,
|
|
7
|
+
"allow_session_override": true,
|
|
8
|
+
"apply_changes_when_idle": true
|
|
9
|
+
},
|
|
10
|
+
"aliases": {
|
|
11
|
+
"panda": "ronin"
|
|
12
|
+
},
|
|
13
|
+
"characters": [
|
|
54
14
|
{
|
|
55
|
-
"id": "
|
|
56
|
-
"name": "
|
|
57
|
-
"description": "The Star Guardians
|
|
15
|
+
"id": "barracuda",
|
|
16
|
+
"name": "BARRACUDA",
|
|
17
|
+
"description": "The Star Guardians speed navigator and tactical pilot.",
|
|
58
18
|
"voice_stack_id": "english-default",
|
|
59
19
|
"default_engine": "elevenlabs",
|
|
60
|
-
"elevenlabs_agent_id": "
|
|
20
|
+
"elevenlabs_agent_id": "agent_3001ky0z8wq0fht8xyb0tnkdksdz",
|
|
61
21
|
"elevenlabs_branch_id": null,
|
|
62
|
-
"robovoice_profile_id": "
|
|
22
|
+
"robovoice_profile_id": "barracuda",
|
|
63
23
|
"system_prompt_revision": 1,
|
|
64
|
-
"nx": 0.
|
|
24
|
+
"nx": 0.18,
|
|
25
|
+
"ny": 0.28,
|
|
26
|
+
"img": "/static/robopark/barracuda.png"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "jaguar",
|
|
30
|
+
"name": "JAGUAR",
|
|
31
|
+
"description": "A confident and agile Star Guardians scout.",
|
|
32
|
+
"voice_stack_id": "english-default",
|
|
33
|
+
"default_engine": "elevenlabs",
|
|
34
|
+
"elevenlabs_agent_id": "agent_4201ky0zhwn4fz7bwy9767wt6g3t",
|
|
35
|
+
"elevenlabs_branch_id": null,
|
|
36
|
+
"robovoice_profile_id": "jaguar",
|
|
37
|
+
"system_prompt_revision": 1,
|
|
38
|
+
"nx": 0.34,
|
|
39
|
+
"ny": 0.34,
|
|
40
|
+
"img": "/static/robopark/jaguar.png"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"id": "magnus",
|
|
44
|
+
"name": "MAGNUS",
|
|
45
|
+
"description": "The calm leader of the Star Guardians.",
|
|
46
|
+
"voice_stack_id": "english-default",
|
|
47
|
+
"default_engine": "elevenlabs",
|
|
48
|
+
"elevenlabs_agent_id": "agent_5601ky0ymahreyft2xz9xht0dj4v",
|
|
49
|
+
"elevenlabs_branch_id": null,
|
|
50
|
+
"robovoice_profile_id": "magnus",
|
|
51
|
+
"system_prompt_revision": 1,
|
|
52
|
+
"nx": 0.5,
|
|
65
53
|
"ny": 0.24,
|
|
66
|
-
"img":
|
|
54
|
+
"img": "/static/robopark/magnus.jpg"
|
|
67
55
|
},
|
|
68
56
|
{
|
|
69
|
-
"id": "
|
|
70
|
-
"name": "
|
|
57
|
+
"id": "ronin",
|
|
58
|
+
"name": "RONIN",
|
|
71
59
|
"description": "The Star Guardians story keeper and patient guide.",
|
|
72
60
|
"voice_stack_id": "english-default",
|
|
73
61
|
"default_engine": "elevenlabs",
|
|
74
62
|
"elevenlabs_agent_id": "agent_4701ky0xkz89f8t993tmg5t8t23r",
|
|
75
63
|
"elevenlabs_branch_id": null,
|
|
76
|
-
"robovoice_profile_id": "
|
|
64
|
+
"robovoice_profile_id": "ronin",
|
|
77
65
|
"system_prompt_revision": 1,
|
|
78
|
-
"nx": 0.
|
|
79
|
-
"ny": 0.
|
|
66
|
+
"nx": 0.66,
|
|
67
|
+
"ny": 0.34,
|
|
80
68
|
"img": "/static/robopark/panda.png"
|
|
81
69
|
},
|
|
82
70
|
{
|
|
83
|
-
"id": "
|
|
84
|
-
"name": "
|
|
85
|
-
"description": "The Star Guardians
|
|
86
|
-
"voice_stack_id": "english-default",
|
|
87
|
-
"default_engine": "elevenlabs",
|
|
88
|
-
"elevenlabs_agent_id": "
|
|
89
|
-
"elevenlabs_branch_id": null,
|
|
90
|
-
"robovoice_profile_id": "
|
|
91
|
-
"system_prompt_revision": 1,
|
|
92
|
-
"nx": 0.
|
|
93
|
-
"ny": 0.
|
|
94
|
-
"img": "/static/robopark/
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
"id": "titan",
|
|
98
|
-
"name": "TITAN",
|
|
99
|
-
"description": "The Star Guardians iron guardian and stability anchor.",
|
|
100
|
-
"voice_stack_id": "english-default",
|
|
101
|
-
"default_engine": "elevenlabs",
|
|
102
|
-
"elevenlabs_agent_id": "agent_2501ky0x8t39fh5ske8tz3148b26",
|
|
103
|
-
"elevenlabs_branch_id": null,
|
|
104
|
-
"robovoice_profile_id": "titan",
|
|
105
|
-
"system_prompt_revision": 1,
|
|
106
|
-
"nx": 0.42,
|
|
107
|
-
"ny": 0.68,
|
|
108
|
-
"img": "/static/robopark/titan.jpg"
|
|
109
|
-
},
|
|
110
|
-
{
|
|
111
|
-
"id": "vixen",
|
|
112
|
-
"name": "VIXEN",
|
|
113
|
-
"description": "The Star Guardians strategist and precision planner.",
|
|
114
|
-
"voice_stack_id": "english-default",
|
|
115
|
-
"default_engine": "elevenlabs",
|
|
116
|
-
"elevenlabs_agent_id": "agent_6701ky0nfavke0ysrpj5v38m1cry",
|
|
117
|
-
"elevenlabs_branch_id": null,
|
|
118
|
-
"robovoice_profile_id": "vixen",
|
|
119
|
-
"system_prompt_revision": 1,
|
|
120
|
-
"nx": 0.58,
|
|
121
|
-
"ny": 0.66,
|
|
122
|
-
"img": "/static/robopark/bmw.png"
|
|
71
|
+
"id": "titan",
|
|
72
|
+
"name": "TITAN",
|
|
73
|
+
"description": "The Star Guardians iron guardian and stability anchor.",
|
|
74
|
+
"voice_stack_id": "english-default",
|
|
75
|
+
"default_engine": "elevenlabs",
|
|
76
|
+
"elevenlabs_agent_id": "agent_2501ky0x8t39fh5ske8tz3148b26",
|
|
77
|
+
"elevenlabs_branch_id": null,
|
|
78
|
+
"robovoice_profile_id": "titan",
|
|
79
|
+
"system_prompt_revision": 1,
|
|
80
|
+
"nx": 0.42,
|
|
81
|
+
"ny": 0.68,
|
|
82
|
+
"img": "/static/robopark/titan.jpg"
|
|
123
83
|
},
|
|
124
84
|
{
|
|
125
|
-
"id": "
|
|
126
|
-
"name": "
|
|
127
|
-
"description": "The Star Guardians
|
|
85
|
+
"id": "vixen",
|
|
86
|
+
"name": "VIXEN",
|
|
87
|
+
"description": "The Star Guardians strategist and precision planner.",
|
|
128
88
|
"voice_stack_id": "english-default",
|
|
129
89
|
"default_engine": "elevenlabs",
|
|
130
|
-
"elevenlabs_agent_id": "
|
|
90
|
+
"elevenlabs_agent_id": "agent_6701ky0nfavke0ysrpj5v38m1cry",
|
|
131
91
|
"elevenlabs_branch_id": null,
|
|
132
|
-
"robovoice_profile_id": "
|
|
92
|
+
"robovoice_profile_id": "vixen",
|
|
133
93
|
"system_prompt_revision": 1,
|
|
134
|
-
"nx": 0.
|
|
135
|
-
"ny": 0.
|
|
136
|
-
"img": "/static/robopark/
|
|
94
|
+
"nx": 0.58,
|
|
95
|
+
"ny": 0.66,
|
|
96
|
+
"img": "/static/robopark/bmw.png"
|
|
137
97
|
}
|
|
138
98
|
]
|
|
139
99
|
}
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Installs the RoboPark robot supervisor as a systemd service on a real
|
|
3
|
-
# robot (Raspberry Pi or similar). Auto-starts on boot, restarts on crash.
|
|
4
|
-
set -euo pipefail
|
|
5
|
-
|
|
6
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
|
-
SUPERVISOR_DIR="$(dirname "$SCRIPT_DIR")"
|
|
8
|
-
UNIT_SRC="$SCRIPT_DIR/robopark-supervisor.service"
|
|
9
|
-
UNIT_DST="/etc/systemd/system/robopark-supervisor.service"
|
|
10
|
-
|
|
11
|
-
if [ ! -f "$SUPERVISOR_DIR/robot_supervisor.py" ]; then
|
|
12
|
-
echo "robot_supervisor.py not found in $SUPERVISOR_DIR" >&2
|
|
13
|
-
exit 1
|
|
14
|
-
fi
|
|
15
|
-
|
|
16
|
-
if [ ! -f "$HOME/.robopark/supervisor.json" ]; then
|
|
17
|
-
echo "No ~/.robopark/supervisor.json yet — copy supervisor.example.json there and edit it first:" >&2
|
|
18
|
-
echo " cp $SUPERVISOR_DIR/supervisor.example.json ~/.robopark/supervisor.json" >&2
|
|
19
|
-
exit 1
|
|
20
|
-
fi
|
|
21
|
-
|
|
22
|
-
echo "Installing systemd unit -> $UNIT_DST (requires sudo)"
|
|
23
|
-
sudo sed \
|
|
24
|
-
-e "s#WorkingDirectory=.*#WorkingDirectory=$SUPERVISOR_DIR#" \
|
|
25
|
-
-e "s#User=.*#User=$(whoami)#" \
|
|
26
|
-
"$UNIT_SRC" | sudo tee "$UNIT_DST" > /dev/null
|
|
27
|
-
|
|
28
|
-
sudo systemctl daemon-reload
|
|
29
|
-
sudo systemctl enable robopark-supervisor.service
|
|
30
|
-
sudo systemctl restart robopark-supervisor.service
|
|
31
|
-
|
|
32
|
-
echo "Installed and started. Check status: sudo systemctl status robopark-supervisor.service"
|
|
33
|
-
echo "Logs: journalctl -u robopark-supervisor.service -f (or ~/.robopark/logs/*.log per-service)"
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Installs the RoboPark robot supervisor as a systemd service on a real
|
|
3
|
+
# robot (Raspberry Pi or similar). Auto-starts on boot, restarts on crash.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
|
+
SUPERVISOR_DIR="$(dirname "$SCRIPT_DIR")"
|
|
8
|
+
UNIT_SRC="$SCRIPT_DIR/robopark-supervisor.service"
|
|
9
|
+
UNIT_DST="/etc/systemd/system/robopark-supervisor.service"
|
|
10
|
+
|
|
11
|
+
if [ ! -f "$SUPERVISOR_DIR/robot_supervisor.py" ]; then
|
|
12
|
+
echo "robot_supervisor.py not found in $SUPERVISOR_DIR" >&2
|
|
13
|
+
exit 1
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
if [ ! -f "$HOME/.robopark/supervisor.json" ]; then
|
|
17
|
+
echo "No ~/.robopark/supervisor.json yet — copy supervisor.example.json there and edit it first:" >&2
|
|
18
|
+
echo " cp $SUPERVISOR_DIR/supervisor.example.json ~/.robopark/supervisor.json" >&2
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
echo "Installing systemd unit -> $UNIT_DST (requires sudo)"
|
|
23
|
+
sudo sed \
|
|
24
|
+
-e "s#WorkingDirectory=.*#WorkingDirectory=$SUPERVISOR_DIR#" \
|
|
25
|
+
-e "s#User=.*#User=$(whoami)#" \
|
|
26
|
+
"$UNIT_SRC" | sudo tee "$UNIT_DST" > /dev/null
|
|
27
|
+
|
|
28
|
+
sudo systemctl daemon-reload
|
|
29
|
+
sudo systemctl enable robopark-supervisor.service
|
|
30
|
+
sudo systemctl restart robopark-supervisor.service
|
|
31
|
+
|
|
32
|
+
echo "Installed and started. Check status: sudo systemctl status robopark-supervisor.service"
|
|
33
|
+
echo "Logs: journalctl -u robopark-supervisor.service -f (or ~/.robopark/logs/*.log per-service)"
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
<!doctype html>
|
|
2
2
|
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
3
|
-
<meta name="theme-color" content="#080704"><meta name="robopark-voice-join-build" content="production-
|
|
3
|
+
<meta name="theme-color" content="#080704"><meta name="robopark-voice-join-build" content="production-motion-greeting-20260728-1">
|
|
4
4
|
<title>RoboPark Character Conversation</title>
|
|
5
5
|
<style>
|
|
6
6
|
.deployment{display:grid;gap:8px;margin:14px 0}.deployment-row{display:grid;grid-template-columns:minmax(150px,1fr) 2fr auto;gap:9px;align-items:center;padding:.7rem;border:1px solid var(--line);border-radius:12px;background:rgba(255,255,255,.025)}.deployment-row b{font:.62rem var(--mono);color:var(--pale)}.deployment-row span{font:.54rem var(--mono);color:var(--muted)}.deployment-row .ready{color:var(--ok)}.deployment-actions{display:flex;flex-wrap:wrap;gap:5px}.deployment-actions button{border:1px solid var(--line);border-radius:8px;background:rgba(244,207,114,.09);color:var(--gold);padding:.38rem .5rem;font:.5rem var(--mono);cursor:pointer}
|
|
7
7
|
.audio-controls,.call-controls{position:relative;width:min(440px,94%);margin-top:12px;border:1px solid var(--line);border-radius:14px;background:rgba(0,0,0,.3);text-align:left}.audio-controls summary,.call-controls summary{padding:.65rem .8rem;cursor:pointer;color:var(--gold);font:600 .58rem var(--mono);letter-spacing:.07em;text-transform:uppercase}.audio-controls summary::marker,.call-controls summary::marker{color:var(--gold)}.audio-panel,.call-panel{display:grid;gap:.75rem;padding:0 .85rem .85rem}.audio-row{display:grid;grid-template-columns:105px 1fr 45px;gap:.55rem;align-items:center}.audio-row label,.audio-value,.call-row label{font:600 .54rem var(--mono);color:var(--muted)}.audio-value{text-align:right;color:var(--pale)}.audio-row input[type=range]{width:100%;accent-color:var(--gold)}.audio-note,.call-note{margin:0;color:#7e755f;font:.5rem/1.45 var(--mono)}.call-row{display:grid;gap:.3rem}.call-row select{width:100%;min-width:0;border:1px solid var(--line);border-radius:9px;background:#090805;color:var(--ink);padding:.52rem;font:600 .55rem var(--mono)}.call-actions{display:grid;grid-template-columns:1fr 1fr;gap:7px}.call-actions button{border:1px solid var(--line);border-radius:9px;background:rgba(244,207,114,.1);color:var(--gold);padding:.52rem;font:600 .52rem var(--mono);cursor:pointer}.call-actions button.primary{background:linear-gradient(135deg,#f5d77f,#b87b1d);color:#171005}.call-status.ok{color:var(--ok)}.call-status.bad{color:var(--bad)}.mode-badge{display:inline-flex;align-items:center;gap:.35rem;padding:.28rem .48rem;border:1px solid var(--line);border-radius:999px;color:var(--muted);font:600 .5rem var(--mono)}.mode-badge.production{color:var(--ok);border-color:rgba(100,223,160,.4)}
|
|
8
|
-
:root{--bg:#080704;--panel:rgba(20,17,11,.88);--line:rgba(247,211,116,.22);--gold:#f4cf72;--pale:#fff0bd;--ink:#f7f1df;--muted:#a99e84;--ok:#64dfa0;--warn:#f0bd55;--bad:#ff776c;--mono:"JetBrains Mono","Cascadia Code",monospace;--display:Georgia,"Times New Roman",serif}*{box-sizing:border-box}html,body{margin:0;min-height:100%;background:radial-gradient(circle at 50% 8%,#332510 0,#100d08 35%,#050403 78%);color:var(--ink);font-family:Verdana,Geneva,sans-serif}body:before{content:"";position:fixed;inset:0;pointer-events:none;background:linear-gradient(115deg,transparent 0 48%,rgba(244,207,114,.025) 49% 50%,transparent 51%),repeating-linear-gradient(0deg,transparent 0 34px,rgba(255,255,255,.012) 35px)}button,input{font:inherit}.shell{width:min(1120px,100%);min-height:100vh;margin:auto;padding:clamp(14px,3vw,34px);display:grid;grid-template-rows:auto 1fr auto;gap:18px}.top{display:flex;align-items:center;justify-content:space-between;gap:16px}.brand{font:700 .65rem var(--mono);letter-spacing:.18em;text-transform:uppercase;color:var(--gold)}.ops-toggle{border:1px solid var(--line);border-radius:999px;background:rgba(0,0,0,.35);color:var(--muted);padding:.55rem .8rem;cursor:pointer}.ops-toggle.on{color:var(--pale);border-color:var(--gold)}.stage{position:relative;min-height:620px;display:grid;grid-template-columns:minmax(0,1.3fr) minmax(280px,.7fr);overflow:hidden;border:1px solid var(--line);border-radius:28px;background:linear-gradient(145deg,rgba(34,27,14,.82),rgba(8,7,5,.92));box-shadow:0 28px 90px rgba(0,0,0,.45)}.hero{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:34px;text-align:center;overflow:hidden}.hero:before{content:"";position:absolute;width:500px;height:500px;border:1px solid rgba(244,207,114,.1);border-radius:50%;box-shadow:0 0 0 55px rgba(244,207,114,.018),0 0 0 120px rgba(244,207,114,.012)}.avatar-wrap{position:relative;width:min(330px,72vw);aspect-ratio:1;display:grid;place-items:center;border:1px solid rgba(244,207,114,.3);border-radius:50%;background:radial-gradient(circle,rgba(244,207,114,.16),rgba(8,7,5,.62) 58%,#050403 74%);box-shadow:0 0 90px rgba(214,164,55,.16)}.avatar-wrap.live{animation:breathe 2.2s ease-in-out infinite}.avatar-wrap.speaking{box-shadow:0 0 110px rgba(244,207,114,.42);border-color:var(--pale)}@keyframes breathe{50%{transform:scale(1.018)}}.avatar{position:relative;z-index:2;width:78%;height:78%;object-fit:contain;filter:drop-shadow(0 20px 28px rgba(0,0,0,.58))}.fallback{position:absolute;font:700 4rem var(--display);color:var(--gold)}h1{position:relative;margin:22px 0 5px;font:700 clamp(2rem,5vw,3.5rem) var(--display);color:var(--pale)}.subtitle{position:relative;color:var(--muted);font-size:.8rem}.state{position:relative;margin-top:14px;font:600 .68rem var(--mono);letter-spacing:.08em;text-transform:uppercase;color:var(--gold)}.join{position:relative;margin-top:22px;min-width:210px;border:1px solid var(--gold);border-radius:999px;padding:.9rem 1.25rem;background:linear-gradient(135deg,#f5d77f,#b87b1d);color:#171005;font-weight:800;cursor:pointer;box-shadow:0 12px 30px rgba(184,123,29,.24)}.join:disabled{opacity:.48;cursor:not-allowed}.end{position:relative;margin-top:10px;border:0;background:transparent;color:var(--muted);cursor:pointer}.side{position:relative;border-left:1px solid var(--line);padding:25px;display:flex;flex-direction:column;gap:14px;background:rgba(0,0,0,.2)}.vision{position:relative;aspect-ratio:16/10;border:1px solid var(--line);border-radius:17px;overflow:hidden;background:#050403}.vision img{width:100%;height:100%;object-fit:
|
|
8
|
+
:root{--bg:#080704;--panel:rgba(20,17,11,.88);--line:rgba(247,211,116,.22);--gold:#f4cf72;--pale:#fff0bd;--ink:#f7f1df;--muted:#a99e84;--ok:#64dfa0;--warn:#f0bd55;--bad:#ff776c;--mono:"JetBrains Mono","Cascadia Code",monospace;--display:Georgia,"Times New Roman",serif}*{box-sizing:border-box}html,body{margin:0;min-height:100%;background:radial-gradient(circle at 50% 8%,#332510 0,#100d08 35%,#050403 78%);color:var(--ink);font-family:Verdana,Geneva,sans-serif}body:before{content:"";position:fixed;inset:0;pointer-events:none;background:linear-gradient(115deg,transparent 0 48%,rgba(244,207,114,.025) 49% 50%,transparent 51%),repeating-linear-gradient(0deg,transparent 0 34px,rgba(255,255,255,.012) 35px)}button,input{font:inherit}.shell{width:min(1120px,100%);min-height:100vh;margin:auto;padding:clamp(14px,3vw,34px);display:grid;grid-template-rows:auto 1fr auto;gap:18px}.top{display:flex;align-items:center;justify-content:space-between;gap:16px}.brand{font:700 .65rem var(--mono);letter-spacing:.18em;text-transform:uppercase;color:var(--gold)}.ops-toggle{border:1px solid var(--line);border-radius:999px;background:rgba(0,0,0,.35);color:var(--muted);padding:.55rem .8rem;cursor:pointer}.ops-toggle.on{color:var(--pale);border-color:var(--gold)}.stage{position:relative;min-height:620px;display:grid;grid-template-columns:minmax(0,1.3fr) minmax(280px,.7fr);overflow:hidden;border:1px solid var(--line);border-radius:28px;background:linear-gradient(145deg,rgba(34,27,14,.82),rgba(8,7,5,.92));box-shadow:0 28px 90px rgba(0,0,0,.45)}.hero{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:34px;text-align:center;overflow:hidden}.hero:before{content:"";position:absolute;width:500px;height:500px;border:1px solid rgba(244,207,114,.1);border-radius:50%;box-shadow:0 0 0 55px rgba(244,207,114,.018),0 0 0 120px rgba(244,207,114,.012)}.avatar-wrap{position:relative;width:min(330px,72vw);aspect-ratio:1;display:grid;place-items:center;border:1px solid rgba(244,207,114,.3);border-radius:50%;background:radial-gradient(circle,rgba(244,207,114,.16),rgba(8,7,5,.62) 58%,#050403 74%);box-shadow:0 0 90px rgba(214,164,55,.16)}.avatar-wrap.live{animation:breathe 2.2s ease-in-out infinite}.avatar-wrap.speaking{box-shadow:0 0 110px rgba(244,207,114,.42);border-color:var(--pale)}@keyframes breathe{50%{transform:scale(1.018)}}.avatar{position:relative;z-index:2;width:78%;height:78%;object-fit:contain;filter:drop-shadow(0 20px 28px rgba(0,0,0,.58))}.fallback{position:absolute;font:700 4rem var(--display);color:var(--gold)}h1{position:relative;margin:22px 0 5px;font:700 clamp(2rem,5vw,3.5rem) var(--display);color:var(--pale)}.subtitle{position:relative;color:var(--muted);font-size:.8rem}.state{position:relative;margin-top:14px;font:600 .68rem var(--mono);letter-spacing:.08em;text-transform:uppercase;color:var(--gold)}.join{position:relative;margin-top:22px;min-width:210px;border:1px solid var(--gold);border-radius:999px;padding:.9rem 1.25rem;background:linear-gradient(135deg,#f5d77f,#b87b1d);color:#171005;font-weight:800;cursor:pointer;box-shadow:0 12px 30px rgba(184,123,29,.24)}.join:disabled{opacity:.48;cursor:not-allowed}.end{position:relative;margin-top:10px;border:0;background:transparent;color:var(--muted);cursor:pointer}.side{position:relative;border-left:1px solid var(--line);padding:25px;display:flex;flex-direction:column;gap:14px;background:rgba(0,0,0,.2)}.vision{position:relative;aspect-ratio:16/10;border:1px solid var(--line);border-radius:17px;overflow:hidden;background:#050403}.vision img,.vision video{width:100%;height:100%;object-fit:contain;background:#050403}.vision .label{position:absolute;left:9px;top:9px;padding:.32rem .45rem;border-radius:7px;background:rgba(0,0,0,.7);font:600 .5rem var(--mono);color:var(--gold)}.transcript{flex:1;min-height:220px;max-height:360px;overflow:auto;padding:.65rem;border:1px solid var(--line);border-radius:15px;background:rgba(0,0,0,.22)}.turn{margin:.45rem 0;padding:.55rem .65rem;border-radius:11px;font-size:.75rem;line-height:1.45}.turn.user{margin-left:13%;background:rgba(244,207,114,.12);border:1px solid rgba(244,207,114,.18)}.turn.assistant{margin-right:13%;background:rgba(255,255,255,.045)}.compose{display:flex;gap:8px}.compose input{min-width:0;flex:1;border:1px solid var(--line);border-radius:11px;background:#090805;color:var(--ink);padding:.7rem}.compose button{border:1px solid var(--line);border-radius:11px;background:rgba(244,207,114,.12);color:var(--gold);padding:.7rem;cursor:pointer}.ops{display:none;position:absolute;inset:12px;z-index:8;padding:16px;border:1px solid rgba(244,207,114,.35);border-radius:18px;background:rgba(5,4,3,.94);backdrop-filter:blur(16px);overflow:auto}.ops.open{display:block}.ops-head{display:flex;align-items:center;justify-content:space-between}.ops h2{margin:0;font:700 1.2rem var(--display);color:var(--pale)}.close{border:1px solid var(--line);border-radius:9px;background:transparent;color:var(--muted);padding:.4rem .55rem;cursor:pointer}.facts{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:8px;margin:14px 0}.fact{padding:.7rem;border:1px solid var(--line);border-radius:12px;background:rgba(255,255,255,.025)}.fact span{display:block;color:var(--muted);font:500 .49rem var(--mono);text-transform:uppercase;letter-spacing:.11em}.fact b{display:block;margin-top:.3rem;color:var(--ink);font:600 .64rem var(--mono);word-break:break-all}.pipeline{display:grid;grid-template-columns:repeat(5,1fr);gap:7px;margin:15px 0}.step{padding:.65rem .3rem;border:1px solid var(--line);border-radius:11px;text-align:center;color:var(--muted);font:600 .52rem var(--mono);text-transform:uppercase}.step.done{color:var(--ok);border-color:rgba(100,223,160,.4)}.step.live{color:#171005;border-color:var(--gold);background:var(--gold);box-shadow:0 0 24px rgba(244,207,114,.25)}.step.bad{color:var(--bad);border-color:rgba(255,119,108,.5)}.events{display:grid;gap:7px}.event{display:grid;grid-template-columns:95px 70px 1fr;gap:8px;padding:.6rem;border-bottom:1px solid var(--line);font:500 .55rem var(--mono)}.event .ok{color:var(--ok)}.event .error,.event .failed{color:var(--bad)}.foot{text-align:center;color:#716956;font:500 .52rem var(--mono);letter-spacing:.08em}.error-box{display:none;margin-top:12px;color:var(--bad);font:.65rem var(--mono)}@media(max-width:780px){.shell{padding:10px}.stage{grid-template-columns:1fr;min-height:0}.hero{min-height:600px;padding:24px 15px}.side{border-left:0;border-top:1px solid var(--line)}.pipeline{grid-template-columns:1fr 1fr}.top{padding:4px}.event{grid-template-columns:75px 55px 1fr}}
|
|
9
9
|
</style></head><body><main class="shell"><header class="top"><div class="brand">RoboPark / managed character channel</div><div><button class="ops-toggle on" id="always-on" aria-pressed="true" hidden>Always on: ON</button> <button class="ops-toggle" id="ops-toggle">Operations</button></div></header><section class="stage"><div class="hero"><div class="avatar-wrap" id="avatar-wrap"><div class="fallback" id="fallback">RP</div><img class="avatar" id="avatar" alt=""></div><h1 id="character-name">Loading character...</h1><div class="subtitle" id="subtitle">Secure ElevenLabs conversation managed by RoboPark</div><div class="state" id="state">validating link</div><button class="join" id="join" disabled>Join conversation</button><button class="end" id="end" hidden>End conversation</button><div class="error-box" id="error"></div></div><aside class="side"><div class="vision" id="vision" hidden><img id="robot-video" alt="Live robot camera"><span class="label">LIVE ROBOT VISION</span></div><div class="transcript" id="transcript"><div class="turn assistant">The transcript will appear here when the conversation begins.</div></div><form class="compose" id="compose"><input id="message" placeholder="Type a message" disabled><button id="send" disabled>Send</button></form></aside><section class="ops" id="ops"><div class="ops-head"><h2>Live management overlay</h2><button class="close" id="ops-close">Close</button></div><div class="facts"><div class="fact"><span>Session</span><b id="f-session">not started</b></div><div class="fact"><span>Character</span><b id="f-character">--</b></div><div class="fact"><span>Agent</span><b id="f-agent">--</b></div><div class="fact"><span>Branch</span><b id="f-branch">default</b></div><div class="fact"><span>Robot</span><b id="f-robot">operator only</b></div><div class="fact"><span>Share link</span><b id="f-share">--</b></div></div><div class="pipeline" id="pipeline"><div class="step live" data-stage="configured">configured</div><div class="step" data-stage="permission">permission</div><div class="step" data-stage="signed_session">signed</div><div class="step" data-stage="connected">connected</div><div class="step" data-stage="conversation">conversation</div></div><div class="events" id="events"><div class="event"><span>waiting</span><span>info</span><span>Session telemetry will stream here.</span></div></div></section></section><footer class="foot">No API credentials are stored in this link. Each visit creates an isolated signed session.</footer></main><script src="/vendor/elevenlabs-client.iife.js"></script><script>
|
|
10
10
|
(function(){
|
|
11
|
+
var API_BASE='';
|
|
11
12
|
var deployment=document.createElement('div');deployment.id='deployment';deployment.className='deployment';deployment.innerHTML='<div class="deployment-row"><b>Character hardware</b><span>Loading registered voice, vision, and motor nodes...</span><span></span></div>';document.getElementById('pipeline').before(deployment);
|
|
12
13
|
var audioControls=document.createElement('details');audioControls.id='audio-controls';audioControls.className='audio-controls';audioControls.innerHTML='<summary>Audio levels</summary><div class="audio-panel"><div class="audio-row"><label for="speaker-volume">Speaker</label><input id="speaker-volume" type="range" min="0" max="100" step="1"><output class="audio-value" id="speaker-value"></output></div><div class="audio-row"><label for="mic-sensitivity">Microphone</label><input id="mic-sensitivity" type="range" min="0" max="100" step="1"><output class="audio-value" id="mic-value"></output></div><p class="audio-note">Changes apply immediately and stay on this browser.</p></div>';document.getElementById('error').after(audioControls);
|
|
13
14
|
var callControls=document.createElement('details');callControls.id='call-controls';callControls.className='call-controls';callControls.open=true;callControls.innerHTML='<summary>Microphone and speaker</summary><div class=call-panel id=call-panel></div>';audioControls.open=true;audioControls.after(callControls);
|
|
14
15
|
var q=new URLSearchParams(location.search),character=q.get('character')||'',agent=q.get('agent_id')||'',branch=q.get('branch_id')||'',mode=q.get('mode')==='production'?'production':'test',robot=mode==='production'?(q.get('robot')||character):'',stack=q.get('stack')||'',share=q.get('share_id')||'',session=null,eventToken=null,conversation=null,conversationId=null,keepalive=null,poller=null,retryTimer=null,cameraPublishRetry=null,ending=false,desired=false,alwaysOn=false,connecting=false,generation=0,retryAttempt=0,lastProviderError=null,history=[],seen={},micPipelines=[],micAudioContext=null,localVideoStream=null,activeCameraDevice=null,cameraRoom=null,cameraSdkPromise=null,speakerVolume=storedNumber('robopark.voice.speaker',100,0,100),micSensitivity=storedNumber('robopark.voice.micSensitivity',100,0,100),inputDeviceId='',cameraDeviceId='',outputDeviceId='';
|
|
15
|
-
|
|
16
|
+
// The panel carries two sources: a <video> for local/LiveKit camera tracks and the
|
|
17
|
+
// original <img> for the robot MJPEG feed, which a <video> element cannot decode.
|
|
18
|
+
// Both stay mounted so the ambient vision sampler can read whichever is showing.
|
|
19
|
+
(function(){var old=document.getElementById('robot-video'),video=document.createElement('video');video.id='robot-video';video.autoplay=true;video.muted=true;video.playsInline=true;if(old&&old.parentNode){old.id='robot-stream';old.hidden=true;old.parentNode.insertBefore(video,old)}})();
|
|
16
20
|
function storedNumber(key,fallback,min,max){try{var raw=localStorage.getItem(key);if(raw===null)return fallback;var value=Number(raw);return Number.isFinite(value)?Math.max(min,Math.min(max,value)):fallback}catch(_){return fallback}}
|
|
17
21
|
function persistNumber(key,value){try{localStorage.setItem(key,String(value))}catch(_){}}
|
|
18
22
|
function applySpeakerVolume(){if(conversation&&typeof conversation.setVolume==='function')try{conversation.setVolume({volume:speakerVolume/100})}catch(_){}}
|
|
@@ -40,11 +44,76 @@
|
|
|
40
44
|
function refreshHardware(requestPermission){if(!navigator.mediaDevices||!navigator.mediaDevices.enumerateDevices){setHardwareStatus('Media device selection is unavailable in this browser.','bad');return Promise.reject(new Error('media devices unavailable'))}var permission=Promise.resolve();if(requestPermission){permission=navigator.mediaDevices.getUserMedia({audio:true,video:true}).catch(function(){return navigator.mediaDevices.getUserMedia({audio:true})}).then(function(stream){stream.getTracks().forEach(function(track){track.stop()})})}setHardwareStatus(requestPermission?'Requesting browser media access...':'Scanning browser hardware...','');return permission.then(function(){return navigator.mediaDevices.enumerateDevices()}).then(function(devices){fillHardwareSelect('call-microphone',devices,'audioinput','microphone');fillHardwareSelect('call-camera',devices,'videoinput','camera');fillHardwareSelect('call-speaker',devices,'audiooutput','speaker');var mic=devices.filter(function(device){return device.kind==='audioinput'}).length,cam=devices.filter(function(device){return device.kind==='videoinput'}).length,out=devices.filter(function(device){return device.kind==='audiooutput'}).length;setHardwareStatus('Ready: '+mic+' microphone(s), '+cam+' camera(s), '+out+' speaker output(s).','ok');return devices}).catch(function(error){setHardwareStatus(error&&error.message||String(error),'bad');throw error})}
|
|
41
45
|
function ensureCameraSdk(){if(window.LivekitClient)return Promise.resolve(window.LivekitClient);if(cameraSdkPromise)return cameraSdkPromise;cameraSdkPromise=new Promise(function(resolve,reject){var script=document.createElement('script');script.src='/vendor/livekit-client.umd.js';script.onload=function(){window.LivekitClient?resolve(window.LivekitClient):reject(new Error('LiveKit camera SDK unavailable'))};script.onerror=function(){reject(new Error('LiveKit camera SDK failed to load'))};document.head.appendChild(script)});return cameraSdkPromise}
|
|
42
46
|
function scheduleCameraPublishRetry(){if(cameraPublishRetry||ending||!localVideoStream)return;cameraPublishRetry=setTimeout(function(){cameraPublishRetry=null;publishLocalVideo().catch(function(){})},3000)}
|
|
43
|
-
function publishLocalVideo(){if(!localVideoStream||!localVideoStream.getVideoTracks().length)return Promise.resolve();if(cameraRoom)return Promise.resolve();return Promise.all([ensureCameraSdk(),fetch('/api/
|
|
44
|
-
function startLocalVideo(){if(!navigator.mediaDevices||!navigator.mediaDevices.getUserMedia)return Promise.resolve();if(localVideoStream&&activeCameraDevice===cameraDeviceId)return publishLocalVideo();if(localVideoStream)localVideoStream.getTracks().forEach(function(track){track.stop()});if(cameraRoom){cameraRoom.disconnect();cameraRoom=null}var video=cameraDeviceId?{deviceId:{exact:cameraDeviceId}}:true;return navigator.mediaDevices.getUserMedia({video:video,audio:false}).then(function(stream){localVideoStream=stream;activeCameraDevice=cameraDeviceId;el('robot-video').srcObject=stream;el('vision').hidden=false;return publishLocalVideo().catch(function(){})}).catch(function(error){localVideoStream=null;activeCameraDevice=null;el('robot-video').srcObject=null;el('vision').hidden=true
|
|
47
|
+
function publishLocalVideo(){if(!localVideoStream||!localVideoStream.getVideoTracks().length)return Promise.resolve();if(cameraRoom)return Promise.resolve();return Promise.all([ensureCameraSdk(),fetch(API_BASE+'/api/camera-token',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({character_id:character,robot_id:robot||null})}).then(function(response){if(!response.ok)throw new Error('camera token '+response.status);return response.json()})]).then(function(parts){var LK=parts[0],info=parts[1],room=new LK.Room();cameraRoom=room;room.on(LK.RoomEvent.Disconnected,function(){if(cameraRoom===room){cameraRoom=null;scheduleCameraPublishRetry()}});return room.connect(info.url,info.token).then(function(){return room.localParticipant.publishTrack(localVideoStream.getVideoTracks()[0],{name:'camera',source:LK.Track.Source.Camera})}).then(function(){if(cameraPublishRetry){clearTimeout(cameraPublishRetry);cameraPublishRetry=null}event('camera_published','ok','Camera relay live',{})})}).catch(function(error){cameraRoom=null;event('camera_published','failed',error.message,{});scheduleCameraPublishRetry();throw error})}
|
|
48
|
+
function startLocalVideo(){if(!navigator.mediaDevices||!navigator.mediaDevices.getUserMedia)return Promise.resolve();if(localVideoStream&&activeCameraDevice===cameraDeviceId)return publishLocalVideo();if(localVideoStream)localVideoStream.getTracks().forEach(function(track){track.stop()});if(cameraRoom){cameraRoom.disconnect();cameraRoom=null}var video=cameraDeviceId?{deviceId:{exact:cameraDeviceId}}:true;return navigator.mediaDevices.getUserMedia({video:video,audio:false}).then(function(stream){localVideoStream=stream;activeCameraDevice=cameraDeviceId;el('robot-video').srcObject=stream;if(!robotStreamActive())el('robot-video').hidden=false;el('vision').hidden=false;return publishLocalVideo().catch(function(){})}).catch(function(error){localVideoStream=null;activeCameraDevice=null;el('robot-video').srcObject=null;if(!robotStreamActive()){el('robot-video').hidden=true;el('vision').hidden=true}throw error})}
|
|
49
|
+
function robotStreamActive(){var stream=el('robot-stream');return Boolean(stream&&stream.getAttribute('src'))}
|
|
50
|
+
// Ambient vision. The ElevenLabs contextual_update channel carries text only, so a
|
|
51
|
+
// frame is captioned server-side and the sentence is injected silently — a real
|
|
52
|
+
// image would have to go through sendMultimodalMessage, which speaks as the user
|
|
53
|
+
// and would make the agent answer itself every poll. Frames are only sent when the
|
|
54
|
+
// scene actually changes, so a static view does not grow the agent prompt by a
|
|
55
|
+
// caption every 10s.
|
|
56
|
+
var visionTimer=null,visionCanvas=null,visionProbe=null,visionHash=null,visionLastSent=0,visionBusy=false,visionFailures=0;
|
|
57
|
+
function visionSource(){var stream=el('robot-stream'),video=el('robot-video');if(stream&&!stream.hidden&&stream.naturalWidth)return stream;if(video&&!video.hidden&&video.videoWidth)return video;return null}
|
|
58
|
+
function visionCapture(){var source=visionSource();if(!source)return false;var width=source.videoWidth||source.naturalWidth,height=source.videoHeight||source.naturalHeight;if(!width||!height)return false;if(!visionCanvas)visionCanvas=document.createElement('canvas');var scale=Math.min(1,512/Math.max(width,height));visionCanvas.width=Math.max(32,Math.round(width*scale));visionCanvas.height=Math.max(32,Math.round(height*scale));try{visionCanvas.getContext('2d').drawImage(source,0,0,visionCanvas.width,visionCanvas.height);return true}catch(_){return false}}
|
|
59
|
+
function visionSignature(){if(!visionProbe)visionProbe=document.createElement('canvas');visionProbe.width=8;visionProbe.height=8;var ctx=visionProbe.getContext('2d');try{ctx.drawImage(visionCanvas,0,0,8,8);var data=ctx.getImageData(0,0,8,8).data}catch(_){return null}var levels=[],total=0,i;for(i=0;i<64;i++){var level=(data[i*4]*299+data[i*4+1]*587+data[i*4+2]*114)/1000;levels.push(level);total+=level}var mean=total/64,hash=[];for(i=0;i<64;i++)hash.push(levels[i]>mean?1:0);return hash}
|
|
60
|
+
function visionChanged(hash){if(!visionHash)return true;for(var i=0,distance=0;i<64;i++){if(hash[i]!==visionHash[i])distance++;if(distance>=6)return true}return false}
|
|
61
|
+
function visionTick(myGeneration){
|
|
62
|
+
if(visionBusy||ending||!conversation||myGeneration!==generation)return;
|
|
63
|
+
if(!visionCapture())return;
|
|
64
|
+
var hash=visionSignature();if(!hash)return;
|
|
65
|
+
// A still scene is still refreshed every 45s so the agent's picture of the room
|
|
66
|
+
// never silently ages out of the conversation.
|
|
67
|
+
if(!visionChanged(hash)&&Date.now()-visionLastSent<45000)return;
|
|
68
|
+
visionBusy=true;
|
|
69
|
+
visionCanvas.toBlob(function(blob){
|
|
70
|
+
if(!blob){visionBusy=false;return}
|
|
71
|
+
fetch(API_BASE+'/api/vision/caption',{method:'POST',headers:{'content-type':'image/jpeg','x-robopark-character':character||''},body:blob}).then(function(response){if(!response.ok)return response.text().then(function(body){throw new Error(body||('caption '+response.status))});return response.json()}).then(function(data){
|
|
72
|
+
visionBusy=false;visionFailures=0;
|
|
73
|
+
var caption=String(data&&data.caption||'').trim();
|
|
74
|
+
if(!caption||!conversation||ending||myGeneration!==generation)return;
|
|
75
|
+
visionHash=hash;visionLastSent=Date.now();
|
|
76
|
+
try{conversation.sendContextualUpdate('[live camera] '+caption)}catch(_){return}
|
|
77
|
+
event('vision_context','ok',caption,{model:data.model||'',elapsed_ms:data.elapsed_ms||0})
|
|
78
|
+
}).catch(function(error){
|
|
79
|
+
visionBusy=false;visionFailures++;
|
|
80
|
+
if(visionFailures>=3){event('vision_context','failed','Ambient vision paused: '+(error&&error.message||error),{});stopVision()}
|
|
81
|
+
})
|
|
82
|
+
},'image/jpeg',.6)
|
|
83
|
+
}
|
|
84
|
+
function startVision(myGeneration){stopVision();visionHash=null;visionLastSent=0;visionFailures=0;visionTimer=setInterval(function(){visionTick(myGeneration)},10000);setTimeout(function(){visionTick(myGeneration)},1500)}
|
|
85
|
+
function stopVision(){if(visionTimer){clearInterval(visionTimer);visionTimer=null}visionBusy=false}
|
|
86
|
+
// Motion greeting for production always-on calls. contextual_update is silent but
|
|
87
|
+
// never starts a turn, so the agent is prompted with a user_message carrying a
|
|
88
|
+
// directive; it is suppressed from the visible transcript and the ledger so the
|
|
89
|
+
// visitor only ever sees the greeting it produces. Guards mirror the robovoice
|
|
90
|
+
// wake path in voice_agent.py: cooldown, listening-only, and no firing during an
|
|
91
|
+
// active conversation, with the cooldown reserved before sending so a burst of
|
|
92
|
+
// motion cannot queue duplicate greetings.
|
|
93
|
+
var motionTimer=null,motionCanvas=null,motionPrev=null,motionLastFire=0,motionArmedAt=0,motionMode='listening',motionLastTurn=0,motionDirective='',motionSent={};
|
|
94
|
+
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;
|
|
95
|
+
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}
|
|
96
|
+
function motionTick(myGeneration){
|
|
97
|
+
// Checked per tick, not at start, so the always-on toggle takes effect live.
|
|
98
|
+
if(ending||!conversation||myGeneration!==generation||!alwaysOn)return;
|
|
99
|
+
var level=motionLevel(),now=Date.now();
|
|
100
|
+
if(level<0||now<motionArmedAt)return;
|
|
101
|
+
if(level<motionThreshold)return;
|
|
102
|
+
if(now-motionLastFire<motionCooldown)return;
|
|
103
|
+
if(motionMode!=='listening')return;
|
|
104
|
+
if(now-motionLastTurn<motionQuiet)return;
|
|
105
|
+
motionLastFire=now;
|
|
106
|
+
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.';
|
|
107
|
+
motionSent[directive]=1;
|
|
108
|
+
try{conversation.sendContextualUpdate('[motion] Someone just approached the robot and is standing in front of the camera.')}catch(_){}
|
|
109
|
+
try{conversation.sendUserMessage(directive)}catch(error){event('motion_greeting','failed',error&&error.message||String(error),{});return}
|
|
110
|
+
event('motion_greeting','ok','Motion greeting triggered',{level:Number(level.toFixed(4)),threshold:motionThreshold})
|
|
111
|
+
}
|
|
112
|
+
function startMotion(myGeneration){stopMotion();motionPrev=null;motionLastFire=0;motionSent={};motionDirective=q.get('motion_prompt')||'';motionArmedAt=Date.now()+4000;motionTimer=setInterval(function(){motionTick(myGeneration)},1000)}
|
|
113
|
+
function stopMotion(){if(motionTimer){clearInterval(motionTimer);motionTimer=null}motionPrev=null}
|
|
45
114
|
function showRobotVideo(){return startLocalVideo().catch(function(){})}
|
|
46
115
|
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()}
|
|
47
|
-
function loadProductionRobots(){return Promise.all([fetch('/
|
|
116
|
+
function loadProductionRobots(){return Promise.all([fetch(API_BASE+'/api/character-presets',{cache:'no-store'}).then(function(r){if(!r.ok)throw new Error('character inventory '+r.status);return r.json()}),fetch(API_BASE+'/api/devices',{cache:'no-store'}).then(function(r){if(!r.ok)throw new Error('device inventory '+r.status);return r.json()})]).then(function(data){var characters=Array.isArray(data[0])?data[0]:(data[0]&&data[0].presets)||[],devices=Array.isArray(data[1])?data[1]:(data[1]&&data[1].devices)||[],select=el('call-robot'),seen={};select.innerHTML='<option value=none>Select a character...</option>';characters.forEach(function(c){var cid=String(c.id||c.preset_id||c.character_id||'');if(!cid||seen[cid.toLowerCase()])return;seen[cid.toLowerCase()]=1;var matches=devices.filter(function(d){return String(d.character_id||'').toLowerCase()===cid.toLowerCase()});matches.sort(function(a,b){var ar=String(a.device_role||'combined'),br=String(b.device_role||'combined'),ap=ar==='voice_vision'?3:ar==='combined'?2:0,bp=br==='voice_vision'?3:br==='combined'?2:0;return bp-ap||Date.parse(b.last_heartbeat||b.enrolled_at||0)-Date.parse(a.last_heartbeat||a.enrolled_at||0)});var voice=matches[0],id=voice&&(voice.id||voice.device_id)||cid,option=document.createElement('option');option.value=id;option.textContent=String(c.name||cid)+' / '+(voice?(String(voice.device_role||'combined')+' '+(voice.status||'registered')):'awaiting voice/video PC');select.appendChild(option)});if(robot&&Array.from(select.options).some(function(option){return option.value===robot}))select.value=robot;else if(character){var match=Array.from(select.options).find(function(option){return option.textContent.toLowerCase().indexOf(character.toLowerCase()+' /')===0||option.value.toLowerCase()===character.toLowerCase()});if(match){select.value=match.value;robot=match.value}}applyCallMode();return characters}).catch(function(error){setHardwareStatus('Character deployment inventory unavailable: '+error.message,'bad');return []})}
|
|
48
117
|
el('call-mode').value=mode;el('call-robot').value=robot||'none';
|
|
49
118
|
el('call-mode').onchange=function(){if(desired||conversation||connecting)end('mode_changed');applyCallMode()};
|
|
50
119
|
el('call-robot').onchange=function(){robot=this.value==='none'?'':this.value;showRobotVideo();text('f-robot',robot||'missing robot')};
|
|
@@ -59,7 +128,11 @@
|
|
|
59
128
|
var hardwarePatchTimer=setInterval(function(){if(installElevenLabsHardwareRouting())clearInterval(hardwarePatchTimer)},500);
|
|
60
129
|
function stage(name,status){var order=['configured','permission','signed_session','connected','conversation'],index=order.indexOf(name);document.querySelectorAll('.step').forEach(function(node,i){node.className='step '+(status==='bad'&&i===index?'bad':i<index?'done':i===index?'live':'')})}
|
|
61
130
|
function headers(){return {'content-type':'application/json','X-RoboPark-Session-Token':eventToken||''}}
|
|
62
|
-
function api(path,options){options=options||{};if(path==='/api/sessions/voice-call'&&options.body){var payload=JSON.parse(options.body);payload.call_mode=mode;payload.robot_id=mode==='production'?(robot||null):null;options.body=JSON.stringify(payload)}
|
|
131
|
+
function api(path,options){options=options||{};if(path==='/api/sessions/voice-call'&&options.body){var payload=JSON.parse(options.body);payload.call_mode=mode;payload.robot_id=mode==='production'?(robot||null):null;options.body=JSON.stringify(payload)}var request=Object.assign({cache:'no-store'},options);
|
|
132
|
+
// Without a deadline a stalled control request hung the join forever: no
|
|
133
|
+
// error surfaced, so nothing ever retried.
|
|
134
|
+
if(!request.signal&&window.AbortSignal&&AbortSignal.timeout)request.signal=AbortSignal.timeout(25000);
|
|
135
|
+
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()})}
|
|
63
136
|
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}
|
|
64
137
|
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(){})}
|
|
65
138
|
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()}
|
|
@@ -68,16 +141,31 @@
|
|
|
68
141
|
function pollDetail(){applySpeakerVolume()}
|
|
69
142
|
function plainDetails(value){try{return JSON.parse(JSON.stringify(value||{}))}catch(_){return {raw:String(value||'')}}}
|
|
70
143
|
function closeManaged(reason,details){var oldSession=session,oldToken=eventToken;if(!oldSession||!oldToken)return Promise.resolve();return api('/api/sessions/'+encodeURIComponent(oldSession)+'/end?reason='+encodeURIComponent(reason),{method:'POST',headers:{'content-type':'application/json','X-RoboPark-Session-Token':oldToken},body:JSON.stringify({details:plainDetails(details),source:'public_join'})}).catch(function(){})}
|
|
71
|
-
function stopTimers(){if(retryTimer){clearTimeout(retryTimer);retryTimer=null}if(keepalive){clearInterval(keepalive);keepalive=null}if(poller){clearInterval(poller);poller=null}}
|
|
144
|
+
function stopTimers(){if(retryTimer){clearTimeout(retryTimer);retryTimer=null}if(keepalive){clearInterval(keepalive);keepalive=null}if(poller){clearInterval(poller);poller=null}stopVision();stopMotion()}
|
|
72
145
|
function scheduleReconnect(){if(!desired||!alwaysOn||ending)return;connecting=false;conversation=null;conversationId=null;var attempt=++retryAttempt,delay=Math.min(30000,Math.round(750*Math.pow(2,Math.min(attempt-1,6))+Math.random()*500));text('state','reconnecting in '+Math.ceil(delay/1000)+'s');stage('connected');el('avatar-wrap').classList.remove('speaking');if(retryTimer)clearTimeout(retryTimer);retryTimer=setTimeout(function(){retryTimer=null;connectAttempt('reconnect')},delay)}
|
|
73
|
-
function recover(myGeneration,details){if(myGeneration!==generation||ending||!desired)return;var captured=plainDetails(details);captured.last_provider_error=lastProviderError;event('provider_error','
|
|
74
|
-
function end(reason){if(ending)return;ending=true;desired=false;generation++;stopTimers();var active=conversation,details={manual:true,reason:reason||'visitor'};if(active)try{active.endSession()}catch(_){};if(cameraRoom){cameraRoom.disconnect();cameraRoom=null}if(localVideoStream){localVideoStream.getTracks().forEach(function(track){track.stop()});localVideoStream=null}closeManaged(reason||'visitor',details);session=null;eventToken=null;conversation=null;conversationId=null;connecting=false;retryAttempt=0;stage('conversation','done');text('state','manually disconnected - supervisor stopped');el('avatar-wrap').classList.remove('live','speaking');el('join').hidden=false;el('join').disabled=false;el('join').textContent='Start conversation';el('end').hidden=true;el('message').disabled=true;el('send').disabled=true;ending=false}
|
|
146
|
+
function recover(myGeneration,details){if(myGeneration!==generation||ending||!desired)return;var captured=plainDetails(details);captured.last_provider_error=lastProviderError;event('provider_error','warning','ElevenLabs disconnected; automatic recovery is running',captured);closeManaged('provider_disconnected',captured);stopTimers();session=null;eventToken=null;conversation=null;conversationId=null;connecting=false;el('avatar-wrap').classList.remove('live','speaking');el('join').hidden=true;el('end').hidden=false;el('message').disabled=true;el('send').disabled=true;scheduleReconnect()}
|
|
147
|
+
function end(reason){if(ending)return;ending=true;desired=false;generation++;stopTimers();var active=conversation,details={manual:true,reason:reason||'visitor'};if(active)try{active.endSession()}catch(_){};if(cameraRoom){cameraRoom.disconnect();cameraRoom=null}if(localVideoStream){localVideoStream.getTracks().forEach(function(track){track.stop()});localVideoStream=null}var robotStream=el('robot-stream');if(robotStream){robotStream.removeAttribute('src');robotStream.hidden=true}el('robot-video').hidden=false;closeManaged(reason||'visitor',details);session=null;eventToken=null;conversation=null;conversationId=null;connecting=false;retryAttempt=0;stage('conversation','done');text('state','manually disconnected - supervisor stopped');el('avatar-wrap').classList.remove('live','speaking');el('join').hidden=false;el('join').disabled=false;el('join').textContent='Start conversation';el('end').hidden=true;el('message').disabled=true;el('send').disabled=true;ending=false}
|
|
148
|
+
// This call lives on an ElevenLabs WebSocket and never joins a LiveKit room,
|
|
149
|
+
// so the control center cannot observe it. Check in on a timer, and close the
|
|
150
|
+
// session on unload, or the dashboard cannot tell a live call from a closed
|
|
151
|
+
// tab and keeps showing the character as busy.
|
|
152
|
+
function startKeepalive(){if(keepalive)clearInterval(keepalive);keepalive=setInterval(function(){management('/heartbeat',{source:'public_join'})},30000)}
|
|
153
|
+
window.addEventListener('pagehide',function(){
|
|
154
|
+
if(!session||!eventToken||!navigator.sendBeacon)return;
|
|
155
|
+
try{navigator.sendBeacon(API_BASE+'/api/sessions/'+encodeURIComponent(session)+'/end?reason=page_closed',new Blob([JSON.stringify({details:{page_closed:true},source:'public_join'})],{type:'application/json'}))}catch(_){}
|
|
156
|
+
});
|
|
75
157
|
function callPayload(){return {character_preset_id:character,voice_stack_id:stack||null,client_name:'Public character link',engine:'elevenlabs',agent_id:agent,branch_id:branch||null,robot_id:robot||null,join_link_id:share||null}}
|
|
76
|
-
function connectAttempt(kind){if(!desired||ending||connecting||conversation)return;connecting=true;var myGeneration=++generation;text('state',kind==='reconnect'?'obtaining fresh voice session':'connecting');stage('signed_session');api('/api/sessions/voice-call',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify(callPayload())}).then(function(call){if(myGeneration!==generation||!desired)throw new Error('connection superseded');session=call.session_id;eventToken=call.event_token;text('f-session',session);text('f-agent',call.agent_id);text('f-branch',call.branch_id||'default');text('f-robot',call.robot_id||'operator only');if(call.robot_video_ref&&robot){el('vision').hidden=false;el('robot-
|
|
77
|
-
|
|
158
|
+
function connectAttempt(kind){if(!desired||ending||connecting||conversation)return;connecting=true;var myGeneration=++generation;text('state',kind==='reconnect'?'obtaining fresh voice session':'connecting');stage('signed_session');api('/api/sessions/voice-call',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify(callPayload())}).then(function(call){if(myGeneration!==generation||!desired)throw new Error('connection superseded');session=call.session_id;eventToken=call.event_token;text('f-session',session);text('f-agent',call.agent_id);text('f-branch',call.branch_id||'default');text('f-robot',call.robot_id||'operator only');if(call.robot_video_ref&&robot){el('vision').hidden=false;el('robot-stream').src=API_BASE+'/api/robots/'+encodeURIComponent(call.robot_video_ref)+'/video_feed';el('robot-stream').hidden=false;el('robot-video').hidden=true}stage('connected');event('signed_session','ok','Fresh signed ElevenLabs session created',{join_link_id:share,robot_id:robot,reconnect_attempt:retryAttempt});return ensureElevenLabsClient().then(function(SDK){return SDK.Conversation.startSession({signedUrl:call.signed_url,connectionType:'websocket',useWakeLock:true,onConnect:function(info){if(myGeneration!==generation)return;conversationId=info.conversationId;lastProviderError=null;retryAttempt=0;text('state','listening');el('error').style.display='none';el('avatar-wrap').classList.add('live');stage('conversation');management('/joined',{conversation_id:conversationId,connection_type:'websocket',engine:'elevenlabs',robot_id:robot,join_link_id:share,source:'public_join'});event('provider_connected','ok','ElevenLabs conversation connected',{conversation_id:conversationId,supervised:true});startVision(myGeneration);motionLastTurn=Date.now();if(mode==='production')startMotion(myGeneration)},onDisconnect:function(info){recover(myGeneration,info||{reason:'provider_disconnected'})},onError:function(message,context){if(myGeneration!==generation)return;lastProviderError={message:String(message||'Provider error'),context:plainDetails(context)};event('provider_error','failed',lastProviderError.message,lastProviderError);text('state','provider error')},onStatusChange:function(info){if(myGeneration===generation)event('provider_status',info&&info.status==='disconnected'?'warning':'ok',info&&info.status||'unknown',{})},onMessage:function(info){if(myGeneration!==generation)return;var message=String(info&&info.message||'').trim(),role=info&&info.role==='user'?'user':'assistant';if(role==='user'&&motionSent[message])return;motionLastTurn=Date.now();append(role,message);if(message){history.push({role:role,text:message});history=history.slice(-20)}saveTurn(role,message)},onModeChange:function(info){if(myGeneration!==generation)return;var speaking=info.mode==='speaking';motionMode=speaking?'speaking':'listening';el('avatar-wrap').classList.toggle('speaking',speaking);text('state',speaking?'speaking':'listening');event('mode','ok',info.mode,{})},onInterruption:function(info){event('interruption','ok','Agent interrupted',info||{})},onMCPToolCall:function(info){event('mcp_tool','running','Tool call',info||{})}})})}).then(function(active){connecting=false;if(myGeneration!==generation||!desired){try{active.endSession()}catch(_){}return}conversation=active;startKeepalive();el('join').hidden=true;el('end').hidden=false;el('message').disabled=false;el('send').disabled=false;pollDetail()}).catch(function(error){connecting=false;if(myGeneration!==generation||!desired)return;var details={message:error&&error.message||String(error),last_provider_error:lastProviderError};event('provider_error','failed','Voice connection attempt failed',details);closeManaged('connection_failed',details);session=null;eventToken=null;conversation=null;conversationId=null;
|
|
159
|
+
// A 4xx means the link itself is wrong and retrying cannot fix it. Anything
|
|
160
|
+
// else (5xx, signing timeout, network drop) is transient, and dead-ending
|
|
161
|
+
// here is what left a failed call with no reconnect until a manual re-join.
|
|
162
|
+
var status=Number(error&&error.status||0),permanent=status>=400&&status<500;
|
|
163
|
+
if(!permanent&&alwaysOn&&desired&&!ending){el('join').hidden=true;el('end').hidden=false;scheduleReconnect();return}
|
|
164
|
+
fail(details.message)})}
|
|
165
|
+
function start(){if(desired||conversation||connecting)return;if(mode==='production'&&!robot){fail('Select a registered production robot first.');return}alwaysOn=true;el('always-on').classList.add('on');el('always-on').setAttribute('aria-pressed','true');el('always-on').textContent='Always on: ON';if(mode==='test')ensureMicAudioContext();desired=true;retryAttempt=0;el('join').disabled=true;text('state','requesting microphone permission');stage('permission');var audio=inputDeviceId?{deviceId:{exact:inputDeviceId}}:true;navigator.mediaDevices.getUserMedia({audio:audio}).then(function(stream){stream.getTracks().forEach(function(track){track.stop()});return startLocalVideo()}).then(function(){connectAttempt('initial')}).catch(function(error){if(desired){text('state','microphone unavailable; retrying');scheduleReconnect()}else{fail(error&&error.message||String(error))}})}
|
|
78
166
|
el('always-on').onclick=function(){alwaysOn=!alwaysOn;this.classList.toggle('on',alwaysOn);this.setAttribute('aria-pressed',String(alwaysOn));this.textContent='Always on: '+(alwaysOn?'ON':'OFF');if(!alwaysOn&&retryTimer){clearTimeout(retryTimer);retryTimer=null;text('state','disconnected - automatic recovery paused')}else if(alwaysOn&&desired&&!conversation&&!connecting){scheduleReconnect()}};
|
|
79
167
|
function pollDetail(){applySpeakerVolume()}
|
|
80
|
-
function deploymentRequest(path,options){var token=q.get('token')||'',suffix=token?(path.indexOf('?')>=0?'&':'?')+'token='+encodeURIComponent(token):'';return fetch(
|
|
168
|
+
function deploymentRequest(path,options){var token=q.get('token')||'',suffix=token?(path.indexOf('?')>=0?'&':'?')+'token='+encodeURIComponent(token):'';return fetch(API_BASE+path+suffix,Object.assign({cache:'no-store'},options||{})).then(function(response){if(!response.ok)throw new Error('request '+response.status);return response.json()})}
|
|
81
169
|
function renderDeployment(data){var token=q.get('token')||'',rows=[];[['voice_vision_boxes','Windows voice + vision',['vision','conversation']],['motor_nodes','Pi motor server',['motor']]].forEach(function(group){var devices=data[group[0]]||[];if(!devices.length){rows.push('<div class="deployment-row"><b>'+group[1]+'</b><span>not registered</span><span></span></div>');return}devices.forEach(function(device){var seen=Date.parse(device.last_heartbeat||''),live=isFinite(seen)&&Date.now()-seen<90000,buttons=token?group[2].map(function(service){return '<button data-device="'+device.id+'" data-service="'+service+'" data-action="start">Start '+service+'</button><button data-device="'+device.id+'" data-service="'+service+'" data-action="restart">Restart</button><button data-device="'+device.id+'" data-service="'+service+'" data-action="stop">Stop</button>'}).join(''):'management token required for controls';rows.push('<div class="deployment-row"><b>'+group[1]+'</b><span class="'+(live?'ready':'')+'">'+String(device.name||device.id)+' / '+(live?'online':'stale')+'</span><div class="deployment-actions">'+buttons+'</div></div>')})});deployment.innerHTML=rows.join('');deployment.querySelectorAll('button[data-action]').forEach(function(button){button.onclick=function(){var action=button.getAttribute('data-action'),service=button.getAttribute('data-service'),device=button.getAttribute('data-device');if((action==='stop'||action==='restart')&&!confirm(action+' '+service+'?'))return;button.disabled=true;deploymentRequest('/api/devices/'+encodeURIComponent(device)+'/services/'+encodeURIComponent(service)+'/'+action,{method:'POST',headers:{'content-type':'application/json'}}).then(function(){button.textContent='queued'}).catch(function(error){button.textContent='failed';fail(error.message)}).then(function(){setTimeout(function(){button.disabled=false},1800)})}})}
|
|
82
170
|
function loadDeployment(){deploymentRequest('/api/characters/'+encodeURIComponent(character)+'/deployment').then(renderDeployment).catch(function(){deployment.innerHTML='<div class="deployment-row"><b>Character hardware</b><span>deployment status unavailable</span><span></span></div>'})}
|
|
83
171
|
el('speaker-volume').value=String(speakerVolume);text('speaker-value',speakerVolume+'%');el('mic-sensitivity').value=String(micSensitivity);text('mic-value',micSensitivity+'%');el('speaker-volume').oninput=function(){speakerVolume=Math.max(0,Math.min(100,Number(this.value)||0));text('speaker-value',speakerVolume+'%');persistNumber('robopark.voice.speaker',speakerVolume);applySpeakerVolume()};el('mic-sensitivity').oninput=function(){setMicSensitivity(this.value);text('mic-value',micSensitivity+'%');persistNumber('robopark.voice.micSensitivity',micSensitivity)};
|