agentvibes 5.10.1 → 5.11.1

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.
Files changed (160) hide show
  1. package/.agentvibes/config.json +15 -1
  2. package/.agentvibes/install-manifest.json +342 -0
  3. package/.claude/audio/ui/CREDITS.txt +16 -0
  4. package/.claude/audio/ui/bling-success.wav +0 -0
  5. package/.claude/commands/agent-vibes/receiver.md +64 -0
  6. package/.claude/commands/agent-vibes-bmad-voices.md +117 -117
  7. package/.claude/commands/agent-vibes-rdp.md +24 -24
  8. package/.claude/config/audio-effects.cfg +5 -4
  9. package/.claude/config/audio-effects.cfg.sample +52 -52
  10. package/.claude/config/background-music-enabled.txt +1 -1
  11. package/.claude/config/background-music-position.txt +27 -0
  12. package/.claude/config/background-music-volume.txt +1 -0
  13. package/.claude/config/background-music.cfg +1 -0
  14. package/.claude/config/background-music.txt +1 -0
  15. package/.claude/config/language.txt +1 -0
  16. package/.claude/config/personality.txt +1 -0
  17. package/.claude/config/reverb-level.txt +1 -0
  18. package/.claude/config/tts-speech-rate.txt +1 -0
  19. package/.claude/config/tts-verbosity.txt +1 -0
  20. package/.claude/docs/TERMUX_SETUP.md +408 -408
  21. package/.claude/github-star-reminder.txt +1 -1
  22. package/.claude/hooks/audio-cache-utils.sh +0 -0
  23. package/.claude/hooks/audio-processor.sh +57 -18
  24. package/.claude/hooks/background-music-manager.sh +0 -0
  25. package/.claude/hooks/bmad-party-speak.sh +0 -0
  26. package/.claude/hooks/bmad-speak-enhanced.sh +0 -0
  27. package/.claude/hooks/bmad-speak.sh +0 -0
  28. package/.claude/hooks/bmad-tts-injector.sh +0 -0
  29. package/.claude/hooks/bmad-voice-manager.sh +0 -0
  30. package/.claude/hooks/clawdbot-receiver-SECURE.sh +0 -0
  31. package/.claude/hooks/clawdbot-receiver.sh +0 -0
  32. package/.claude/hooks/clean-audio-cache.sh +0 -0
  33. package/.claude/hooks/cleanup-cache.sh +0 -0
  34. package/.claude/hooks/configure-rdp-mode.sh +0 -0
  35. package/.claude/hooks/download-extra-voices.sh +0 -0
  36. package/.claude/hooks/effects-manager.sh +0 -0
  37. package/.claude/hooks/github-star-reminder.sh +0 -0
  38. package/.claude/hooks/kokoro-installer.sh +117 -0
  39. package/.claude/hooks/kokoro-server.py +219 -0
  40. package/.claude/hooks/kokoro-tts.py +141 -0
  41. package/.claude/hooks/language-manager.sh +0 -0
  42. package/.claude/hooks/learn-manager.sh +0 -0
  43. package/.claude/hooks/macos-voice-manager.sh +0 -0
  44. package/.claude/hooks/migrate-background-music.sh +0 -0
  45. package/.claude/hooks/migrate-to-agentvibes.sh +0 -0
  46. package/.claude/hooks/optimize-background-music.sh +0 -0
  47. package/.claude/hooks/path-resolver.sh +0 -0
  48. package/.claude/hooks/personality-manager.sh +0 -0
  49. package/.claude/hooks/piper-download-voices.sh +39 -16
  50. package/.claude/hooks/piper-installer.sh +11 -6
  51. package/.claude/hooks/piper-multispeaker-registry.sh +0 -0
  52. package/.claude/hooks/piper-voice-manager.sh +6 -6
  53. package/.claude/hooks/play-tts-agentvibes-receiver-for-voiceless-connections.sh +3 -1
  54. package/.claude/hooks/play-tts-agentvibes-receiver.sh +1 -0
  55. package/.claude/hooks/play-tts-elevenlabs.sh +360 -0
  56. package/.claude/hooks/play-tts-enhanced.sh +0 -0
  57. package/.claude/hooks/play-tts-kokoro.sh +127 -0
  58. package/.claude/hooks/play-tts-macos.sh +0 -0
  59. package/.claude/hooks/play-tts-piper.sh +0 -0
  60. package/.claude/hooks/play-tts-soprano.sh +0 -0
  61. package/.claude/hooks/play-tts-ssh-remote.sh +9 -2
  62. package/.claude/hooks/play-tts-termux-ssh.sh +3 -1
  63. package/.claude/hooks/play-tts-windows-receiver.sh +0 -0
  64. package/.claude/hooks/play-tts.sh +78 -2
  65. package/.claude/hooks/prepare-release.sh +0 -0
  66. package/.claude/hooks/provider-commands.sh +71 -22
  67. package/.claude/hooks/provider-manager.sh +15 -7
  68. package/.claude/hooks/replay-target-audio.sh +0 -0
  69. package/.claude/hooks/requirements.txt +6 -6
  70. package/.claude/hooks/sentiment-manager.sh +0 -0
  71. package/.claude/hooks/session-start-tts.sh +0 -0
  72. package/.claude/hooks/soprano-gradio-synth.py +139 -139
  73. package/.claude/hooks/speed-manager.sh +0 -0
  74. package/.claude/hooks/stop-tts.sh +0 -0
  75. package/.claude/hooks/termux-installer.sh +0 -0
  76. package/.claude/hooks/translate-manager.sh +0 -0
  77. package/.claude/hooks/translator.py +237 -237
  78. package/.claude/hooks/tts-queue-worker.sh +0 -0
  79. package/.claude/hooks/tts-queue.sh +0 -0
  80. package/.claude/hooks/verbosity-manager.sh +0 -0
  81. package/.claude/hooks/voice-manager.sh +0 -0
  82. package/.claude/hooks-windows/audio-cache-utils.ps1.user.bak +119 -0
  83. package/.claude/hooks-windows/kokoro-server.py +219 -0
  84. package/.claude/hooks-windows/kokoro-tts.py +107 -0
  85. package/.claude/hooks-windows/play-tts-kokoro.ps1 +191 -0
  86. package/.claude/hooks-windows/play-tts.ps1 +29 -1
  87. package/.claude/hooks-windows/soprano-gradio-synth.py +153 -153
  88. package/.claude/hooks-windows/soprano-gradio-synth.py.user.bak +153 -0
  89. package/.claude/piper-voices-dir.txt +1 -0
  90. package/.claude/verbosity.txt +1 -1
  91. package/.clawdbot/README.md +105 -105
  92. package/.mcp.json +6 -19
  93. package/README.md +127 -2116
  94. package/RELEASE_NOTES.md +73 -0
  95. package/WINDOWS-SETUP.md +208 -208
  96. package/bin/agent-vibes +39 -39
  97. package/bin/agentvibes-voice-browser.js +0 -0
  98. package/bin/agentvibes.js +0 -0
  99. package/bin/mcp-server.js +121 -121
  100. package/bin/mcp-server.sh +0 -0
  101. package/bin/test-bmad-pr +78 -78
  102. package/mcp-server/QUICK_START.md +203 -203
  103. package/mcp-server/README.md +345 -345
  104. package/mcp-server/WINDOWS_SETUP.md +0 -0
  105. package/mcp-server/examples/claude_desktop_config.json +11 -11
  106. package/mcp-server/examples/claude_desktop_config_piper.json +9 -9
  107. package/mcp-server/examples/custom_instructions.md +169 -169
  108. package/mcp-server/install-deps.js +0 -0
  109. package/mcp-server/server.py +1807 -1807
  110. package/mcp-server/test_server.py +0 -0
  111. package/package.json +4 -3
  112. package/src/cli/list-personalities.js +110 -110
  113. package/src/cli/list-voices.js +114 -114
  114. package/src/commands/bmad-voices.js +394 -394
  115. package/src/commands/install-mcp.js +3 -3
  116. package/src/console/app.js +25 -9
  117. package/src/console/audio-env.js +85 -1
  118. package/src/console/brand-colors.js +13 -13
  119. package/src/console/constants/personalities.js +44 -44
  120. package/src/console/navigation.js +4 -0
  121. package/src/console/tabs/agents-tab.js +12 -6
  122. package/src/console/tabs/help-tab.js +314 -314
  123. package/src/console/tabs/music-tab.js +7 -25
  124. package/src/console/tabs/readme-tab.js +272 -272
  125. package/src/console/tabs/setup-tab.js +1704 -120
  126. package/src/console/tabs/voices-tab.js +72 -88
  127. package/src/console/widgets/destroy-list.js +25 -25
  128. package/src/console/widgets/format-utils.js +14 -2
  129. package/src/console/widgets/help-bar.js +55 -0
  130. package/src/console/widgets/notice.js +55 -55
  131. package/src/console/widgets/reverb-picker.js +429 -41
  132. package/src/console/widgets/track-picker.js +60 -51
  133. package/src/i18n/de.js +2 -1
  134. package/src/i18n/en.js +1 -0
  135. package/src/i18n/es.js +2 -1
  136. package/src/i18n/fr.js +2 -1
  137. package/src/i18n/hi.js +2 -1
  138. package/src/i18n/ja.js +2 -1
  139. package/src/i18n/ko.js +2 -1
  140. package/src/i18n/pt.js +2 -1
  141. package/src/i18n/strings.js +9 -9
  142. package/src/i18n/zh-CN.js +2 -1
  143. package/src/installer/language-screen.js +31 -31
  144. package/src/installer/music-file-input.js +304 -304
  145. package/src/installer.js +95 -7
  146. package/src/services/config-service.js +264 -264
  147. package/src/services/language-service.js +47 -47
  148. package/src/services/provider-service.js +35 -0
  149. package/src/services/provider-voice-catalog.js +126 -0
  150. package/src/services/tts-engine-service.js +51 -2
  151. package/src/utils/audio-duration-validator.js +53 -10
  152. package/src/utils/audio-format-validator.js +277 -277
  153. package/src/utils/dependency-checker.js +469 -469
  154. package/src/utils/file-ownership-verifier.js +358 -358
  155. package/src/utils/music-file-validator.js +285 -285
  156. package/src/utils/preview-list-prompt.js +144 -136
  157. package/src/utils/provider-validator.js +70 -0
  158. package/src/utils/secure-music-storage.js +412 -412
  159. package/templates/agentvibes-receiver.sh +235 -231
  160. package/templates/audio/welcome-music.mp3 +0 -0
