voice-mode 2.33.3__tar.gz → 2.34.0__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.
Files changed (118) hide show
  1. {voice_mode-2.33.3 → voice_mode-2.34.0}/CHANGELOG.md +38 -0
  2. {voice_mode-2.33.3 → voice_mode-2.34.0}/PKG-INFO +1 -1
  3. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/__version__.py +1 -1
  4. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/tools/services/kokoro/install.py +207 -2
  5. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/tools/services/whisper/install.py +331 -187
  6. {voice_mode-2.33.3 → voice_mode-2.34.0}/.gitignore +0 -0
  7. {voice_mode-2.33.3 → voice_mode-2.34.0}/README.md +0 -0
  8. {voice_mode-2.33.3 → voice_mode-2.34.0}/build_hooks.py +0 -0
  9. {voice_mode-2.33.3 → voice_mode-2.34.0}/pyproject.toml +0 -0
  10. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/__init__.py +0 -0
  11. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/__main__.py +0 -0
  12. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/cli.py +0 -0
  13. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/cli_commands/__init__.py +0 -0
  14. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/cli_commands/exchanges.py +0 -0
  15. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/config.py +0 -0
  16. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/conversation_logger.py +0 -0
  17. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/core.py +0 -0
  18. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/data/versions.json +0 -0
  19. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/exchanges/__init__.py +0 -0
  20. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/exchanges/conversations.py +0 -0
  21. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/exchanges/filters.py +0 -0
  22. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/exchanges/formatters.py +0 -0
  23. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/exchanges/models.py +0 -0
  24. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/exchanges/reader.py +0 -0
  25. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/exchanges/stats.py +0 -0
  26. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/frontend/README.md +0 -0
  27. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/frontend/app/api/connection-details/route.ts +0 -0
  28. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/frontend/app/favicon.ico +0 -0
  29. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/frontend/app/globals.css +0 -0
  30. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/frontend/app/layout.tsx +0 -0
  31. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/frontend/app/page.tsx +0 -0
  32. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/frontend/components/CloseIcon.tsx +0 -0
  33. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/frontend/components/NoAgentNotification.tsx +0 -0
  34. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/frontend/components/TranscriptionView.tsx +0 -0
  35. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/frontend/hooks/useCombinedTranscriptions.ts +0 -0
  36. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/frontend/hooks/useLocalMicTrack.ts +0 -0
  37. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/frontend/next-env.d.ts +0 -0
  38. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/frontend/next.config.mjs +0 -0
  39. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/frontend/package-lock.json +0 -0
  40. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/frontend/package.json +0 -0
  41. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/frontend/pnpm-lock.yaml +0 -0
  42. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/frontend/postcss.config.mjs +0 -0
  43. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/frontend/tailwind.config.ts +0 -0
  44. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/frontend/tsconfig.json +0 -0
  45. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/prompts/README.md +0 -0
  46. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/prompts/__init__.py +0 -0
  47. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/prompts/converse.py +0 -0
  48. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/prompts/release_notes.py +0 -0
  49. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/prompts/services.py +0 -0
  50. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/provider_discovery.py +0 -0
  51. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/providers.py +0 -0
  52. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/resources/__init__.py +0 -0
  53. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/resources/audio_files.py +0 -0
  54. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/resources/changelog.py +0 -0
  55. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/resources/configuration.py +0 -0
  56. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/resources/statistics.py +0 -0
  57. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/resources/version.py +0 -0
  58. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/resources/whisper_models.py +0 -0
  59. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/server.py +0 -0
  60. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/shared.py +0 -0
  61. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/simple_failover.py +0 -0
  62. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/statistics.py +0 -0
  63. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/streaming.py +0 -0
  64. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/templates/__init__.py +0 -0
  65. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/templates/launchd/com.voicemode.frontend.plist +0 -0
  66. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/templates/launchd/com.voicemode.kokoro.plist +0 -0
  67. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/templates/launchd/com.voicemode.livekit.plist +0 -0
  68. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/templates/launchd/com.voicemode.whisper.plist +0 -0
  69. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/templates/launchd/start-kokoro-with-health-check.sh +0 -0
  70. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/templates/launchd/start-whisper-with-health-check.sh +0 -0
  71. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/templates/scripts/__init__.py +0 -0
  72. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/templates/scripts/start-whisper-server.sh +0 -0
  73. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/templates/systemd/voicemode-frontend.service +0 -0
  74. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/templates/systemd/voicemode-kokoro.service +0 -0
  75. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/templates/systemd/voicemode-livekit.service +0 -0
  76. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/templates/systemd/voicemode-whisper.service +0 -0
  77. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/tools/__init__.py +0 -0
  78. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/tools/configuration_management.py +0 -0
  79. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/tools/converse.py +0 -0
  80. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/tools/dependencies.py +0 -0
  81. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/tools/devices.py +0 -0
  82. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/tools/diagnostics.py +0 -0
  83. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/tools/providers.py +0 -0
  84. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/tools/service.py +0 -0
  85. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/tools/services/kokoro/uninstall.py +0 -0
  86. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/tools/services/list_versions.py +0 -0
  87. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/tools/services/livekit/__init__.py +0 -0
  88. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/tools/services/livekit/frontend.py +0 -0
  89. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/tools/services/livekit/install.py +0 -0
  90. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/tools/services/livekit/production_server.py +0 -0
  91. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/tools/services/livekit/uninstall.py +0 -0
  92. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/tools/services/version_info.py +0 -0
  93. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/tools/services/whisper/__init__.py +0 -0
  94. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/tools/services/whisper/list_models.py +0 -0
  95. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/tools/services/whisper/model_active.py +0 -0
  96. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/tools/services/whisper/model_benchmark.py +0 -0
  97. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/tools/services/whisper/model_install.py +0 -0
  98. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/tools/services/whisper/model_remove.py +0 -0
  99. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/tools/services/whisper/models.py +0 -0
  100. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/tools/services/whisper/uninstall.py +0 -0
  101. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/tools/statistics.py +0 -0
  102. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/tools/voice_registry.py +0 -0
  103. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/utils/__init__.py +0 -0
  104. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/utils/audio_diagnostics.py +0 -0
  105. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/utils/event_logger.py +0 -0
  106. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/utils/ffmpeg_check.py +0 -0
  107. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/utils/format_migration.py +0 -0
  108. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/utils/gpu_detection.py +0 -0
  109. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/utils/migration_helpers.py +0 -0
  110. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/utils/services/common.py +0 -0
  111. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/utils/services/coreml_setup.py +0 -0
  112. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/utils/services/kokoro_helpers.py +0 -0
  113. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/utils/services/livekit_helpers.py +0 -0
  114. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/utils/services/whisper_helpers.py +0 -0
  115. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/utils/services/whisper_version.py +0 -0
  116. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/utils/version_helpers.py +0 -0
  117. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/version.py +0 -0
  118. {voice_mode-2.33.3 → voice_mode-2.34.0}/voice_mode/voice_preferences.py +0 -0
