@kimicode/cli 1.0.39 → 1.0.41

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 (532) hide show
  1. package/LICENSE.md +12 -0
  2. package/README.md +1007 -28
  3. package/bin/stt/requirements.txt +20 -0
  4. package/bin/stt/stt_server.py +544 -0
  5. package/bin/tts/requirements.txt +21 -0
  6. package/bin/tts/tts_server.py +404 -0
  7. package/cli-wrapper.cjs +4 -0
  8. package/install.cjs +156 -2
  9. package/llms.txt +115 -0
  10. package/package.json +30 -11
  11. package/web/dist/assets/AssistantsView-dhdYVIy0.js +1 -0
  12. package/web/dist/assets/AutomationView-D1dU5ZV7.js +1 -0
  13. package/web/dist/assets/ChannelConfigView-CEPVZntU.js +8 -0
  14. package/web/dist/assets/ChartsView-DdN-beHW.js +1 -0
  15. package/web/dist/assets/ConnectorsView-CseCss9C.js +1 -0
  16. package/web/dist/assets/FileTreeView-Dnc3-gRF.js +1 -0
  17. package/web/dist/assets/HookConfigView-DA2h4HdJ.js +1 -0
  18. package/web/dist/assets/InputArea-DAgOOjOc.js +14 -0
  19. package/web/dist/assets/MemoryView-D6OUBi4-.js +1 -0
  20. package/web/dist/assets/MessageBubble-CCTRbIMZ.css +1 -0
  21. package/web/dist/assets/MessageBubble-CV16DZMG.js +453 -0
  22. package/web/dist/assets/ModelsView-BuqD9vWl.js +1 -0
  23. package/web/dist/assets/PluginsView-CIziDHFJ.js +2 -0
  24. package/web/dist/assets/ProjectsView-c6faWy32.js +1 -0
  25. package/web/dist/assets/SessionGrid-BZJHcklh.js +4 -0
  26. package/web/dist/assets/SessionGrid-Bk5CAXEn.css +1 -0
  27. package/web/dist/assets/SettingsModal-BqdGEGW-.js +4 -0
  28. package/web/dist/assets/SkillsView-CB6CKkmb.js +1 -0
  29. package/web/dist/assets/TemplatesTab-B8efVwVZ.js +1 -0
  30. package/web/dist/assets/ThreadsView-CDoM0aNb.js +2 -0
  31. package/web/dist/assets/ToolsView-BG-xsWsO.js +1 -0
  32. package/web/dist/assets/abnfDiagram-WEYOIA3C-DGUPHfRO.js +1 -0
  33. package/web/dist/assets/agentIconSet-Btsb_SC4.js +1 -0
  34. package/web/dist/assets/api-CCt9DRQm.js +1 -0
  35. package/web/dist/assets/arc-Y1ARJVZ4.js +1 -0
  36. package/web/dist/assets/architecture-WOLXFQ4H-BUERx_-B.js +1 -0
  37. package/web/dist/assets/architectureDiagram-ZHVYKT53-BPsQL7O5.js +36 -0
  38. package/web/dist/assets/array-BifhSqXX.js +1 -0
  39. package/web/dist/assets/blockDiagram-RJAE7QA3-pB7EXp6p.js +140 -0
  40. package/web/dist/assets/c4Diagram-XDPPPE54-BnPeCq_L.js +38 -0
  41. package/web/dist/assets/channel-CSrLW66f.js +1 -0
  42. package/web/dist/assets/channelMeta-BGQXQxgj.js +1 -0
  43. package/web/dist/assets/chunk-2E7WHVMD-xX282MrC.js +88 -0
  44. package/web/dist/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
  45. package/web/dist/assets/chunk-4EMNKR2T-BAQDLVqt.js +1 -0
  46. package/web/dist/assets/chunk-55XCU76K-BwAE3Eqq.js +1 -0
  47. package/web/dist/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
  48. package/web/dist/assets/chunk-7S6XSTFO-DwwaClMo.js +2 -0
  49. package/web/dist/assets/chunk-AQDTSKZB-DHG5Km_p.js +72 -0
  50. package/web/dist/assets/chunk-DSNLSHJZ-a6-B52h-.js +129 -0
  51. package/web/dist/assets/chunk-DUW6YSOI-Dgx8z5s3.js +1 -0
  52. package/web/dist/assets/chunk-HAGFOTED-DZMcAj3o.js +62 -0
  53. package/web/dist/assets/chunk-HNE4Q36P-gl_4xI6B.js +1 -0
  54. package/web/dist/assets/chunk-J5ZVWO5B-oDCd-gWO.js +1 -0
  55. package/web/dist/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
  56. package/web/dist/assets/chunk-NGNAAXSQ-Bw6tHVuc.js +136 -0
  57. package/web/dist/assets/chunk-NQOKRVIF-B8Q-YmhL.js +213 -0
  58. package/web/dist/assets/chunk-UHW7PFBP-isP5lYMB.js +1 -0
  59. package/web/dist/assets/chunk-VQ5W62QI-Bn68syIc.js +1 -0
  60. package/web/dist/assets/chunk-WQQL4CDK-CdZpmEYA.js +10 -0
  61. package/web/dist/assets/chunk-XXDRQBXY-CnbRkih_.js +1 -0
  62. package/web/dist/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
  63. package/web/dist/assets/chunk-YZO67HFG-DX9GhJkR.js +1 -0
  64. package/web/dist/assets/chunk-ZAUX2LTX-Ki22UlYp.js +1 -0
  65. package/web/dist/assets/chunk-ZCHMQSOR-BVj0Zx6L.js +2 -0
  66. package/web/dist/assets/classDiagram-v2-4MYWR3UA-C7PV1veb.js +217 -0
  67. package/web/dist/assets/cn-rx4uumOJ.js +1 -0
  68. package/web/dist/assets/common-BHXgEvgk.js +192 -0
  69. package/web/dist/assets/cose-bilkent-JH36ORCC-DHFtpmij.js +1 -0
  70. package/web/dist/assets/cynefin-EF2NZ3EQ-1rI2T0cc.js +1 -0
  71. package/web/dist/assets/cynefinDiagram-WF24UDRP-C8O_v8xU.js +62 -0
  72. package/web/dist/assets/cytoscape.esm-BY277UBX.js +321 -0
  73. package/web/dist/assets/dagre-NK4GIZVX-BWGoHIUd.js +4 -0
  74. package/web/dist/assets/defaultLocale-BFoDCU3G.js +1 -0
  75. package/web/dist/assets/diagram-CBK3MWTC-kf9v35RN.js +24 -0
  76. package/web/dist/assets/diagram-I5XIIV2D-Ro-ig56_.js +24 -0
  77. package/web/dist/assets/diagram-ODIL2UCW-CtGga01G.js +30 -0
  78. package/web/dist/assets/diagram-PFBEJUXE-C14lcR-6.js +3 -0
  79. package/web/dist/assets/diagram-RJA3I4VV-BBbqaSKU.js +41 -0
  80. package/web/dist/assets/diagram-TYUNFSNR-BynvjQsr.js +200 -0
  81. package/web/dist/assets/dist-CumnGVmJ.js +1 -0
  82. package/web/dist/assets/ebnfDiagram-V4P3TTPT-BdqUCZnk.js +1 -0
  83. package/web/dist/assets/elk-QONTBCTQ-CYCP_9Jy.js +27 -0
  84. package/web/dist/assets/erDiagram-KYQIGQAA-C3L55l_F.js +99 -0
  85. package/web/dist/assets/eventmodeling-K75KTNOO-CTQdA_1v.js +1 -0
  86. package/web/dist/assets/flowDiagram-IUGLLWBU-COLKOzBg.js +1 -0
  87. package/web/dist/assets/ganttDiagram-R7GPNTJY-Be4Qk8Hx.js +292 -0
  88. package/web/dist/assets/gitGraph-VSP46ZUC-B59HXvj1.js +1 -0
  89. package/web/dist/assets/gitGraphDiagram-PDYL7XHY-C2A0VA6q.js +106 -0
  90. package/web/dist/assets/graphlib-DS17s2tU.js +1 -0
  91. package/web/dist/assets/icons-BvYhTENi.js +1 -0
  92. package/web/dist/assets/index-BNI6CNsY.css +1 -0
  93. package/web/dist/assets/index-BX-d2OBW.js +130 -0
  94. package/web/dist/assets/info-OHQRW6UA-DWlkKvmi.js +1 -0
  95. package/web/dist/assets/infoDiagram-MVDRKAX5-B8e6dTjN.js +2 -0
  96. package/web/dist/assets/init-C-OQMol4.js +1 -0
  97. package/web/dist/assets/ishikawaDiagram-HPEHV2GE-udXPYi9H.js +70 -0
  98. package/web/dist/assets/journeyDiagram-BOSHW7O7-DTyDkf5d.js +139 -0
  99. package/web/dist/assets/kanban-definition-FNSA4HC5-BXgV20qe.js +89 -0
  100. package/web/dist/assets/katex-B940_AAO.js +257 -0
  101. package/web/dist/assets/line-DVabuQAu.js +1 -0
  102. package/web/dist/assets/linear-gLkGxN9m.js +1 -0
  103. package/web/dist/assets/markdown-DAQZeY5V.js +14 -0
  104. package/web/dist/assets/mermaid-parser.core-BrXnuEs7.js +7 -0
  105. package/web/dist/assets/mermaid.core-BGbz3k1k.js +44 -0
  106. package/web/dist/assets/mindmap-definition-6OY57UG3-DbtP-bmk.js +98 -0
  107. package/web/dist/assets/ordinal-BDEzSJ7C.js +1 -0
  108. package/web/dist/assets/packet-JDAUHWVQ-iW8w3JF5.js +1 -0
  109. package/web/dist/assets/panels-DYiQiVWh.js +1 -0
  110. package/web/dist/assets/path-fybaL0A-.js +1 -0
  111. package/web/dist/assets/pegDiagram-ECIUJNBX-rBWJbaq4.js +1 -0
  112. package/web/dist/assets/pie-XZMESJXO-KHXEq-Yx.js +1 -0
  113. package/web/dist/assets/pieDiagram-GNMPY74T-DsW7yHm0.js +39 -0
  114. package/web/dist/assets/preload-helper-BLdHbfdN.js +1 -0
  115. package/web/dist/assets/profileChange-xrd7jkii.js +1 -0
  116. package/web/dist/assets/quadrantDiagram-D57K4MC3-DF_pwNhM.js +7 -0
  117. package/web/dist/assets/radar-ABXABTNO-BkAUJ_Zd.js +1 -0
  118. package/web/dist/assets/railroad-I3PHUGI6-BdnWRdGt.js +1 -0
  119. package/web/dist/assets/railroad-abnf-LNEFI6M7-C8K36-8b.js +1 -0
  120. package/web/dist/assets/railroad-ebnf-DWYD2UWJ-BlS25T3f.js +1 -0
  121. package/web/dist/assets/railroad-peg-JR7BVNR7-CBKGDi75.js +1 -0
  122. package/web/dist/assets/railroadDiagram-Y72NRBL7-DmrlMIvv.js +1 -0
  123. package/web/dist/assets/react-vendor-BlngCquv.js +19 -0
  124. package/web/dist/assets/requirementDiagram-EQYSW5RA-cvUq9H3I.js +84 -0
  125. package/web/dist/assets/rolldown-runtime-COiEfhsA.js +1 -0
  126. package/web/dist/assets/rough.esm-Dy-Kn_BL.js +1 -0
  127. package/web/dist/assets/sankeyDiagram-SY24JHO5-BdxXAbER.js +40 -0
  128. package/web/dist/assets/sequenceDiagram-BLEHNHQU-Druumi0k.js +169 -0
  129. package/web/dist/assets/settingsStore-RLUAWgIk.js +1 -0
  130. package/web/dist/assets/sizeCapture-INFHLROL-3uHXrFqA.js +1 -0
  131. package/web/dist/assets/src-49LUQhkG.js +1 -0
  132. package/web/dist/assets/src-BWF7jMDv.js +1 -0
  133. package/web/dist/assets/state-_ZfNVAsd.js +1 -0
  134. package/web/dist/assets/stateDiagram-v2-A6IESKWZ-Jgh6qlrr.js +291 -0
  135. package/web/dist/assets/sttLanguages-aqoVC79g.js +2 -0
  136. package/web/dist/assets/swimlanes-B3ENRKFQ-4xKumvXQ.js +1 -0
  137. package/web/dist/assets/swimlanesDiagram-AYQRV4AK-bHcLKL3d.js +8 -0
  138. package/web/dist/assets/timeline-definition-JXYKGLGB-BYKhga9x.js +120 -0
  139. package/web/dist/assets/treeView-D4JQ5SDB-6-eTVUuH.js +1 -0
  140. package/web/dist/assets/treemap-SAJKECNS-Dxl8yCLE.js +1 -0
  141. package/web/dist/assets/ttsSettings-DHo3OsOq.js +15 -0
  142. package/web/dist/assets/types-JssxRFJn.js +1 -0
  143. package/web/dist/assets/useTranslation-BGmCDuQI.js +1 -0
  144. package/web/dist/assets/usecaseDiagram-WPOLBNNX-BaQ8nAv9.js +328 -0
  145. package/web/dist/assets/vennDiagram-Q3Y3IDCW-DkPvfmfT.js +34 -0
  146. package/web/dist/assets/wardley-7MLQ67FV-CZtI22aa.js +1 -0
  147. package/web/dist/assets/wardleyDiagram-IVAIDKWA-Dgi2OWQ4.js +78 -0
  148. package/web/dist/assets/websocket-C9e3h7VU.js +1 -0
  149. package/web/dist/assets/whatsNewStore-Bf_7lW8o.js +1 -0
  150. package/web/dist/assets/workspaceSemantics-RaIRer76.js +3 -0
  151. package/web/dist/assets/xychartDiagram-4URNHDU4-Ch96dep-.js +7 -0
  152. package/web/dist/favicon.svg +18 -39
  153. package/web/dist/icons/icon-1024.png +0 -0
  154. package/web/dist/icons/icon-180.png +0 -0
  155. package/web/dist/icons/icon-192.png +0 -0
  156. package/web/dist/icons/icon-512.png +0 -0
  157. package/web/dist/icons/icon-maskable-512.png +0 -0
  158. package/web/dist/index.html +46 -12
  159. package/web/dist/logo.svg +25 -39
  160. package/web/dist/manifest.webmanifest +2 -2
  161. package/web/dist/assets/AssistantsView-0aLB_cG6.js +0 -1
  162. package/web/dist/assets/AutomationView-CAMjPhJ2.js +0 -1
  163. package/web/dist/assets/ChannelConfigView-FcioeeYb.js +0 -8
  164. package/web/dist/assets/ConnectorsView-DERP7zAW.js +0 -1
  165. package/web/dist/assets/FileTreeView-hBtHBbAV.js +0 -11
  166. package/web/dist/assets/HookConfigView-NModtSB8.js +0 -1
  167. package/web/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  168. package/web/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  169. package/web/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  170. package/web/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  171. package/web/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  172. package/web/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  173. package/web/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  174. package/web/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  175. package/web/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  176. package/web/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  177. package/web/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  178. package/web/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  179. package/web/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  180. package/web/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  181. package/web/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  182. package/web/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  183. package/web/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  184. package/web/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  185. package/web/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  186. package/web/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  187. package/web/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  188. package/web/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  189. package/web/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  190. package/web/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  191. package/web/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  192. package/web/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  193. package/web/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  194. package/web/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  195. package/web/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  196. package/web/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  197. package/web/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  198. package/web/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  199. package/web/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  200. package/web/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  201. package/web/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  202. package/web/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  203. package/web/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  204. package/web/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  205. package/web/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  206. package/web/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  207. package/web/dist/assets/ModelsView-BPpO50C_.js +0 -1
  208. package/web/dist/assets/PluginsView-D0NE7zrt.js +0 -1
  209. package/web/dist/assets/ProjectsView-PIv7Gx4q.js +0 -1
  210. package/web/dist/assets/SessionGrid-BGpz0-op.css +0 -1
  211. package/web/dist/assets/SessionGrid-BsDL3lov.js +0 -47
  212. package/web/dist/assets/SettingsModal-DdebvEmv.js +0 -10
  213. package/web/dist/assets/SkillsView-BNoigImJ.js +0 -1
  214. package/web/dist/assets/TemplatesTab-D8Pa68fj.js +0 -1
  215. package/web/dist/assets/ToolsView-NY-yk5YX.js +0 -1
  216. package/web/dist/assets/WorkspaceDirPicker-C5knnArH.js +0 -6
  217. package/web/dist/assets/abap-BdImnpbu.js +0 -1
  218. package/web/dist/assets/actionscript-3-B3316cI-.js +0 -1
  219. package/web/dist/assets/ada-bCR0ucgS.js +0 -1
  220. package/web/dist/assets/ahk-CsyLZFj1.js +0 -1
  221. package/web/dist/assets/ahk2-8Zs4aa1G.js +0 -1
  222. package/web/dist/assets/andromeeda-C4gqWexZ.js +0 -1
  223. package/web/dist/assets/angular-html-DA-rfuFy.js +0 -1
  224. package/web/dist/assets/angular-ts-BrjP3tb8.js +0 -1
  225. package/web/dist/assets/apache-Pmp26Uib.js +0 -1
  226. package/web/dist/assets/apex-DhZFqWV2.js +0 -1
  227. package/web/dist/assets/apl-CORt7UWP.js +0 -1
  228. package/web/dist/assets/applescript-Co6uUVPk.js +0 -1
  229. package/web/dist/assets/ara-BRHolxvo.js +0 -1
  230. package/web/dist/assets/asciidoc-CSVQ5wI8.js +0 -1
  231. package/web/dist/assets/asm-D_Q5rh1f.js +0 -1
  232. package/web/dist/assets/astro-HNnZUWAn.js +0 -1
  233. package/web/dist/assets/aurora-x-D-2ljcwZ.js +0 -1
  234. package/web/dist/assets/awk-DMzUqQB5.js +0 -1
  235. package/web/dist/assets/ayu-dark-DYE7WIF3.js +0 -1
  236. package/web/dist/assets/ayu-light-BA47KaF1.js +0 -1
  237. package/web/dist/assets/ayu-mirage-32ctXXKs.js +0 -1
  238. package/web/dist/assets/ballerina-BFfxhgS-.js +0 -1
  239. package/web/dist/assets/bat-CickPsom.js +0 -1
  240. package/web/dist/assets/beancount-k_qm7-4y.js +0 -1
  241. package/web/dist/assets/berry-uYugtg8r.js +0 -1
  242. package/web/dist/assets/bibtex-CHM0blh-.js +0 -1
  243. package/web/dist/assets/bicep-Bmn6On1c.js +0 -1
  244. package/web/dist/assets/bird2-Bx8U0n9b.js +0 -1
  245. package/web/dist/assets/blade-2xfisSek.js +0 -1
  246. package/web/dist/assets/bsl-DlhNcFeZ.js +0 -1
  247. package/web/dist/assets/c-BIGW1oBm.js +0 -1
  248. package/web/dist/assets/c3-Dp5svz6Z.js +0 -1
  249. package/web/dist/assets/cadence-Bv_4Rxtq.js +0 -1
  250. package/web/dist/assets/cairo-KRGpt6FW.js +0 -1
  251. package/web/dist/assets/catppuccin-frappe-CZL1YF0i.js +0 -1
  252. package/web/dist/assets/catppuccin-latte-DH-8KZSZ.js +0 -1
  253. package/web/dist/assets/catppuccin-macchiato-B7yYVSCf.js +0 -1
  254. package/web/dist/assets/catppuccin-mocha-Ct7hS0mc.js +0 -1
  255. package/web/dist/assets/chapel-DTp_pixX.js +0 -1
  256. package/web/dist/assets/clarity-Dn5IMItf.js +0 -1
  257. package/web/dist/assets/clojure-P80f7IUj.js +0 -1
  258. package/web/dist/assets/cmake-D1j8_8rp.js +0 -1
  259. package/web/dist/assets/cobol-nBiQ_Alo.js +0 -1
  260. package/web/dist/assets/codeowners-Bp6g37R7.js +0 -1
  261. package/web/dist/assets/codeql-DsOJ9woJ.js +0 -1
  262. package/web/dist/assets/coffee-Ch7k5sss.js +0 -1
  263. package/web/dist/assets/common-lisp-Cg-RD9OK.js +0 -1
  264. package/web/dist/assets/coq-C7JzOVbR.js +0 -1
  265. package/web/dist/assets/cpp-BMRokrvK.js +0 -1
  266. package/web/dist/assets/crystal-DGywbUpC.js +0 -1
  267. package/web/dist/assets/csharp-DSvCPggb.js +0 -1
  268. package/web/dist/assets/css-CLj8gQPS.js +0 -1
  269. package/web/dist/assets/csv-fuZLfV_i.js +0 -1
  270. package/web/dist/assets/cue-D82EKSYY.js +0 -1
  271. package/web/dist/assets/cypher-COkxafJQ.js +0 -1
  272. package/web/dist/assets/d-85-TOEBH.js +0 -1
  273. package/web/dist/assets/dark-plus-C3mMm8J8.js +0 -1
  274. package/web/dist/assets/dart-bE4Kk8sk.js +0 -1
  275. package/web/dist/assets/dax-CEL-wOlO.js +0 -1
  276. package/web/dist/assets/desktop-BmXAJ9_W.js +0 -1
  277. package/web/dist/assets/diff-D97Zzqfu.js +0 -1
  278. package/web/dist/assets/docker-BcOcwvcX.js +0 -1
  279. package/web/dist/assets/dotenv-Da5cRb03.js +0 -1
  280. package/web/dist/assets/dracula-BzJJZx-M.js +0 -1
  281. package/web/dist/assets/dracula-soft-BXkSAIEj.js +0 -1
  282. package/web/dist/assets/dream-maker-BtqSS_iP.js +0 -1
  283. package/web/dist/assets/edge-FbVlp4U3.js +0 -1
  284. package/web/dist/assets/elixir-CkH2-t6x.js +0 -1
  285. package/web/dist/assets/elm-DbKCFpqz.js +0 -1
  286. package/web/dist/assets/emacs-lisp-C_m_b--Z.js +0 -1
  287. package/web/dist/assets/erb-DXfck5VN.js +0 -1
  288. package/web/dist/assets/erlang-DsQrWhSR.js +0 -1
  289. package/web/dist/assets/everforest-dark-BgDCqdQA.js +0 -1
  290. package/web/dist/assets/everforest-light-C8M2exoo.js +0 -1
  291. package/web/dist/assets/fennel-BYunw83y.js +0 -1
  292. package/web/dist/assets/fish-BvzEVeQv.js +0 -1
  293. package/web/dist/assets/fluent-C4IJs8-o.js +0 -1
  294. package/web/dist/assets/fortran-fixed-form-CkoXwp7k.js +0 -1
  295. package/web/dist/assets/fortran-free-form-BxgE0vQu.js +0 -1
  296. package/web/dist/assets/fsharp-CXgrBDvD.js +0 -1
  297. package/web/dist/assets/gdresource-TyuKm33G.js +0 -1
  298. package/web/dist/assets/gdscript-DqcFQ5yU.js +0 -1
  299. package/web/dist/assets/gdshader-DkwncUOv.js +0 -1
  300. package/web/dist/assets/genie-D0YGMca9.js +0 -1
  301. package/web/dist/assets/gherkin-DyxjwDmM.js +0 -1
  302. package/web/dist/assets/git-commit-F4YmCXRG.js +0 -1
  303. package/web/dist/assets/git-rebase-r7XF79zn.js +0 -1
  304. package/web/dist/assets/github-dark-DHJKELXO.js +0 -1
  305. package/web/dist/assets/github-dark-default-Cuk6v7N8.js +0 -1
  306. package/web/dist/assets/github-dark-dimmed-DH5Ifo-i.js +0 -1
  307. package/web/dist/assets/github-dark-high-contrast-E3gJ1_iC.js +0 -1
  308. package/web/dist/assets/github-light-DAi9KRSo.js +0 -1
  309. package/web/dist/assets/github-light-default-D7oLnXFd.js +0 -1
  310. package/web/dist/assets/github-light-high-contrast-BfjtVDDH.js +0 -1
  311. package/web/dist/assets/gleam-BspZqrRM.js +0 -1
  312. package/web/dist/assets/glimmer-js-ByusRIyA.js +0 -1
  313. package/web/dist/assets/glimmer-ts-BfAWNZQY.js +0 -1
  314. package/web/dist/assets/glsl-DplSGwfg.js +0 -1
  315. package/web/dist/assets/gn-n2N0HUVH.js +0 -1
  316. package/web/dist/assets/gnuplot-DdkO51Og.js +0 -1
  317. package/web/dist/assets/go-C27-OAKa.js +0 -1
  318. package/web/dist/assets/graphql-ChdNCCLP.js +0 -1
  319. package/web/dist/assets/groovy-gcz8RCvz.js +0 -1
  320. package/web/dist/assets/gruvbox-dark-hard-CFHQjOhq.js +0 -1
  321. package/web/dist/assets/gruvbox-dark-medium-GsRaNv29.js +0 -1
  322. package/web/dist/assets/gruvbox-dark-soft-CVdnzihN.js +0 -1
  323. package/web/dist/assets/gruvbox-light-hard-CH1njM8p.js +0 -1
  324. package/web/dist/assets/gruvbox-light-medium-DRw_LuNl.js +0 -1
  325. package/web/dist/assets/gruvbox-light-soft-hJgmCMqR.js +0 -1
  326. package/web/dist/assets/hack-BWmVpMyf.js +0 -1
  327. package/web/dist/assets/haml-D5jkg6IW.js +0 -1
  328. package/web/dist/assets/handlebars-BpdQsYii.js +0 -1
  329. package/web/dist/assets/haskell-Df6bDoY_.js +0 -1
  330. package/web/dist/assets/haxe-CfZj7gIn.js +0 -1
  331. package/web/dist/assets/hcl-BWvSN4gD.js +0 -1
  332. package/web/dist/assets/hjson-D5-asLiD.js +0 -1
  333. package/web/dist/assets/hlsl-D3lLCCz7.js +0 -1
  334. package/web/dist/assets/horizon-BUw7H-hv.js +0 -1
  335. package/web/dist/assets/horizon-bright-CUuTKBJd.js +0 -1
  336. package/web/dist/assets/houston-DnULxvSX.js +0 -1
  337. package/web/dist/assets/html-derivative-DlHx6ybY.js +0 -1
  338. package/web/dist/assets/html-pp8916En.js +0 -1
  339. package/web/dist/assets/http-jrhK8wxY.js +0 -1
  340. package/web/dist/assets/hurl-irOxFIW8.js +0 -1
  341. package/web/dist/assets/hxml-2-FPmUDs.js +0 -1
  342. package/web/dist/assets/hy-DFXneXwc.js +0 -1
  343. package/web/dist/assets/icons-BxNxyk-c.js +0 -804
  344. package/web/dist/assets/imba-DGztddWO.js +0 -1
  345. package/web/dist/assets/index-DaiVo1RY.css +0 -1
  346. package/web/dist/assets/index-RB5ffy32.js +0 -620
  347. package/web/dist/assets/ini-BEwlwnbL.js +0 -1
  348. package/web/dist/assets/java-CylS5w8V.js +0 -1
  349. package/web/dist/assets/javascript-wDzz0qaB.js +0 -1
  350. package/web/dist/assets/jinja-f2NsQr07.js +0 -1
  351. package/web/dist/assets/jison-wvAkD_A8.js +0 -1
  352. package/web/dist/assets/json-Cp-IABpG.js +0 -1
  353. package/web/dist/assets/json5-C9tS-k6U.js +0 -1
  354. package/web/dist/assets/jsonc-Des-eS-w.js +0 -1
  355. package/web/dist/assets/jsonl-DcaNXYhu.js +0 -1
  356. package/web/dist/assets/jsonnet-DFQXde-d.js +0 -1
  357. package/web/dist/assets/jssm-C2t-YnRu.js +0 -1
  358. package/web/dist/assets/jsx-g9-lgVsj.js +0 -1
  359. package/web/dist/assets/julia-5Bft2YPA.js +0 -1
  360. package/web/dist/assets/just-Cwhn7H3k.js +0 -1
  361. package/web/dist/assets/kanagawa-dragon-BuwD2xS4.js +0 -1
  362. package/web/dist/assets/kanagawa-lotus-C3DzagqV.js +0 -1
  363. package/web/dist/assets/kanagawa-wave-DFGoQZhC.js +0 -1
  364. package/web/dist/assets/kdl-DV7GczEv.js +0 -1
  365. package/web/dist/assets/kotlin-BdnUsdx6.js +0 -1
  366. package/web/dist/assets/kusto-wEQ09or8.js +0 -1
  367. package/web/dist/assets/laserwave-DUszq2jm.js +0 -1
  368. package/web/dist/assets/latex-D5pSuvFb.js +0 -1
  369. package/web/dist/assets/lean-BZvkOJ9d.js +0 -1
  370. package/web/dist/assets/less-B1dDrJ26.js +0 -1
  371. package/web/dist/assets/light-plus-B7mTdjB0.js +0 -1
  372. package/web/dist/assets/liquid-C0sCDyMI.js +0 -1
  373. package/web/dist/assets/llvm-BZoOZj88.js +0 -1
  374. package/web/dist/assets/log-2UxHyX5q.js +0 -1
  375. package/web/dist/assets/logo-BtOb2qkB.js +0 -1
  376. package/web/dist/assets/lua-BaeVxFsk.js +0 -1
  377. package/web/dist/assets/luau-BnpPk5vE.js +0 -1
  378. package/web/dist/assets/make-CHLpvVh8.js +0 -1
  379. package/web/dist/assets/markdown-B2L4Mq-x.js +0 -30
  380. package/web/dist/assets/markdown-Cvjx9yec.js +0 -1
  381. package/web/dist/assets/marko-DjSrsDqO.js +0 -1
  382. package/web/dist/assets/material-theme-D5KoaKCx.js +0 -1
  383. package/web/dist/assets/material-theme-darker-BfHTSMKl.js +0 -1
  384. package/web/dist/assets/material-theme-lighter-B0m2ddpp.js +0 -1
  385. package/web/dist/assets/material-theme-ocean-CyktbL80.js +0 -1
  386. package/web/dist/assets/material-theme-palenight-Csfq5Kiy.js +0 -1
  387. package/web/dist/assets/matlab-D7o27uSR.js +0 -1
  388. package/web/dist/assets/mdc-D1_yUvq7.js +0 -1
  389. package/web/dist/assets/mdx-Cmh6b_Ma.js +0 -1
  390. package/web/dist/assets/mermaid-CQcHuHx7.js +0 -1
  391. package/web/dist/assets/min-dark-CafNBF8u.js +0 -1
  392. package/web/dist/assets/min-light-CTRr51gU.js +0 -1
  393. package/web/dist/assets/mipsasm-CKIfxQSi.js +0 -1
  394. package/web/dist/assets/mojo-DJz3ZmWd.js +0 -1
  395. package/web/dist/assets/monokai-BRVnQi9A.js +0 -1
  396. package/web/dist/assets/moonbit-CHtswR0a.js +0 -1
  397. package/web/dist/assets/move-el3G9tDJ.js +0 -1
  398. package/web/dist/assets/narrat-DRg8JJMk.js +0 -1
  399. package/web/dist/assets/nextflow-C-mBbutL.js +0 -1
  400. package/web/dist/assets/nextflow-groovy-vE_lwT2v.js +0 -1
  401. package/web/dist/assets/nginx-BpAMiNFr.js +0 -1
  402. package/web/dist/assets/night-owl-C39BiMTA.js +0 -1
  403. package/web/dist/assets/night-owl-light-CMTm3GFP.js +0 -1
  404. package/web/dist/assets/nim-BIad80T-.js +0 -1
  405. package/web/dist/assets/nix-CwoSXNpI.js +0 -1
  406. package/web/dist/assets/nord-Ddv68eIx.js +0 -1
  407. package/web/dist/assets/nsis-BlV79W_Q.js +0 -1
  408. package/web/dist/assets/nushell-D3jzshHO.js +0 -1
  409. package/web/dist/assets/objective-c-DXmwc3jG.js +0 -1
  410. package/web/dist/assets/objective-cpp-CLxacb5B.js +0 -1
  411. package/web/dist/assets/ocaml-C0hk2d4L.js +0 -1
  412. package/web/dist/assets/odin-BBf5iR-q.js +0 -1
  413. package/web/dist/assets/one-dark-pro-DVMEJ2y_.js +0 -1
  414. package/web/dist/assets/one-light-C3Wv6jpd.js +0 -1
  415. package/web/dist/assets/openscad-C4EeE6gA.js +0 -1
  416. package/web/dist/assets/org-DM6o9KBp.js +0 -1
  417. package/web/dist/assets/panels-P_I7Q72_.js +0 -1
  418. package/web/dist/assets/pascal-D93ZcfNL.js +0 -1
  419. package/web/dist/assets/perl-B9cMNwum.js +0 -1
  420. package/web/dist/assets/php-Csjmro_R.js +0 -1
  421. package/web/dist/assets/pkl-u5AG7uiY.js +0 -1
  422. package/web/dist/assets/plastic-3e1v2bzS.js +0 -1
  423. package/web/dist/assets/plsql-ChMvpjG-.js +0 -1
  424. package/web/dist/assets/po-BTJTHyun.js +0 -1
  425. package/web/dist/assets/poimandres-CS3Unz2-.js +0 -1
  426. package/web/dist/assets/polar-C0HS_06l.js +0 -1
  427. package/web/dist/assets/postcss-CXtECtnM.js +0 -1
  428. package/web/dist/assets/powerquery-CEu0bR-o.js +0 -1
  429. package/web/dist/assets/powershell-BmBUJMz7.js +0 -1
  430. package/web/dist/assets/prisma-Vru482bI.js +0 -1
  431. package/web/dist/assets/profileChange-C7Jg9C61.js +0 -1
  432. package/web/dist/assets/prolog-CbFg5uaA.js +0 -1
  433. package/web/dist/assets/proto-C7zT0LnQ.js +0 -1
  434. package/web/dist/assets/pug-DKIMFp6K.js +0 -1
  435. package/web/dist/assets/puppet-BMWR74SV.js +0 -1
  436. package/web/dist/assets/purescript-CklMAg4u.js +0 -1
  437. package/web/dist/assets/python-B6aJPvgy.js +0 -1
  438. package/web/dist/assets/qml-3beO22l8.js +0 -1
  439. package/web/dist/assets/qmldir-C8lEn-DE.js +0 -1
  440. package/web/dist/assets/qss-IeuSbFQv.js +0 -1
  441. package/web/dist/assets/r-Cf5RLm7j.js +0 -1
  442. package/web/dist/assets/racket-BqYA7rlc.js +0 -1
  443. package/web/dist/assets/raku-DXvB9xmW.js +0 -1
  444. package/web/dist/assets/razor-BjBPvh-w.js +0 -1
  445. package/web/dist/assets/rbs-CpoqiR4B.js +0 -1
  446. package/web/dist/assets/react-vendor-B2V_5iSS.js +0 -9
  447. package/web/dist/assets/red-hvxz__6c.js +0 -1
  448. package/web/dist/assets/reg-C-SQnVFl.js +0 -1
  449. package/web/dist/assets/regexp-CDVJQ6XC.js +0 -1
  450. package/web/dist/assets/rel-C3B-1QV4.js +0 -1
  451. package/web/dist/assets/riscv-BM1_JUlF.js +0 -1
  452. package/web/dist/assets/ron-D8l8udqQ.js +0 -1
  453. package/web/dist/assets/rose-pine-5qJOZa0Y.js +0 -1
  454. package/web/dist/assets/rose-pine-dawn-zx0QlTCp.js +0 -1
  455. package/web/dist/assets/rose-pine-moon-C9pEdX9L.js +0 -1
  456. package/web/dist/assets/rosmsg-BJDFO7_C.js +0 -1
  457. package/web/dist/assets/rst-bs7f0vWN.js +0 -1
  458. package/web/dist/assets/ruby-C0TQ7zu5.js +0 -1
  459. package/web/dist/assets/rust-B1yitclQ.js +0 -1
  460. package/web/dist/assets/sas-DEy46yEz.js +0 -1
  461. package/web/dist/assets/sass-Cj5Yp3dK.js +0 -1
  462. package/web/dist/assets/scala-CqE71os6.js +0 -1
  463. package/web/dist/assets/scheme-C98Dy4si.js +0 -1
  464. package/web/dist/assets/scss-D5BDwBP9.js +0 -1
  465. package/web/dist/assets/sdbl-DVxCFoDh.js +0 -1
  466. package/web/dist/assets/shaderlab-Dg9Lc6iA.js +0 -1
  467. package/web/dist/assets/shellscript-Yzrsuije.js +0 -1
  468. package/web/dist/assets/shellsession-BADoaaVG.js +0 -1
  469. package/web/dist/assets/slack-dark-BthQWCQV.js +0 -1
  470. package/web/dist/assets/slack-ochin-DqwNpetd.js +0 -1
  471. package/web/dist/assets/smalltalk-BOQMe2GC.js +0 -1
  472. package/web/dist/assets/smithy-cds9vsN8.js +0 -1
  473. package/web/dist/assets/snazzy-light-Bw305WKR.js +0 -1
  474. package/web/dist/assets/solarized-dark-CpvCGNkr.js +0 -1
  475. package/web/dist/assets/solarized-light-Dlz6yCKv.js +0 -1
  476. package/web/dist/assets/solidity-DijEV5ha.js +0 -1
  477. package/web/dist/assets/soy-8wufbnw4.js +0 -1
  478. package/web/dist/assets/sparql-rVzFXLq3.js +0 -1
  479. package/web/dist/assets/splunk-BtCnVYZw.js +0 -1
  480. package/web/dist/assets/sql-CRqJ_cUM.js +0 -1
  481. package/web/dist/assets/ssh-config-_ykCGR6B.js +0 -1
  482. package/web/dist/assets/stata-DI20mbqo.js +0 -1
  483. package/web/dist/assets/state-0T92JV0u.js +0 -17
  484. package/web/dist/assets/stylus-BEDo0Tqx.js +0 -1
  485. package/web/dist/assets/surrealql-Cjom0U5J.js +0 -1
  486. package/web/dist/assets/svelte-Cy7k_4gC.js +0 -1
  487. package/web/dist/assets/swift-C2oV4EkX.js +0 -1
  488. package/web/dist/assets/synthwave-84-CbfX1IO0.js +0 -1
  489. package/web/dist/assets/system-verilog-0hqHdDBg.js +0 -1
  490. package/web/dist/assets/systemd-4A_iFExJ.js +0 -1
  491. package/web/dist/assets/talonscript-CkByrt1z.js +0 -1
  492. package/web/dist/assets/tasl-QIJgUcNo.js +0 -1
  493. package/web/dist/assets/tcl-dwOrl1Do.js +0 -1
  494. package/web/dist/assets/templ-DhtptRzy.js +0 -1
  495. package/web/dist/assets/terraform-BETggiCN.js +0 -1
  496. package/web/dist/assets/tex-D96PA37w.js +0 -1
  497. package/web/dist/assets/tokyo-night-hegEt444.js +0 -1
  498. package/web/dist/assets/toml-vGWfd6FD.js +0 -1
  499. package/web/dist/assets/ts-tags-D351s5mN.js +0 -1
  500. package/web/dist/assets/tsv-B_m7g4N7.js +0 -1
  501. package/web/dist/assets/tsx-COt5Ahok.js +0 -1
  502. package/web/dist/assets/turtle-BsS91CYL.js +0 -1
  503. package/web/dist/assets/twig-27uCiNez.js +0 -1
  504. package/web/dist/assets/typescript-BPQ3VLAy.js +0 -1
  505. package/web/dist/assets/typespec-CAFt9gP4.js +0 -1
  506. package/web/dist/assets/typst-BUadGCkm.js +0 -1
  507. package/web/dist/assets/v-BGw2Nkan.js +0 -1
  508. package/web/dist/assets/vala-CsfeWuGM.js +0 -1
  509. package/web/dist/assets/vb-Cu-pLBUe.js +0 -1
  510. package/web/dist/assets/verilog-nZwndyjY.js +0 -1
  511. package/web/dist/assets/vesper-DRje8inN.js +0 -1
  512. package/web/dist/assets/vhdl-CeAyd5Ju.js +0 -1
  513. package/web/dist/assets/viml-CJc9bBzg.js +0 -1
  514. package/web/dist/assets/vitesse-black-Bkuqu6BP.js +0 -1
  515. package/web/dist/assets/vitesse-dark-D0r3Knsf.js +0 -1
  516. package/web/dist/assets/vitesse-light-CVO1_9PV.js +0 -1
  517. package/web/dist/assets/vue-BqiEGhQt.js +0 -1
  518. package/web/dist/assets/vue-html-AaS7Mt5G.js +0 -1
  519. package/web/dist/assets/vue-vine-BoDAl6tE.js +0 -1
  520. package/web/dist/assets/vyper-CDx5xZoG.js +0 -1
  521. package/web/dist/assets/wasm-CG6Dc4jp.js +0 -1
  522. package/web/dist/assets/wasm-MzD3tlZU.js +0 -1
  523. package/web/dist/assets/wenyan-BV7otONQ.js +0 -1
  524. package/web/dist/assets/wgsl-Dx-B1_4e.js +0 -1
  525. package/web/dist/assets/wikitext-BhOHFoWU.js +0 -1
  526. package/web/dist/assets/wit-5i3qLPDT.js +0 -1
  527. package/web/dist/assets/wolfram-lXgVvXCa.js +0 -1
  528. package/web/dist/assets/xml-sdJ4AIDG.js +0 -1
  529. package/web/dist/assets/xsl-CtQFsRM5.js +0 -1
  530. package/web/dist/assets/yaml-Buea-lGh.js +0 -1
  531. package/web/dist/assets/zenscript-DVFEvuxE.js +0 -1
  532. package/web/dist/assets/zig-VOosw3JB.js +0 -1
