voice-mode 2.33.4__py3-none-any.whl → 2.34.1__py3-none-any.whl

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 (146) hide show
  1. voice_mode/__version__.py +1 -1
  2. voice_mode/config.py +1 -1
  3. voice_mode/frontend/package-lock.json +1 -154
  4. voice_mode/templates/launchd/com.voicemode.whisper.plist +4 -4
  5. voice_mode/tools/service.py +11 -5
  6. voice_mode/tools/services/kokoro/install.py +207 -2
  7. voice_mode/tools/services/whisper/install.py +329 -189
  8. {voice_mode-2.33.4.dist-info → voice_mode-2.34.1.dist-info}/METADATA +1 -1
  9. voice_mode-2.34.1.dist-info/RECORD +116 -0
  10. voice_mode/frontend/.next/BUILD_ID +0 -1
  11. voice_mode/frontend/.next/app-build-manifest.json +0 -28
  12. voice_mode/frontend/.next/app-path-routes-manifest.json +0 -1
  13. voice_mode/frontend/.next/build-manifest.json +0 -32
  14. voice_mode/frontend/.next/export-marker.json +0 -1
  15. voice_mode/frontend/.next/images-manifest.json +0 -1
  16. voice_mode/frontend/.next/next-minimal-server.js.nft.json +0 -1
  17. voice_mode/frontend/.next/next-server.js.nft.json +0 -1
  18. voice_mode/frontend/.next/package.json +0 -1
  19. voice_mode/frontend/.next/prerender-manifest.json +0 -1
  20. voice_mode/frontend/.next/react-loadable-manifest.json +0 -1
  21. voice_mode/frontend/.next/required-server-files.json +0 -1
  22. voice_mode/frontend/.next/routes-manifest.json +0 -1
  23. voice_mode/frontend/.next/server/app/_not-found/page.js +0 -1
  24. voice_mode/frontend/.next/server/app/_not-found/page.js.nft.json +0 -1
  25. voice_mode/frontend/.next/server/app/_not-found/page_client-reference-manifest.js +0 -1
  26. voice_mode/frontend/.next/server/app/_not-found.html +0 -1
  27. voice_mode/frontend/.next/server/app/_not-found.meta +0 -6
  28. voice_mode/frontend/.next/server/app/_not-found.rsc +0 -9
  29. voice_mode/frontend/.next/server/app/api/connection-details/route.js +0 -12
  30. voice_mode/frontend/.next/server/app/api/connection-details/route.js.nft.json +0 -1
  31. voice_mode/frontend/.next/server/app/favicon.ico/route.js +0 -12
  32. voice_mode/frontend/.next/server/app/favicon.ico/route.js.nft.json +0 -1
  33. voice_mode/frontend/.next/server/app/favicon.ico.body +0 -0
  34. voice_mode/frontend/.next/server/app/favicon.ico.meta +0 -1
  35. voice_mode/frontend/.next/server/app/index.html +0 -1
  36. voice_mode/frontend/.next/server/app/index.meta +0 -5
  37. voice_mode/frontend/.next/server/app/index.rsc +0 -7
  38. voice_mode/frontend/.next/server/app/page.js +0 -11
  39. voice_mode/frontend/.next/server/app/page.js.nft.json +0 -1
  40. voice_mode/frontend/.next/server/app/page_client-reference-manifest.js +0 -1
  41. voice_mode/frontend/.next/server/app-paths-manifest.json +0 -6
  42. voice_mode/frontend/.next/server/chunks/463.js +0 -1
  43. voice_mode/frontend/.next/server/chunks/682.js +0 -6
  44. voice_mode/frontend/.next/server/chunks/948.js +0 -2
  45. voice_mode/frontend/.next/server/chunks/994.js +0 -2
  46. voice_mode/frontend/.next/server/chunks/font-manifest.json +0 -1
  47. voice_mode/frontend/.next/server/font-manifest.json +0 -1
  48. voice_mode/frontend/.next/server/functions-config-manifest.json +0 -1
  49. voice_mode/frontend/.next/server/interception-route-rewrite-manifest.js +0 -1
  50. voice_mode/frontend/.next/server/middleware-build-manifest.js +0 -1
  51. voice_mode/frontend/.next/server/middleware-manifest.json +0 -6
  52. voice_mode/frontend/.next/server/middleware-react-loadable-manifest.js +0 -1
  53. voice_mode/frontend/.next/server/next-font-manifest.js +0 -1
  54. voice_mode/frontend/.next/server/next-font-manifest.json +0 -1
  55. voice_mode/frontend/.next/server/pages/404.html +0 -1
  56. voice_mode/frontend/.next/server/pages/500.html +0 -1
  57. voice_mode/frontend/.next/server/pages/_app.js +0 -1
  58. voice_mode/frontend/.next/server/pages/_app.js.nft.json +0 -1
  59. voice_mode/frontend/.next/server/pages/_document.js +0 -1
  60. voice_mode/frontend/.next/server/pages/_document.js.nft.json +0 -1
  61. voice_mode/frontend/.next/server/pages/_error.js +0 -1
  62. voice_mode/frontend/.next/server/pages/_error.js.nft.json +0 -1
  63. voice_mode/frontend/.next/server/pages-manifest.json +0 -1
  64. voice_mode/frontend/.next/server/server-reference-manifest.js +0 -1
  65. voice_mode/frontend/.next/server/server-reference-manifest.json +0 -1
  66. voice_mode/frontend/.next/server/webpack-runtime.js +0 -1
  67. voice_mode/frontend/.next/standalone/.next/BUILD_ID +0 -1
  68. voice_mode/frontend/.next/standalone/.next/app-build-manifest.json +0 -28
  69. voice_mode/frontend/.next/standalone/.next/app-path-routes-manifest.json +0 -1
  70. voice_mode/frontend/.next/standalone/.next/build-manifest.json +0 -32
  71. voice_mode/frontend/.next/standalone/.next/package.json +0 -1
  72. voice_mode/frontend/.next/standalone/.next/prerender-manifest.json +0 -1
  73. voice_mode/frontend/.next/standalone/.next/react-loadable-manifest.json +0 -1
  74. voice_mode/frontend/.next/standalone/.next/required-server-files.json +0 -1
  75. voice_mode/frontend/.next/standalone/.next/routes-manifest.json +0 -1
  76. voice_mode/frontend/.next/standalone/.next/server/app/_not-found/page.js +0 -1
  77. voice_mode/frontend/.next/standalone/.next/server/app/_not-found/page.js.nft.json +0 -1
  78. voice_mode/frontend/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +0 -1
  79. voice_mode/frontend/.next/standalone/.next/server/app/_not-found.html +0 -1
  80. voice_mode/frontend/.next/standalone/.next/server/app/_not-found.meta +0 -6
  81. voice_mode/frontend/.next/standalone/.next/server/app/_not-found.rsc +0 -9
  82. voice_mode/frontend/.next/standalone/.next/server/app/api/connection-details/route.js +0 -12
  83. voice_mode/frontend/.next/standalone/.next/server/app/api/connection-details/route.js.nft.json +0 -1
  84. voice_mode/frontend/.next/standalone/.next/server/app/favicon.ico/route.js +0 -12
  85. voice_mode/frontend/.next/standalone/.next/server/app/favicon.ico/route.js.nft.json +0 -1
  86. voice_mode/frontend/.next/standalone/.next/server/app/favicon.ico.body +0 -0
  87. voice_mode/frontend/.next/standalone/.next/server/app/favicon.ico.meta +0 -1
  88. voice_mode/frontend/.next/standalone/.next/server/app/index.html +0 -1
  89. voice_mode/frontend/.next/standalone/.next/server/app/index.meta +0 -5
  90. voice_mode/frontend/.next/standalone/.next/server/app/index.rsc +0 -7
  91. voice_mode/frontend/.next/standalone/.next/server/app/page.js +0 -11
  92. voice_mode/frontend/.next/standalone/.next/server/app/page.js.nft.json +0 -1
  93. voice_mode/frontend/.next/standalone/.next/server/app/page_client-reference-manifest.js +0 -1
  94. voice_mode/frontend/.next/standalone/.next/server/app-paths-manifest.json +0 -6
  95. voice_mode/frontend/.next/standalone/.next/server/chunks/463.js +0 -1
  96. voice_mode/frontend/.next/standalone/.next/server/chunks/682.js +0 -6
  97. voice_mode/frontend/.next/standalone/.next/server/chunks/948.js +0 -2
  98. voice_mode/frontend/.next/standalone/.next/server/chunks/994.js +0 -2
  99. voice_mode/frontend/.next/standalone/.next/server/font-manifest.json +0 -1
  100. voice_mode/frontend/.next/standalone/.next/server/middleware-build-manifest.js +0 -1
  101. voice_mode/frontend/.next/standalone/.next/server/middleware-manifest.json +0 -6
  102. voice_mode/frontend/.next/standalone/.next/server/middleware-react-loadable-manifest.js +0 -1
  103. voice_mode/frontend/.next/standalone/.next/server/next-font-manifest.js +0 -1
  104. voice_mode/frontend/.next/standalone/.next/server/next-font-manifest.json +0 -1
  105. voice_mode/frontend/.next/standalone/.next/server/pages/404.html +0 -1
  106. voice_mode/frontend/.next/standalone/.next/server/pages/500.html +0 -1
  107. voice_mode/frontend/.next/standalone/.next/server/pages/_app.js +0 -1
  108. voice_mode/frontend/.next/standalone/.next/server/pages/_app.js.nft.json +0 -1
  109. voice_mode/frontend/.next/standalone/.next/server/pages/_document.js +0 -1
  110. voice_mode/frontend/.next/standalone/.next/server/pages/_document.js.nft.json +0 -1
  111. voice_mode/frontend/.next/standalone/.next/server/pages/_error.js +0 -1
  112. voice_mode/frontend/.next/standalone/.next/server/pages/_error.js.nft.json +0 -1
  113. voice_mode/frontend/.next/standalone/.next/server/pages-manifest.json +0 -1
  114. voice_mode/frontend/.next/standalone/.next/server/server-reference-manifest.js +0 -1
  115. voice_mode/frontend/.next/standalone/.next/server/server-reference-manifest.json +0 -1
  116. voice_mode/frontend/.next/standalone/.next/server/webpack-runtime.js +0 -1
  117. voice_mode/frontend/.next/standalone/package.json +0 -40
  118. voice_mode/frontend/.next/standalone/server.js +0 -38
  119. voice_mode/frontend/.next/static/chunks/117-40bc79a2b97edb21.js +0 -2
  120. voice_mode/frontend/.next/static/chunks/144d3bae-2d5f122b82426d88.js +0 -1
  121. voice_mode/frontend/.next/static/chunks/471-bd4b96a33883dfa2.js +0 -3
  122. voice_mode/frontend/.next/static/chunks/app/_not-found/page-5011050e402ab9c8.js +0 -1
  123. voice_mode/frontend/.next/static/chunks/app/layout-47219099102c5d79.js +0 -1
  124. voice_mode/frontend/.next/static/chunks/app/page-85f325377cba9fba.js +0 -1
  125. voice_mode/frontend/.next/static/chunks/fd9d1056-af324d327b243cf1.js +0 -1
  126. voice_mode/frontend/.next/static/chunks/framework-f66176bb897dc684.js +0 -1
  127. voice_mode/frontend/.next/static/chunks/main-3163eca598b76a9f.js +0 -1
  128. voice_mode/frontend/.next/static/chunks/main-app-a8b9b6fe38c11e50.js +0 -1
  129. voice_mode/frontend/.next/static/chunks/pages/_app-72b849fbd24ac258.js +0 -1
  130. voice_mode/frontend/.next/static/chunks/pages/_error-7ba65e1336b92748.js +0 -1
  131. voice_mode/frontend/.next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
  132. voice_mode/frontend/.next/static/chunks/webpack-0ea9b80f19935b70.js +0 -1
  133. voice_mode/frontend/.next/static/css/a2f49a47752b5010.css +0 -3
  134. voice_mode/frontend/.next/static/media/01099be941da1820-s.woff2 +0 -0
  135. voice_mode/frontend/.next/static/media/39883d31a7792467-s.p.woff2 +0 -0
  136. voice_mode/frontend/.next/static/media/6368404d2e8d66fe-s.woff2 +0 -0
  137. voice_mode/frontend/.next/static/y_E42BoG1rMRxX7c_GYcB/_buildManifest.js +0 -1
  138. voice_mode/frontend/.next/static/y_E42BoG1rMRxX7c_GYcB/_ssgManifest.js +0 -1
  139. voice_mode/frontend/.next/trace +0 -43
  140. voice_mode/frontend/.next/types/app/api/connection-details/route.ts +0 -343
  141. voice_mode/frontend/.next/types/app/layout.ts +0 -79
  142. voice_mode/frontend/.next/types/app/page.ts +0 -79
  143. voice_mode/frontend/.next/types/package.json +0 -1
  144. voice_mode-2.33.4.dist-info/RECORD +0 -250
  145. {voice_mode-2.33.4.dist-info → voice_mode-2.34.1.dist-info}/WHEEL +0 -0
  146. {voice_mode-2.33.4.dist-info → voice_mode-2.34.1.dist-info}/entry_points.txt +0 -0
