pisper 0.5.13-linux-x64.0 → 0.5.13

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 (291) hide show
  1. package/README.en.md +226 -0
  2. package/README.md +88 -88
  3. package/bin/pisper.mjs +48 -0
  4. package/lib/install.mjs +392 -0
  5. package/lib/npm-platform.mjs +41 -0
  6. package/lib/npm-update.mjs +165 -0
  7. package/lib/platform.mjs +43 -0
  8. package/lib/postinstall.mjs +3 -0
  9. package/package.json +36 -14
  10. package/updater.pubkey +1 -0
  11. package/web/.vite/manifest.json +2248 -0
  12. package/web/assets/AnimatedContent-BiVNqD3m.js +1 -0
  13. package/web/assets/AnimatedList-CJTx-B8I.js +1 -0
  14. package/web/assets/AppOverlays-C2Ah7oL7.js +1 -0
  15. package/web/assets/AppSelect-C31aArme.js +1 -0
  16. package/web/assets/AssetsPage-0mh8UdNf.js +1 -0
  17. package/web/assets/ChannelsPage-DggaYnBh.js +1 -0
  18. package/web/assets/ChatHistoryPage-DfYozFip.js +1 -0
  19. package/web/assets/ChatPage-CXC2G5r0.js +9 -0
  20. package/web/assets/ClickSpark-BhxL8WOQ.js +1 -0
  21. package/web/assets/ConfigPage-C4TdRH-k.js +1 -0
  22. package/web/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  23. package/web/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  24. package/web/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  25. package/web/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  26. package/web/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  27. package/web/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  28. package/web/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  29. package/web/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  30. package/web/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  31. package/web/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  32. package/web/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  33. package/web/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  34. package/web/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  35. package/web/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  36. package/web/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  37. package/web/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  38. package/web/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  39. package/web/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  40. package/web/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  41. package/web/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  42. package/web/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  43. package/web/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  44. package/web/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  45. package/web/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  46. package/web/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  47. package/web/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  48. package/web/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  49. package/web/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  50. package/web/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  51. package/web/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  52. package/web/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  53. package/web/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  54. package/web/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  55. package/web/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  56. package/web/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  57. package/web/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  58. package/web/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  59. package/web/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  60. package/web/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  61. package/web/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  62. package/web/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  63. package/web/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  64. package/web/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  65. package/web/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  66. package/web/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  67. package/web/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  68. package/web/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  69. package/web/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  70. package/web/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  71. package/web/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  72. package/web/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  73. package/web/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  74. package/web/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  75. package/web/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  76. package/web/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  77. package/web/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  78. package/web/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  79. package/web/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  80. package/web/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  81. package/web/assets/MarkdownMessage-Qlm2GKd9.js +1 -0
  82. package/web/assets/MemoryPage-CuMu3xO6.js +1 -0
  83. package/web/assets/PageHeader-pm_Faea_.js +1 -0
  84. package/web/assets/PluginsPage-BE58-xBC.js +1 -0
  85. package/web/assets/PreviewPages-DQZg9e3Q.js +1 -0
  86. package/web/assets/SchedulesPage-D5230V_M.js +1 -0
  87. package/web/assets/ShinyText-fuaKxKqJ.js +1 -0
  88. package/web/assets/SkillsPage-uk4fVUON.js +1 -0
  89. package/web/assets/StarOrbit-BlI6MhVr.js +1 -0
  90. package/web/assets/TerminalPanel-BrP-ENHg.css +1 -0
  91. package/web/assets/TerminalPanel-CTwNy7j5.js +18 -0
  92. package/web/assets/Threads-DskDSsK6.js +1 -0
  93. package/web/assets/WelcomeEffects-DTdnAdJD.js +1 -0
  94. package/web/assets/WorkflowsPage-ROYjAN97.js +1 -0
  95. package/web/assets/WorkspacePicker-nC9FXgh7.js +1 -0
  96. package/web/assets/alert-B7D8joCd.js +1 -0
  97. package/web/assets/alert-dialog-C34TKN83.js +1 -0
  98. package/web/assets/andromeeda-vGVdxbeo.js +1 -0
  99. package/web/assets/angular-html-17rlCfBS.js +1 -0
  100. package/web/assets/angular-ts-it3MaXWH.js +1 -0
  101. package/web/assets/astro-BEVDzCov.js +1 -0
  102. package/web/assets/aurora-x-CDeNXAV0.js +1 -0
  103. package/web/assets/ayu-dark-DluEY0Gj.js +1 -0
  104. package/web/assets/ayu-light-C3h-C4tm.js +1 -0
  105. package/web/assets/ayu-mirage-Bqwy1Gya.js +1 -0
  106. package/web/assets/blade-B3yUZIOx.js +1 -0
  107. package/web/assets/brand-CFOXmGdd.js +1 -0
  108. package/web/assets/button-CDkfouZn.js +1 -0
  109. package/web/assets/c-aGSZMVdM.js +1 -0
  110. package/web/assets/catppuccin-frappe-C9Q5zVZY.js +1 -0
  111. package/web/assets/catppuccin-latte-CtsyrtPq.js +1 -0
  112. package/web/assets/catppuccin-macchiato-R8nAf1vR.js +1 -0
  113. package/web/assets/catppuccin-mocha-DjuaAJKi.js +1 -0
  114. package/web/assets/chevron-down-Cb6bnqQ4.js +1 -0
  115. package/web/assets/circle-dot-D28dfrea.js +1 -0
  116. package/web/assets/coffee-ouOQ86EC.js +1 -0
  117. package/web/assets/copy-BqjQWrFh.js +1 -0
  118. package/web/assets/cpp-Bm3vm43L.js +1 -0
  119. package/web/assets/css-CWcS1xXm.js +1 -0
  120. package/web/assets/csv-Dx-8-gkx.js +1 -0
  121. package/web/assets/dark-plus-Cs2F2srj.js +1 -0
  122. package/web/assets/dist-BwTidasF.js +1 -0
  123. package/web/assets/dist-DAyD2Kf6.js +1 -0
  124. package/web/assets/dracula-BHWKrbxM.js +1 -0
  125. package/web/assets/dracula-soft-5eyTD99u.js +1 -0
  126. package/web/assets/earth-EOGCuCPR.js +1 -0
  127. package/web/assets/es2015-BhXS8JEs.js +41 -0
  128. package/web/assets/everforest-dark-sB-x3p7T.js +1 -0
  129. package/web/assets/everforest-light-Df2xbC6M.js +1 -0
  130. package/web/assets/field-DsAM6lRR.js +1 -0
  131. package/web/assets/file-D4w8IUw4.js +1 -0
  132. package/web/assets/file-code-corner-Dqjb0Cug.js +1 -0
  133. package/web/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  134. package/web/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  135. package/web/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  136. package/web/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  137. package/web/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  138. package/web/assets/github-dark-C-LZuMrd.js +1 -0
  139. package/web/assets/github-dark-default-DXG-b-1a.js +1 -0
  140. package/web/assets/github-dark-dimmed-Bx1FflLF.js +1 -0
  141. package/web/assets/github-dark-high-contrast-B_tTalzw.js +1 -0
  142. package/web/assets/github-light-EUqPIrTm.js +1 -0
  143. package/web/assets/github-light-default-BXViO-2h.js +1 -0
  144. package/web/assets/github-light-high-contrast-B68TUdTA.js +1 -0
  145. package/web/assets/glsl-TOBAB6bO.js +1 -0
  146. package/web/assets/graphql-BEmIBNve.js +1 -0
  147. package/web/assets/gruvbox-dark-hard-C820rvS2.js +1 -0
  148. package/web/assets/gruvbox-dark-medium-BPjhmG05.js +1 -0
  149. package/web/assets/gruvbox-dark-soft-MrdJrrXF.js +1 -0
  150. package/web/assets/gruvbox-light-hard-BC_s9l72.js +1 -0
  151. package/web/assets/gruvbox-light-medium-BAWPOn9u.js +1 -0
  152. package/web/assets/gruvbox-light-soft-BSMLrYjP.js +1 -0
  153. package/web/assets/haml-CDWtFtU9.js +1 -0
  154. package/web/assets/handlebars-BNtjo3pL.js +1 -0
  155. package/web/assets/horizon-CE9ld1lL.js +1 -0
  156. package/web/assets/horizon-bright-DSNQnXHK.js +1 -0
  157. package/web/assets/houston-CsvMBhTu.js +1 -0
  158. package/web/assets/html-BVyvp26C.js +1 -0
  159. package/web/assets/html-derivative-BseLpURQ.js +1 -0
  160. package/web/assets/http-CkCfSMV9.js +1 -0
  161. package/web/assets/hurl-syYMt6Rn.js +1 -0
  162. package/web/assets/image-DfmT5l79.js +1 -0
  163. package/web/assets/imba-DsUTQ-LC.js +1 -0
  164. package/web/assets/index-5_Mm9BFd.js +41 -0
  165. package/web/assets/index-CtjPR5TT.css +2 -0
  166. package/web/assets/java-duG_UTKe.js +1 -0
  167. package/web/assets/javascript-DWVbOe4X.js +1 -0
  168. package/web/assets/jinja-Boj4niAz.js +1 -0
  169. package/web/assets/jison-CHwnC1Pv.js +1 -0
  170. package/web/assets/json-qhed-kSA.js +1 -0
  171. package/web/assets/json5-BR5RXkoi.js +1 -0
  172. package/web/assets/jsonc-CYpm1nAK.js +1 -0
  173. package/web/assets/jsonl-CmCQp5Yx.js +1 -0
  174. package/web/assets/jsx-fcCEM_-L.js +1 -0
  175. package/web/assets/julia-VoF7UKfw.js +1 -0
  176. package/web/assets/kanagawa-dragon-Bozd7klA.js +1 -0
  177. package/web/assets/kanagawa-lotus-BmuOG9ZW.js +1 -0
  178. package/web/assets/kanagawa-wave-BML5mF2J.js +1 -0
  179. package/web/assets/laserwave-C_8bwKvT.js +1 -0
  180. package/web/assets/less-DVTAwKKz.js +1 -0
  181. package/web/assets/light-plus-DVQuIRkW.js +1 -0
  182. package/web/assets/link-2-CDKitOSW.js +1 -0
  183. package/web/assets/loader-circle-XRCZj0q4.js +1 -0
  184. package/web/assets/markdown-BYOwaDjH.js +1 -0
  185. package/web/assets/marko-Cupn1xhw.js +1 -0
  186. package/web/assets/material-theme-Bm3Qr25_.js +1 -0
  187. package/web/assets/material-theme-darker-2IIEA8gg.js +1 -0
  188. package/web/assets/material-theme-lighter-uhdI0v04.js +1 -0
  189. package/web/assets/material-theme-ocean-CHQ94UKr.js +1 -0
  190. package/web/assets/material-theme-palenight-B5W6OYN7.js +1 -0
  191. package/web/assets/mdc-4FfK5iWR.js +1 -0
  192. package/web/assets/mdx-DQZ5AkYe.js +1 -0
  193. package/web/assets/message-circle-BW-CPcmG.js +1 -0
  194. package/web/assets/min-dark-BSWPekZh.js +1 -0
  195. package/web/assets/min-light-DDpmG2fV.js +1 -0
  196. package/web/assets/monokai-B_bV9PQ9.js +1 -0
  197. package/web/assets/network-KzJTl7t7.js +1 -0
  198. package/web/assets/night-owl-DhmEMT88.js +1 -0
  199. package/web/assets/night-owl-light-eJ-hLW7d.js +1 -0
  200. package/web/assets/nord-Cb4Vim4T.js +1 -0
  201. package/web/assets/one-dark-pro-CLwyXe_n.js +1 -0
  202. package/web/assets/one-light-D7Lr4KcI.js +1 -0
  203. package/web/assets/panel-top-DByYVkbw.js +1 -0
  204. package/web/assets/paperclip-CI8irkCX.js +1 -0
  205. package/web/assets/pencil-C67WdsWU.js +1 -0
  206. package/web/assets/php-CUREQQ7f.js +1 -0
  207. package/web/assets/pick-system-directory-BnIFt85A.js +1 -0
  208. package/web/assets/plastic-DQwYfKfQ.js +1 -0
  209. package/web/assets/play-CRwfAjca.js +1 -0
  210. package/web/assets/poimandres-DRFjx7u4.js +1 -0
  211. package/web/assets/postcss-BXeXVLqQ.js +1 -0
  212. package/web/assets/progress-DOArAQPW.js +6 -0
  213. package/web/assets/pug-CQyGKZpv.js +1 -0
  214. package/web/assets/python-gzcpVVnB.js +1 -0
  215. package/web/assets/r-C5FwQflr.js +1 -0
  216. package/web/assets/react-bits-DQli9s7O.css +1 -0
  217. package/web/assets/red-DX1BQOs8.js +1 -0
  218. package/web/assets/regexp--aR2_iz7.js +1 -0
  219. package/web/assets/rolldown-runtime-B0Z9INg1.js +1 -0
  220. package/web/assets/rose-pine-aMsaMD1D.js +1 -0
  221. package/web/assets/rose-pine-dawn-8PwUyIlP.js +1 -0
  222. package/web/assets/rose-pine-moon-DO-qXg-I.js +1 -0
  223. package/web/assets/sass-DXrisJhu.js +1 -0
  224. package/web/assets/save-DAkFWHyI.js +1 -0
  225. package/web/assets/scss-Davl4E3T.js +1 -0
  226. package/web/assets/search-CAq4c_43.js +1 -0
  227. package/web/assets/shellscript-CLZ0U2zV.js +1 -0
  228. package/web/assets/shield-check-C62jDUnP.js +1 -0
  229. package/web/assets/slack-dark-DnToyrRv.js +1 -0
  230. package/web/assets/slack-ochin-B2OO5cIa.js +1 -0
  231. package/web/assets/smithy-Dgaz7aur.js +1 -0
  232. package/web/assets/snazzy-light-4G7pJPwS.js +1 -0
  233. package/web/assets/solarized-dark-UdsmMDCL.js +1 -0
  234. package/web/assets/solarized-light-C1IUL_tW.js +1 -0
  235. package/web/assets/sql-b92rrcVl.js +1 -0
  236. package/web/assets/square-D2anbrZz.js +1 -0
  237. package/web/assets/square-terminal-Pww63y-M.js +1 -0
  238. package/web/assets/stylus-B6D30XZt.js +1 -0
  239. package/web/assets/sun-BcZVxVDx.js +1 -0
  240. package/web/assets/svelte-BfstjgUc.js +1 -0
  241. package/web/assets/synthwave-84-nFMaYfgc.js +1 -0
  242. package/web/assets/tag-CqZUIGvX.js +1 -0
  243. package/web/assets/tokyo-night-oM2G3aXe.js +1 -0
  244. package/web/assets/trash-2-CsljwZ9-.js +1 -0
  245. package/web/assets/ts-tags-Cckpt1jI.js +1 -0
  246. package/web/assets/tsx-JMDX3aQ7.js +1 -0
  247. package/web/assets/typescript-BAz-bF8J.js +1 -0
  248. package/web/assets/use-i18n-CI7pHimf.js +1 -0
  249. package/web/assets/usePagePrimaryAction-Bw0OFTDZ.js +1 -0
  250. package/web/assets/utils-C8uzG5AZ.js +1 -0
  251. package/web/assets/vendor-dockview-D5adAaCg.css +1 -0
  252. package/web/assets/vendor-dockview-yY5mBM3U.js +1 -0
  253. package/web/assets/vendor-markdown-CWK5AVhB.js +130 -0
  254. package/web/assets/vendor-markdown-plugins-DCl4I7Pz.js +263 -0
  255. package/web/assets/vendor-motion-XxgxfZSY.js +9 -0
  256. package/web/assets/vendor-react-CHuvPdJE.js +9 -0
  257. package/web/assets/vendor-router-Bw2aAdMs.js +4 -0
  258. package/web/assets/vendor-shiki-runtime-CxE5QolQ.js +153 -0
  259. package/web/assets/vendor-state-DE1O_s6m.js +1 -0
  260. package/web/assets/vendor-ui-DvrYggeS.js +1 -0
  261. package/web/assets/vendor-xyflow-CnjXtcyK.js +7 -0
  262. package/web/assets/vendor-xyflow-DLioOiRN.css +1 -0
  263. package/web/assets/vesper-DdrHHSXu.js +1 -0
  264. package/web/assets/vitesse-black-fwtXNY1n.js +1 -0
  265. package/web/assets/vitesse-dark-BZCL-v6S.js +1 -0
  266. package/web/assets/vitesse-light-VbXTXTou.js +1 -0
  267. package/web/assets/vue-3H7P6ncC.js +1 -0
  268. package/web/assets/vue-html-DZGW9MGR.js +1 -0
  269. package/web/assets/vue-vine-BECt67gq.js +1 -0
  270. package/web/assets/wasm-BnjxR4X6.js +1 -0
  271. package/web/assets/wasm-ByWQv1Qj.js +1 -0
  272. package/web/assets/wgsl-BsKzXJz4.js +1 -0
  273. package/web/assets/wit-DdvCle-K.js +1 -0
  274. package/web/assets/wrench-BIgib2eu.js +1 -0
  275. package/web/assets/xml-CNfvW5JP.js +1 -0
  276. package/web/assets/yaml-rwi0_p6S.js +1 -0
  277. package/web/assets/zap-evw8kNGN.js +1 -0
  278. package/web/connect.html +133 -0
  279. package/web/connect.js +208 -0
  280. package/web/favicon.svg +25 -0
  281. package/web/icons.svg +24 -0
  282. package/web/index.html +72 -0
  283. package/web/memory-galaxy-background.webp +0 -0
  284. package/web/notification-sw.js +13 -0
  285. package/web/tauri-pet.css +117 -0
  286. package/web/tauri-pet.html +16 -0
  287. package/web/tauri-pet.js +117 -0
  288. package/components/runtime/Pisper_Runtime_Node_0.5.12_linux_x86_64.tar.gz +0 -0
  289. package/components/runtime/Pisper_Runtime_Node_0.5.12_linux_x86_64.tar.gz.sig +0 -1
  290. package/components/tui/Pisper_TUI_Component_0.5.8_linux_x86_64.tar.gz +0 -0
  291. package/components/tui/Pisper_TUI_Component_0.5.8_linux_x86_64.tar.gz.sig +0 -1
