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
package/scheduler/main.py
CHANGED
|
@@ -16,13 +16,16 @@ import hmac
|
|
|
16
16
|
import time
|
|
17
17
|
import uuid
|
|
18
18
|
from datetime import datetime, timedelta
|
|
19
|
+
from html import escape as html_escape
|
|
20
|
+
from pathlib import Path
|
|
19
21
|
from typing import Any, Optional, List
|
|
22
|
+
from urllib.parse import quote
|
|
20
23
|
from contextlib import asynccontextmanager
|
|
21
24
|
|
|
22
25
|
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, HTTPException, Query, Header
|
|
23
26
|
from fastapi.middleware.cors import CORSMiddleware
|
|
24
27
|
from fastapi.staticfiles import StaticFiles
|
|
25
|
-
from fastapi.responses import HTMLResponse
|
|
28
|
+
from fastapi.responses import HTMLResponse, FileResponse
|
|
26
29
|
from pydantic import BaseModel
|
|
27
30
|
import httpx
|
|
28
31
|
import aiosqlite
|
|
@@ -1740,6 +1743,60 @@ async def _select_available_server(db):
|
|
|
1740
1743
|
raise HTTPException(503, "All servers at capacity")
|
|
1741
1744
|
return server
|
|
1742
1745
|
|
|
1746
|
+
async def _claim_character_device(
|
|
1747
|
+
db,
|
|
1748
|
+
*,
|
|
1749
|
+
character_id: str,
|
|
1750
|
+
join_link_id: str,
|
|
1751
|
+
requested_robot_id: str,
|
|
1752
|
+
client_name: str | None,
|
|
1753
|
+
now: datetime,
|
|
1754
|
+
):
|
|
1755
|
+
"""Bind whichever device opened a character link to that character.
|
|
1756
|
+
|
|
1757
|
+
Production calls used to demand a pre-enrolled robot, which meant a freshly
|
|
1758
|
+
imaged voice/vision box could not take a call until someone ran `robopark
|
|
1759
|
+
enroll` on it. Instead the link itself is the identity: the claim is keyed on
|
|
1760
|
+
the join link when there is one and on the character otherwise, so opening the
|
|
1761
|
+
same link from a different browser transfers the character to that device and
|
|
1762
|
+
ends the previous holder's live session.
|
|
1763
|
+
|
|
1764
|
+
Returns (robot_id, robot_video_ref).
|
|
1765
|
+
"""
|
|
1766
|
+
claim_key = (join_link_id or f"character:{character_id}").strip()[:120]
|
|
1767
|
+
device_id = "claim_" + hashlib.sha256(claim_key.encode("utf-8")).hexdigest()[:16]
|
|
1768
|
+
label = (requested_robot_id or client_name or character_id or "claimed device").strip()[:80]
|
|
1769
|
+
stamp = now.isoformat()
|
|
1770
|
+
|
|
1771
|
+
async with db.execute("SELECT id, name FROM devices WHERE id = ?", (device_id,)) as cursor:
|
|
1772
|
+
existing = await cursor.fetchone()
|
|
1773
|
+
|
|
1774
|
+
if existing:
|
|
1775
|
+
# Same link opened again: the newest device wins. Close whatever session
|
|
1776
|
+
# the previous holder still had open so two browsers cannot both drive
|
|
1777
|
+
# the character, then re-point the claim at the current caller.
|
|
1778
|
+
await db.execute(
|
|
1779
|
+
"UPDATE sessions SET ended_at = ?, end_reason = ? WHERE robot_id = ? AND ended_at IS NULL",
|
|
1780
|
+
(stamp, "claim_transferred", device_id),
|
|
1781
|
+
)
|
|
1782
|
+
await db.execute(
|
|
1783
|
+
"UPDATE devices SET character_id = ?, name = ?, status = ?, last_heartbeat = ? WHERE id = ?",
|
|
1784
|
+
(character_id, label, "claimed", stamp, device_id),
|
|
1785
|
+
)
|
|
1786
|
+
else:
|
|
1787
|
+
await db.execute(
|
|
1788
|
+
"""INSERT INTO devices (id, name, character_id, status, device_role,
|
|
1789
|
+
last_heartbeat, enrolled_at, notes)
|
|
1790
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
|
|
1791
|
+
(device_id, label, character_id, "claimed", "voice_vision", stamp, stamp,
|
|
1792
|
+
f"auto-claimed via join link {claim_key}"),
|
|
1793
|
+
)
|
|
1794
|
+
await db.commit()
|
|
1795
|
+
# No MJPEG ref: a claimed device publishes its own browser camera rather than
|
|
1796
|
+
# exposing a robot-side vision server, so the page falls back to local video.
|
|
1797
|
+
return device_id, None
|
|
1798
|
+
|
|
1799
|
+
|
|
1743
1800
|
@app.post("/api/sessions/voice-call")
|
|
1744
1801
|
async def create_voice_call(payload: VoiceCallRequest):
|
|
1745
1802
|
"""Create a browser voice call using a frozen character and voice stack.
|
|
@@ -1788,17 +1845,30 @@ async def create_voice_call(payload: VoiceCallRequest):
|
|
|
1788
1845
|
robot_id = "operator-test"
|
|
1789
1846
|
robot_video_ref = None
|
|
1790
1847
|
if call_mode == "production":
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1848
|
+
# Claim-based binding. A production call no longer requires hardware to
|
|
1849
|
+
# have been enrolled ahead of time: whichever device opens the character
|
|
1850
|
+
# link claims that character, and holds it until another device opens the
|
|
1851
|
+
# same link. An explicitly requested robot that is already registered
|
|
1852
|
+
# still wins, so existing enrolled fleets behave exactly as before.
|
|
1853
|
+
robot_device = None
|
|
1854
|
+
if requested_robot_id:
|
|
1855
|
+
async with db.execute(
|
|
1856
|
+
"SELECT id, name FROM devices WHERE id = ? OR lower(name) = lower(?) ORDER BY last_heartbeat DESC LIMIT 1",
|
|
1857
|
+
(requested_robot_id, requested_robot_id),
|
|
1858
|
+
) as cursor:
|
|
1859
|
+
robot_device = await cursor.fetchone()
|
|
1860
|
+
if robot_device:
|
|
1861
|
+
robot_id = robot_device["id"]
|
|
1862
|
+
robot_video_ref = robot_device["name"] or robot_device["id"]
|
|
1863
|
+
else:
|
|
1864
|
+
robot_id, robot_video_ref = await _claim_character_device(
|
|
1865
|
+
db,
|
|
1866
|
+
character_id=character["id"],
|
|
1867
|
+
join_link_id=join_link_id,
|
|
1868
|
+
requested_robot_id=requested_robot_id,
|
|
1869
|
+
client_name=payload.client_name,
|
|
1870
|
+
now=now,
|
|
1871
|
+
)
|
|
1802
1872
|
metadata = {
|
|
1803
1873
|
"control_center": True,
|
|
1804
1874
|
"character_id": character["id"],
|
|
@@ -6993,6 +7063,114 @@ async def voice_client():
|
|
|
6993
7063
|
</body></html>
|
|
6994
7064
|
"""
|
|
6995
7065
|
|
|
7066
|
+
# ==========================================================================
|
|
7067
|
+
# Operator-facing pages served directly by the scheduler.
|
|
7068
|
+
#
|
|
7069
|
+
# These used to live behind the Next frontend (Docker) and the federation mesh
|
|
7070
|
+
# hub, which meant a one-command deploy was impossible and the join page existed
|
|
7071
|
+
# in three diverging copies. The scheduler already owns the character bindings
|
|
7072
|
+
# and mints the ElevenLabs signed URLs, so it serves the pages too: `robopark
|
|
7073
|
+
# serve` alone is now enough, and joining devices need only a browser.
|
|
7074
|
+
# ==========================================================================
|
|
7075
|
+
|
|
7076
|
+
STATIC_DIR = Path(__file__).resolve().parent / "static"
|
|
7077
|
+
|
|
7078
|
+
|
|
7079
|
+
@app.get("/voice-join.html", include_in_schema=False)
|
|
7080
|
+
async def voice_join_page():
|
|
7081
|
+
"""The character call page. Visitors open this; no install, no credentials."""
|
|
7082
|
+
page = STATIC_DIR / "voice-join.html"
|
|
7083
|
+
if not page.exists():
|
|
7084
|
+
raise HTTPException(404, "voice-join.html is missing from this install")
|
|
7085
|
+
return FileResponse(page, media_type="text/html; charset=utf-8")
|
|
7086
|
+
|
|
7087
|
+
|
|
7088
|
+
@app.get("/vendor/elevenlabs-client.iife.js", include_in_schema=False)
|
|
7089
|
+
async def elevenlabs_browser_client():
|
|
7090
|
+
"""Vendored ElevenLabs browser SDK, so joining devices need no npm install."""
|
|
7091
|
+
asset = STATIC_DIR / "vendor" / "elevenlabs-client.iife.js"
|
|
7092
|
+
if not asset.exists():
|
|
7093
|
+
raise HTTPException(404, "ElevenLabs browser client is missing from this install")
|
|
7094
|
+
return FileResponse(asset, media_type="application/javascript; charset=utf-8")
|
|
7095
|
+
|
|
7096
|
+
|
|
7097
|
+
def _call_link(character_id: str, agent_id: str, branch_id: str | None, mode: str) -> str:
|
|
7098
|
+
query = f"character={quote(character_id)}&agent_id={quote(agent_id)}&mode={mode}"
|
|
7099
|
+
if branch_id:
|
|
7100
|
+
query += f"&branch_id={quote(branch_id)}"
|
|
7101
|
+
return f"/voice-join.html?{query}"
|
|
7102
|
+
|
|
7103
|
+
|
|
7104
|
+
@app.get("/calls", response_class=HTMLResponse, include_in_schema=False)
|
|
7105
|
+
async def call_links_page():
|
|
7106
|
+
"""One page listing every preconfigured character call link, ready to copy.
|
|
7107
|
+
|
|
7108
|
+
Production links no longer depend on enrolled hardware — the first device to
|
|
7109
|
+
open one claims that character — so every character with a bound ElevenLabs
|
|
7110
|
+
agent gets a working production link here.
|
|
7111
|
+
"""
|
|
7112
|
+
async with aiosqlite.connect(DB_PATH) as db:
|
|
7113
|
+
db.row_factory = aiosqlite.Row
|
|
7114
|
+
async with db.execute(
|
|
7115
|
+
"""SELECT id, name, elevenlabs_agent_id, elevenlabs_branch_id, default_engine
|
|
7116
|
+
FROM character_presets ORDER BY name"""
|
|
7117
|
+
) as cursor:
|
|
7118
|
+
characters = await cursor.fetchall()
|
|
7119
|
+
|
|
7120
|
+
rows = []
|
|
7121
|
+
ready = 0
|
|
7122
|
+
for character in characters:
|
|
7123
|
+
name = html_escape(str(character["name"] or character["id"]))
|
|
7124
|
+
agent_id = (character["elevenlabs_agent_id"] or "").strip()
|
|
7125
|
+
if not agent_id:
|
|
7126
|
+
rows.append(
|
|
7127
|
+
f'<tr class="blocked"><td>{name}</td><td colspan="2">'
|
|
7128
|
+
"no ElevenLabs agent bound — set one in the Control Center</td></tr>"
|
|
7129
|
+
)
|
|
7130
|
+
continue
|
|
7131
|
+
ready += 1
|
|
7132
|
+
link = _call_link(character["id"], agent_id, character["elevenlabs_branch_id"], "production")
|
|
7133
|
+
test = _call_link(character["id"], agent_id, character["elevenlabs_branch_id"], "test")
|
|
7134
|
+
rows.append(
|
|
7135
|
+
f"<tr><td>{name}<span class=agent>{html_escape(agent_id)}</span></td>"
|
|
7136
|
+
f'<td><a href="{link}">production</a>'
|
|
7137
|
+
f'<button data-link="{link}">copy</button></td>'
|
|
7138
|
+
f'<td><a href="{test}">test</a><button data-link="{test}">copy</button></td></tr>'
|
|
7139
|
+
)
|
|
7140
|
+
|
|
7141
|
+
return f"""<!doctype html><html lang=en><head><meta charset=utf-8>
|
|
7142
|
+
<meta name=viewport content="width=device-width,initial-scale=1">
|
|
7143
|
+
<title>RoboPark character call links</title><style>
|
|
7144
|
+
body{{margin:0;padding:clamp(16px,4vw,40px);background:#0b0a07;color:#f4efe2;
|
|
7145
|
+
font:15px/1.5 Verdana,Geneva,sans-serif}}
|
|
7146
|
+
h1{{margin:0 0 4px;font:700 1.6rem Georgia,serif;color:#ffe9a8}}
|
|
7147
|
+
p.sub{{margin:0 0 22px;color:#a99e84;font-size:.85rem}}
|
|
7148
|
+
table{{width:100%;border-collapse:collapse;max-width:1000px}}
|
|
7149
|
+
th{{text-align:left;padding:.55rem .6rem;color:#a99e84;font:600 .7rem monospace;
|
|
7150
|
+
text-transform:uppercase;letter-spacing:.1em;border-bottom:1px solid #3a3222}}
|
|
7151
|
+
td{{padding:.7rem .6rem;border-bottom:1px solid #211d14;vertical-align:top}}
|
|
7152
|
+
.agent{{display:block;color:#7e755f;font:.62rem monospace;margin-top:.2rem}}
|
|
7153
|
+
a{{color:#f4cf72}}button{{margin-left:.5rem;border:1px solid #4a4130;border-radius:7px;
|
|
7154
|
+
background:#171308;color:#f4cf72;padding:.24rem .5rem;font:.68rem monospace;cursor:pointer}}
|
|
7155
|
+
button:hover{{background:#241d0e}}tr.blocked td{{color:#ff9d93}}
|
|
7156
|
+
</style></head><body>
|
|
7157
|
+
<h1>Character call links</h1>
|
|
7158
|
+
<p class=sub>{ready} of {len(characters)} characters ready ·
|
|
7159
|
+
production links claim the character for whichever device opens them ·
|
|
7160
|
+
no enrollment and no install required</p>
|
|
7161
|
+
<table><tr><th>Character</th><th>Production</th><th>Browser test</th></tr>
|
|
7162
|
+
{''.join(rows)}</table>
|
|
7163
|
+
<script>
|
|
7164
|
+
document.addEventListener('click',function(e){{
|
|
7165
|
+
var b=e.target.closest('button[data-link]');if(!b)return;
|
|
7166
|
+
var url=location.origin+b.getAttribute('data-link');
|
|
7167
|
+
navigator.clipboard.writeText(url).then(function(){{
|
|
7168
|
+
var t=b.textContent;b.textContent='copied';setTimeout(function(){{b.textContent=t}},1200);
|
|
7169
|
+
}});
|
|
7170
|
+
}});
|
|
7171
|
+
</script></body></html>"""
|
|
7172
|
+
|
|
7173
|
+
|
|
6996
7174
|
@app.get("/", response_class=HTMLResponse)
|
|
6997
7175
|
async def dashboard():
|
|
6998
7176
|
"""Serve embedded dashboard"""
|
|
@@ -7009,14 +7187,15 @@ async def dashboard():
|
|
|
7009
7187
|
</head>
|
|
7010
7188
|
<body class="bg-gray-900 text-white">
|
|
7011
7189
|
<div class="bg-amber-950 border-b border-amber-600 px-4 py-3 text-sm text-amber-100">
|
|
7012
|
-
<strong>
|
|
7013
|
-
<a id="fullControlCenterLink" class="underline ml-2" href="
|
|
7190
|
+
<strong>RoboPark Control Center.</strong> Character call links are ready to copy — production links claim the character for whichever device opens them, so no enrollment is needed.
|
|
7191
|
+
<a id="fullControlCenterLink" class="underline ml-2" href="/calls">Open character call links</a>
|
|
7014
7192
|
</div>
|
|
7015
7193
|
<div id="app"></div>
|
|
7016
7194
|
<script>
|
|
7017
|
-
|
|
7018
|
-
|
|
7019
|
-
|
|
7195
|
+
// The call links are served by this same process now; the old link
|
|
7196
|
+
// pointed at the federation mesh hub on 47913, which is no longer
|
|
7197
|
+
// required to run a deployment.
|
|
7198
|
+
document.getElementById('fullControlCenterLink').href = '/calls';
|
|
7020
7199
|
const { createApp, ref, computed, onMounted, onUnmounted } = Vue;
|
|
7021
7200
|
|
|
7022
7201
|
createApp({
|
package/scheduler/media_lock.py
CHANGED
|
@@ -1,57 +1,57 @@
|
|
|
1
|
-
"""Cross-process media ownership for Linux robot appliances."""
|
|
2
|
-
from __future__ import annotations
|
|
3
|
-
|
|
4
|
-
import os
|
|
5
|
-
import sys
|
|
6
|
-
import time
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
from typing import Optional
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class MediaLock:
|
|
12
|
-
def __init__(self, kind: str, timeout: float = 5.0):
|
|
13
|
-
self.kind = kind
|
|
14
|
-
self.timeout = timeout
|
|
15
|
-
self.fd: Optional[int] = None
|
|
16
|
-
|
|
17
|
-
def acquire(self) -> "MediaLock":
|
|
18
|
-
if not sys.platform.startswith("linux"):
|
|
19
|
-
return self
|
|
20
|
-
import fcntl
|
|
21
|
-
|
|
22
|
-
lock_dir = Path("/run/lock") if os.access("/run/lock", os.W_OK) else Path("/tmp")
|
|
23
|
-
path = lock_dir / f"robopark-{self.kind}.lock"
|
|
24
|
-
self.fd = os.open(path, os.O_CREAT | os.O_RDWR, 0o660)
|
|
25
|
-
deadline = time.monotonic() + self.timeout
|
|
26
|
-
while True:
|
|
27
|
-
try:
|
|
28
|
-
fcntl.flock(self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
|
29
|
-
os.ftruncate(self.fd, 0)
|
|
30
|
-
os.write(self.fd, f"{os.getpid()}\n".encode("ascii"))
|
|
31
|
-
return self
|
|
32
|
-
except BlockingIOError:
|
|
33
|
-
if time.monotonic() >= deadline:
|
|
34
|
-
self.release()
|
|
35
|
-
raise TimeoutError(f"{self.kind} is owned by another RoboPark media process")
|
|
36
|
-
time.sleep(0.05)
|
|
37
|
-
|
|
38
|
-
def release(self) -> None:
|
|
39
|
-
if self.fd is None:
|
|
40
|
-
return
|
|
41
|
-
try:
|
|
42
|
-
if sys.platform.startswith("linux"):
|
|
43
|
-
import fcntl
|
|
44
|
-
fcntl.flock(self.fd, fcntl.LOCK_UN)
|
|
45
|
-
finally:
|
|
46
|
-
os.close(self.fd)
|
|
47
|
-
self.fd = None
|
|
48
|
-
|
|
49
|
-
def __enter__(self) -> "MediaLock":
|
|
50
|
-
return self.acquire()
|
|
51
|
-
|
|
52
|
-
def __exit__(self, exc_type, exc, traceback) -> None:
|
|
53
|
-
self.release()
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def media_lock(kind: str, timeout: float = 5.0) -> MediaLock:
|
|
57
|
-
return MediaLock(kind, timeout)
|
|
1
|
+
"""Cross-process media ownership for Linux robot appliances."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
import time
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class MediaLock:
|
|
12
|
+
def __init__(self, kind: str, timeout: float = 5.0):
|
|
13
|
+
self.kind = kind
|
|
14
|
+
self.timeout = timeout
|
|
15
|
+
self.fd: Optional[int] = None
|
|
16
|
+
|
|
17
|
+
def acquire(self) -> "MediaLock":
|
|
18
|
+
if not sys.platform.startswith("linux"):
|
|
19
|
+
return self
|
|
20
|
+
import fcntl
|
|
21
|
+
|
|
22
|
+
lock_dir = Path("/run/lock") if os.access("/run/lock", os.W_OK) else Path("/tmp")
|
|
23
|
+
path = lock_dir / f"robopark-{self.kind}.lock"
|
|
24
|
+
self.fd = os.open(path, os.O_CREAT | os.O_RDWR, 0o660)
|
|
25
|
+
deadline = time.monotonic() + self.timeout
|
|
26
|
+
while True:
|
|
27
|
+
try:
|
|
28
|
+
fcntl.flock(self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
|
29
|
+
os.ftruncate(self.fd, 0)
|
|
30
|
+
os.write(self.fd, f"{os.getpid()}\n".encode("ascii"))
|
|
31
|
+
return self
|
|
32
|
+
except BlockingIOError:
|
|
33
|
+
if time.monotonic() >= deadline:
|
|
34
|
+
self.release()
|
|
35
|
+
raise TimeoutError(f"{self.kind} is owned by another RoboPark media process")
|
|
36
|
+
time.sleep(0.05)
|
|
37
|
+
|
|
38
|
+
def release(self) -> None:
|
|
39
|
+
if self.fd is None:
|
|
40
|
+
return
|
|
41
|
+
try:
|
|
42
|
+
if sys.platform.startswith("linux"):
|
|
43
|
+
import fcntl
|
|
44
|
+
fcntl.flock(self.fd, fcntl.LOCK_UN)
|
|
45
|
+
finally:
|
|
46
|
+
os.close(self.fd)
|
|
47
|
+
self.fd = None
|
|
48
|
+
|
|
49
|
+
def __enter__(self) -> "MediaLock":
|
|
50
|
+
return self.acquire()
|
|
51
|
+
|
|
52
|
+
def __exit__(self, exc_type, exc, traceback) -> None:
|
|
53
|
+
self.release()
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def media_lock(kind: str, timeout: float = 5.0) -> MediaLock:
|
|
57
|
+
return MediaLock(kind, timeout)
|