voice_mode/__version__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  # This file is automatically updated by 'make release'
2
2
  # Do not edit manually
3
- __version__ = "2.33.4"
3
+ __version__ = "2.34.1"
voice_mode/config.py CHANGED
@@ -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", "4.0")) # Give users 4s to start speaking
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
@@ -21,7 +21,6 @@
21
21
  "@types/node": "^20.17.13",
22
22
  "@types/react": "^18.3.18",
23
23
  "@types/react-dom": "^18.3.5",
24
- "autoprefixer": "^10.4.21",
25
24
  "eslint": "^8.57.1",
26
25
  "eslint-config-next": "14.2.29",
27
26
  "eslint-config-prettier": "9.1.0",
@@ -1255,44 +1254,6 @@
1255
1254
  "node": ">= 0.4"
1256
1255
  }
1257
1256
  },
1258
- "node_modules/autoprefixer": {
1259
- "version": "10.4.21",
1260
- "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz",
1261
- "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==",
1262
- "dev": true,
1263
- "funding": [
1264
- {
1265
- "type": "opencollective",
1266
- "url": "https://opencollective.com/postcss/"
1267
- },
1268
- {
1269
- "type": "tidelift",
1270
- "url": "https://tidelift.com/funding/github/npm/autoprefixer"
1271
- },
1272
- {
1273
- "type": "github",
1274
- "url": "https://github.com/sponsors/ai"
1275
- }
1276
- ],
1277
- "license": "MIT",
1278
- "dependencies": {
1279
- "browserslist": "^4.24.4",
1280
- "caniuse-lite": "^1.0.30001702",
1281
- "fraction.js": "^4.3.7",
1282
- "normalize-range": "^0.1.2",
1283
- "picocolors": "^1.1.1",
1284
- "postcss-value-parser": "^4.2.0"
1285
- },
1286
- "bin": {
1287
- "autoprefixer": "bin/autoprefixer"
1288
- },
1289
- "engines": {
1290
- "node": "^10 || ^12 || >=14"
1291
- },
1292
- "peerDependencies": {
1293
- "postcss": "^8.1.0"
1294
- }
1295
- },
1296
1257
  "node_modules/available-typed-arrays": {
1297
1258
  "version": "1.0.7",
1298
1259
  "dev": true,
@@ -1359,39 +1320,6 @@
1359
1320
  "node": ">=8"
1360
1321
  }
