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.
- package/README.en.md +226 -0
- package/README.md +88 -88
- package/bin/pisper.mjs +48 -0
- package/lib/install.mjs +392 -0
- package/lib/npm-platform.mjs +41 -0
- package/lib/npm-update.mjs +165 -0
- package/lib/platform.mjs +43 -0
- package/lib/postinstall.mjs +3 -0
- package/package.json +36 -14
- package/updater.pubkey +1 -0
- package/web/.vite/manifest.json +2248 -0
- package/web/assets/AnimatedContent-BiVNqD3m.js +1 -0
- package/web/assets/AnimatedList-CJTx-B8I.js +1 -0
- package/web/assets/AppOverlays-C2Ah7oL7.js +1 -0
- package/web/assets/AppSelect-C31aArme.js +1 -0
- package/web/assets/AssetsPage-0mh8UdNf.js +1 -0
- package/web/assets/ChannelsPage-DggaYnBh.js +1 -0
- package/web/assets/ChatHistoryPage-DfYozFip.js +1 -0
- package/web/assets/ChatPage-CXC2G5r0.js +9 -0
- package/web/assets/ClickSpark-BhxL8WOQ.js +1 -0
- package/web/assets/ConfigPage-C4TdRH-k.js +1 -0
- package/web/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/web/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/web/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/web/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/web/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/web/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/web/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/web/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/web/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/web/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/web/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/web/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/web/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/web/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/web/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/web/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/web/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/web/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/web/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/web/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/web/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/web/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/web/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/web/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/web/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/web/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/web/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/web/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/web/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/web/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/web/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/web/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/web/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/web/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/web/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/web/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/web/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/web/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/web/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/web/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/web/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/web/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/web/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/web/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/web/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/web/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/web/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/web/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/web/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/web/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/web/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/web/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/web/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/web/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/web/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/web/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/web/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/web/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/web/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/web/assets/MarkdownMessage-Qlm2GKd9.js +1 -0
- package/web/assets/MemoryPage-CuMu3xO6.js +1 -0
- package/web/assets/PageHeader-pm_Faea_.js +1 -0
- package/web/assets/PluginsPage-BE58-xBC.js +1 -0
- package/web/assets/PreviewPages-DQZg9e3Q.js +1 -0
- package/web/assets/SchedulesPage-D5230V_M.js +1 -0
- package/web/assets/ShinyText-fuaKxKqJ.js +1 -0
- package/web/assets/SkillsPage-uk4fVUON.js +1 -0
- package/web/assets/StarOrbit-BlI6MhVr.js +1 -0
- package/web/assets/TerminalPanel-BrP-ENHg.css +1 -0
- package/web/assets/TerminalPanel-CTwNy7j5.js +18 -0
- package/web/assets/Threads-DskDSsK6.js +1 -0
- package/web/assets/WelcomeEffects-DTdnAdJD.js +1 -0
- package/web/assets/WorkflowsPage-ROYjAN97.js +1 -0
- package/web/assets/WorkspacePicker-nC9FXgh7.js +1 -0
- package/web/assets/alert-B7D8joCd.js +1 -0
- package/web/assets/alert-dialog-C34TKN83.js +1 -0
- package/web/assets/andromeeda-vGVdxbeo.js +1 -0
- package/web/assets/angular-html-17rlCfBS.js +1 -0
- package/web/assets/angular-ts-it3MaXWH.js +1 -0
- package/web/assets/astro-BEVDzCov.js +1 -0
- package/web/assets/aurora-x-CDeNXAV0.js +1 -0
- package/web/assets/ayu-dark-DluEY0Gj.js +1 -0
- package/web/assets/ayu-light-C3h-C4tm.js +1 -0
- package/web/assets/ayu-mirage-Bqwy1Gya.js +1 -0
- package/web/assets/blade-B3yUZIOx.js +1 -0
- package/web/assets/brand-CFOXmGdd.js +1 -0
- package/web/assets/button-CDkfouZn.js +1 -0
- package/web/assets/c-aGSZMVdM.js +1 -0
- package/web/assets/catppuccin-frappe-C9Q5zVZY.js +1 -0
- package/web/assets/catppuccin-latte-CtsyrtPq.js +1 -0
- package/web/assets/catppuccin-macchiato-R8nAf1vR.js +1 -0
- package/web/assets/catppuccin-mocha-DjuaAJKi.js +1 -0
- package/web/assets/chevron-down-Cb6bnqQ4.js +1 -0
- package/web/assets/circle-dot-D28dfrea.js +1 -0
- package/web/assets/coffee-ouOQ86EC.js +1 -0
- package/web/assets/copy-BqjQWrFh.js +1 -0
- package/web/assets/cpp-Bm3vm43L.js +1 -0
- package/web/assets/css-CWcS1xXm.js +1 -0
- package/web/assets/csv-Dx-8-gkx.js +1 -0
- package/web/assets/dark-plus-Cs2F2srj.js +1 -0
- package/web/assets/dist-BwTidasF.js +1 -0
- package/web/assets/dist-DAyD2Kf6.js +1 -0
- package/web/assets/dracula-BHWKrbxM.js +1 -0
- package/web/assets/dracula-soft-5eyTD99u.js +1 -0
- package/web/assets/earth-EOGCuCPR.js +1 -0
- package/web/assets/es2015-BhXS8JEs.js +41 -0
- package/web/assets/everforest-dark-sB-x3p7T.js +1 -0
- package/web/assets/everforest-light-Df2xbC6M.js +1 -0
- package/web/assets/field-DsAM6lRR.js +1 -0
- package/web/assets/file-D4w8IUw4.js +1 -0
- package/web/assets/file-code-corner-Dqjb0Cug.js +1 -0
- package/web/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/web/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/web/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/web/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/web/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/web/assets/github-dark-C-LZuMrd.js +1 -0
- package/web/assets/github-dark-default-DXG-b-1a.js +1 -0
- package/web/assets/github-dark-dimmed-Bx1FflLF.js +1 -0
- package/web/assets/github-dark-high-contrast-B_tTalzw.js +1 -0
- package/web/assets/github-light-EUqPIrTm.js +1 -0
- package/web/assets/github-light-default-BXViO-2h.js +1 -0
- package/web/assets/github-light-high-contrast-B68TUdTA.js +1 -0
- package/web/assets/glsl-TOBAB6bO.js +1 -0
- package/web/assets/graphql-BEmIBNve.js +1 -0
- package/web/assets/gruvbox-dark-hard-C820rvS2.js +1 -0
- package/web/assets/gruvbox-dark-medium-BPjhmG05.js +1 -0
- package/web/assets/gruvbox-dark-soft-MrdJrrXF.js +1 -0
- package/web/assets/gruvbox-light-hard-BC_s9l72.js +1 -0
- package/web/assets/gruvbox-light-medium-BAWPOn9u.js +1 -0
- package/web/assets/gruvbox-light-soft-BSMLrYjP.js +1 -0
- package/web/assets/haml-CDWtFtU9.js +1 -0
- package/web/assets/handlebars-BNtjo3pL.js +1 -0
- package/web/assets/horizon-CE9ld1lL.js +1 -0
- package/web/assets/horizon-bright-DSNQnXHK.js +1 -0
- package/web/assets/houston-CsvMBhTu.js +1 -0
- package/web/assets/html-BVyvp26C.js +1 -0
- package/web/assets/html-derivative-BseLpURQ.js +1 -0
- package/web/assets/http-CkCfSMV9.js +1 -0
- package/web/assets/hurl-syYMt6Rn.js +1 -0
- package/web/assets/image-DfmT5l79.js +1 -0
- package/web/assets/imba-DsUTQ-LC.js +1 -0
- package/web/assets/index-5_Mm9BFd.js +41 -0
- package/web/assets/index-CtjPR5TT.css +2 -0
- package/web/assets/java-duG_UTKe.js +1 -0
- package/web/assets/javascript-DWVbOe4X.js +1 -0
- package/web/assets/jinja-Boj4niAz.js +1 -0
- package/web/assets/jison-CHwnC1Pv.js +1 -0
- package/web/assets/json-qhed-kSA.js +1 -0
- package/web/assets/json5-BR5RXkoi.js +1 -0
- package/web/assets/jsonc-CYpm1nAK.js +1 -0
- package/web/assets/jsonl-CmCQp5Yx.js +1 -0
- package/web/assets/jsx-fcCEM_-L.js +1 -0
- package/web/assets/julia-VoF7UKfw.js +1 -0
- package/web/assets/kanagawa-dragon-Bozd7klA.js +1 -0
- package/web/assets/kanagawa-lotus-BmuOG9ZW.js +1 -0
- package/web/assets/kanagawa-wave-BML5mF2J.js +1 -0
- package/web/assets/laserwave-C_8bwKvT.js +1 -0
- package/web/assets/less-DVTAwKKz.js +1 -0
- package/web/assets/light-plus-DVQuIRkW.js +1 -0
- package/web/assets/link-2-CDKitOSW.js +1 -0
- package/web/assets/loader-circle-XRCZj0q4.js +1 -0
- package/web/assets/markdown-BYOwaDjH.js +1 -0
- package/web/assets/marko-Cupn1xhw.js +1 -0
- package/web/assets/material-theme-Bm3Qr25_.js +1 -0
- package/web/assets/material-theme-darker-2IIEA8gg.js +1 -0
- package/web/assets/material-theme-lighter-uhdI0v04.js +1 -0
- package/web/assets/material-theme-ocean-CHQ94UKr.js +1 -0
- package/web/assets/material-theme-palenight-B5W6OYN7.js +1 -0
- package/web/assets/mdc-4FfK5iWR.js +1 -0
- package/web/assets/mdx-DQZ5AkYe.js +1 -0
- package/web/assets/message-circle-BW-CPcmG.js +1 -0
- package/web/assets/min-dark-BSWPekZh.js +1 -0
- package/web/assets/min-light-DDpmG2fV.js +1 -0
- package/web/assets/monokai-B_bV9PQ9.js +1 -0
- package/web/assets/network-KzJTl7t7.js +1 -0
- package/web/assets/night-owl-DhmEMT88.js +1 -0
- package/web/assets/night-owl-light-eJ-hLW7d.js +1 -0
- package/web/assets/nord-Cb4Vim4T.js +1 -0
- package/web/assets/one-dark-pro-CLwyXe_n.js +1 -0
- package/web/assets/one-light-D7Lr4KcI.js +1 -0
- package/web/assets/panel-top-DByYVkbw.js +1 -0
- package/web/assets/paperclip-CI8irkCX.js +1 -0
- package/web/assets/pencil-C67WdsWU.js +1 -0
- package/web/assets/php-CUREQQ7f.js +1 -0
- package/web/assets/pick-system-directory-BnIFt85A.js +1 -0
- package/web/assets/plastic-DQwYfKfQ.js +1 -0
- package/web/assets/play-CRwfAjca.js +1 -0
- package/web/assets/poimandres-DRFjx7u4.js +1 -0
- package/web/assets/postcss-BXeXVLqQ.js +1 -0
- package/web/assets/progress-DOArAQPW.js +6 -0
- package/web/assets/pug-CQyGKZpv.js +1 -0
- package/web/assets/python-gzcpVVnB.js +1 -0
- package/web/assets/r-C5FwQflr.js +1 -0
- package/web/assets/react-bits-DQli9s7O.css +1 -0
- package/web/assets/red-DX1BQOs8.js +1 -0
- package/web/assets/regexp--aR2_iz7.js +1 -0
- package/web/assets/rolldown-runtime-B0Z9INg1.js +1 -0
- package/web/assets/rose-pine-aMsaMD1D.js +1 -0
- package/web/assets/rose-pine-dawn-8PwUyIlP.js +1 -0
- package/web/assets/rose-pine-moon-DO-qXg-I.js +1 -0
- package/web/assets/sass-DXrisJhu.js +1 -0
- package/web/assets/save-DAkFWHyI.js +1 -0
- package/web/assets/scss-Davl4E3T.js +1 -0
- package/web/assets/search-CAq4c_43.js +1 -0
- package/web/assets/shellscript-CLZ0U2zV.js +1 -0
- package/web/assets/shield-check-C62jDUnP.js +1 -0
- package/web/assets/slack-dark-DnToyrRv.js +1 -0
- package/web/assets/slack-ochin-B2OO5cIa.js +1 -0
- package/web/assets/smithy-Dgaz7aur.js +1 -0
- package/web/assets/snazzy-light-4G7pJPwS.js +1 -0
- package/web/assets/solarized-dark-UdsmMDCL.js +1 -0
- package/web/assets/solarized-light-C1IUL_tW.js +1 -0
- package/web/assets/sql-b92rrcVl.js +1 -0
- package/web/assets/square-D2anbrZz.js +1 -0
- package/web/assets/square-terminal-Pww63y-M.js +1 -0
- package/web/assets/stylus-B6D30XZt.js +1 -0
- package/web/assets/sun-BcZVxVDx.js +1 -0
- package/web/assets/svelte-BfstjgUc.js +1 -0
- package/web/assets/synthwave-84-nFMaYfgc.js +1 -0
- package/web/assets/tag-CqZUIGvX.js +1 -0
- package/web/assets/tokyo-night-oM2G3aXe.js +1 -0
- package/web/assets/trash-2-CsljwZ9-.js +1 -0
- package/web/assets/ts-tags-Cckpt1jI.js +1 -0
- package/web/assets/tsx-JMDX3aQ7.js +1 -0
- package/web/assets/typescript-BAz-bF8J.js +1 -0
- package/web/assets/use-i18n-CI7pHimf.js +1 -0
- package/web/assets/usePagePrimaryAction-Bw0OFTDZ.js +1 -0
- package/web/assets/utils-C8uzG5AZ.js +1 -0
- package/web/assets/vendor-dockview-D5adAaCg.css +1 -0
- package/web/assets/vendor-dockview-yY5mBM3U.js +1 -0
- package/web/assets/vendor-markdown-CWK5AVhB.js +130 -0
- package/web/assets/vendor-markdown-plugins-DCl4I7Pz.js +263 -0
- package/web/assets/vendor-motion-XxgxfZSY.js +9 -0
- package/web/assets/vendor-react-CHuvPdJE.js +9 -0
- package/web/assets/vendor-router-Bw2aAdMs.js +4 -0
- package/web/assets/vendor-shiki-runtime-CxE5QolQ.js +153 -0
- package/web/assets/vendor-state-DE1O_s6m.js +1 -0
- package/web/assets/vendor-ui-DvrYggeS.js +1 -0
- package/web/assets/vendor-xyflow-CnjXtcyK.js +7 -0
- package/web/assets/vendor-xyflow-DLioOiRN.css +1 -0
- package/web/assets/vesper-DdrHHSXu.js +1 -0
- package/web/assets/vitesse-black-fwtXNY1n.js +1 -0
- package/web/assets/vitesse-dark-BZCL-v6S.js +1 -0
- package/web/assets/vitesse-light-VbXTXTou.js +1 -0
- package/web/assets/vue-3H7P6ncC.js +1 -0
- package/web/assets/vue-html-DZGW9MGR.js +1 -0
- package/web/assets/vue-vine-BECt67gq.js +1 -0
- package/web/assets/wasm-BnjxR4X6.js +1 -0
- package/web/assets/wasm-ByWQv1Qj.js +1 -0
- package/web/assets/wgsl-BsKzXJz4.js +1 -0
- package/web/assets/wit-DdvCle-K.js +1 -0
- package/web/assets/wrench-BIgib2eu.js +1 -0
- package/web/assets/xml-CNfvW5JP.js +1 -0
- package/web/assets/yaml-rwi0_p6S.js +1 -0
- package/web/assets/zap-evw8kNGN.js +1 -0
- package/web/connect.html +133 -0
- package/web/connect.js +208 -0
- package/web/favicon.svg +25 -0
- package/web/icons.svg +24 -0
- package/web/index.html +72 -0
- package/web/memory-galaxy-background.webp +0 -0
- package/web/notification-sw.js +13 -0
- package/web/tauri-pet.css +117 -0
- package/web/tauri-pet.html +16 -0
- package/web/tauri-pet.js +117 -0
- package/components/runtime/Pisper_Runtime_Node_0.5.12_linux_x86_64.tar.gz +0 -0
- package/components/runtime/Pisper_Runtime_Node_0.5.12_linux_x86_64.tar.gz.sig +0 -1
- package/components/tui/Pisper_TUI_Component_0.5.8_linux_x86_64.tar.gz +0 -0
- package/components/tui/Pisper_TUI_Component_0.5.8_linux_x86_64.tar.gz.sig +0 -1
package/lib/install.mjs
ADDED
|
@@ -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
|
+
}
|
package/lib/platform.mjs
ADDED
|
@@ -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
|
+
}
|