openvoiceui 1.0.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/.env.example +104 -0
- package/Dockerfile +30 -0
- package/LICENSE +21 -0
- package/README.md +638 -0
- package/SETUP.md +360 -0
- package/app.py +232 -0
- package/auto-approve-devices.js +111 -0
- package/cli/index.js +372 -0
- package/config/__init__.py +4 -0
- package/config/default.yaml +43 -0
- package/config/flags.yaml +67 -0
- package/config/loader.py +203 -0
- package/config/providers.yaml +71 -0
- package/config/speech_normalization.yaml +182 -0
- package/config/theme.json +4 -0
- package/data/greetings.json +25 -0
- package/default-pages/ai-image-creator.html +915 -0
- package/default-pages/bulk-image-uploader.html +492 -0
- package/default-pages/desktop.html +2865 -0
- package/default-pages/file-explorer.html +854 -0
- package/default-pages/interactive-map.html +655 -0
- package/default-pages/style-guide.html +1005 -0
- package/default-pages/website-setup.html +1623 -0
- package/deploy/openclaw/Dockerfile +46 -0
- package/deploy/openvoiceui.service +30 -0
- package/deploy/setup-nginx.sh +50 -0
- package/deploy/setup-sudo.sh +306 -0
- package/deploy/skill-runner/Dockerfile +19 -0
- package/deploy/skill-runner/requirements.txt +14 -0
- package/deploy/skill-runner/server.py +269 -0
- package/deploy/supertonic/Dockerfile +22 -0
- package/deploy/supertonic/server.py +79 -0
- package/docker-compose.pinokio.yml +11 -0
- package/docker-compose.yml +59 -0
- package/greetings.json +25 -0
- package/index.html +65 -0
- package/inject-device-identity.js +142 -0
- package/package.json +82 -0
- package/profiles/default.json +114 -0
- package/profiles/manager.py +354 -0
- package/profiles/schema.json +337 -0
- package/prompts/voice-system-prompt.md +149 -0
- package/providers/__init__.py +39 -0
- package/providers/base.py +63 -0
- package/providers/llm/__init__.py +12 -0
- package/providers/llm/base.py +71 -0
- package/providers/llm/clawdbot_provider.py +112 -0
- package/providers/llm/zai_provider.py +115 -0
- package/providers/registry.py +320 -0
- package/providers/stt/__init__.py +12 -0
- package/providers/stt/base.py +58 -0
- package/providers/stt/webspeech_provider.py +49 -0
- package/providers/stt/whisper_provider.py +100 -0
- package/providers/tts/__init__.py +20 -0
- package/providers/tts/base.py +91 -0
- package/providers/tts/groq_provider.py +74 -0
- package/providers/tts/supertonic_provider.py +72 -0
- package/requirements.txt +38 -0
- package/routes/__init__.py +10 -0
- package/routes/admin.py +515 -0
- package/routes/canvas.py +1315 -0
- package/routes/chat.py +51 -0
- package/routes/conversation.py +2158 -0
- package/routes/elevenlabs_hybrid.py +306 -0
- package/routes/greetings.py +98 -0
- package/routes/icons.py +279 -0
- package/routes/image_gen.py +364 -0
- package/routes/instructions.py +190 -0
- package/routes/music.py +838 -0
- package/routes/onboarding.py +43 -0
- package/routes/pi.py +62 -0
- package/routes/profiles.py +215 -0
- package/routes/report_issue.py +68 -0
- package/routes/static_files.py +533 -0
- package/routes/suno.py +664 -0
- package/routes/theme.py +81 -0
- package/routes/transcripts.py +199 -0
- package/routes/vision.py +348 -0
- package/routes/workspace.py +288 -0
- package/server.py +1510 -0
- package/services/__init__.py +1 -0
- package/services/auth.py +143 -0
- package/services/canvas_versioning.py +239 -0
- package/services/db_pool.py +107 -0
- package/services/gateway.py +16 -0
- package/services/gateway_manager.py +333 -0
- package/services/gateways/__init__.py +12 -0
- package/services/gateways/base.py +110 -0
- package/services/gateways/compat.py +264 -0
- package/services/gateways/openclaw.py +1134 -0
- package/services/health.py +100 -0
- package/services/memory_client.py +455 -0
- package/services/paths.py +26 -0
- package/services/speech_normalizer.py +285 -0
- package/services/tts.py +270 -0
- package/setup-config.js +262 -0
- package/sounds/air_horn.mp3 +0 -0
- package/sounds/bruh.mp3 +0 -0
- package/sounds/crowd_cheer.mp3 +0 -0
- package/sounds/gunshot.mp3 +0 -0
- package/sounds/impact.mp3 +0 -0
- package/sounds/lets_go.mp3 +0 -0
- package/sounds/record_stop.mp3 +0 -0
- package/sounds/rewind.mp3 +0 -0
- package/sounds/sad_trombone.mp3 +0 -0
- package/sounds/scratch_long.mp3 +0 -0
- package/sounds/yeah.mp3 +0 -0
- package/src/adapters/ClawdBotAdapter.js +264 -0
- package/src/adapters/_template.js +133 -0
- package/src/adapters/elevenlabs-classic.js +841 -0
- package/src/adapters/elevenlabs-hybrid.js +812 -0
- package/src/adapters/hume-evi.js +676 -0
- package/src/admin.html +1339 -0
- package/src/app.js +8802 -0
- package/src/core/Config.js +173 -0
- package/src/core/EmotionEngine.js +307 -0
- package/src/core/EventBridge.js +180 -0
- package/src/core/EventBus.js +117 -0
- package/src/core/VoiceSession.js +607 -0
- package/src/face/BaseFace.js +259 -0
- package/src/face/EyeFace.js +208 -0
- package/src/face/HaloSmokeFace.js +509 -0
- package/src/face/manifest.json +27 -0
- package/src/face/previews/eyes.svg +16 -0
- package/src/face/previews/orb.svg +29 -0
- package/src/features/MusicPlayer.js +620 -0
- package/src/features/Soundboard.js +128 -0
- package/src/providers/DeepgramSTT.js +472 -0
- package/src/providers/DeepgramStreamingSTT.js +766 -0
- package/src/providers/GroqSTT.js +559 -0
- package/src/providers/TTSPlayer.js +323 -0
- package/src/providers/WebSpeechSTT.js +479 -0
- package/src/providers/tts/BaseTTSProvider.js +81 -0
- package/src/providers/tts/HumeProvider.js +77 -0
- package/src/providers/tts/SupertonicProvider.js +174 -0
- package/src/providers/tts/index.js +140 -0
- package/src/shell/adapter-registry.js +154 -0
- package/src/shell/caller-bridge.js +35 -0
- package/src/shell/camera-bridge.js +28 -0
- package/src/shell/canvas-bridge.js +32 -0
- package/src/shell/commercial-bridge.js +44 -0
- package/src/shell/face-bridge.js +44 -0
- package/src/shell/music-bridge.js +60 -0
- package/src/shell/orchestrator.js +233 -0
- package/src/shell/profile-discovery.js +303 -0
- package/src/shell/sounds-bridge.js +28 -0
- package/src/shell/transcript-bridge.js +61 -0
- package/src/shell/waveform-bridge.js +33 -0
- package/src/styles/base.css +2862 -0
- package/src/styles/face.css +417 -0
- package/src/styles/pi-overrides.css +89 -0
- package/src/styles/theme-dark.css +67 -0
- package/src/test-tts.html +175 -0
- package/src/ui/AppShell.js +544 -0
- package/src/ui/ProfileSwitcher.js +228 -0
- package/src/ui/SessionControl.js +240 -0
- package/src/ui/face/FacePicker.js +195 -0
- package/src/ui/face/FaceRenderer.js +309 -0
- package/src/ui/settings/PlaylistEditor.js +366 -0
- package/src/ui/settings/SettingsPanel.css +684 -0
- package/src/ui/settings/SettingsPanel.js +419 -0
- package/src/ui/settings/TTSVoicePreview.js +210 -0
- package/src/ui/themes/ThemeManager.js +213 -0
- package/src/ui/visualizers/BaseVisualizer.js +29 -0
- package/src/ui/visualizers/PartyFXVisualizer.css +291 -0
- package/src/ui/visualizers/PartyFXVisualizer.js +637 -0
- package/static/emulators/jsdos/js-dos.css +1 -0
- package/static/emulators/jsdos/js-dos.js +22 -0
- package/static/favicon.svg +55 -0
- package/static/icons/apple-touch-icon.png +0 -0
- package/static/icons/favicon-32.png +0 -0
- package/static/icons/icon-192.png +0 -0
- package/static/icons/icon-512.png +0 -0
- package/static/install.html +449 -0
- package/static/manifest.json +26 -0
- package/static/sw.js +21 -0
- package/tts_providers/__init__.py +136 -0
- package/tts_providers/base_provider.py +319 -0
- package/tts_providers/groq_provider.py +155 -0
- package/tts_providers/hume_provider.py +226 -0
- package/tts_providers/providers_config.json +119 -0
- package/tts_providers/qwen3_provider.py +371 -0
- package/tts_providers/resemble_provider.py +315 -0
- package/tts_providers/supertonic_provider.py +557 -0
- package/tts_providers/supertonic_tts.py +399 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
FROM node:22-bookworm-slim
|
|
2
|
+
|
|
3
|
+
# System packages for native Node modules (canvas, sharp, etc.)
|
|
4
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
5
|
+
build-essential \
|
|
6
|
+
python3 \
|
|
7
|
+
git \
|
|
8
|
+
ca-certificates \
|
|
9
|
+
curl \
|
|
10
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
11
|
+
|
|
12
|
+
# Use pnpm via corepack (handles native modules better than npm)
|
|
13
|
+
RUN corepack enable && corepack prepare pnpm@latest --activate
|
|
14
|
+
ENV PNPM_HOME=/usr/local/share/pnpm
|
|
15
|
+
ENV PATH=$PNPM_HOME:$PATH
|
|
16
|
+
RUN mkdir -p $PNPM_HOME
|
|
17
|
+
|
|
18
|
+
# OpenClaw version — pinned to the version tested with this OpenVoiceUI release.
|
|
19
|
+
# Override at build time: docker compose build --build-arg OPENCLAW_VERSION=2026.3.7
|
|
20
|
+
ARG OPENCLAW_VERSION=2026.3.13
|
|
21
|
+
RUN pnpm add -g openclaw@${OPENCLAW_VERSION} && pnpm approve-builds -g
|
|
22
|
+
|
|
23
|
+
# Optional: install a coding CLI so the coding-agent skill is available.
|
|
24
|
+
# Set CODING_CLI at build time: docker compose build --build-arg CODING_CLI=codex
|
|
25
|
+
# Or set CODING_CLI=codex in your .env and use the docker-compose args block.
|
|
26
|
+
#
|
|
27
|
+
# Options (same as openclaw's setup wizard):
|
|
28
|
+
# codex — OpenAI Codex CLI (requires OPENAI_API_KEY)
|
|
29
|
+
# claude — Anthropic Claude Code (requires ANTHROPIC_API_KEY)
|
|
30
|
+
# opencode — OpenCode (bring your own provider key)
|
|
31
|
+
# pi — Pi coding agent (bring your own provider key)
|
|
32
|
+
# none — skip (coding-agent skill will be inactive)
|
|
33
|
+
ARG CODING_CLI=none
|
|
34
|
+
RUN if [ "$CODING_CLI" = "codex" ]; then \
|
|
35
|
+
pnpm add -g @openai/codex; \
|
|
36
|
+
elif [ "$CODING_CLI" = "claude" ]; then \
|
|
37
|
+
pnpm add -g @anthropic-ai/claude-code; \
|
|
38
|
+
elif [ "$CODING_CLI" = "opencode" ]; then \
|
|
39
|
+
pnpm add -g opencode-ai; \
|
|
40
|
+
elif [ "$CODING_CLI" = "pi" ]; then \
|
|
41
|
+
pnpm add -g @mariozechner/pi-coding-agent; \
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
EXPOSE 18791
|
|
45
|
+
|
|
46
|
+
CMD ["openclaw", "gateway", "--port", "18791"]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# OpenVoiceUI systemd service template
|
|
3
|
+
#
|
|
4
|
+
# !! EDIT BEFORE USING !!
|
|
5
|
+
# Replace all /path/to/OpenVoiceUI and YOUR_USER with real values, then:
|
|
6
|
+
#
|
|
7
|
+
# sudo cp deploy/openvoiceui.service /etc/systemd/system/
|
|
8
|
+
# sudo systemctl daemon-reload
|
|
9
|
+
# sudo systemctl enable openvoiceui
|
|
10
|
+
# sudo systemctl start openvoiceui
|
|
11
|
+
#
|
|
12
|
+
# Or use deploy/setup-sudo.sh which fills these in automatically.
|
|
13
|
+
# =============================================================================
|
|
14
|
+
|
|
15
|
+
[Unit]
|
|
16
|
+
Description=OpenVoiceUI Voice Agent Server
|
|
17
|
+
After=network.target
|
|
18
|
+
|
|
19
|
+
[Service]
|
|
20
|
+
Type=simple
|
|
21
|
+
User=YOUR_USER # ← your Linux username
|
|
22
|
+
WorkingDirectory=/path/to/OpenVoiceUI # ← full path to repo
|
|
23
|
+
ExecStart=/path/to/OpenVoiceUI/venv/bin/python3 /path/to/OpenVoiceUI/server.py
|
|
24
|
+
Restart=always
|
|
25
|
+
RestartSec=10
|
|
26
|
+
Environment=PATH=/usr/bin:/usr/local/bin
|
|
27
|
+
EnvironmentFile=/path/to/OpenVoiceUI/.env # ← full path to .env
|
|
28
|
+
|
|
29
|
+
[Install]
|
|
30
|
+
WantedBy=multi-user.target
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# OpenVoiceUI — quick nginx config for a single domain
|
|
4
|
+
#
|
|
5
|
+
# For a full setup (SSL, systemd service, www dirs) use deploy/setup-sudo.sh instead.
|
|
6
|
+
# This script is for manual nginx-only configuration.
|
|
7
|
+
#
|
|
8
|
+
# Edit DOMAIN, PORT, and EMAIL before running.
|
|
9
|
+
# Run as: sudo bash setup-nginx.sh
|
|
10
|
+
# =============================================================================
|
|
11
|
+
|
|
12
|
+
DOMAIN="your-domain.com" # ← EDIT: your actual domain
|
|
13
|
+
PORT=5001 # ← match PORT in your .env (default: 5001)
|
|
14
|
+
EMAIL="your-email@example.com" # ← EDIT: for Let's Encrypt notifications
|
|
15
|
+
|
|
16
|
+
# Guard: refuse to run with placeholder values
|
|
17
|
+
if [ "$DOMAIN" = "your-domain.com" ] || [ "$EMAIL" = "your-email@example.com" ]; then
|
|
18
|
+
echo "ERROR: Edit DOMAIN and EMAIL at the top of this script before running."
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
cat << NGINX | sudo tee /etc/nginx/sites-available/${DOMAIN}
|
|
23
|
+
server {
|
|
24
|
+
listen 80;
|
|
25
|
+
listen [::]:80;
|
|
26
|
+
server_name ${DOMAIN};
|
|
27
|
+
|
|
28
|
+
location / {
|
|
29
|
+
proxy_pass http://127.0.0.1:${PORT};
|
|
30
|
+
proxy_http_version 1.1;
|
|
31
|
+
proxy_set_header Upgrade \$http_upgrade;
|
|
32
|
+
proxy_set_header Connection 'upgrade';
|
|
33
|
+
proxy_set_header Host \$host;
|
|
34
|
+
proxy_set_header X-Real-IP \$remote_addr;
|
|
35
|
+
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
|
36
|
+
proxy_set_header X-Forwarded-Proto \$scheme;
|
|
37
|
+
proxy_cache_bypass \$http_upgrade;
|
|
38
|
+
proxy_read_timeout 86400;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
client_max_body_size 100M;
|
|
42
|
+
}
|
|
43
|
+
NGINX
|
|
44
|
+
|
|
45
|
+
sudo ln -sf /etc/nginx/sites-available/${DOMAIN} /etc/nginx/sites-enabled/
|
|
46
|
+
sudo nginx -t && sudo systemctl reload nginx
|
|
47
|
+
sudo certbot --nginx -d ${DOMAIN} --non-interactive --agree-tos -m ${EMAIL}
|
|
48
|
+
|
|
49
|
+
echo ""
|
|
50
|
+
echo "Done. OpenVoiceUI accessible at https://${DOMAIN}"
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# OpenVoiceUI — generic sudo setup script
|
|
4
|
+
# Creates: nginx config, Let's Encrypt SSL, systemd service, prestart script
|
|
5
|
+
# Run as: sudo bash deploy/setup-sudo.sh
|
|
6
|
+
# =============================================================================
|
|
7
|
+
set -e
|
|
8
|
+
|
|
9
|
+
# ── Configure these before running ──────────────────────────────────────────
|
|
10
|
+
INSTALL_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
11
|
+
DOMAIN="your-domain.com" # ← EDIT: your actual domain
|
|
12
|
+
PORT=5001 # ← match PORT in your .env (default: 5001)
|
|
13
|
+
EMAIL="your@email.com" # ← EDIT: for Let's Encrypt notifications
|
|
14
|
+
SERVICE_NAME="openvoiceui"
|
|
15
|
+
RUN_USER="${SUDO_USER:-$(whoami)}"
|
|
16
|
+
WWW_DIR="/var/www/${SERVICE_NAME}" # canvas pages + any web assets
|
|
17
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
# Guard: refuse to run with placeholder values
|
|
20
|
+
if [ "$DOMAIN" = "your-domain.com" ] || [ "$EMAIL" = "your@email.com" ]; then
|
|
21
|
+
echo "ERROR: Edit DOMAIN and EMAIL at the top of this script before running."
|
|
22
|
+
echo " Open deploy/setup-sudo.sh in a text editor and set your real domain and email."
|
|
23
|
+
exit 1
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
# Check .env exists
|
|
27
|
+
if [ ! -f "${INSTALL_DIR}/.env" ]; then
|
|
28
|
+
echo "ERROR: No .env file found at ${INSTALL_DIR}/.env"
|
|
29
|
+
echo " Run: cp ${INSTALL_DIR}/.env.example ${INSTALL_DIR}/.env"
|
|
30
|
+
echo " Then edit .env and set your API keys before running this script."
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
# ── OpenClaw gateway detection ────────────────────────────────────────────────
|
|
35
|
+
# Helper: is OpenClaw currently listening on its default port?
|
|
36
|
+
_openclaw_running() {
|
|
37
|
+
ss -tlnp 2>/dev/null | grep -q ':18791'
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# Helper: is the openclaw binary installed anywhere?
|
|
41
|
+
_openclaw_installed() {
|
|
42
|
+
command -v openclaw >/dev/null 2>&1 \
|
|
43
|
+
|| [ -f "/usr/local/bin/openclaw" ] \
|
|
44
|
+
|| [ -f "${HOME}/.local/bin/openclaw" ]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
# Helper: try to start openclaw and wait up to 5s for port to open
|
|
48
|
+
_openclaw_start() {
|
|
49
|
+
echo " Attempting to start OpenClaw..."
|
|
50
|
+
if command -v openclaw >/dev/null 2>&1; then
|
|
51
|
+
openclaw start 2>/dev/null &
|
|
52
|
+
elif [ -f "/usr/local/bin/openclaw" ]; then
|
|
53
|
+
/usr/local/bin/openclaw start 2>/dev/null &
|
|
54
|
+
else
|
|
55
|
+
return 1
|
|
56
|
+
fi
|
|
57
|
+
local i=0
|
|
58
|
+
while [ $i -lt 5 ]; do
|
|
59
|
+
sleep 1
|
|
60
|
+
_openclaw_running && return 0
|
|
61
|
+
i=$((i+1))
|
|
62
|
+
done
|
|
63
|
+
return 1
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
echo ""
|
|
67
|
+
echo "── OpenClaw Gateway ──────────────────────────────────────────────────────"
|
|
68
|
+
|
|
69
|
+
OPENCLAW_GATEWAY_URL=$(grep -E "^CLAWDBOT_GATEWAY_URL=" "${INSTALL_DIR}/.env" 2>/dev/null | cut -d= -f2 | tr -d '"' | tr -d "'")
|
|
70
|
+
OPENCLAW_GATEWAY_URL="${OPENCLAW_GATEWAY_URL:-ws://127.0.0.1:18791}"
|
|
71
|
+
OPENCLAW_TOKEN=$(grep -E "^CLAWDBOT_AUTH_TOKEN=" "${INSTALL_DIR}/.env" 2>/dev/null | cut -d= -f2 | tr -d '"' | tr -d "'")
|
|
72
|
+
OPENCLAW_CONFIGURED=false
|
|
73
|
+
|
|
74
|
+
if _openclaw_running; then
|
|
75
|
+
echo " ✓ OpenClaw is running (port 18791 active)"
|
|
76
|
+
printf " Use this OpenClaw instance for OpenVoiceUI? [Y/n] "
|
|
77
|
+
read -r REPLY
|
|
78
|
+
if [[ $REPLY =~ ^[Nn]$ ]]; then
|
|
79
|
+
printf " Enter your gateway WebSocket URL [${OPENCLAW_GATEWAY_URL}]: "
|
|
80
|
+
read -r CUSTOM_URL
|
|
81
|
+
if [ -n "$CUSTOM_URL" ]; then
|
|
82
|
+
sed -i "s|^CLAWDBOT_GATEWAY_URL=.*|CLAWDBOT_GATEWAY_URL=${CUSTOM_URL}|" "${INSTALL_DIR}/.env"
|
|
83
|
+
OPENCLAW_GATEWAY_URL="$CUSTOM_URL"
|
|
84
|
+
echo " Updated CLAWDBOT_GATEWAY_URL in .env"
|
|
85
|
+
fi
|
|
86
|
+
fi
|
|
87
|
+
OPENCLAW_CONFIGURED=true
|
|
88
|
+
|
|
89
|
+
elif _openclaw_installed; then
|
|
90
|
+
echo " OpenClaw is installed but not running."
|
|
91
|
+
printf " Start OpenClaw now? [Y/n] "
|
|
92
|
+
read -r REPLY
|
|
93
|
+
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
|
|
94
|
+
if _openclaw_start; then
|
|
95
|
+
echo " ✓ OpenClaw started successfully."
|
|
96
|
+
OPENCLAW_CONFIGURED=true
|
|
97
|
+
else
|
|
98
|
+
echo " ⚠ Could not start OpenClaw automatically."
|
|
99
|
+
echo " Start it manually (e.g. 'openclaw start' or restart its service),"
|
|
100
|
+
echo " then re-run this script — or continue and start it later."
|
|
101
|
+
printf " Continue anyway? [y/N] "
|
|
102
|
+
read -r REPLY
|
|
103
|
+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
104
|
+
echo "Stopping. Start OpenClaw and re-run this script."
|
|
105
|
+
exit 1
|
|
106
|
+
fi
|
|
107
|
+
fi
|
|
108
|
+
else
|
|
109
|
+
OPENCLAW_CONFIGURED=false
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
else
|
|
113
|
+
echo " OpenClaw is not installed on this system."
|
|
114
|
+
echo " OpenClaw is the AI gateway that processes all voice conversations."
|
|
115
|
+
echo ""
|
|
116
|
+
printf " Install OpenClaw now? [Y/n] "
|
|
117
|
+
read -r REPLY
|
|
118
|
+
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
|
|
119
|
+
echo ""
|
|
120
|
+
echo " ── OpenClaw Installation ─────────────────────────────────────"
|
|
121
|
+
echo " 1. Visit https://openclaw.ai and follow the install guide"
|
|
122
|
+
echo " 2. Run the installer for your OS"
|
|
123
|
+
echo " 3. Start OpenClaw (it will listen on ws://127.0.0.1:18791)"
|
|
124
|
+
echo " 4. Create an agent workspace and copy your auth token"
|
|
125
|
+
echo " ──────────────────────────────────────────────────────────────"
|
|
126
|
+
echo ""
|
|
127
|
+
printf " Press Enter once OpenClaw is installed and running (Ctrl+C to abort)... "
|
|
128
|
+
read -r
|
|
129
|
+
if _openclaw_running; then
|
|
130
|
+
echo " ✓ OpenClaw detected — continuing."
|
|
131
|
+
OPENCLAW_CONFIGURED=true
|
|
132
|
+
else
|
|
133
|
+
echo " ⚠ OpenClaw port 18791 is still not responding."
|
|
134
|
+
printf " Continue anyway and configure OpenClaw later? [y/N] "
|
|
135
|
+
read -r REPLY
|
|
136
|
+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
137
|
+
echo "Stopping. Install and start OpenClaw, then re-run this script."
|
|
138
|
+
exit 1
|
|
139
|
+
fi
|
|
140
|
+
fi
|
|
141
|
+
else
|
|
142
|
+
echo ""
|
|
143
|
+
echo " ┌────────────────────────────────────────────────────────────────┐"
|
|
144
|
+
echo " │ OpenVoiceUI requires a gateway framework to process voice │"
|
|
145
|
+
echo " │ conversations. Without one the server will start but the │"
|
|
146
|
+
echo " │ voice agent will not respond to any input. │"
|
|
147
|
+
echo " │ │"
|
|
148
|
+
echo " │ You can install a compatible gateway later and point .env at │"
|
|
149
|
+
echo " │ it via CLAWDBOT_GATEWAY_URL + CLAWDBOT_AUTH_TOKEN. │"
|
|
150
|
+
echo " │ See plugins/README.md for building a custom gateway plugin. │"
|
|
151
|
+
echo " └────────────────────────────────────────────────────────────────┘"
|
|
152
|
+
echo ""
|
|
153
|
+
printf " Continue setup without a gateway? [y/N] "
|
|
154
|
+
read -r REPLY
|
|
155
|
+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
156
|
+
echo "Stopping. Visit https://openclaw.ai to get started."
|
|
157
|
+
exit 1
|
|
158
|
+
fi
|
|
159
|
+
echo " Continuing — the server will be installed but conversations will not work"
|
|
160
|
+
echo " until a gateway is configured."
|
|
161
|
+
fi
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
# Check auth token is set and not placeholder
|
|
165
|
+
if [ "$OPENCLAW_CONFIGURED" = "true" ]; then
|
|
166
|
+
if [ -z "$OPENCLAW_TOKEN" ] || [ "$OPENCLAW_TOKEN" = "your-openclaw-gateway-token" ]; then
|
|
167
|
+
echo ""
|
|
168
|
+
echo " ⚠ CLAWDBOT_AUTH_TOKEN is not set in .env."
|
|
169
|
+
echo " OpenClaw is running but OpenVoiceUI cannot authenticate to it."
|
|
170
|
+
echo " Copy your agent auth token from your OpenClaw workspace and set:"
|
|
171
|
+
echo " CLAWDBOT_AUTH_TOKEN=your-token-here"
|
|
172
|
+
echo " in ${INSTALL_DIR}/.env, then restart the service."
|
|
173
|
+
else
|
|
174
|
+
echo " ✓ Auth token configured. Gateway: ${OPENCLAW_GATEWAY_URL}"
|
|
175
|
+
fi
|
|
176
|
+
echo ""
|
|
177
|
+
echo " Note: The AI model (LLM) is configured inside your OpenClaw agent"
|
|
178
|
+
echo " workspace — OpenVoiceUI works with any model OpenClaw is connected to."
|
|
179
|
+
echo " Voice settings (TTS, profile, wake words) are in the admin dashboard:"
|
|
180
|
+
echo " https://${DOMAIN}/src/admin.html"
|
|
181
|
+
fi
|
|
182
|
+
echo ""
|
|
183
|
+
|
|
184
|
+
echo "=== OpenVoiceUI setup: ${DOMAIN} on port ${PORT} ==="
|
|
185
|
+
echo " Install dir : ${INSTALL_DIR}"
|
|
186
|
+
echo " Service user: ${RUN_USER}"
|
|
187
|
+
echo " WWW dir : ${WWW_DIR}"
|
|
188
|
+
echo ""
|
|
189
|
+
|
|
190
|
+
# 0. Per-instance www directory (canvas pages, isolated from other users)
|
|
191
|
+
echo "[0/5] Creating www directory for ${RUN_USER}..."
|
|
192
|
+
mkdir -p "${WWW_DIR}/canvas-pages"
|
|
193
|
+
chown -R "${RUN_USER}:${RUN_USER}" "${WWW_DIR}"
|
|
194
|
+
chmod -R 755 "${WWW_DIR}"
|
|
195
|
+
|
|
196
|
+
# 1. Prestart script (kills stale process on port before service starts)
|
|
197
|
+
echo "[1/5] Creating prestart script..."
|
|
198
|
+
cat > /usr/local/bin/prestart-${SERVICE_NAME}.sh << PRESTART
|
|
199
|
+
#!/bin/bash
|
|
200
|
+
PORT=${PORT}
|
|
201
|
+
LOG=/var/log/${SERVICE_NAME}.log
|
|
202
|
+
PID=\$(fuser \${PORT}/tcp 2>/dev/null)
|
|
203
|
+
if [ -n "\$PID" ]; then
|
|
204
|
+
echo "\$(date): Found stale process \$PID on port \$PORT, killing..." | tee -a \$LOG
|
|
205
|
+
kill \$PID 2>/dev/null
|
|
206
|
+
sleep 2
|
|
207
|
+
if kill -0 \$PID 2>/dev/null; then
|
|
208
|
+
kill -9 \$PID 2>/dev/null
|
|
209
|
+
sleep 1
|
|
210
|
+
fi
|
|
211
|
+
fi
|
|
212
|
+
exit 0
|
|
213
|
+
PRESTART
|
|
214
|
+
chmod +x /usr/local/bin/prestart-${SERVICE_NAME}.sh
|
|
215
|
+
|
|
216
|
+
# 2. Systemd service
|
|
217
|
+
echo "[2/5] Creating systemd service..."
|
|
218
|
+
cat > /etc/systemd/system/${SERVICE_NAME}.service << SERVICE
|
|
219
|
+
[Unit]
|
|
220
|
+
Description=OpenVoiceUI Voice Agent (${DOMAIN})
|
|
221
|
+
After=network.target
|
|
222
|
+
|
|
223
|
+
[Service]
|
|
224
|
+
Type=simple
|
|
225
|
+
User=${RUN_USER}
|
|
226
|
+
WorkingDirectory=${INSTALL_DIR}
|
|
227
|
+
ExecStartPre=/usr/local/bin/prestart-${SERVICE_NAME}.sh
|
|
228
|
+
ExecStart=${INSTALL_DIR}/venv/bin/python3 ${INSTALL_DIR}/server.py
|
|
229
|
+
Restart=always
|
|
230
|
+
RestartSec=10
|
|
231
|
+
Environment=PATH=/usr/bin:/usr/local/bin
|
|
232
|
+
EnvironmentFile=${INSTALL_DIR}/.env
|
|
233
|
+
|
|
234
|
+
[Install]
|
|
235
|
+
WantedBy=multi-user.target
|
|
236
|
+
SERVICE
|
|
237
|
+
|
|
238
|
+
# 3. Nginx config
|
|
239
|
+
echo "[3/5] Creating nginx config..."
|
|
240
|
+
cat > /etc/nginx/sites-available/${DOMAIN} << NGINX
|
|
241
|
+
server {
|
|
242
|
+
listen 80;
|
|
243
|
+
listen [::]:80;
|
|
244
|
+
server_name ${DOMAIN};
|
|
245
|
+
return 301 https://\$host\$request_uri;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
server {
|
|
249
|
+
listen 443 ssl http2;
|
|
250
|
+
listen [::]:443 ssl http2;
|
|
251
|
+
server_name ${DOMAIN};
|
|
252
|
+
|
|
253
|
+
ssl_certificate /etc/letsencrypt/live/${DOMAIN}/fullchain.pem;
|
|
254
|
+
ssl_certificate_key /etc/letsencrypt/live/${DOMAIN}/privkey.pem;
|
|
255
|
+
include /etc/letsencrypt/options-ssl-nginx.conf;
|
|
256
|
+
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
|
257
|
+
|
|
258
|
+
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
259
|
+
add_header X-Content-Type-Options "nosniff" always;
|
|
260
|
+
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
|
261
|
+
|
|
262
|
+
location / {
|
|
263
|
+
proxy_pass http://127.0.0.1:${PORT};
|
|
264
|
+
proxy_http_version 1.1;
|
|
265
|
+
proxy_set_header Upgrade \$http_upgrade;
|
|
266
|
+
proxy_set_header Connection 'upgrade';
|
|
267
|
+
proxy_set_header Host \$host;
|
|
268
|
+
proxy_set_header X-Real-IP \$remote_addr;
|
|
269
|
+
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
|
270
|
+
proxy_set_header X-Forwarded-Proto \$scheme;
|
|
271
|
+
proxy_cache_bypass \$http_upgrade;
|
|
272
|
+
proxy_read_timeout 86400;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
client_max_body_size 100M;
|
|
276
|
+
}
|
|
277
|
+
NGINX
|
|
278
|
+
|
|
279
|
+
ln -sf /etc/nginx/sites-available/${DOMAIN} /etc/nginx/sites-enabled/${DOMAIN}
|
|
280
|
+
|
|
281
|
+
# 4. SSL cert
|
|
282
|
+
echo "[4/5] Obtaining SSL certificate..."
|
|
283
|
+
if [ ! -f "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" ]; then
|
|
284
|
+
certbot certonly --nginx -d ${DOMAIN} --non-interactive --agree-tos -m ${EMAIL}
|
|
285
|
+
else
|
|
286
|
+
echo " SSL cert already exists, skipping."
|
|
287
|
+
fi
|
|
288
|
+
|
|
289
|
+
# 5. Enable and start service
|
|
290
|
+
echo "[5/5] Enabling and starting service..."
|
|
291
|
+
nginx -t
|
|
292
|
+
systemctl reload nginx
|
|
293
|
+
systemctl daemon-reload
|
|
294
|
+
systemctl enable ${SERVICE_NAME}.service
|
|
295
|
+
systemctl restart ${SERVICE_NAME}.service
|
|
296
|
+
|
|
297
|
+
sleep 3
|
|
298
|
+
systemctl status ${SERVICE_NAME}.service --no-pager
|
|
299
|
+
|
|
300
|
+
echo ""
|
|
301
|
+
echo "=== Done! OpenVoiceUI running at https://${DOMAIN} ==="
|
|
302
|
+
echo ""
|
|
303
|
+
echo "Useful commands:"
|
|
304
|
+
echo " sudo systemctl status ${SERVICE_NAME}"
|
|
305
|
+
echo " sudo systemctl restart ${SERVICE_NAME}"
|
|
306
|
+
echo " sudo journalctl -u ${SERVICE_NAME} -f"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
FROM python:3.12-slim
|
|
2
|
+
|
|
3
|
+
WORKDIR /app
|
|
4
|
+
|
|
5
|
+
# Install OS deps needed by lxml (python-docx/pptx) and matplotlib
|
|
6
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
7
|
+
libxml2 \
|
|
8
|
+
libxslt1.1 \
|
|
9
|
+
libfreetype6 \
|
|
10
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
11
|
+
|
|
12
|
+
COPY requirements.txt .
|
|
13
|
+
RUN pip install --no-cache-dir -r requirements.txt
|
|
14
|
+
|
|
15
|
+
COPY server.py .
|
|
16
|
+
|
|
17
|
+
EXPOSE 8900
|
|
18
|
+
|
|
19
|
+
CMD ["python", "server.py"]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# skill-runner — shared heavy-dependency service for all JamBot users
|
|
2
|
+
# All packages live here so the openvoiceui/openclaw containers stay lean.
|
|
3
|
+
|
|
4
|
+
flask>=3.0.0
|
|
5
|
+
|
|
6
|
+
# Document text extraction
|
|
7
|
+
pypdf>=4.0.0
|
|
8
|
+
python-docx>=1.1.0
|
|
9
|
+
openpyxl>=3.1.0
|
|
10
|
+
python-pptx>=0.6.23
|
|
11
|
+
|
|
12
|
+
# CSV / data analysis
|
|
13
|
+
pandas>=2.0.0
|
|
14
|
+
matplotlib>=3.7.0
|