1361
1322
  },
1362
- "node_modules/browserslist": {
1363
- "version": "4.25.3",
1364
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.3.tgz",
1365
- "integrity": "sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ==",
1366
- "dev": true,
1367
- "funding": [
1368
- {
1369
- "type": "opencollective",
1370
- "url": "https://opencollective.com/browserslist"
1371
- },
1372
- {
1373
- "type": "tidelift",
1374
- "url": "https://tidelift.com/funding/github/npm/browserslist"
1375
- },
1376
- {
1377
- "type": "github",
1378
- "url": "https://github.com/sponsors/ai"
1379
- }
1380
- ],
1381
- "license": "MIT",
1382
- "dependencies": {
1383
- "caniuse-lite": "^1.0.30001735",
1384
- "electron-to-chromium": "^1.5.204",
1385
- "node-releases": "^2.0.19",
1386
- "update-browserslist-db": "^1.1.3"
1387
- },
1388
- "bin": {
1389
- "browserslist": "cli.js"
1390
- },
1391
- "engines": {
1392
- "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
1393
- }
1394
- },
1395
1323
  "node_modules/busboy": {
1396
1324
  "version": "1.6.0",
1397
1325
  "dev": true,
@@ -1489,9 +1417,7 @@
1489
1417
  }
1490
1418
  },