@@ -0,0 +1,404 @@
1
+ """TTS(语音合成)sidecar — Kokoro-82M(ONNX),零 torch。
2
+
3
+ 与 `packages/stt/stt_server.py` 同构:Node 侧分配端口后用 `--port` 传入、靠 `/health`
4
+ 判断就绪、`--parent-pid` 看门狗防孤儿进程;父进程退出后本进程自杀。
5
+ 权重**不由本进程下载**(Node 侧 `ttsDownload.ts` 负责,含国内镜像选择),本进程只从
6
+ models_dir 加载。
7
+
8
+ 环境变量:
9
+ DSC_TTS_PORT 监听端口(默认 0 = 系统分配)
10
+ DSC_TTS_MODELS_DIR 权重目录(默认 ~/.agent/tts-models)
11
+ DSC_TTS_MODEL 模型文件名(默认 model_uint8f16.onnx)
12
+ DSC_TTS_VOICES 音色文件名(默认 voices-v1.0.bin)
13
+
14
+ 端点:
15
+ GET /health 进程就绪(不依赖模型加载完成)
16
+ GET /status 依赖与权重就绪情况(供「缺什么 + 一键装」文案使用)
17
+ POST /synthesize {"text","voice","speed","lang","stream"}
18
+ 非流式 -> application/wav 整段返回
19
+ 流式 -> audio/L16 裸 PCM(s16le 24kHz 单声道),HTTP/1.0 连接关闭定界
20
+
21
+ 中文音素**必须**走 misaki(见 docs/architecture-design/PERSONAL_REMOTE_AGENT_PLAN.md
22
+ gotcha 11):kokoro-onnx 自带的 `phonemize(text, "cmn")` 走 espeak,产出丢声母且**完全无音调**,
23
+ 中文因此听不懂——而且不报错、不掉字,只有人耳能发现。Kokoro 的中文音色是用 misaki 的
24
+ 拼音 IPA(带 `↓→↗↘` 调号)训练的,所以这里自己产出音素,再以 `is_phonemes=True` 送入。
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ import asyncio
30
+ import io
31
+ import json
32
+ import os
33
+ import re
34
+ import sys
35
+ import threading
36
+ import time
37
+ import wave
38
+ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
39
+ from pathlib import Path
40
+ from typing import Any, Callable
41
+
42
+ import numpy as np
43
+
44
+ DEFAULT_MODELS_DIR = str(Path.home() / ".agent" / "tts-models")
45
+ SAMPLE_RATE = 24_000
46
+ DEFAULT_MODEL = "model_uint8f16.onnx"
47
+ DEFAULT_VOICES = "voices-v1.0.bin"
48
+ # 每档语言的默认音色(Kokoro v1.0 音色名;未知音色会被 resolve_voice 挡下并列出可选值)
49
+ DEFAULT_VOICE = {"zh": "zf_xiaoxiao", "en": "af_heart"}
50
+ FALLBACK_VOICE = "af_heart"
51
+ MAX_TEXT_CHARS = 5_000
52
+ # 中英混排切分:英文段(含文件名/路径里的 . _ - /)与其余段。见 gotcha 11 的切分理由。
53
+ _MIXED_RE = re.compile(r"[A-Za-z][A-Za-z0-9_.\-/]*|[^A-Za-z]+")
54
+
55
+
56
+ def log(message: str) -> None:
57
+ print(f"[tts] {message}", flush=True)
58
+
59
+
60
+ def _arg_value(name: str) -> str | None:
61
+ """从 sys.argv 取 `--x value` 或 `--x=value`(与 stt_server.py 同一约定)。"""
62
+ argv = sys.argv
63
+ for index, item in enumerate(argv):
64
+ if item == name and index + 1 < len(argv):
65
+ return argv[index + 1]
66
+ if item.startswith(f"{name}="):
67
+ return item.split("=", 1)[1]
68
+ return None
69
+
70
+
71
+ def is_chinese(lang: str) -> bool:
72
+ normalized = (lang or "").lower().replace("_", "-")
73
+ return normalized.startswith("zh") or normalized.startswith("cmn") or normalized.startswith("yue")
74
+
75
+
76
+ def lang_key(lang: str) -> str:
77
+ return "zh" if is_chinese(lang) else "en"
78
+
79
+
80
+ def parent_alive(pid: int) -> bool:
81
+ """父进程是否还在。
82
+
83
+ **不要**用 `os.kill(pid, 0)` 探活:Windows 上它并非「发 0 号信号」,CPython 会走
84
+ `OpenProcess(PROCESS_ALL_ACCESS)` + `TerminateProcess`(文档:除 CTRL_C_EVENT /
85
+ CTRL_BREAK_EVENT 外的 sig 会「unconditionally killed by the TerminateProcess API」),
86
+ 而且 `PROCESS_ALL_ACCESS` 在子进程带 `CREATE_NO_WINDOW`(Node 的 `windowsHide: true`)
87
+ 时**会失败**并抛 OSError → 看门狗误判「父进程已死」→ 服务约 5 秒后自杀,表现为
88
+ 第一个请求连接被重置、之后一律 ConnectionRefused。实测见 .agent/tts-probe/shape_probe.py。
89
+ """
90
+ if os.name == "nt":
91
+ import ctypes
92
+
93
+ kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
94
+ # 只申请「查询」权限,不做任何写/终止操作
95
+ handle = kernel32.OpenProcess(0x1000, False, pid)
96
+ if not handle:
97
+ return False
98
+ try:
99
+ code = ctypes.c_ulong()
100
+ if not kernel32.GetExitCodeProcess(handle, ctypes.byref(code)):
101
+ return False
102
+ return code.value == 259 # STILL_ACTIVE
103
+ finally:
104
+ kernel32.CloseHandle(handle)
105
+ try:
106
+ os.kill(pid, 0)
107
+ return True
108
+ except (OSError, ValueError):
109
+ return False
110
+
111
+
112
+ def build_phonemes(text: str, lang: str, tokenizer: Any, zh_g2p: Any) -> str:
113
+ """text -> 音素串。非中文交给 kokoro-onnx 原生 espeak 路径;中文走 misaki,
114
+ 并把中英混排自己切开——否则英文段会被原样保留、随后被 tokenizer 静默丢弃。"""
115
+ if not is_chinese(lang):
116
+ return tokenizer.phonemize(text, lang)
117
+ parts: list[str] = []
118
+ for match in _MIXED_RE.finditer(text):
119
+ chunk = match.group(0)
120
+ if not chunk.strip():
121
+ continue
122
+ if chunk[0].isascii() and chunk[0].isalpha():
123
+ parts.append(tokenizer.phonemize(chunk, "en-us"))
124
+ else:
125
+ phonemes, _ = zh_g2p(chunk)
126
+ parts.append(phonemes)
127
+ return " ".join(part for part in parts if part)
128
+
129
+
130
+ def to_pcm16(samples: Any) -> bytes:
131
+ clipped = np.clip(np.asarray(samples, dtype=np.float32), -1.0, 1.0)
132
+ return (clipped * 32767.0).astype("<i2").tobytes()
133
+
134
+
135
+ def wav_bytes(samples: Any, sample_rate: int) -> bytes:
136
+ buffer = io.BytesIO()
137
+ with wave.open(buffer, "wb") as handle:
138
+ handle.setnchannels(1)
139
+ handle.setsampwidth(2)
140
+ handle.setframerate(sample_rate)
141
+ handle.writeframes(to_pcm16(samples))
142
+ return buffer.getvalue()
143
+
144
+
145
+ class TtsEngine:
146
+ """懒加载 Kokoro。加载失败时把原因留在 probe() 里,供前端「缺什么」文案使用。"""
147
+
148
+ def __init__(self, models_dir: str, model_file: str, voices_file: str) -> None:
149
+ self.models_dir = models_dir
150
+ self.model_file = model_file
151
+ self.voices_file = voices_file
152
+ self.model_path = str(Path(models_dir) / model_file)
153
+ self.voices_path = str(Path(models_dir) / voices_file)
154
+ self._kokoro: Any = None
155
+ self._zh_g2p: Any = None
156
+ self._load_error: str | None = None
157
+ self._lock = threading.Lock()
158
+
159
+ def missing_weights(self) -> list[str]:
160
+ pairs = ((self.model_file, self.model_path), (self.voices_file, self.voices_path))
161
+ return [name for name, path in pairs if not Path(path).is_file()]
162
+
163
+ def probe(self) -> dict:
164
+ """不加载模型,只探测依赖可导入性与权重是否就位。"""
165
+ try:
166
+ import kokoro_onnx # noqa: F401
167
+ import misaki # noqa: F401
168
+
169
+ import_error: str | None = None
170
+ except Exception as exc: # noqa: BLE001
171
+ import_error = f"{type(exc).__name__}: {exc}"
172
+ missing = self.missing_weights()
173
+ status: dict = {
174
+ "installed": import_error is None and not missing,
175
+ "loaded": self._kokoro is not None,
176
+ "model": self.model_file,
177
+ "voicesFile": self.voices_file,
178
+ "voiceNames": self.voice_names(),
179
+ "device": "cpu",
180
+ "missingWeights": missing,
181
+ }
182
+ if import_error:
183
+ status["error"] = import_error
184
+ elif self._load_error:
185
+ status["error"] = self._load_error
186
+ return status
187
+
188
+ def load(self) -> Any:
189
+ with self._lock:
190
+ if self._kokoro is not None:
191
+ return self._kokoro
192
+ if self._load_error:
193
+ raise RuntimeError(self._load_error)
194
+ missing = self.missing_weights()
195
+ if missing:
196
+ raise RuntimeError(f"missing weights: {', '.join(missing)}")
197
+ try:
198
+ from kokoro_onnx import Kokoro
199
+ from misaki import zh
200
+
201
+ self._zh_g2p = zh.ZHG2P()
202
+ self._kokoro = Kokoro(self.model_path, self.voices_path)
203
+ except Exception as exc: # noqa: BLE001
204
+ self._load_error = f"{type(exc).__name__}: {exc}"
205
+ raise RuntimeError(self._load_error) from exc
206
+ log(f"model loaded ({self.model_file}, {len(self._kokoro.voices)} voices)")
207
+ return self._kokoro
208
+
209
+ def voice_names(self) -> list[str]:
210
+ """音色名清单(voices 文件的 key),**不加载 ONNX 模型**。
211
+
212
+ 为什么不用 available_voices():那条路径要过 load(),会把 114 MiB 模型读进内存,
213
+ 而 probe() 是被设置页高频轮询的——这个代价付不起。模型已加载时直接取它的键,
214
+ 与读文件是同一份数据,只是省一次解析。
215
+ """
216
+ if self._kokoro is not None:
217
+ return sorted(self._kokoro.voices)
218
+ if not Path(self.voices_path).is_file():
219
+ return []
220
+ try:
221
+ import numpy
222
+
223
+ return sorted(numpy.load(self.voices_path, allow_pickle=False).files)
224
+ except Exception as exc: # noqa: BLE001
225
+ # 拿不到清单不该让整个状态接口失败:合成侧仍会按 lang 选默认音色
226
+ log(f"voice list unavailable: {exc}")
227
+ return []
228
+
229
+ def available_voices(self) -> list[str]:
230
+ return sorted(self.load().voices)
231
+
232
+ def resolve_voice(self, lang: str, voice: str | None) -> str:
233
+ available = self.available_voices()
234
+ wanted = voice or DEFAULT_VOICE.get(lang_key(lang), FALLBACK_VOICE)
235
+ if wanted in available:
236
+ return wanted
237
+ raise ValueError(f"unknown voice {wanted!r}; available: {', '.join(available)}")
238
+
239
+ def _phonemes(self, kokoro: Any, text: str, lang: str) -> str:
240
+ return build_phonemes(text, lang, kokoro.tokenizer, self._zh_g2p)
241
+
242
+ def synthesize(self, text: str, lang: str, voice: str | None, speed: float) -> tuple[bytes, int]:
243
+ kokoro = self.load()
244
+ phonemes = self._phonemes(kokoro, text, lang)
245
+ if not phonemes.strip():
246
+ raise ValueError("text produced no phonemes")
247
+ audio, rate = kokoro.create(
248
+ phonemes,
249
+ voice=self.resolve_voice(lang, voice),
250
+ speed=speed,
251
+ lang=lang,
252
+ is_phonemes=True,
253
+ )
254
+ return wav_bytes(audio, rate), rate
255
+
256
+ def synthesize_stream(
257
+ self, text: str, lang: str, voice: str | None, speed: float, write: Callable[[bytes], None]
258
+ ) -> None:
259
+ """边合成边写裸 PCM。放弃生成器即停止后续批次,客户端断连自然收敛。"""
260
+ kokoro = self.load()
261
+ phonemes = self._phonemes(kokoro, text, lang)
262
+ if not phonemes.strip():
263
+ raise ValueError("text produced no phonemes")
264
+ style = self.resolve_voice(lang, voice)
265
+
266
+ async def run() -> None:
267
+ async for audio, _rate in kokoro.create_stream(
268
+ phonemes, voice=style, speed=speed, lang=lang, is_phonemes=True
269
+ ):
270
+ write(to_pcm16(audio))
271
+
272
+ asyncio.run(run())
273
+
274
+
275
+ class TtsHttpServer(ThreadingHTTPServer):
276
+ daemon_threads = True
277
+
278
+ def __init__(self, address: tuple[str, int], engine: TtsEngine) -> None:
279
+ super().__init__(address, TtsHandler)
280
+ self.engine = engine
281
+
282
+
283
+ class TtsHandler(BaseHTTPRequestHandler):
284
+ server_version = "DeepSeekCodeTts/1.0"
285
+ # 流式响应无 Content-Length,靠连接关闭定界;HTTP/1.0 正好是这个语义
286
+ protocol_version = "HTTP/1.0"
287
+
288
+ def log_message(self, fmt: str, *args: Any) -> None:
289
+ """静音 http.server 默认的逐请求 stderr 噪音。"""
290
+
291
+ @property
292
+ def engine(self) -> TtsEngine:
293
+ return self.server.engine # type: ignore[attr-defined]
294
+
295
+ def _send_json(self, status: int, payload: dict) -> None:
296
+ body = json.dumps(payload, ensure_ascii=False).encode("utf-8")
297
+ self.send_response(status)
298
+ self.send_header("Content-Type", "application/json; charset=utf-8")
299
+ self.send_header("Content-Length", str(len(body)))
300
+ self.end_headers()
301
+ self.wfile.write(body)
302
+
303
+ def do_GET(self) -> None: # noqa: N802 (http.server 的约定命名)
304
+ if self.path == "/health":
305
+ self._send_json(200, {"ok": True})
306
+ elif self.path == "/status":
307
+ self._send_json(200, self.engine.probe())
308
+ else:
309
+ self._send_json(404, {"error": "not found"})
310
+
311
+ def do_POST(self) -> None: # noqa: N802
312
+ if self.path != "/synthesize":
313
+ self._send_json(404, {"error": "not found"})
314
+ return
315
+ try:
316
+ length = int(self.headers.get("Content-Length") or 0)
317
+ payload = json.loads(self.rfile.read(length) or b"{}")
318
+ except Exception as exc: # noqa: BLE001
319
+ self._send_json(400, {"error": f"bad request body: {exc}"})
320
+ return
321
+ log(f"-> synthesize {len(payload.get('text') or '')} chars lang={payload.get('lang')}")
322
+
323
+ text = str(payload.get("text") or "").strip()
324
+ if not text:
325
+ self._send_json(400, {"error": "text is required"})
326
+ return
327
+ if len(text) > MAX_TEXT_CHARS:
328
+ self._send_json(400, {"error": f"text too long (> {MAX_TEXT_CHARS} chars)"})
329
+ return
330
+
331
+ lang = str(payload.get("lang") or "en-us")
332
+ voice = payload.get("voice") or None
333
+ speed = float(payload.get("speed") or 1.0)
334
+ if not 0.5 <= speed <= 2.0:
335
+ self._send_json(400, {"error": f"speed must be within 0.5~2.0, got {speed}"})
336
+ return
337
+
338
+ if payload.get("stream"):
339
+ self._stream(text, lang, voice, speed)
340
+ return
341
+
342
+ try:
343
+ audio, rate = self.engine.synthesize(text, lang, voice, speed)
344
+ except ValueError as exc:
345
+ self._send_json(400, {"error": str(exc)})
346
+ except Exception as exc: # noqa: BLE001
347
+ self._send_json(500, {"error": f"{type(exc).__name__}: {exc}"})
348
+ else:
349
+ log(f"<- synthesized {len(audio)} bytes, responding")
350
+ self.send_response(200)
351
+ self.send_header("Content-Type", "audio/wav")
352
+ self.send_header("X-Sample-Rate", str(rate))
353
+ self.send_header("Content-Length", str(len(audio)))
354
+ self.end_headers()
355
+ self.wfile.write(audio)
356
+
357
+ def _stream(self, text: str, lang: str, voice: str | None, speed: float) -> None:
358
+ """头一旦发出就无法再改状态码,所以失败只能记日志 + 断流(客户端按截断处理)。"""
359
+ self.send_response(200)
360
+ self.send_header("Content-Type", "audio/L16")
361
+ self.send_header("X-Sample-Rate", str(SAMPLE_RATE))
362
+ self.send_header("Connection", "close")
363
+ self.end_headers()
364
+ try:
365
+ self.engine.synthesize_stream(text, lang, voice, speed, self.wfile.write)
366
+ except (BrokenPipeError, ConnectionResetError):
367
+ log("client closed the stream")
368
+ except Exception as exc: # noqa: BLE001
369
+ log(f"stream failed: {type(exc).__name__}: {exc}")
370
+
371
+
372
+ def main() -> None:
373
+ models_dir = os.environ.get("DSC_TTS_MODELS_DIR") or DEFAULT_MODELS_DIR
374
+ Path(models_dir).mkdir(parents=True, exist_ok=True)
375
+ model_file = os.environ.get("DSC_TTS_MODEL") or DEFAULT_MODEL
376
+ voices_file = os.environ.get("DSC_TTS_VOICES") or DEFAULT_VOICES
377
+ port = int(_arg_value("--port") or os.environ.get("DSC_TTS_PORT") or "0")
378
+ raw_parent = _arg_value("--parent-pid")
379
+ parent_pid = int(raw_parent) if raw_parent and raw_parent.isdigit() else None
380
+
381
+ engine = TtsEngine(models_dir, model_file, voices_file)
382
+ server = TtsHttpServer(("127.0.0.1", port), engine)
383
+ log(f"listening on 127.0.0.1:{server.server_address[1]} (models_dir={models_dir})")
384
+
385
+ def watchdog() -> None:
386
+ """父进程(Node server)退出后自动退出,避免残留孤儿进程(Windows 上尤为必要)。"""
387
+ while True:
388
+ time.sleep(5)
389
+ if not parent_alive(parent_pid):
390
+ log(f"parent pid {parent_pid} gone, exiting")
391
+ server.shutdown()
392
+ return
393
+
394
+ if parent_pid is not None:
395
+ threading.Thread(target=watchdog, daemon=True).start()
396
+
397
+ try:
398
+ server.serve_forever()
399
+ except KeyboardInterrupt:
400
+ pass
401
+
402
+
403
+ if __name__ == "__main__":
404
+ main()
package/cli-wrapper.cjs CHANGED
@@ -86,6 +86,10 @@ function main() {
86
86
  const result = spawnSync(binaryPath, process.argv.slice(2), {
87
87
  stdio: 'inherit',
88
88
  env: { ...process.env, DEEPSEEKCODE_INSTALLED_VIA_NPM_WRAPPER: '1' },
89
+ // windowsHide:本 wrapper 会被 daemon 当作 process.argv[1] 再次执行(见
90
+ // src/instance.ts 的 detached spawn,那层已带 windowsHide)。无控制台宿主下
91
+ // 这里拉起原生二进制若不隐藏,每次启动 daemon 都会补闪一个黑框。
92
+ windowsHide: true,
89
93
  })
90
94
  if (result.error) {
91
95
  console.error(`[${WRAPPER_NAME}] Failed to execute native binary at ` + binaryPath)
package/install.cjs CHANGED
@@ -15,6 +15,23 @@
15
15
  // application" error, so its message is best-effort; install.cjs failure output
16
16
  // above is the primary guidance when postinstall does run.
17
17
  //
18
+ // WHY repairWindowsShims() EXISTS
19
+ //
20
+ // npm reads bin/dscode.exe's shebang while generating the `dscode` /
21
+ // `deepseekcode` shims. The placeholder has to start with `#!/bin/sh` (that is
22
+ // the runnable "not installed yet" message on Unix before this script replaces
23
+ // it), so on Windows cmd-shim copies that interpreter verbatim:
24
+ //
25
+ // & "/bin/sh" "$basedir/node_modules/@deepseekcode/cli/bin/dscode.exe" $args
26
+ //
27
+ // /bin/sh does not exist there, so every fresh `npm i -g` on Windows lands a
28
+ // `dscode` that dies with `/bin/sh.exe is not recognized` — while the real
29
+ // 100MB PE binary sits right next to it. The shims are written BEFORE this
30
+ // script runs, so they cannot be avoided; repairWindowsShims() rewrites them to
31
+ // exec the binary that is on disk by now. (`npm rebuild -g <pkg>` also repairs
32
+ // them: re-linking reads the shebang of the then-real PE binary, which has
33
+ // none.)
34
+ //
18
35
  // Platform detection + PLATFORMS map is duplicated in cli-wrapper.cjs — keep in sync.
19
36
 
20
37
  const { spawnSync } = require('child_process')
@@ -37,6 +54,14 @@ const WRAPPER_NAME = require('./package.json').name
37
54
  const PACKAGE_PREFIX = WRAPPER_NAME
38
55
  const BINARY_NAME = 'dscode'
39
56
 
57
+ // The command names npm creates shims for (dscode + deepseekcode). Taken from
58
+ // the published bin map so a rename never leaves a stale shim behind.
59
+ const BIN_MAP = require('./package.json').bin || {}
60
+ const SHIM_NAMES = Object.keys(BIN_MAP)
61
+ // Where the native binary lands, and therefore what every shim must exec: the
62
+ // published wrapper's bin field points at this path.
63
+ const BIN_TARGET = path.join(__dirname, 'bin', 'dscode.exe')
64
+
40
65
 
41
66
  const PLATFORMS = {
42
67
  'darwin-arm64': { pkg: PACKAGE_PREFIX + '-darwin-arm64', bin: BINARY_NAME },
@@ -118,6 +143,121 @@ function placeBinary(src, dest) {
118
143
  }
119
144
  }
120
145
 
146
+ // ---- Windows shim repair -------------------------------------------------
147
+ // The three templates below mirror what npm writes for a non-JS bin target, i.e.
148
+ // the shape it produces after re-linking against the real (shebang-less) PE
149
+ // binary — a repaired shim is indistinguishable from a healthy one.
150
+
151
+ function ps1Shim(target) {
152
+ return [
153
+ '#!/usr/bin/env pwsh',
154
+ '$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent',
155
+ '',
156
+ '$ret=0',
157
+ '# Support pipeline input',
158
+ 'if ($MyInvocation.ExpectingInput) {',
159
+ ' $input | & "$basedir/' + target + '" $args',
160
+ '} else {',
161
+ ' & "$basedir/' + target + '" $args',
162
+ '}',
163
+ '$ret=$LASTEXITCODE',
164
+ 'exit $ret',
165
+ '',
166
+ ].join('\n')
167
+ }
168
+
169
+ function cmdShim(target) {
170
+ return [
171
+ '@ECHO off',
172
+ 'GOTO start',
173
+ ':find_dp0',
174
+ 'SET dp0=%~dp0',
175
+ 'EXIT /b',
176
+ ':start',
177
+ 'SETLOCAL',
178
+ 'CALL :find_dp0',
179
+ '"%dp0%\\' + target + '" %*',
180
+ '',
181
+ ].join('\n')
182
+ }
183
+
184
+ function shShim(target) {
185
+ return [
186
+ '#!/bin/sh',
187
+ "basedir=$(dirname \"$(echo \"$0\" | sed -e 's,\\\\,/,g')\")",
188
+ '',
189
+ 'case `uname` in',
190
+ ' *CYGWIN*|*MINGW*|*MSYS*)',
191
+ ' if command -v cygpath > /dev/null 2>&1; then',
192
+ ' basedir=`cygpath -w "$basedir"`',
193
+ ' fi',
194
+ ' ;;',
195
+ 'esac',
196
+ '',
197
+ 'exec "$basedir/' + target + '" "$@"',
198
+ '',
199
+ ].join('\n')
200
+ }
201
+
202
+ // Path from the shim's directory to the binary, spelled the way each shim kind
203
+ // expects: npm uses forward slashes in .ps1/.sh and backslashes in .cmd.
204
+ function shimTarget(shimDir, exeAbs, sep) {
205
+ const rel = path.relative(shimDir, exeAbs)
206
+ const picked = rel && !path.isAbsolute(rel) ? rel : exeAbs
207
+ return sep === '\\' ? picked.replace(/\//g, '\\') : picked.replace(/\\/g, '/')
208
+ }
209
+
210
+ function shimFor(ext, target) {
211
+ if (ext === '.ps1') return ps1Shim(target)
212
+ if (ext === '.cmd') return cmdShim(target)
213
+ return shShim(target)
214
+ }
215
+
216
+ // Rewrite every shim under shimDirs that still calls the POSIX interpreter, and
217
+ // return the repaired paths (empty when there was nothing to do). Inputs are
218
+ // arguments so a test can drive it with a temp dir on any platform.
219
+ function repairShims(shimDirs, exeAbs) {
220
+ const repaired = []
221
+ for (const dir of shimDirs) {
222
+ for (const name of SHIM_NAMES) {
223
+ for (const ext of ['.ps1', '.cmd', '']) {
224
+ const file = path.join(dir, name + ext)
225
+ let text
226
+ try {
227
+ text = readFileSync(file, 'utf8')
228
+ } catch {
229
+ continue
230
+ }
231
+ // Skip the shebang line: a healthy .sh shim legitimately starts with
232
+ // `#!/bin/sh`. Only /bin/sh used as an *interpreter* is the bug.
233
+ if (!text.replace(/^[^\n]*\n/, '').includes('/bin/sh')) continue
234
+ try {
235
+ const target = shimTarget(dir, exeAbs, ext === '.cmd' ? '\\' : '/')
236
+ writeFileSync(file, shimFor(ext, target), { mode: 0o755 })
237
+ repaired.push(file)
238
+ } catch {
239
+ // A read-only prefix (`sudo npm i -g`, then `dscode` as a user) must
240
+ // not fail the install — the binary itself is already in place.
241
+ }
242
+ }
243
+ }
244
+ }
245
+ return repaired
246
+ }
247
+
248
+ // Candidate shim directories: the npm prefix (global install) and
249
+ // node_modules/.bin (local install). Both derive from the package location, so
250
+ // the same code covers `npm i -g`, a project dependency and pnpm-style layouts.
251
+ function shimDirsFor(pkgDir) {
252
+ const nodeModules = path.dirname(path.dirname(pkgDir))
253
+ return [path.dirname(nodeModules), path.join(nodeModules, '.bin')]
254
+ }
255
+
256
+ function repairWindowsShims() {
257
+ if (process.platform !== 'win32') return []
258
+ return repairShims(shimDirsFor(__dirname), BIN_TARGET)
259
+ }
260
+
121
261
  function main() {
122
262
  const platformKey = getPlatformKey()
123
263
  const info = PLATFORMS[platformKey]
@@ -162,11 +302,22 @@ function main() {
162
302
  // On Unix npm links `bin/dscode` → `bin/dscode.exe` and ignores the
163
303
  // extension, so overwriting the .exe path replaces the running target on
164
304
  // every platform. Same pattern as Claude Code / Bun npm packages.
165
- const dest = path.join(__dirname, 'bin', 'dscode.exe')
305
+ const dest = BIN_TARGET
166
306
 
167
307
  try {
168
308
  placeBinary(src, dest)
169
309
  console.log(`[${WRAPPER_NAME} postinstall] Native binary placed → ${dest}`)
310
+ try {
311
+ const repaired = repairWindowsShims()
312
+ if (repaired.length > 0) {
313
+ console.log(
314
+ `[${WRAPPER_NAME} postinstall] Repaired ${repaired.length} Windows shim(s) → exec the native binary`,
315
+ )
316
+ }
317
+ } catch {
318
+ // Never fail the install over cosmetic shims: the binary is placed, and
319
+ // `npm rebuild -g ${WRAPPER_NAME}` repairs them too.
320
+ }
170
321
  } catch (err) {
171
322
  console.error(`[${WRAPPER_NAME} postinstall] Failed to place binary: ${err.message}`)
172
323
  console.error(' Fallback: node ' + path.join(__dirname, 'cli-wrapper.cjs'))
@@ -174,4 +325,7 @@ function main() {
174
325
  }
175
326
  }
176
327
 
177
- main()
328
+ if (require.main === module) main()
329
+
330
+ // Exported for the shim-repair tests; npm runs this file directly.
331
+ module.exports = { repairShims, shimFor, shimTarget }