voice-mode 2.33.4__tar.gz → 2.34.1__tar.gz
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.
- {voice_mode-2.33.4 → voice_mode-2.34.1}/CHANGELOG.md +43 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/PKG-INFO +1 -1
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/__version__.py +1 -1
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/config.py +1 -1
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/templates/launchd/com.voicemode.whisper.plist +4 -4
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/tools/service.py +11 -5
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/tools/services/kokoro/install.py +207 -2
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/tools/services/whisper/install.py +329 -189
- {voice_mode-2.33.4 → voice_mode-2.34.1}/.gitignore +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/README.md +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/build_hooks.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/pyproject.toml +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/__init__.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/__main__.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/cli.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/cli_commands/__init__.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/cli_commands/exchanges.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/conversation_logger.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/core.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/data/versions.json +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/exchanges/__init__.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/exchanges/conversations.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/exchanges/filters.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/exchanges/formatters.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/exchanges/models.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/exchanges/reader.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/exchanges/stats.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/frontend/README.md +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/frontend/app/api/connection-details/route.ts +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/frontend/app/favicon.ico +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/frontend/app/globals.css +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/frontend/app/layout.tsx +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/frontend/app/page.tsx +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/frontend/components/CloseIcon.tsx +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/frontend/components/NoAgentNotification.tsx +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/frontend/components/TranscriptionView.tsx +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/frontend/hooks/useCombinedTranscriptions.ts +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/frontend/hooks/useLocalMicTrack.ts +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/frontend/next-env.d.ts +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/frontend/next.config.mjs +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/frontend/package-lock.json +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/frontend/package.json +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/frontend/pnpm-lock.yaml +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/frontend/postcss.config.mjs +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/frontend/tailwind.config.ts +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/frontend/tsconfig.json +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/prompts/README.md +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/prompts/__init__.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/prompts/converse.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/prompts/release_notes.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/prompts/services.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/provider_discovery.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/providers.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/resources/__init__.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/resources/audio_files.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/resources/changelog.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/resources/configuration.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/resources/statistics.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/resources/version.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/resources/whisper_models.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/server.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/shared.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/simple_failover.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/statistics.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/streaming.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/templates/__init__.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/templates/launchd/com.voicemode.frontend.plist +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/templates/launchd/com.voicemode.kokoro.plist +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/templates/launchd/com.voicemode.livekit.plist +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/templates/launchd/start-kokoro-with-health-check.sh +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/templates/launchd/start-whisper-with-health-check.sh +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/templates/scripts/__init__.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/templates/scripts/start-whisper-server.sh +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/templates/systemd/voicemode-frontend.service +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/templates/systemd/voicemode-kokoro.service +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/templates/systemd/voicemode-livekit.service +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/templates/systemd/voicemode-whisper.service +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/tools/__init__.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/tools/configuration_management.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/tools/converse.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/tools/dependencies.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/tools/devices.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/tools/diagnostics.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/tools/providers.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/tools/services/kokoro/uninstall.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/tools/services/list_versions.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/tools/services/livekit/__init__.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/tools/services/livekit/frontend.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/tools/services/livekit/install.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/tools/services/livekit/production_server.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/tools/services/livekit/uninstall.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/tools/services/version_info.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/tools/services/whisper/__init__.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/tools/services/whisper/list_models.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/tools/services/whisper/model_active.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/tools/services/whisper/model_benchmark.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/tools/services/whisper/model_install.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/tools/services/whisper/model_remove.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/tools/services/whisper/models.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/tools/services/whisper/uninstall.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/tools/statistics.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/tools/voice_registry.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/utils/__init__.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/utils/audio_diagnostics.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/utils/event_logger.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/utils/ffmpeg_check.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/utils/format_migration.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/utils/gpu_detection.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/utils/migration_helpers.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/utils/services/common.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/utils/services/coreml_setup.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/utils/services/kokoro_helpers.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/utils/services/livekit_helpers.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/utils/services/whisper_helpers.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/utils/services/whisper_version.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/utils/version_helpers.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/version.py +0 -0
- {voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/voice_preferences.py +0 -0
@@ -7,12 +7,55 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
7
7
|
|
8
8
|
## [Unreleased]
|
9
9
|
|
10
|
+
## [2.34.1] - 2025-08-26
|
11
|
+
|
12
|
+
### Fixed
|
13
|
+
- **Whisper service enable command**
|
14
|
+
- Fixed `voicemode whisper enable` using incorrect template variable names
|
15
|
+
- Changed from WHISPER_BIN/MODEL_FILE to START_SCRIPT_PATH/INSTALL_DIR to match plist template
|
16
|
+
- Correctly locates start-whisper-server.sh script in whisper install directory
|
17
|
+
- Fixes KeyError 'START_SCRIPT_PATH' when enabling whisper service after installation
|
18
|
+
|
19
|
+
## [2.34.0] - 2025-08-26
|
20
|
+
|
21
|
+
### Changed
|
22
|
+
- **Installer improvements**
|
23
|
+
- Refactored installer to use permanent `uv tool install --upgrade` instead of `uvx --refresh`
|
24
|
+
- Added `uv tool update-shell` for automatic PATH configuration
|
25
|
+
- Improved shell completion detection with smart fallbacks
|
26
|
+
- Added Homebrew zsh completion directory support on macOS
|
27
|
+
- Implemented XDG-compliant paths for bash completions
|
28
|
+
- Removed fish shell support to simplify maintenance (bash/zsh only)
|
29
|
+
- Zsh completions now correctly use underscore prefix (_voicemode)
|
30
|
+
- MCP configuration now uses plain `voice-mode` command for better performance
|
31
|
+
|
32
|
+
### Fixed
|
33
|
+
- **Service file updates on reinstall**
|
34
|
+
- Fixed whisper and kokoro installers to always update service files (plist/systemd) even when service is already installed
|
35
|
+
- Ensures paths are properly expanded (no `~` symbols) in service files
|
36
|
+
- Fixes issue where broken service files with unexpanded paths would remain broken after running install.sh
|
37
|
+
- Service files now updated to latest templates on every install, ensuring users always get working configurations
|
38
|
+
|
10
39
|
## [2.33.4] - 2025-08-26
|
11
40
|
|
41
|
+
### Fixed
|
42
|
+
- **CoreML support restoration**
|
43
|
+
- Re-enabled CoreML acceleration after fixing plist template path issues
|
44
|
+
- Improved CoreML compilation flags handling in whisper installer
|
45
|
+
|
12
46
|
## [2.33.3] - 2025-08-26
|
13
47
|
|
48
|
+
### Changed
|
49
|
+
- Version bump for testing installer fixes
|
50
|
+
|
14
51
|
## [2.33.2] - 2025-08-26
|
15
52
|
|
53
|
+
### Fixed
|
54
|
+
- **Whisper service installation**
|
55
|
+
- Corrected plist template path in whisper installer
|
56
|
+
- Fixed CoreML support compilation flags (disabled then re-enabled after testing)
|
57
|
+
- Removed duplicate inline plist fallback to prevent template divergence
|
58
|
+
|
16
59
|
## [2.33.1] - 2025-08-26
|
17
60
|
|
18
61
|
### Fixed
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: voice-mode
|
3
|
-
Version: 2.
|
3
|
+
Version: 2.34.1
|
4
4
|
Summary: VoiceMode - Voice interaction capabilities for AI assistants (formerly voice-mcp)
|
5
5
|
Project-URL: Homepage, https://github.com/mbailey/voicemode
|
6
6
|
Project-URL: Repository, https://github.com/mbailey/voicemode
|
@@ -286,7 +286,7 @@ VAD_AGGRESSIVENESS = int(os.getenv("VOICEMODE_VAD_AGGRESSIVENESS", "2")) # 0-3,
|
|
286
286
|
SILENCE_THRESHOLD_MS = int(os.getenv("VOICEMODE_SILENCE_THRESHOLD_MS", "1000")) # Stop after 1000ms (1 second) of silence
|
287
287
|
MIN_RECORDING_DURATION = float(os.getenv("VOICEMODE_MIN_RECORDING_DURATION", "0.5")) # Minimum 0.5s recording
|
288
288
|
VAD_CHUNK_DURATION_MS = 30 # VAD frame size (must be 10, 20, or 30ms)
|
289
|
-
INITIAL_SILENCE_GRACE_PERIOD = float(os.getenv("VOICEMODE_INITIAL_SILENCE_GRACE_PERIOD", "
|
289
|
+
INITIAL_SILENCE_GRACE_PERIOD = float(os.getenv("VOICEMODE_INITIAL_SILENCE_GRACE_PERIOD", "1")) # No initial silence grace period by default
|
290
290
|
|
291
291
|
# Default listen duration for converse tool
|
292
292
|
DEFAULT_LISTEN_DURATION = float(os.getenv("VOICEMODE_DEFAULT_LISTEN_DURATION", "120.0")) # Default 120s listening time
|
{voice_mode-2.33.4 → voice_mode-2.34.1}/voice_mode/templates/launchd/com.voicemode.whisper.plist
RENAMED
@@ -9,18 +9,18 @@
|
|
9
9
|
<string>com.voicemode.whisper</string>
|
10
10
|
<key>ProgramArguments</key>
|
11
11
|
<array>
|
12
|
-
<string
|
12
|
+
<string>{START_SCRIPT_PATH}</string>
|
13
13
|
</array>
|
14
14
|
<key>RunAtLoad</key>
|
15
15
|
<true/>
|
16
16
|
<key>KeepAlive</key>
|
17
17
|
<true/>
|
18
18
|
<key>StandardOutPath</key>
|
19
|
-
<string
|
19
|
+
<string>{LOG_DIR}/whisper/whisper.out.log</string>
|
20
20
|
<key>StandardErrorPath</key>
|
21
|
-
<string
|
21
|
+
<string>{LOG_DIR}/whisper/whisper.err.log</string>
|
22
22
|
<key>WorkingDirectory</key>
|
23
|
-
<string
|
23
|
+
<string>{INSTALL_DIR}</string>
|
24
24
|
<key>EnvironmentVariables</key>
|
25
25
|
<dict>
|
26
26
|
<key>PATH</key>
|
@@ -582,12 +582,18 @@ async def enable_service(service_name: str) -> str:
|
|
582
582
|
if not model_file:
|
583
583
|
return "❌ No Whisper model found. Please run download_model first."
|
584
584
|
|
585
|
+
# Find the start script
|
586
|
+
# whisper_bin is at ~/.voicemode/services/whisper/build/bin/whisper-server
|
587
|
+
# start script is at ~/.voicemode/services/whisper/bin/start-whisper-server.sh
|
588
|
+
install_dir = Path(whisper_bin).parent.parent.parent # Go up from build/bin/whisper-server to whisper dir
|
589
|
+
start_script_path = install_dir / "bin" / "start-whisper-server.sh"
|
590
|
+
if not start_script_path.exists():
|
591
|
+
return f"❌ Start script not found: {start_script_path}"
|
592
|
+
|
585
593
|
content = template.format(
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
LOG_DIR=logs_dir,
|
590
|
-
WORKING_DIR=Path(whisper_bin).parent
|
594
|
+
START_SCRIPT_PATH=str(start_script_path),
|
595
|
+
LOG_DIR=str(logs_dir),
|
596
|
+
INSTALL_DIR=str(install_dir)
|
591
597
|
)
|
592
598
|
elif service_name == "kokoro":
|
593
599
|
kokoro_dir = find_kokoro_fastapi()
|
@@ -22,6 +22,152 @@ from voice_mode.utils.migration_helpers import auto_migrate_if_needed
|
|
22
22
|
logger = logging.getLogger("voice-mode")
|
23
23
|
|
24
24
|
|
25
|
+
async def update_kokoro_service_files(
|
26
|
+
install_dir: str,
|
27
|
+
voicemode_dir: str,
|
28
|
+
port: int,
|
29
|
+
start_script_path: str,
|
30
|
+
auto_enable: Optional[bool] = None
|
31
|
+
) -> Dict[str, Any]:
|
32
|
+
"""Update service files (plist/systemd) for kokoro service.
|
33
|
+
|
34
|
+
This function updates the service files without reinstalling kokoro itself.
|
35
|
+
It ensures paths are properly expanded and templates are up to date.
|
36
|
+
|
37
|
+
Returns:
|
38
|
+
Dict with success status and details about what was updated
|
39
|
+
"""
|
40
|
+
system = platform.system()
|
41
|
+
result = {"success": False, "updated": False}
|
42
|
+
|
43
|
+
# Create log directory
|
44
|
+
log_dir = os.path.join(voicemode_dir, 'logs', 'kokoro')
|
45
|
+
os.makedirs(log_dir, exist_ok=True)
|
46
|
+
|
47
|
+
if system == "Darwin":
|
48
|
+
logger.info("Updating launchagent for kokoro...")
|
49
|
+
launchagents_dir = os.path.expanduser("~/Library/LaunchAgents")
|
50
|
+
os.makedirs(launchagents_dir, exist_ok=True)
|
51
|
+
|
52
|
+
plist_name = "com.voicemode.kokoro.plist"
|
53
|
+
plist_path = os.path.join(launchagents_dir, plist_name)
|
54
|
+
|
55
|
+
plist_content = f"""<?xml version="1.0" encoding="UTF-8"?>
|
56
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
57
|
+
<plist version="1.0">
|
58
|
+
<dict>
|
59
|
+
<key>Label</key>
|
60
|
+
<string>com.voicemode.kokoro</string>
|
61
|
+
<key>ProgramArguments</key>
|
62
|
+
<array>
|
63
|
+
<string>{start_script_path}</string>
|
64
|
+
</array>
|
65
|
+
<key>WorkingDirectory</key>
|
66
|
+
<string>{install_dir}</string>
|
67
|
+
<key>RunAtLoad</key>
|
68
|
+
<true/>
|
69
|
+
<key>KeepAlive</key>
|
70
|
+
<true/>
|
71
|
+
<key>StandardOutPath</key>
|
72
|
+
<string>{os.path.join(voicemode_dir, 'logs', 'kokoro', 'kokoro.log')}</string>
|
73
|
+
<key>StandardErrorPath</key>
|
74
|
+
<string>{os.path.join(voicemode_dir, 'logs', 'kokoro', 'kokoro.error.log')}</string>
|
75
|
+
<key>EnvironmentVariables</key>
|
76
|
+
<dict>
|
77
|
+
<key>PATH</key>
|
78
|
+
<string>{os.path.expanduser("~/.local/bin")}:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/homebrew/bin</string>
|
79
|
+
</dict>
|
80
|
+
</dict>
|
81
|
+
</plist>"""
|
82
|
+
|
83
|
+
# Unload if already loaded (ignore errors)
|
84
|
+
try:
|
85
|
+
subprocess.run(["launchctl", "unload", plist_path], capture_output=True)
|
86
|
+
except:
|
87
|
+
pass
|
88
|
+
|
89
|
+
# Write updated plist
|
90
|
+
with open(plist_path, 'w') as f:
|
91
|
+
f.write(plist_content)
|
92
|
+
|
93
|
+
result["success"] = True
|
94
|
+
result["updated"] = True
|
95
|
+
result["plist_path"] = plist_path
|
96
|
+
|
97
|
+
# Handle auto_enable if specified
|
98
|
+
if auto_enable is None:
|
99
|
+
auto_enable = SERVICE_AUTO_ENABLE
|
100
|
+
|
101
|
+
if auto_enable:
|
102
|
+
logger.info("Auto-enabling kokoro service...")
|
103
|
+
from voice_mode.tools.service import enable_service
|
104
|
+
enable_result = await enable_service("kokoro")
|
105
|
+
if "✅" in enable_result:
|
106
|
+
result["enabled"] = True
|
107
|
+
else:
|
108
|
+
logger.warning(f"Auto-enable failed: {enable_result}")
|
109
|
+
result["enabled"] = False
|
110
|
+
|
111
|
+
elif system == "Linux":
|
112
|
+
logger.info("Updating systemd user service for kokoro...")
|
113
|
+
systemd_user_dir = os.path.expanduser("~/.config/systemd/user")
|
114
|
+
os.makedirs(systemd_user_dir, exist_ok=True)
|
115
|
+
|
116
|
+
service_name = "voicemode-kokoro.service"
|
117
|
+
service_path = os.path.join(systemd_user_dir, service_name)
|
118
|
+
|
119
|
+
service_content = f"""[Unit]
|
120
|
+
Description=VoiceMode Kokoro TTS Service
|
121
|
+
After=network.target
|
122
|
+
[Service]
|
123
|
+
Type=simple
|
124
|
+
ExecStart={start_script_path}
|
125
|
+
Restart=on-failure
|
126
|
+
RestartSec=10
|
127
|
+
WorkingDirectory={install_dir}
|
128
|
+
StandardOutput=append:{os.path.join(voicemode_dir, 'logs', 'kokoro', 'kokoro.log')}
|
129
|
+
StandardError=append:{os.path.join(voicemode_dir, 'logs', 'kokoro', 'kokoro.error.log')}
|
130
|
+
Environment="PATH=/usr/local/bin:/usr/bin:/bin:/home/m/.local/bin"
|
131
|
+
[Install]
|
132
|
+
WantedBy=default.target
|
133
|
+
"""
|
134
|
+
|
135
|
+
with open(service_path, 'w') as f:
|
136
|
+
f.write(service_content)
|
137
|
+
|
138
|
+
# Reload systemd
|
139
|
+
try:
|
140
|
+
subprocess.run(["systemctl", "--user", "daemon-reload"], check=True)
|
141
|
+
result["success"] = True
|
142
|
+
result["updated"] = True
|
143
|
+
result["service_path"] = service_path
|
144
|
+
except subprocess.CalledProcessError as e:
|
145
|
+
logger.warning(f"Failed to reload systemd: {e}")
|
146
|
+
result["success"] = True # Still consider it success if file was written
|
147
|
+
result["updated"] = True
|
148
|
+
result["service_path"] = service_path
|
149
|
+
|
150
|
+
# Handle auto_enable if specified
|
151
|
+
if auto_enable is None:
|
152
|
+
auto_enable = SERVICE_AUTO_ENABLE
|
153
|
+
|
154
|
+
if auto_enable:
|
155
|
+
logger.info("Auto-enabling kokoro service...")
|
156
|
+
from voice_mode.tools.service import enable_service
|
157
|
+
enable_result = await enable_service("kokoro")
|
158
|
+
if "✅" in enable_result:
|
159
|
+
result["enabled"] = True
|
160
|
+
else:
|
161
|
+
logger.warning(f"Auto-enable failed: {enable_result}")
|
162
|
+
result["enabled"] = False
|
163
|
+
|
164
|
+
else:
|
165
|
+
result["success"] = False
|
166
|
+
result["error"] = f"Unsupported platform: {system}"
|
167
|
+
|
168
|
+
return result
|
169
|
+
|
170
|
+
|
25
171
|
@mcp.tool()
|
26
172
|
async def kokoro_install(
|
27
173
|
install_dir: Optional[str] = None,
|
@@ -101,13 +247,58 @@ async def kokoro_install(
|
|
101
247
|
# Check if the requested version is already installed
|
102
248
|
if is_version_installed(Path(install_dir), version):
|
103
249
|
current_version = get_current_version(Path(install_dir))
|
250
|
+
|
251
|
+
# Determine which start script to use
|
252
|
+
system = platform.system()
|
253
|
+
if system == "Darwin":
|
254
|
+
start_script_name = "start-gpu_mac.sh"
|
255
|
+
else:
|
256
|
+
start_script_name = "start-gpu.sh" # Default to GPU version
|
257
|
+
|
258
|
+
start_script_path = os.path.join(install_dir, start_script_name)
|
259
|
+
|
260
|
+
# If a custom port is requested, create custom start script
|
261
|
+
if port != 8880 and os.path.exists(start_script_path):
|
262
|
+
logger.info(f"Creating custom start script for port {port}")
|
263
|
+
with open(start_script_path, 'r') as f:
|
264
|
+
script_content = f.read()
|
265
|
+
modified_script = script_content.replace("--port 8880", f"--port {port}")
|
266
|
+
custom_script_name = f"start-custom-{port}.sh"
|
267
|
+
custom_script_path = os.path.join(install_dir, custom_script_name)
|
268
|
+
with open(custom_script_path, 'w') as f:
|
269
|
+
f.write(modified_script)
|
270
|
+
os.chmod(custom_script_path, 0o755)
|
271
|
+
start_script_path = custom_script_path
|
272
|
+
|
273
|
+
# Always update service files even if kokoro is already installed
|
274
|
+
logger.info("Kokoro is already installed, updating service files...")
|
275
|
+
service_update_result = await update_kokoro_service_files(
|
276
|
+
install_dir=install_dir,
|
277
|
+
voicemode_dir=voicemode_dir,
|
278
|
+
port=port,
|
279
|
+
start_script_path=start_script_path,
|
280
|
+
auto_enable=auto_enable
|
281
|
+
)
|
282
|
+
|
283
|
+
# Build response message
|
284
|
+
message = f"kokoro-fastapi version {current_version} already installed."
|
285
|
+
if service_update_result.get("updated"):
|
286
|
+
message += " Service files updated."
|
287
|
+
if service_update_result.get("enabled"):
|
288
|
+
message += " Service auto-enabled."
|
289
|
+
|
104
290
|
return {
|
105
291
|
"success": True,
|
106
292
|
"install_path": install_dir,
|
107
293
|
"models_path": models_dir,
|
108
294
|
"already_installed": True,
|
295
|
+
"service_files_updated": service_update_result.get("updated", False),
|
109
296
|
"version": current_version,
|
110
|
-
"
|
297
|
+
"plist_path": service_update_result.get("plist_path"),
|
298
|
+
"service_path": service_update_result.get("service_path"),
|
299
|
+
"start_script": start_script_path,
|
300
|
+
"service_url": f"http://127.0.0.1:{port}",
|
301
|
+
"message": message
|
111
302
|
}
|
112
303
|
|
113
304
|
# Check Python version
|
@@ -215,7 +406,21 @@ async def kokoro_install(
|
|
215
406
|
"message": f"Kokoro-fastapi {current_version} installed. Run: cd {install_dir} && ./{os.path.basename(start_script_path)}{' (' + migration_msg + ')' if migration_msg else ''}"
|
216
407
|
}
|
217
408
|
|
218
|
-
# Install
|
409
|
+
# Install/update service files
|
410
|
+
service_update_result = await update_kokoro_service_files(
|
411
|
+
install_dir=install_dir,
|
412
|
+
voicemode_dir=voicemode_dir,
|
413
|
+
port=port,
|
414
|
+
start_script_path=start_script_path,
|
415
|
+
auto_enable=auto_enable
|
416
|
+
)
|
417
|
+
|
418
|
+
if not service_update_result.get("success"):
|
419
|
+
logger.error(f"Failed to update service files: {service_update_result.get('error', 'Unknown error')}")
|
420
|
+
result["error"] = f"Service file update failed: {service_update_result.get('error', 'Unknown error')}"
|
421
|
+
return result
|
422
|
+
|
423
|
+
# Update result with service file information based on platform
|
219
424
|
if system == "Darwin":
|
220
425
|
logger.info("Installing launchagent for automatic startup...")
|
221
426
|
launchagents_dir = os.path.expanduser("~/Library/LaunchAgents")
|