1491
1419
  "node_modules/caniuse-lite": {
1492
- "version": "1.0.30001737",
1493
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001737.tgz",
1494
- "integrity": "sha512-BiloLiXtQNrY5UyF0+1nSJLXUENuhka2pzy2Fx5pGxqavdrxSCW4U6Pn/PoG3Efspi2frRbHpBV2XsrPE6EDlw==",
1420
+ "version": "1.0.30001726",
1495
1421
  "dev": true,
1496
1422
  "funding": [
1497
1423
  {
@@ -1773,13 +1699,6 @@
1773
1699
  "dev": true,
1774
1700
  "license": "MIT"
1775
1701
  },
1776
- "node_modules/electron-to-chromium": {
1777
- "version": "1.5.209",
1778
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.209.tgz",
1779
- "integrity": "sha512-Xoz0uMrim9ZETCQt8UgM5FxQF9+imA7PBpokoGcZloA1uw2LeHzTlip5cb5KOAsXZLjh/moN2vReN3ZjJmjI9A==",
1780
- "dev": true,
1781
- "license": "ISC"
1782
- },
1783
1702
  "node_modules/emoji-regex": {
1784
1703
  "version": "9.2.2",
1785
1704
  "dev": true,
@@ -1946,16 +1865,6 @@
1946
1865
  "url": "https://github.com/sponsors/ljharb"
1947
1866
  }
1948
1867
  },
1949
- "node_modules/escalade": {
1950
- "version": "3.2.0",
1951
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
1952
- "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
1953
- "dev": true,
1954
- "license": "MIT",
1955
- "engines": {
1956
- "node": ">=6"
1957
- }
1958
- },
1959
1868
  "node_modules/escape-string-regexp": {
1960
1869
  "version": "4.0.0",
1961
1870
  "dev": true,
@@ -2553,20 +2462,6 @@
2553
2462
  "url": "https://github.com/sponsors/isaacs"
2554
2463
  }
2555
2464
  },