@@ -0,0 +1,392 @@
1
+ import { execFile } from 'node:child_process'
2
+ import { chmod, mkdir, open, readFile, rename, rm, stat, writeFile } from 'node:fs/promises'
3
+ import { createRequire } from 'node:module'
4
+ import { dirname, join } from 'node:path'
5
+ import process from 'node:process'
6
+ import { promisify } from 'node:util'
7
+ import { fileURLToPath } from 'node:url'
8
+ import { PublicKey, Signature } from '@threema/wasm-minisign-verify'
9
+ import { x as extractTar } from 'tar'
10
+ import { npmPlatformAlias, npmPlatformTarget, npmPlatformVersion } from './npm-platform.mjs'
11
+ import {
12
+ componentsRoot,
13
+ executableName,
14
+ releaseArchitecture,
15
+ releasePlatform,
16
+ supportedTarget,
17
+ } from './platform.mjs'
18
+
19
+ const execFileAsync = promisify(execFile)
20
+ const require = createRequire(import.meta.url)
21
+ const packageRoot = fileURLToPath(new URL('..', import.meta.url))
22
+ const packageManifest = JSON.parse(await readFile(join(packageRoot, 'package.json'), 'utf8'))
23
+ const tuiVersion = packageManifest.pisper?.tuiVersion
24
+ const runtimeVersion = packageManifest.pisper?.runtimeVersion
25
+ const MAX_ARCHIVE_BYTES = 128 * 1024 * 1024
26
+ const INSTALL_WAIT_MS = 600_000
27
+ const LEGACY_LOCK_GRACE_MS = 300_000
28
+
29
+ for (const [label, value] of [
30
+ ['TUI', tuiVersion],
31
+ ['Runtime', runtimeVersion],
32
+ ]) {
33
+ if (!/^\d+\.\d+\.\d+$/.test(value || '')) {
34
+ throw new Error(`pisper package metadata does not contain a valid ${label} version`)
35
+ }
36
+ }
37
+
38
+ function decodeWrapped(value) {
39
+ const trimmed = String(value).trim()
40
+ if (trimmed.startsWith('untrusted comment:')) return trimmed
41
+ if (!/^[A-Za-z0-9+/=\r\n]+$/.test(trimmed)) return trimmed
42
+ const decoded = Buffer.from(trimmed.replace(/\s/g, ''), 'base64').toString('utf8').trim()
43
+ return decoded.startsWith('untrusted comment:') ? decoded : trimmed
44
+ }
45
+
46
+ function componentAsset(component, version, platform = process.platform, arch = process.arch) {
47
+ const target = supportedTarget(platform, arch)
48
+ const label = component === 'tui' ? 'TUI_Component' : 'Runtime_Node'
49
+ return `Pisper_${label}_${version}_${target}.tar.gz`
50
+ }
51
+
52
+ export function releaseAsset(
53
+ version = tuiVersion,
54
+ platform = process.platform,
55
+ arch = process.arch,
56
+ ) {
57
+ return componentAsset('tui', version, platform, arch)
58
+ }
59
+
60
+ function compareVersions(left, right) {
61
+ const a = left.split('.').map(Number)
62
+ const b = right.split('.').map(Number)
63
+ for (let index = 0; index < 3; index += 1) {
64
+ if (a[index] !== b[index]) return a[index] - b[index]
65
+ }
66
+ return 0
67
+ }
68
+
69
+ async function isFile(path) {
70
+ try {
71
+ return (await stat(path)).isFile()
72
+ } catch (error) {
73
+ if (error?.code === 'ENOENT') return false
74
+ throw error
75
+ }
76
+ }
77
+
78
+ function commandName(component) {
79
+ if (component === 'tui') return executableName()
80
+ return 'sidecar-runtime/runtime/sidecar.mjs'
81
+ }
82
+
83
+ async function validateComponent(component, version, destination) {
84
+ const executable = join(destination, commandName(component))
85
+ if (!(await isFile(executable))) return ''
86
+ if (
87
+ component === 'runtime' &&
88
+ !(await isFile(join(destination, 'sidecar-runtime', 'package.json')))
89
+ ) {
90
+ return ''
91
+ }
92
+ try {
93
+ const manifest = JSON.parse(await readFile(join(destination, 'manifest.json'), 'utf8'))
94
+ if (
95
+ manifest.version !== version ||
96
+ manifest.platform !== releasePlatform() ||
97
+ manifest.arch !== releaseArchitecture() ||
98
+ manifest.command !== commandName(component)
99
+ ) {
100
+ return ''
101
+ }
102
+ } catch {
103
+ return ''
104
+ }
105
+ return executable
106
+ }
107
+
108
+ async function installedComponent(component, version, destination) {
109
+ const executable = await validateComponent(component, version, destination)
110
+ return executable ? { version, destination, executable } : null
111
+ }
112
+
113
+ async function resolveCurrentComponent(root, component, minimumVersion) {
114
+ try {
115
+ const pointer = JSON.parse(await readFile(join(root, component, 'current.json'), 'utf8'))
116
+ if (!/^\d+\.\d+\.\d+$/.test(pointer.version || '')) return null
117
+ if (compareVersions(pointer.version, minimumVersion) < 0) return null
118
+ const destination = join(root, component, 'versions', pointer.version)
119
+ return installedComponent(component, pointer.version, destination)
120
+ } catch (error) {
121
+ if (error?.code === 'ENOENT' || error instanceof SyntaxError) return null
122
+ throw error
123
+ }
124
+ }
125
+
126
+ async function resolvePlatformBundle() {
127
+ const target = npmPlatformTarget()
128
+ const alias = npmPlatformAlias()
129
+ let manifestPath
130
+ try {
131
+ manifestPath = require.resolve(`${alias}/package.json`)
132
+ } catch (error) {
133
+ throw new Error(
134
+ `the npm platform package ${alias} is missing; reinstall pisper without --omit=optional`,
135
+ { cause: error },
136
+ )
137
+ }
138
+ const manifest = JSON.parse(await readFile(manifestPath, 'utf8'))
139
+ const metadata = manifest.pisperBundle
140
+ const expectedVersion = npmPlatformVersion(packageManifest.version)
141
+ if (
142
+ manifest.name !== 'pisper' ||
143
+ manifest.version !== expectedVersion ||
144
+ metadata?.target !== target.release ||
145
+ metadata?.tuiVersion !== tuiVersion ||
146
+ metadata?.runtimeVersion !== runtimeVersion
147
+ ) {
148
+ throw new Error(
149
+ `npm platform package ${alias} does not match pisper@${packageManifest.version}`,
150
+ )
151
+ }
152
+ return dirname(manifestPath)
153
+ }
154
+
155
+ async function readBundledAsset(bundleRoot, component, version) {
156
+ const asset = componentAsset(component, version)
157
+ const archivePath = join(bundleRoot, 'components', component, asset)
158
+ const signaturePath = `${archivePath}.sig`
159
+ let size
160
+ try {
161
+ size = (await stat(archivePath)).size
162
+ } catch (error) {
163
+ throw new Error(`npm platform package is missing ${component} archive ${asset}`, {
164
+ cause: error,
165
+ })
166
+ }
167
+ if (size <= 0 || size > MAX_ARCHIVE_BYTES) {
168
+ throw new Error(`npm platform package contains an invalid ${component} archive size: ${size}`)
169
+ }
170
+ const [archive, signature] = await Promise.all([readFile(archivePath), readFile(signaturePath)])
171
+ return { archive, signature }
172
+ }
173
+
174
+ async function verifyArchive(component, archive, signatureBytes) {
175
+ const publicKeyText = await readFile(join(packageRoot, 'updater.pubkey'), 'utf8')
176
+ const publicKey = PublicKey.decode(decodeWrapped(publicKeyText))
177
+ const signature = Signature.decode(decodeWrapped(signatureBytes.toString('utf8')))
178
+ try {
179
+ if (!publicKey.verify(archive, signature)) {
180
+ throw new Error('signature verification returned false')
181
+ }
182
+ } catch (error) {
183
+ throw new Error(`${component} archive signature verification failed: ${error.message}`, {
184
+ cause: error,
185
+ })
186
+ } finally {
187
+ signature.free()
188
+ publicKey.free()
189
+ }
190
+ }
191
+
192
+ function nativeTarCommand() {
193
+ if (process.platform === 'win32') {
194
+ return join(
195
+ process.env.SystemRoot || process.env.WINDIR || 'C:\\Windows',
196
+ 'System32',
197
+ 'tar.exe',
198
+ )
199
+ }
200
+ if (process.platform === 'darwin') return '/usr/bin/tar'
201
+ return 'tar'
202
+ }
203
+
204
+ async function extractWithNodeTar(archivePath, extractionRoot) {
205
+ await extractTar({
206
+ cwd: extractionRoot,
207
+ file: archivePath,
208
+ preservePaths: false,
209
+ strict: true,
210
+ strip: 1,
211
+ filter: (_path, entry) => entry.type === 'File' || entry.type === 'Directory',
212
+ })
213
+ }
214
+
215
+ export async function extractComponentArchive(
216
+ archivePath,
217
+ extractionRoot,
218
+ tarCommand = nativeTarCommand(),
219
+ ) {
220
+ try {
221
+ await execFileAsync(
222
+ tarCommand,
223
+ ['-xzf', archivePath, '-C', extractionRoot, '--strip-components=1'],
224
+ { windowsHide: true },
225
+ )
226
+ } catch {
227
+ await rm(extractionRoot, { recursive: true, force: true })
228
+ await mkdir(extractionRoot, { recursive: true })
229
+ await extractWithNodeTar(archivePath, extractionRoot)
230
+ }
231
+ }
232
+
233
+ async function wait(milliseconds) {
234
+ await new Promise((resolve) => setTimeout(resolve, milliseconds))
235
+ }
236
+
237
+ async function installerLockIsActive(lockPath) {
238
+ try {
239
+ const [raw, details] = await Promise.all([readFile(lockPath, 'utf8'), stat(lockPath)])
240
+ const age = Math.max(0, Date.now() - details.mtimeMs)
241
+ if (age >= INSTALL_WAIT_MS) return false
242
+
243
+ let metadata
244
+ try {
245
+ metadata = JSON.parse(raw)
246
+ } catch {
247
+ // Older installers created empty locks. Give a live legacy install time to finish.
248
+ return age < LEGACY_LOCK_GRACE_MS
249
+ }
250
+ if (!Number.isSafeInteger(metadata?.pid) || metadata.pid <= 0) {
251
+ return age < LEGACY_LOCK_GRACE_MS
252
+ }
253
+ try {
254
+ process.kill(metadata.pid, 0)
255
+ return true
256
+ } catch (error) {
257
+ if (error?.code === 'ESRCH') return false
258
+ if (error?.code === 'EPERM') return true
259
+ throw error
260
+ }
261
+ } catch (error) {
262
+ if (error?.code === 'ENOENT') return false
263
+ throw error
264
+ }
265
+ }
266
+
267
+ export async function acquireInstallLock(lockPath, validate) {
268
+ const deadline = Date.now() + INSTALL_WAIT_MS
269
+ while (true) {
270
+ try {
271
+ const lock = await open(lockPath, 'wx')
272
+ try {
273
+ await lock.writeFile(`${JSON.stringify({ pid: process.pid, createdAt: Date.now() })}\n`)
274
+ return lock
275
+ } catch (error) {
276
+ await lock.close()
277
+ await rm(lockPath, { force: true })
278
+ throw error
279
+ }
280
+ } catch (error) {
281
+ if (error?.code !== 'EEXIST') throw error
282
+ if (await validate()) return null
283
+ if (!(await installerLockIsActive(lockPath))) {
284
+ try {
285
+ await rm(lockPath, { force: true })
286
+ continue
287
+ } catch (removeError) {
288
+ if (!['EACCES', 'EBUSY', 'EPERM'].includes(removeError?.code)) throw removeError
289
+ }
290
+ }
291
+ if (Date.now() >= deadline) {
292
+ throw new Error('timed out waiting for another Pisper installer')
293
+ }
294
+ await wait(250)
295
+ }
296
+ }
297
+ }
298
+
299
+ async function installComponent({ root, bundleRoot, component, version, destination }) {
300
+ const lockRoot = join(root, 'npm')
301
+ await mkdir(lockRoot, { recursive: true })
302
+ const lockPath = join(lockRoot, `${component}-install.lock`)
303
+ const validate = () => validateComponent(component, version, destination)
304
+ const lock = await acquireInstallLock(lockPath, validate)
305
+ if (!lock) return { version, destination, executable: await validate() }
306
+
307
+ const staging = `${destination}.tmp-${process.pid}-${Date.now()}`
308
+ // Renaming a tree with paths over MAX_PATH fails on Windows even though tar can extract it.
309
+ const extractionRoot = process.platform === 'win32' ? destination : staging
310
+ const archivePath = `${staging}.tar.gz`
311
+ try {
312
+ const existing = await validate()
313
+ if (existing) return { version, destination, executable: existing }
314
+
315
+ console.log(`pisper: installing ${component} ${version} from the npm platform package`)
316
+ await mkdir(dirname(destination), { recursive: true })
317
+ await rm(extractionRoot, { recursive: true, force: true })
318
+ await mkdir(extractionRoot, { recursive: true })
319
+ const { archive, signature } = await readBundledAsset(bundleRoot, component, version)
320
+ console.log(`pisper: verifying ${component} ${version} signature`)
321
+ await verifyArchive(component, archive, signature)
322
+ console.log(`pisper: extracting ${component} ${version}`)
323
+ await writeFile(archivePath, archive, { mode: 0o600 })
324
+ await extractComponentArchive(archivePath, extractionRoot)
325
+ if (process.platform !== 'win32') {
326
+ await chmod(join(extractionRoot, commandName(component)), 0o755)
327
+ }
328
+ const stagedExecutable = await validateComponent(component, version, extractionRoot)
329
+ if (!stagedExecutable) {
330
+ throw new Error(`bundled ${component} archive has an invalid package layout`)
331
+ }
332
+ if (extractionRoot !== destination) {
333
+ await rm(destination, { recursive: true, force: true })
334
+ await rename(staging, destination)
335
+ }
336
+ console.log(`pisper: ${component} ${version} is ready`)
337
+ return { version, destination, executable: join(destination, commandName(component)) }
338
+ } finally {
339
+ await lock.close()
340
+ await Promise.all([
341
+ rm(lockPath, { force: true }),
342
+ rm(staging, { recursive: true, force: true }),
343
+ rm(archivePath, { force: true }),
344
+ ])
345
+ }
346
+ }
347
+
348
+ export async function ensurePisperInstallation() {
349
+ const root = componentsRoot()
350
+ const npmTuiDestination = join(root, 'npm', 'versions', tuiVersion)
351
+ const runtimeDestination = join(root, 'npm-runtime', 'versions', runtimeVersion)
352
+ const [tui, runtime] = await Promise.all([
353
+ resolveCurrentComponent(root, 'tui', tuiVersion).then(
354
+ (current) => current || installedComponent('tui', tuiVersion, npmTuiDestination),
355
+ ),
356
+ installedComponent('runtime', runtimeVersion, runtimeDestination),
357
+ ])
358
+ if (tui && runtime) {
359
+ return {
360
+ executable: tui.executable,
361
+ appRoot: join(runtime.destination, 'sidecar-runtime'),
362
+ }
363
+ }
364
+
365
+ const bundleRoot = await resolvePlatformBundle()
366
+ const [installedTui, installedRuntime] = await Promise.all([
367
+ tui ||
368
+ installComponent({
369
+ root,
370
+ bundleRoot,
371
+ component: 'tui',
372
+ version: tuiVersion,
373
+ destination: npmTuiDestination,
374
+ }),
375
+ runtime ||
376
+ installComponent({
377
+ root,
378
+ bundleRoot,
379
+ component: 'runtime',
380
+ version: runtimeVersion,
381
+ destination: runtimeDestination,
382
+ }),
383
+ ])
384
+ return {
385
+ executable: installedTui.executable,
386
+ appRoot: join(installedRuntime.destination, 'sidecar-runtime'),
387
+ }
388
+ }
389
+
390
+ export async function ensurePisperExecutable() {
391
+ return (await ensurePisperInstallation()).executable
392
+ }
@@ -0,0 +1,41 @@
1
+ import process from 'node:process'
2
+
3
+ export const NPM_PLATFORM_TARGETS = Object.freeze([
4
+ Object.freeze({ platform: 'win32', arch: 'x64', release: 'windows_x86_64', slug: 'win32-x64' }),
5
+ Object.freeze({ platform: 'darwin', arch: 'x64', release: 'darwin_x86_64', slug: 'darwin-x64' }),
6
+ Object.freeze({
7
+ platform: 'darwin',
8
+ arch: 'arm64',
9
+ release: 'darwin_aarch64',
10
+ slug: 'darwin-arm64',
11
+ }),
12
+ Object.freeze({ platform: 'linux', arch: 'x64', release: 'linux_x86_64', slug: 'linux-x64' }),
13
+ ])
14
+
15
+ export function npmPlatformTarget(platform = process.platform, arch = process.arch) {
16
+ const target = NPM_PLATFORM_TARGETS.find(
17
+ (candidate) => candidate.platform === platform && candidate.arch === arch,
18
+ )
19
+ if (!target) throw new Error(`Pisper does not publish an npm package for ${platform}-${arch}`)
20
+ return target
21
+ }
22
+
23
+ export function npmPlatformAlias(platform = process.platform, arch = process.arch) {
24
+ return `pisper-binary-${npmPlatformTarget(platform, arch).slug}`
25
+ }
26
+
27
+ export function npmPlatformVersion(version, platform = process.platform, arch = process.arch) {
28
+ if (!/^\d+\.\d+\.\d+$/.test(version || '')) {
29
+ throw new Error(`invalid Pisper npm version: ${version}`)
30
+ }
31
+ return `${version}-${npmPlatformTarget(platform, arch).slug}.0`
32
+ }
33
+
34
+ export function npmPlatformOptionalDependencies(version) {
35
+ return Object.fromEntries(
36
+ NPM_PLATFORM_TARGETS.map(({ platform, arch }) => [
37
+ npmPlatformAlias(platform, arch),
38
+ `npm:pisper@${npmPlatformVersion(version, platform, arch)}`,
39
+ ]),
40
+ )
41
+ }
@@ -0,0 +1,165 @@
1
+ import { execFile, spawn } from 'node:child_process'
2
+ import { readFile } from 'node:fs/promises'
3
+ import { join } from 'node:path'
4
+ import process from 'node:process'
5
+ import { promisify } from 'node:util'
6
+ import { fileURLToPath } from 'node:url'
7
+
8
+ const execFileAsync = promisify(execFile)
9
+ const packageRoot = fileURLToPath(new URL('..', import.meta.url))
10
+ const packageManifest = JSON.parse(await readFile(join(packageRoot, 'package.json'), 'utf8'))
11
+
12
+ function compareVersions(left, right) {
13
+ const a = left.split('.').map(Number)
14
+ const b = right.split('.').map(Number)
15
+ for (let index = 0; index < 3; index += 1) {
16
+ if (a[index] !== b[index]) return a[index] - b[index]
17
+ }
18
+ return 0
19
+ }
20
+
21
+ function npmInvocation(arguments_) {
22
+ if (process.platform === 'win32') {
23
+ return {
24
+ command: process.env.ComSpec || 'cmd.exe',
25
+ arguments: ['/d', '/s', '/c', 'npm.cmd', ...arguments_],
26
+ }
27
+ }
28
+ return { command: 'npm', arguments: arguments_ }
29
+ }
30
+
31
+ const NPM_CLI_HELP = `Pisper CLI
32
+
33
+ Start a coding session in your terminal.
34
+
35
+ Usage:
36
+ pisper [OPTIONS]
37
+ pisper resume [OPTIONS]
38
+ pisper doctor [OPTIONS]
39
+ pisper web [OPTIONS]
40
+ pisper update [--check]
41
+ pisper help [COMMAND]
42
+
43
+ Commands:
44
+ resume Choose and resume a conversation from any workspace
45
+ doctor Check the TUI, Runtime connection, and capability catalogs
46
+ web Open the bundled Web UI and Provider settings in your browser
47
+ update Update the complete Pisper distribution through npm
48
+ help Print this help or help for a command
49
+
50
+ Options:
51
+ --cwd <directory> Use a specific workspace (default: current directory)
52
+ -h, --help Print help
53
+ -V, --version Print the installed TUI version
54
+
55
+ Getting started:
56
+ 1. Change to your project directory.
57
+ 2. Run \`pisper\`. Use \`/provider\` to choose a Provider and save its API Key.
58
+ 3. Type a request and press Enter. Type \`/\` to browse commands.
59
+ 4. Run \`pisper web\` for the bundled visual settings and workspace UI.
60
+ 5. Run \`pisper update\` to update through the configured npm registry.`
61
+
62
+ export function parseNpmHelpRequest(arguments_) {
63
+ if (arguments_.length === 1 && ['help', '--help', '-h'].includes(arguments_[0])) return true
64
+ return false
65
+ }
66
+
67
+ export function parseNpmUpdateRequest(arguments_) {
68
+ if (arguments_[0] === 'help' && arguments_[1] === 'update' && arguments_.length === 2) {
69
+ return { checkOnly: false, help: true }
70
+ }
71
+ if (arguments_[0] !== 'update') return null
72
+ let checkOnly = false
73
+ let help = false
74
+ for (const argument of arguments_.slice(1)) {
75
+ if (argument === '--check') {
76
+ checkOnly = true
77
+ } else if (argument === '--help' || argument === '-h') {
78
+ help = true
79
+ } else {
80
+ throw new Error('npm updates do not accept component names; run `pisper update`')
81
+ }
82
+ }
83
+ return { checkOnly, help }
84
+ }
85
+
86
+ async function queryLatestVersion() {
87
+ const invocation = npmInvocation(['view', 'pisper@latest', 'version', '--json'])
88
+ const { stdout } = await execFileAsync(invocation.command, invocation.arguments, {
89
+ encoding: 'utf8',
90
+ windowsHide: true,
91
+ })
92
+ const version = JSON.parse(stdout)
93
+ if (!/^\d+\.\d+\.\d+$/.test(version || '')) {
94
+ throw new Error('npm registry returned an invalid pisper version')
95
+ }
96
+ return version
97
+ }
98
+
99
+ async function installLatestVersion() {
100
+ await new Promise((resolve, reject) => {
101
+ const invocation = npmInvocation([
102
+ 'install',
103
+ '--global',
104
+ 'pisper@latest',
105
+ '--progress=true',
106
+ '--foreground-scripts',
107
+ ])
108
+ const child = spawn(invocation.command, invocation.arguments, {
109
+ stdio: 'inherit',
110
+ windowsHide: false,
111
+ })
112
+ child.once('error', reject)
113
+ child.once('exit', (code, signal) => {
114
+ if (signal) {
115
+ reject(new Error(`npm update was terminated by ${signal}`))
116
+ } else if (code !== 0) {
117
+ reject(new Error(`npm update failed with exit code ${code ?? 1}`))
118
+ } else {
119
+ resolve()
120
+ }
121
+ })
122
+ })
123
+ }
124
+
125
+ export function handleNpmHelp(arguments_, { log = console.log } = {}) {
126
+ if (!parseNpmHelpRequest(arguments_)) return false
127
+ log(NPM_CLI_HELP)
128
+ return true
129
+ }
130
+
131
+ export async function handleNpmUpdate(
132
+ arguments_,
133
+ {
134
+ currentVersion = packageManifest.version,
135
+ queryLatest = queryLatestVersion,
136
+ installLatest = installLatestVersion,
137
+ log = console.log,
138
+ } = {},
139
+ ) {
140
+ const request = parseNpmUpdateRequest(arguments_)
141
+ if (!request) return false
142
+
143
+ if (request.help) {
144
+ log(`Pisper npm update
145
+
146
+ Usage:
147
+ pisper update [--check]
148
+
149
+ Options:
150
+ --check Check the configured npm registry without installing
151
+ -h, --help Print update help`)
152
+ return true
153
+ }
154
+
155
+ const latest = await queryLatest()
156
+ if (compareVersions(currentVersion, latest) >= 0) {
157
+ log(`pisper: npm package ${currentVersion} is current`)
158
+ return true
159
+ }
160
+ log(`pisper: npm package update ${currentVersion} -> ${latest}`)
161
+ if (request.checkOnly) return true
162
+ await installLatest()
163
+ log(`pisper: npm package ${latest} is ready`)
164
+ return true
165
+ }
@@ -0,0 +1,43 @@
1
+ import { homedir } from 'node:os'
2
+ import { join } from 'node:path'
3
+ import process from 'node:process'
4
+
5
+ export function releasePlatform(platform = process.platform) {
6
+ if (platform === 'win32') return 'windows'
7
+ if (platform === 'darwin') return 'darwin'
8
+ if (platform === 'linux') return 'linux'
9
+ throw new Error(`unsupported operating system: ${platform}`)
10
+ }
11
+
12
+ export function releaseArchitecture(arch = process.arch) {
13
+ if (arch === 'x64') return 'x86_64'
14
+ if (arch === 'arm64') return 'aarch64'
15
+ throw new Error(`unsupported CPU architecture: ${arch}`)
16
+ }
17
+
18
+ export function supportedTarget(platform = process.platform, arch = process.arch) {
19
+ const target = `${releasePlatform(platform)}_${releaseArchitecture(arch)}`
20
+ const supported = new Set(['windows_x86_64', 'darwin_x86_64', 'darwin_aarch64', 'linux_x86_64'])
21
+ if (!supported.has(target)) {
22
+ throw new Error(`Pisper does not publish a TUI package for ${target}`)
23
+ }
24
+ return target
25
+ }
26
+
27
+ export function executableName(platform = process.platform) {
28
+ return platform === 'win32' ? 'pisper.exe' : 'pisper'
29
+ }
30
+
31
+ export function localDataDirectory(platform = process.platform, env = process.env) {
32
+ if (env.PISPER_NPM_INSTALL_DIR) return env.PISPER_NPM_INSTALL_DIR
33
+ if (platform === 'win32') {
34
+ if (!env.LOCALAPPDATA) throw new Error('LOCALAPPDATA is unavailable')
35
+ return env.LOCALAPPDATA
36
+ }
37
+ if (platform === 'darwin') return join(homedir(), 'Library', 'Application Support')
38
+ return env.XDG_DATA_HOME || join(homedir(), '.local', 'share')
39
+ }
40
+
41
+ export function componentsRoot(platform = process.platform, env = process.env) {
42
+ return join(localDataDirectory(platform, env), 'com.lingkongran.pisper', 'components')
43
+ }
@@ -0,0 +1,3 @@
1
+ import { ensurePisperInstallation } from './install.mjs'
2
+
3
+ await ensurePisperInstallation()