@@ -7,10 +7,48 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.34.0] - 2025-08-26
11
+
12
+ ## [2.24.0] - 2025-08-26
13
+
14
+ ### Changed
15
+ - **Installer improvements**
16
+ - Refactored installer to use permanent `uv tool install --upgrade` instead of `uvx --refresh`
17
+ - Added `uv tool update-shell` for automatic PATH configuration
18
+ - Improved shell completion detection with smart fallbacks
19
+ - Added Homebrew zsh completion directory support on macOS
20
+ - Implemented XDG-compliant paths for bash completions
21
+ - Removed fish shell support to simplify maintenance (bash/zsh only)
22
+ - Zsh completions now correctly use underscore prefix (_voicemode)
23
+ - MCP configuration now uses plain `voice-mode` command for better performance
24
+
25
+ ### Fixed
26
+ - **Service file updates on reinstall**
27
+ - Fixed whisper and kokoro installers to always update service files (plist/systemd) even when service is already installed
28
+ - Ensures paths are properly expanded (no `~` symbols) in service files
29
+ - Fixes issue where broken service files with unexpanded paths would remain broken after running install.sh
30
+ - Service files now updated to latest templates on every install, ensuring users always get working configurations
31
+
32
+ ## [2.33.4] - 2025-08-26
33
+
34
+ ### Fixed
35
+ - **CoreML support restoration**
36
+ - Re-enabled CoreML acceleration after fixing plist template path issues
37
+ - Improved CoreML compilation flags handling in whisper installer
38
+
10
39
  ## [2.33.3] - 2025-08-26
11
40
 
41
+ ### Changed
42
+ - Version bump for testing installer fixes
43
+
12
44
  ## [2.33.2] - 2025-08-26
13
45
 
46
+ ### Fixed
47
+ - **Whisper service installation**
48
+ - Corrected plist template path in whisper installer
49
+ - Fixed CoreML support compilation flags (disabled then re-enabled after testing)
50
+ - Removed duplicate inline plist fallback to prevent template divergence
51
+
14
52
  ## [2.33.1] - 2025-08-26
15
53
 
16
54
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: voice-mode
3
- Version: 2.33.3
3
+ Version: 2.34.0
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
@@ -1,3 +1,3 @@
1
1
  # This file is automatically updated by 'make release'
2
2
  # Do not edit manually
3
- __version__ = "2.33.3"
3
+ __version__ = "2.34.0"
@@ -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
- "message": f"kokoro-fastapi version {current_version} already installed. Use force_reinstall=True to reinstall."
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 launchagent on macOS
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")