2556
- "node_modules/fraction.js": {
2557
- "version": "4.3.7",
2558
- "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
2559
- "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==",
2560
- "dev": true,
2561
- "license": "MIT",
2562
- "engines": {
2563
- "node": "*"
2564
- },
2565
- "funding": {
2566
- "type": "patreon",
2567
- "url": "https://github.com/sponsors/rawify"
2568
- }
2569
- },
2570
2465
  "node_modules/framer-motion": {
2571
2466
  "version": "11.18.2",
2572
2467
  "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz",
@@ -3798,13 +3693,6 @@
3798
3693
  "node": "^10 || ^12 || >=14"
3799
3694
  }
3800
3695
  },
3801
- "node_modules/node-releases": {
3802
- "version": "2.0.19",
3803
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
3804
- "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
3805
- "dev": true,
3806
- "license": "MIT"
3807
- },
3808
3696
  "node_modules/normalize-path": {
3809
3697
  "version": "3.0.0",
3810
3698
  "dev": true,
@@ -3813,16 +3701,6 @@
3813
3701
  "node": ">=0.10.0"
3814
3702
  }
3815
3703
  },
3816
- "node_modules/normalize-range": {
3817
- "version": "0.1.2",
3818
- "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
3819
- "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
3820
- "dev": true,
3821
- "license": "MIT",
3822
- "engines": {
3823
- "node": ">=0.10.0"
3824
- }
3825
- },
3826
3704
  "node_modules/object-assign": {
3827
3705
  "version": "4.1.1",
3828
3706
  "dev": true,
@@ -5340,37 +5218,6 @@
5340
5218
  "@unrs/resolver-binding-win32-x64-msvc": "1.9.2"
5341
5219
  }
5342
5220
  },
5343
- "node_modules/update-browserslist-db": {
5344
- "version": "1.1.3",
5345
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
5346
- "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
5347
- "dev": true,
5348
- "funding": [
5349
- {
5350
- "type": "opencollective",
5351
- "url": "https://opencollective.com/browserslist"
5352
- },
5353
- {
5354
- "type": "tidelift",
5355
- "url": "https://tidelift.com/funding/github/npm/browserslist"
5356
- },
5357
- {
5358
- "type": "github",
5359
- "url": "https://github.com/sponsors/ai"
5360
- }
5361
- ],
5362
- "license": "MIT",
5363
- "dependencies": {
5364
- "escalade": "^3.2.0",
5365
- "picocolors": "^1.1.1"
5366
- },
5367
- "bin": {
5368
- "update-browserslist-db": "cli.js"
5369
- },
5370
- "peerDependencies": {
5371
- "browserslist": ">= 4.21.0"
5372
- }
5373
- },
5374
5221
  "node_modules/uri-js": {
5375
5222
  "version": "4.4.1",
5376
5223
  "dev": true,
@@ -9,18 +9,18 @@
9
9
  <string>com.voicemode.whisper</string>
10
10
  <key>ProgramArguments</key>
11
11
  <array>
12
- <string>~/.voicemode/services/whisper/bin/start-whisper-server.sh</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>~/.voicemode/logs/whisper/whisper.out.log</string>
19
+ <string>{LOG_DIR}/whisper/whisper.out.log</string>
20
20
  <key>StandardErrorPath</key>
21
- <string>~/.voicemode/logs/whisper/whisper.err.log</string>
21
+ <string>{LOG_DIR}/whisper/whisper.err.log</string>
22
22
  <key>WorkingDirectory</key>
23
- <string>~/.voicemode/services/whisper</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
- WHISPER_BIN=whisper_bin,
587
- WHISPER_PORT=WHISPER_PORT,
588
- MODEL_FILE=model_file,
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
- "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")