@@ -1,231 +1,235 @@
1
- #!/usr/bin/env bash
2
- #
3
- # File: agentvibes-receiver.sh
4
- # Location: User installs to ~/.termux/agentvibes-play.sh or ~/.agentvibes/play-remote.sh
5
- #
6
- # AgentVibes SSH-TTS Receiver
7
- # Receives base64-encoded JSON payload from remote server via SSH,
8
- # decodes it, applies voice/music/effects, and plays with local AgentVibes.
9
- #
10
- # Payload format: base64({"text","voice","effects","music","volume","pretext","speed","provider","llm"})
11
- #
12
- # SSH ForceCommand: SSH_ORIGINAL_COMMAND carries the base64 payload.
13
- # Direct invocation: pass the payload as first argument.
14
- #
15
- # Installation:
16
- # curl -sSL https://raw.githubusercontent.com/paulpreibisch/AgentVibes/main/scripts/install-ssh-receiver.sh | bash
17
- # OR
18
- # agentvibes install --ssh-receiver
19
- #
20
- # Copyright (c) 2025 Paul Preibisch
21
- # Licensed under Apache-2.0
22
- #
23
-
24
- set -euo pipefail
25
-
26
- # Handle -- argument separator (skip it if present)
27
- if [[ "${1:-}" == "--" ]]; then
28
- shift
29
- fi
30
-
31
- # ---------------------------------------------------------------------------
32
- # Resolve encoded payload: arg → SSH_ORIGINAL_COMMAND → stdin (legacy)
33
- # ---------------------------------------------------------------------------
34
-
35
- ENCODED_PAYLOAD="${1:-}"
36
- if [[ -z "$ENCODED_PAYLOAD" ]]; then
37
- ENCODED_PAYLOAD="${SSH_ORIGINAL_COMMAND:-}"
38
- fi
39
- if [[ -z "$ENCODED_PAYLOAD" ]]; then
40
- ENCODED_PAYLOAD=$(cat 2>/dev/null || true)
41
- fi
42
-
43
- if [[ -z "$ENCODED_PAYLOAD" ]]; then
44
- echo "❌ No payload provided" >&2
45
- echo "Usage: $0 <base64-json-payload>" >&2
46
- exit 1
47
- fi
48
-
49
- # Reject oversized payloads (DoS guard: 64 KB is generous for any TTS request)
50
- _MAX_PAYLOAD_BYTES=65536
51
- if [[ ${#ENCODED_PAYLOAD} -gt $_MAX_PAYLOAD_BYTES ]]; then
52
- echo "❌ Payload too large (max ${_MAX_PAYLOAD_BYTES} bytes)" >&2
53
- exit 1
54
- fi
55
-
56
- # Validate it looks like base64
57
- if [[ ! "$ENCODED_PAYLOAD" =~ ^[A-Za-z0-9+/=]+$ ]]; then
58
- echo "❌ Payload must be base64-encoded" >&2
59
- exit 1
60
- fi
61
-
62
- # Decode
63
- DECODED=$(printf '%s' "$ENCODED_PAYLOAD" | base64 -d 2>/dev/null) || {
64
- echo "❌ Failed to decode base64 payload" >&2; exit 1
65
- }
66
-
67
- # ---------------------------------------------------------------------------
68
- # Parse JSON payload — python3 is required for JSON parsing
69
- # ---------------------------------------------------------------------------
70
-
71
- if ! command -v python3 &>/dev/null; then
72
- echo "❌ python3 required for JSON parsing" >&2; exit 1
73
- fi
74
-
75
- # Parse all fields in one python call to avoid repeated process spawning
76
- # and to log a clear error if the payload is malformed JSON.
77
- _PARSE_OUT=$(python3 - "$DECODED" 2>&1 <<'PYEOF'
78
- import json, sys
79
- try:
80
- d = json.loads(sys.argv[1])
81
- for field in ("text","voice","music","volume","effects","pretext","speed","provider","llm"):
82
- print(d.get(field, ""))
83
- except json.JSONDecodeError as e:
84
- print(f"JSON_PARSE_ERROR: {e}", file=sys.stderr)
85
- sys.exit(1)
86
- PYEOF
87
- ) || { echo "❌ Invalid JSON in payload: $_PARSE_OUT" >&2; exit 1; }
88
-
89
- # Assign fields from ordered output lines
90
- TEXT=$( sed -n '1p' <<< "$_PARSE_OUT")
91
- VOICE=$( sed -n '2p' <<< "$_PARSE_OUT")
92
- MUSIC=$( sed -n '3p' <<< "$_PARSE_OUT")
93
- VOLUME=$( sed -n '4p' <<< "$_PARSE_OUT")
94
- EFFECTS=$( sed -n '5p' <<< "$_PARSE_OUT")
95
- PRETEXT=$( sed -n '6p' <<< "$_PARSE_OUT")
96
- SPEED=$( sed -n '7p' <<< "$_PARSE_OUT")
97
- PROVIDER=$(sed -n '8p' <<< "$_PARSE_OUT")
98
- LLM=$( sed -n '9p' <<< "$_PARSE_OUT")
99
-
100
- # Fall back to defaults
101
- [[ -z "$VOICE" ]] && VOICE="en_US-lessac-medium"
102
- [[ -z "$VOLUME" ]] && VOLUME="0.10"
103
- [[ -z "$PROVIDER" ]] && PROVIDER="piper"
104
-
105
- # Validate text
106
- if [[ -z "$TEXT" ]]; then
107
- echo "❌ No text in payload" >&2; exit 1
108
- fi
109
-
110
- # SECURITY: Validate voice — letters, digits, underscore, hyphen, period, colon
111
- if [[ ! "$VOICE" =~ ^[a-zA-Z0-9_.:/-]+$ ]]; then
112
- echo "❌ Invalid voice format: $VOICE" >&2; exit 1
113
- fi
114
-
115
- # Validate volume is numeric
116
- if [[ ! "$VOLUME" =~ ^[0-9]+\.?[0-9]*$ ]]; then
117
- VOLUME="0.10"
118
- fi
119
-
120
- # Validate provider
121
- case "$PROVIDER" in
122
- piper|soprano|macos|windows-sapi) ;;
123
- *) PROVIDER="piper" ;;
124
- esac
125
-
126
- # Prepend pretext if present (same logic as PS receiver)
127
- if [[ -n "$PRETEXT" ]]; then
128
- TEXT="${PRETEXT}. ${TEXT}"
129
- fi
130
-
131
- # ---------------------------------------------------------------------------
132
- # Test mode: dump what would be played without calling play-tts
133
- # ---------------------------------------------------------------------------
134
-
135
- if [[ "${AGENTVIBES_TEST_MODE:-false}" == "true" ]]; then
136
- python3 -c "
137
- import json, sys
138
- print(json.dumps({
139
- 'text': sys.argv[1],
140
- 'voice': sys.argv[2],
141
- 'music': sys.argv[3],
142
- 'volume': sys.argv[4],
143
- 'effects': sys.argv[5],
144
- 'pretext': sys.argv[6],
145
- 'provider': sys.argv[7],
146
- 'llm': sys.argv[8],
147
- }, ensure_ascii=False))
148
- " "$TEXT" "$VOICE" "$MUSIC" "$VOLUME" "$EFFECTS" "$PRETEXT" "$PROVIDER" "$LLM"
149
- exit 0
150
- fi
151
-
152
- # ---------------------------------------------------------------------------
153
- # Find AgentVibes installation
154
- # ---------------------------------------------------------------------------
155
-
156
- # Suppress GitHub star reminders (receiver mode)
157
- export AGENTVIBES_NO_REMINDERS=1
158
-
159
- find_agentvibes() {
160
- if command -v agentvibes >/dev/null 2>&1; then
161
- local bin_path
162
- bin_path=$(which agentvibes)
163
- if [[ -L "$bin_path" ]]; then
164
- bin_path=$(readlink -f "$bin_path" 2>/dev/null || realpath "$bin_path" 2>/dev/null || echo "$bin_path")
165
- fi
166
- local lib_path
167
- lib_path="$(dirname "$(dirname "$bin_path")")/lib/node_modules/agentvibes"
168
- if [[ -d "$lib_path" ]]; then
169
- echo "$lib_path"
170
- return 0
171
- fi
172
- fi
173
-
174
- local search_paths=(
175
- "$HOME/.npm-global/lib/node_modules/agentvibes"
176
- "/usr/local/lib/node_modules/agentvibes"
177
- "/data/data/com.termux/files/usr/lib/node_modules/agentvibes"
178
- )
179
-
180
- if [[ -d "$HOME/.nvm/versions/node" ]]; then
181
- local nvm_path
182
- nvm_path=$(find "$HOME/.nvm/versions/node" -maxdepth 3 -type d -name "agentvibes" -path "*/lib/node_modules/*" 2>/dev/null | head -1)
183
- if [[ -n "$nvm_path" ]] && [[ -d "$nvm_path" ]]; then
184
- echo "$nvm_path"
185
- return 0
186
- fi
187
- fi
188
-
189
- for p in "${search_paths[@]}"; do
190
- [[ -d "$p" ]] && { echo "$p"; return 0; }
191
- done
192
-
193
- return 1
194
- }
195
-
196
- AGENTVIBES_ROOT=$(find_agentvibes)
197
-
198
- if [[ -z "$AGENTVIBES_ROOT" ]]; then
199
- echo "❌ AgentVibes not found" >&2
200
- echo "💡 Install: npm install -g agentvibes" >&2
201
- exit 1
202
- fi
203
-
204
- PLAY_TTS="$AGENTVIBES_ROOT/.claude/hooks/play-tts.sh"
205
-
206
- if [[ ! -f "$PLAY_TTS" ]]; then
207
- echo "❌ play-tts.sh not found at $PLAY_TTS" >&2; exit 1
208
- fi
209
-
210
- # ---------------------------------------------------------------------------
211
- # Apply music / effects overrides via env vars for play-tts.sh
212
- # ---------------------------------------------------------------------------
213
-
214
- [[ -n "$MUSIC" ]] && export AGENTVIBES_OVERRIDE_MUSIC="$MUSIC"
215
- [[ -n "$VOLUME" ]] && export AGENTVIBES_OVERRIDE_VOLUME="$VOLUME"
216
- [[ -n "$EFFECTS" ]] && export AGENTVIBES_OVERRIDE_EFFECTS="$EFFECTS"
217
- [[ -n "$SPEED" ]] && export AGENTVIBES_OVERRIDE_SPEED="$SPEED"
218
-
219
- if [[ "${AGENTVIBES_DEBUG:-0}" == "1" ]]; then
220
- echo "[DEBUG] Voice: $VOICE" >&2
221
- echo "[DEBUG] Music: ${MUSIC:-none}" >&2
222
- echo "[DEBUG] Volume: ${VOLUME:-none}" >&2
223
- echo "[DEBUG] Effects: ${EFFECTS:-none}" >&2
224
- echo "[DEBUG] Pretext: ${PRETEXT:-none}" >&2
225
- echo "[DEBUG] Provider: $PROVIDER" >&2
226
- fi
227
-
228
- echo "🎵 Playing via AgentVibes: ${TEXT:0:50}..." >&2
229
- bash "$PLAY_TTS" "$TEXT" "$VOICE"
230
-
231
- exit 0
1
+ #!/usr/bin/env bash
2
+ #
3
+ # File: agentvibes-receiver.sh
4
+ # Location: User installs to ~/.termux/agentvibes-play.sh or ~/.agentvibes/play-remote.sh
5
+ #
6
+ # AgentVibes SSH-TTS Receiver
7
+ # Receives base64-encoded JSON payload from remote server via SSH,
8
+ # decodes it, applies voice/music/effects, and plays with local AgentVibes.
9
+ #
10
+ # Payload format: base64({"text","voice","effects","music","volume","pretext","speed","provider","llm"})
11
+ #
12
+ # SSH ForceCommand: SSH_ORIGINAL_COMMAND carries the base64 payload.
13
+ # Direct invocation: pass the payload as first argument.
14
+ #
15
+ # Installation:
16
+ # curl -sSL https://raw.githubusercontent.com/paulpreibisch/AgentVibes/main/scripts/install-ssh-receiver.sh | bash
17
+ # OR
18
+ # agentvibes install --ssh-receiver
19
+ #
20
+ # Copyright (c) 2025 Paul Preibisch
21
+ # Licensed under Apache-2.0
22
+ #
23
+
24
+ set -euo pipefail
25
+
26
+ # Handle -- argument separator (skip it if present)
27
+ if [[ "${1:-}" == "--" ]]; then
28
+ shift
29
+ fi
30
+
31
+ # ---------------------------------------------------------------------------
32
+ # Resolve encoded payload: arg → SSH_ORIGINAL_COMMAND → stdin (legacy)
33
+ # ---------------------------------------------------------------------------
34
+
35
+ ENCODED_PAYLOAD="${1:-}"
36
+ if [[ -z "$ENCODED_PAYLOAD" ]]; then
37
+ ENCODED_PAYLOAD="${SSH_ORIGINAL_COMMAND:-}"
38
+ fi
39
+ if [[ -z "$ENCODED_PAYLOAD" ]]; then
40
+ ENCODED_PAYLOAD=$(cat 2>/dev/null || true)
41
+ fi
42
+
43
+ if [[ -z "$ENCODED_PAYLOAD" ]]; then
44
+ echo "❌ No payload provided" >&2
45
+ echo "Usage: $0 <base64-json-payload>" >&2
46
+ exit 1
47
+ fi
48
+
49
+ # Reject oversized payloads (DoS guard: 64 KB is generous for any TTS request)
50
+ _MAX_PAYLOAD_BYTES=65536
51
+ if [[ ${#ENCODED_PAYLOAD} -gt $_MAX_PAYLOAD_BYTES ]]; then
52
+ echo "❌ Payload too large (max ${_MAX_PAYLOAD_BYTES} bytes)" >&2
53
+ exit 1
54
+ fi
55
+
56
+ # Validate it looks like base64
57
+ if [[ ! "$ENCODED_PAYLOAD" =~ ^[A-Za-z0-9+/=]+$ ]]; then
58
+ echo "❌ Payload must be base64-encoded" >&2
59
+ exit 1
60
+ fi
61
+
62
+ # Decode
63
+ DECODED=$(printf '%s' "$ENCODED_PAYLOAD" | base64 -d 2>/dev/null) || {
64
+ echo "❌ Failed to decode base64 payload" >&2; exit 1
65
+ }
66
+
67
+ # ---------------------------------------------------------------------------
68
+ # Parse JSON payload — python3 is required for JSON parsing
69
+ # ---------------------------------------------------------------------------
70
+
71
+ if ! command -v python3 &>/dev/null; then
72
+ echo "❌ python3 required for JSON parsing" >&2; exit 1
73
+ fi
74
+
75
+ # Parse all fields in one python call to avoid repeated process spawning
76
+ # and to log a clear error if the payload is malformed JSON.
77
+ _PARSE_OUT=$(python3 - "$DECODED" 2>&1 <<'PYEOF'
78
+ import json, sys
79
+ try:
80
+ d = json.loads(sys.argv[1])
81
+ for field in ("text","voice","music","volume","effects","pretext","speed","provider","llm"):
82
+ print(d.get(field, ""))
83
+ except json.JSONDecodeError as e:
84
+ print(f"JSON_PARSE_ERROR: {e}", file=sys.stderr)
85
+ sys.exit(1)
86
+ PYEOF
87
+ ) || { echo "❌ Invalid JSON in payload: $_PARSE_OUT" >&2; exit 1; }
88
+
89
+ # Assign fields from ordered output lines
90
+ TEXT=$( sed -n '1p' <<< "$_PARSE_OUT")
91
+ VOICE=$( sed -n '2p' <<< "$_PARSE_OUT")
92
+ MUSIC=$( sed -n '3p' <<< "$_PARSE_OUT")
93
+ VOLUME=$( sed -n '4p' <<< "$_PARSE_OUT")
94
+ EFFECTS=$( sed -n '5p' <<< "$_PARSE_OUT")
95
+ PRETEXT=$( sed -n '6p' <<< "$_PARSE_OUT")
96
+ SPEED=$( sed -n '7p' <<< "$_PARSE_OUT")
97
+ PROVIDER=$(sed -n '8p' <<< "$_PARSE_OUT")
98
+ LLM=$( sed -n '9p' <<< "$_PARSE_OUT")
99
+
100
+ # Fall back to defaults
101
+ [[ -z "$VOICE" ]] && VOICE="en_US-lessac-medium"
102
+ [[ -z "$VOLUME" ]] && VOLUME="0.10"
103
+ [[ -z "$PROVIDER" ]] && PROVIDER="piper"
104
+
105
+ # Validate text
106
+ if [[ -z "$TEXT" ]]; then
107
+ echo "❌ No text in payload" >&2; exit 1
108
+ fi
109
+
110
+ # SECURITY: Validate voice — letters, digits, underscore, hyphen, period, colon
111
+ if [[ ! "$VOICE" =~ ^[a-zA-Z0-9_.:/-]+$ ]]; then
112
+ echo "❌ Invalid voice format: $VOICE" >&2; exit 1
113
+ fi
114
+
115
+ # Validate volume is numeric
116
+ if [[ ! "$VOLUME" =~ ^[0-9]+\.?[0-9]*$ ]]; then
117
+ VOLUME="0.10"
118
+ fi
119
+
120
+ # Validate provider
121
+ case "$PROVIDER" in
122
+ piper|soprano|macos|windows-sapi|kokoro|elevenlabs) ;;
123
+ *) PROVIDER="piper" ;;
124
+ esac
125
+
126
+ # Prepend pretext if present (same logic as PS receiver)
127
+ if [[ -n "$PRETEXT" ]]; then
128
+ TEXT="${PRETEXT}. ${TEXT}"
129
+ fi
130
+
131
+ # ---------------------------------------------------------------------------
132
+ # Test mode: dump what would be played without calling play-tts
133
+ # ---------------------------------------------------------------------------
134
+
135
+ if [[ "${AGENTVIBES_TEST_MODE:-false}" == "true" ]]; then
136
+ python3 -c "
137
+ import json, sys
138
+ print(json.dumps({
139
+ 'text': sys.argv[1],
140
+ 'voice': sys.argv[2],
141
+ 'music': sys.argv[3],
142
+ 'volume': sys.argv[4],
143
+ 'effects': sys.argv[5],
144
+ 'pretext': sys.argv[6],
145
+ 'provider': sys.argv[7],
146
+ 'llm': sys.argv[8],
147
+ }, ensure_ascii=False))
148
+ " "$TEXT" "$VOICE" "$MUSIC" "$VOLUME" "$EFFECTS" "$PRETEXT" "$PROVIDER" "$LLM"
149
+ exit 0
150
+ fi
151
+
152
+ # ---------------------------------------------------------------------------
153
+ # Find AgentVibes installation
154
+ # ---------------------------------------------------------------------------
155
+
156
+ # Suppress GitHub star reminders (receiver mode)
157
+ export AGENTVIBES_NO_REMINDERS=1
158
+
159
+ find_agentvibes() {
160
+ if command -v agentvibes >/dev/null 2>&1; then
161
+ local bin_path
162
+ bin_path=$(which agentvibes)
163
+ if [[ -L "$bin_path" ]]; then
164
+ bin_path=$(readlink -f "$bin_path" 2>/dev/null || realpath "$bin_path" 2>/dev/null || echo "$bin_path")
165
+ fi
166
+ local lib_path
167
+ lib_path="$(dirname "$(dirname "$bin_path")")/lib/node_modules/agentvibes"
168
+ if [[ -d "$lib_path" ]]; then
169
+ echo "$lib_path"
170
+ return 0
171
+ fi
172
+ fi
173
+
174
+ local search_paths=(
175
+ "$HOME/.npm-global/lib/node_modules/agentvibes"
176
+ "/usr/local/lib/node_modules/agentvibes"
177
+ "/data/data/com.termux/files/usr/lib/node_modules/agentvibes"
178
+ )
179
+
180
+ if [[ -d "$HOME/.nvm/versions/node" ]]; then
181
+ local nvm_path
182
+ nvm_path=$(find "$HOME/.nvm/versions/node" -maxdepth 3 -type d -name "agentvibes" -path "*/lib/node_modules/*" 2>/dev/null | head -1)
183
+ if [[ -n "$nvm_path" ]] && [[ -d "$nvm_path" ]]; then
184
+ echo "$nvm_path"
185
+ return 0
186
+ fi
187
+ fi
188
+
189
+ for p in "${search_paths[@]}"; do
190
+ [[ -d "$p" ]] && { echo "$p"; return 0; }
191
+ done
192
+
193
+ return 1
194
+ }
195
+
196
+ AGENTVIBES_ROOT=$(find_agentvibes)
197
+
198
+ if [[ -z "$AGENTVIBES_ROOT" ]]; then
199
+ echo "❌ AgentVibes not found" >&2
200
+ echo "💡 Install: npm install -g agentvibes" >&2
201
+ exit 1
202
+ fi
203
+
204
+ PLAY_TTS="$AGENTVIBES_ROOT/.claude/hooks/play-tts.sh"
205
+
206
+ if [[ ! -f "$PLAY_TTS" ]]; then
207
+ echo "❌ play-tts.sh not found at $PLAY_TTS" >&2; exit 1
208
+ fi
209
+
210
+ # ---------------------------------------------------------------------------
211
+ # Apply music / effects overrides via env vars for play-tts.sh
212
+ # ---------------------------------------------------------------------------
213
+
214
+ [[ -n "$MUSIC" ]] && export AGENTVIBES_OVERRIDE_MUSIC="$MUSIC"
215
+ [[ -n "$VOLUME" ]] && export AGENTVIBES_OVERRIDE_VOLUME="$VOLUME"
216
+ [[ -n "$EFFECTS" ]] && export AGENTVIBES_OVERRIDE_EFFECTS="$EFFECTS"
217
+ [[ -n "$SPEED" ]] && export AGENTVIBES_OVERRIDE_SPEED="$SPEED"
218
+
219
+ if [[ "${AGENTVIBES_DEBUG:-0}" == "1" ]]; then
220
+ echo "[DEBUG] Voice: $VOICE" >&2
221
+ echo "[DEBUG] Music: ${MUSIC:-none}" >&2
222
+ echo "[DEBUG] Volume: ${VOLUME:-none}" >&2
223
+ echo "[DEBUG] Effects: ${EFFECTS:-none}" >&2
224
+ echo "[DEBUG] Pretext: ${PRETEXT:-none}" >&2
225
+ echo "[DEBUG] Provider: $PROVIDER" >&2
226
+ fi
227
+
228
+ echo "🎵 Playing via AgentVibes: ${TEXT:0:50}..." >&2
229
+ # AGENTVIBES_FORCE_PROVIDER lets the sender specify which engine the receiver
230
+ # should use (e.g. kokoro, elevenlabs). The receiver's tts-provider.txt takes
231
+ # precedence if already set to a non-default value; FORCE_PROVIDER is the
232
+ # fallback when the receiver has no local preference configured.
233
+ AGENTVIBES_FORCE_PROVIDER="$PROVIDER" bash "$PLAY_TTS" "$TEXT" "$VOICE"
234
+
235
+ exit 0
File without changes