bvm-core 1.1.16 → 1.1.18

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.md CHANGED
@@ -37,21 +37,21 @@
37
37
 
38
38
  BVM uses a smart installation script that automatically detects your OS and network environment (selecting the fastest registry for China/Global users).
39
39
 
40
- ### Method 1: NPM (Recommended for Node.js users)
41
- ```bash
42
- npm install -g bvm-core@latest
43
- ```
44
-
45
- ### Method 2: Shell Script (macOS / Linux)
40
+ ### Method 1: Shell Script (Recommended - macOS / Linux)
46
41
  ```bash
47
42
  curl -fsSL https://bvm-core.pages.dev/install | bash
48
43
  ```
49
44
 
50
- ### Method 3: PowerShell (Windows)
45
+ ### Method 2: PowerShell (Recommended - Windows)
51
46
  ```powershell
52
47
  irm https://bvm-core.pages.dev/install | iex
53
48
  ```
54
49
 
50
+ ### Method 3: NPM (Optional)
51
+ ```bash
52
+ npm install -g bvm-core@latest
53
+ ```
54
+
55
55
  ---
56
56
 
57
57
  ## Key Features
package/README.zh-CN.md CHANGED
@@ -37,21 +37,21 @@
37
37
 
38
38
  BVM 提供了智能安装脚本,**自动检测您的网络环境**。中国用户会自动切换至淘宝镜像源,海外用户使用官方源,无需手动配置。
39
39
 
40
- ### 方式 1: NPM (推荐 Node.js 用户)
41
- ```bash
42
- npm install -g bvm-core@latest
43
- ```
44
-
45
- ### 方式 2: Shell 脚本 (macOS / Linux)
40
+ ### 方式 1: Shell 脚本 (推荐 - macOS / Linux)
46
41
  ```bash
47
42
  curl -fsSL https://bvm-core.pages.dev/install | bash
48
43
  ```
49
44
 
50
- ### 方式 3: PowerShell (Windows)
45
+ ### 方式 2: PowerShell (推荐 - Windows)
51
46
  ```powershell
52
47
  irm https://bvm-core.pages.dev/install | iex
53
48
  ```
54
49
 
50
+ ### 方式 3: NPM (可选)
51
+ ```bash
52
+ npm install -g bvm-core@latest
53
+ ```
54
+
55
55
  ---
56
56
 
57
57
  ## 核心特性
package/bin/bvm-npm.js ADDED
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * BVM NPM Smart Entry Point
5
+ */
6
+
7
+ const { spawnSync } = require('child_process');
8
+ const path = require('path');
9
+ const os = require('os');
10
+ const fs = require('fs');
11
+
12
+ const IS_WINDOWS = os.platform() === 'win32';
13
+ const HOME = process.env.HOME || os.homedir();
14
+ const BVM_DIR = process.env.BVM_DIR || path.join(HOME, '.bvm');
15
+ const BVM_SRC = path.join(BVM_DIR, 'src', 'index.js');
16
+ const BUN_RUNTIME = path.join(BVM_DIR, 'runtime', 'current', 'bin', IS_WINDOWS ? 'bun.exe' : 'bun');
17
+
18
+ function run(bin, args) {
19
+ return spawnSync(bin, args, {
20
+ stdio: 'inherit',
21
+ env: Object.assign({}, process.env, { BVM_DIR }),
22
+ shell: IS_WINDOWS && bin.indexOf(' ') !== -1
23
+ });
24
+ }
25
+
26
+ // 1. 优先尝试 BVM 内部的地堡运行时
27
+ if (fs.existsSync(BUN_RUNTIME)) {
28
+ const result = run(BUN_RUNTIME, [BVM_SRC, ...process.argv.slice(2)]);
29
+ process.exit(result.status || 0);
30
+ }
31
+
32
+ // 2. 其次尝试使用 Node 寻找系统中的 bun (比如通过 npm install -g bun 安装的)
33
+ try {
34
+ // 简单通过 spawn 检查 bun 是否在 PATH 中
35
+ const check = spawnSync(IS_WINDOWS ? 'bun.exe' : 'bun', ['--version'], { stdio: 'ignore' });
36
+ if (check.status === 0) {
37
+ const result = run(IS_WINDOWS ? 'bun.exe' : 'bun', [BVM_SRC, ...process.argv.slice(2)]);
38
+ process.exit(result.status || 0);
39
+ }
40
+ } catch (e) {}
41
+
42
+ // 3. 如果都没有,给出清晰的指引(或者在未来版本支持自动下载)
43
+ console.error('\x1b[31m[bvm] Error: No Bun runtime found.\x1b[0m');
44
+ console.error('BVM requires Bun to function. Since you installed via npm, please run:');
45
+ console.error('\x1b[36m bvm setup\x1b[0m (if available) or install Bun manually.');
46
+ process.exit(1);
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // @bun
3
3
  var t1=Object.create;var{getPrototypeOf:n1,defineProperty:B$,getOwnPropertyNames:o1}=Object;var i1=Object.prototype.hasOwnProperty;var U$=($,q,Q)=>{Q=$!=null?t1(n1($)):{};let J=q||!$||!$.__esModule?B$(Q,"default",{value:$,enumerable:!0}):Q;for(let Y of o1($))if(!i1.call(J,Y))B$(J,Y,{get:()=>$[Y],enumerable:!0});return J};var i$=($,q)=>{for(var Q in q)B$($,Q,{get:q[Q],enumerable:!0,configurable:!0,set:(J)=>q[Q]=()=>J})};var y$=($,q)=>()=>($&&(q=$($=0)),q);var O$=import.meta.require;var s$={};i$(s$,{getBvmDir:()=>a$,getBunAssetName:()=>f$,USER_AGENT:()=>X$,TEST_REMOTE_VERSIONS:()=>Z$,REPO_FOR_BVM_CLI:()=>$6,OS_PLATFORM:()=>B,IS_TEST_MODE:()=>D,EXECUTABLE_NAME:()=>j,CPU_ARCH:()=>z$,BVM_VERSIONS_DIR:()=>L,BVM_SRC_DIR:()=>r1,BVM_SHIMS_DIR:()=>S,BVM_FINGERPRINTS_FILE:()=>M$,BVM_DIR:()=>O,BVM_CURRENT_DIR:()=>b$,BVM_COMPONENTS:()=>J6,BVM_CDN_ROOT:()=>Q6,BVM_CACHE_DIR:()=>_,BVM_BIN_DIR:()=>M,BVM_ALIAS_DIR:()=>x,BUN_GITHUB_RELEASES_API:()=>e1,ASSET_NAME_FOR_BVM:()=>q6});import{homedir as s1}from"os";import{join as o}from"path";function a$(){let $=process.env.HOME||s1();return o($,".bvm")}function f$($){return`bun-${B==="win32"?"windows":B}-${z$==="arm64"?"aarch64":"x64"}.zip`}var B,z$,D,Z$,O,r1,L,M,S,b$,x,_,M$,j,e1="https://api.github.com/repos/oven-sh/bun/releases",$6="EricLLLLLL/bvm",q6,X$="bvm (Bun Version Manager)",Q6,J6;var F=y$(()=>{B=process.platform,z$=process.arch,D=process.env.BVM_TEST_MODE==="true",Z$=["v1.3.4","v1.2.23","v1.0.0","bun-v1.4.0-canary"];O=a$(),r1=o(O,"src"),L=o(O,"versions"),M=o(O,"bin"),S=o(O,"shims"),b$=o(O,"current"),x=o(O,"aliases"),_=o(O,"cache"),M$=o(O,"fingerprints.json"),j=B==="win32"?"bun.exe":"bun",q6=B==="win32"?"bvm.exe":"bvm",Q6=process.env.BVM_CDN_URL||"https://cdn.jsdelivr.net/gh/EricLLLLLL/bvm",J6=[{name:"CLI Core",remotePath:"index.js",localPath:"src/index.js"},{name:"Windows Shim",remotePath:"bvm-shim.js",localPath:"bin/bvm-shim.js",platform:"win32"},{name:"Unix Shim",remotePath:"bvm-shim.sh",localPath:"bin/bvm-shim.sh",platform:"posix"}]});function m($){if(!$)return null;return $.match(/^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/)?$:null}function r$($){let q=m($);return q?q.replace(/^v/,""):null}function J$($){if(!$)return null;let q=$.replace(/^v/,""),J=q.split(/[-+]/)[0].split(".").map(Number);if(J.length===0||J.some((K)=>isNaN(K)))return null;let Y=q.includes("-")?q.split("-")[1].split("+")[0]:void 0;return{major:J[0],minor:J[1],patch:J[2],pre:Y}}function T$($,q){if(!$||!q)return 0;if($.major!==q.major)return $.major-q.major;if($.minor!==q.minor)return $.minor-q.minor;if($.patch!==q.patch)return $.patch-q.patch;if($.pre&&!q.pre)return-1;if(!$.pre&&q.pre)return 1;if($.pre&&q.pre)return $.pre.localeCompare(q.pre);return 0}function e$($,q){let Q=J$($),J=J$(q);return T$(Q,J)}function K$($,q){return e$(q,$)}function k$($,q){return e$($,q)>0}function $1($,q){if(q==="*"||q===""||q==="latest")return!0;let Q=J$($);if(!Q)return!1;let J=q;if(q.startsWith("v"))J=q.substring(1);if(r$($)===r$(q))return!0;let Y=J.split(".");if(Y.length===1){let K=Number(Y[0]);if(Q.major===K)return!0}else if(Y.length===2){let K=Number(Y[0]),b=Number(Y[1]);if(Q.major===K&&Q.minor===b)return!0}if(q.startsWith("~")){let K=J$(q.substring(1));if(!K)return!1;let b=K.patch??0;return Q.major===K.major&&Q.minor===K.minor&&Q.patch>=b}if(q.startsWith("^")){let K=J$(q.substring(1));if(!K)return!1;let b=K.patch??0,X=K.minor??0;if(K.major===0){if(Q.major!==0)return!1;if(Q.minor!==X)return!1;return Q.patch>=b}if(Q.major!==K.major)return!1;if(Q.minor<X)return!1;if(Q.minor===X&&Q.patch<b)return!1;return!0}return!1}import{readdir as Y6,mkdir as Z6,stat as b6,symlink as X6,unlink as q1,rm as Q1,readlink as G6}from"fs/promises";import{join as C$,dirname as H6,basename as W6}from"path";async function N($){await Z6($,{recursive:!0})}async function G($){try{return await b6($),!0}catch(q){if(q.code==="ENOENT")return!1;throw q}}async function J1($,q){try{await q1(q)}catch(J){try{await Q1(q,{recursive:!0,force:!0})}catch(Y){}}let Q=process.platform==="win32"?"junction":"dir";await X6($,q,Q)}async function K1($){try{return await G6($)}catch(q){if(q.code==="ENOENT"||q.code==="EINVAL")return null;throw q}}async function G$($){await Q1($,{recursive:!0,force:!0})}async function p($){try{return await Y6($)}catch(q){if(q.code==="ENOENT")return[];throw q}}async function f($){return await Bun.file($).text()}async function V($,q){await Bun.write($,q)}async function L$($,q,Q={}){let{backup:J=!0}=Q,Y=H6($);await N(Y);let K=`${$}.tmp-${Date.now()}`,b=`${$}.bak`;try{if(await V(K,q),J&&await G($))try{let{rename:H,unlink:z}=await import("fs/promises");if(await G(b))await z(b).catch(()=>{});await H($,b)}catch(H){}let{rename:X}=await import("fs/promises");try{await X(K,$)}catch(H){await Bun.write($,q),await q1(K).catch(()=>{})}}catch(X){let{unlink:H}=await import("fs/promises");throw await H(K).catch(()=>{}),X}}function U($){let q=$.trim();if(q.startsWith("bun-v"))q=q.substring(4);if(!q.startsWith("v")&&/^\d/.test(q))q=`v${q}`;return q}async function P(){return await N(L),(await p(L)).filter((q)=>m(U(q))).sort(K$)}async function l(){if(process.env.BVM_ACTIVE_VERSION)return{version:U(process.env.BVM_ACTIVE_VERSION),source:"env"};let $=C$(process.cwd(),".bvmrc");if(await G($)){let b=(await f($)).trim();return{version:U(b),source:".bvmrc"}}let{getBvmDir:q}=await Promise.resolve().then(() => (F(),s$)),Q=q(),J=C$(Q,"current"),Y=C$(Q,"aliases");if(await G(J)){let{realpath:b}=await import("fs/promises");try{let X=await b(J);return{version:U(W6(X)),source:"current"}}catch(X){}}let K=C$(Y,"default");if(await G(K)){let b=(await f(K)).trim();return{version:U(b),source:"default"}}return{version:null,source:null}}function r($,q){if(!$||q.length===0)return null;let Q=U($);if(q.includes(Q))return Q;if($.toLowerCase()==="latest")return q[0];if(/^\d+\.\d+\.\d+$/.test($.replace(/^v/,"")))return null;if(!/^[v\d]/.test($))return null;let Y=$.startsWith("v")?`~${$.substring(1)}`:`~${$}`,K=q.filter((b)=>$1(b,Y));if(K.length>0)return K.sort(K$)[0];return null}var w=y$(()=>{F()});class R${timer=null;frames=process.platform==="win32"?["-"]:["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"];frameIndex=0;text;isWindows=process.platform==="win32";constructor($){this.text=$}start($){if($)this.text=$;if(this.timer)return;if(this.isWindows){process.stdout.write(`${Z.cyan(">")} ${this.text}
4
4
  `);return}this.timer=setInterval(()=>{process.stdout.write(`\r\x1B[K${Z.cyan(this.frames[this.frameIndex])} ${this.text}`),this.frameIndex=(this.frameIndex+1)%this.frames.length},80)}stop(){if(this.isWindows)return;if(this.timer)clearInterval(this.timer),this.timer=null,process.stdout.write("\r\x1B[K");process.stdout.write("\x1B[?25h")}succeed($){this.stop(),console.log(`${Z.green(" \u2713")} ${$||this.text}`)}fail($){this.stop(),console.log(`${Z.red(" \u2716")} ${$||this.text}`)}info($){this.stop(),console.log(`${Z.blue(" \u2139")} ${$||this.text}`)}update($){if(this.text=$,this.isWindows)console.log(Z.dim(` ... ${this.text}`))}}class D${total;current=0;width=20;constructor($){this.total=$}start(){process.stdout.write("\x1B[?25l"),this.render()}update($,q){this.current=$,this.render(q)}stop(){process.stdout.write(`\x1B[?25h
5
- `)}render($){let q=Math.min(1,this.current/this.total),Q=Math.round(this.width*q),J=this.width-Q,Y=process.platform==="win32",K=Y?"=":"\u2588",b=Y?"-":"\u2591",X=Z.green(K.repeat(Q))+Z.gray(b.repeat(J)),H=(q*100).toFixed(0).padStart(3," "),z=(this.current/1048576).toFixed(1),C=(this.total/1048576).toFixed(1),W=$?` ${$.speed}KB/s`:"";process.stdout.write(`\r\x1B[2K ${X} ${H}% | ${z}/${C}MB${W}`)}}var U6,y6=($)=>$.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),i=($,q,Q=$)=>(J)=>U6?$+J.replace(new RegExp(y6(q),"g"),Q)+q:J,Z;var k=y$(()=>{U6=!process.env.NO_COLOR,Z={red:i("\x1B[1;31m","\x1B[39m"),green:i("\x1B[1;32m","\x1B[39m"),yellow:i("\x1B[1;33m","\x1B[39m"),blue:i("\x1B[1;34m","\x1B[39m"),magenta:i("\x1B[1;35m","\x1B[39m"),cyan:i("\x1B[1;36m","\x1B[39m"),gray:i("\x1B[90m","\x1B[39m"),bold:i("\x1B[1m","\x1B[22m","\x1B[22m\x1B[1m"),dim:i("\x1B[2m","\x1B[22m","\x1B[22m\x1B[2m")}});async function y($,q,Q){if(process.platform==="win32"){console.log(Z.cyan(`> ${$}`));let K={start:(b)=>{if(b)console.log(Z.cyan(`> ${b}`))},stop:()=>{},succeed:(b)=>console.log(Z.green(` \u2713 ${b}`)),fail:(b)=>console.log(Z.red(` \u2716 ${b}`)),info:(b)=>console.log(Z.cyan(` \u2139 ${b}`)),update:(b)=>console.log(Z.dim(` ... ${b}`)),isSpinning:!1};try{return await q(K)}catch(b){let X=y1(b,Q?.failMessage);if(console.log(Z.red(` \u2716 ${X}`)),process.env.BVM_DEBUG,console.log(Z.dim(` Details: ${b.message}`)),b.code)console.log(Z.dim(` Code: ${b.code}`));throw b.reported=!0,b}}let Y=new R$($);Y.start();try{let K=await q(Y);return Y.stop(),K}catch(K){let b=y1(K,Q?.failMessage);throw Y.fail(Z.red(b)),K.reported=!0,K}}function y1($,q){let Q=$ instanceof Error?$.message:String($);if(!q)return Q;if(typeof q==="function")return q($);return`${q}: ${Q}`}var R=y$(()=>{k()});var O1={};i$(O1,{resolveLocalVersion:()=>g,displayVersion:()=>d$});import{join as P6}from"path";async function g($){if($==="current"){let{version:Y}=await l();return Y}if($==="latest"){let Y=await P();if(Y.length>0)return Y[0];return null}let q=P6(x,$);if(await G(q))try{let Y=(await f(q)).trim();return U(Y)}catch{return null}let Q=U($),J=await P();return r($,J)}async function d$($){await y(`Resolving version '${$}'...`,async()=>{let q=await g($);if(q)console.log(q);else throw Error("N/A")},{failMessage:`Failed to resolve version '${$}'`})}var q$=y$(()=>{F();w();R()});import{parseArgs as Y4}from"util";var Y$={name:"bvm-core",version:"1.1.16",description:"The native version manager for Bun. Cross-platform, shell-agnostic, and zero-dependency.",main:"dist/index.js",bin:{bvm:"dist/index.js"},publishConfig:{access:"public"},scripts:{dev:"bun run src/index.ts",build:"bun build src/index.ts --target=bun --outfile dist/index.js --minify && bun run scripts/sync-runtime.ts",test:"bun test",bvm:"bun run src/index.ts","bvm:sandbox":'mkdir -p "$PWD/.sandbox-home" && HOME="$PWD/.sandbox-home" bun run src/index.ts',release:"bun run scripts/release.ts","test:e2e:npm":"bun run scripts/verify-e2e-npm.ts","sync-runtime":"bun run scripts/sync-runtime.ts",postinstall:"node scripts/postinstall.js"},repository:{type:"git",url:"git+https://github.com/EricLLLLLL/bvm.git"},keywords:["bun","version-manager","cli","bvm","nvm","nvm-windows","fnm","nodenv","bun-nvm","version-switching"],files:["dist/index.js","dist/bvm-shim.sh","dist/bvm-shim.js","scripts/postinstall.js","install.sh","install.ps1","README.md"],author:"EricLLLLLL",license:"MIT",type:"commonjs",dependencies:{"cli-progress":"^3.12.0"},optionalDependencies:{"@oven/bun-darwin-aarch64":"^1.3.6","@oven/bun-darwin-x64":"^1.3.6","@oven/bun-linux-aarch64":"^1.3.6","@oven/bun-linux-x64":"^1.3.6","@oven/bun-windows-x64":"^1.3.6"},devDependencies:{"@types/bun":"^1.3.4","@types/cli-progress":"^3.11.6","@types/node":"^24.10.2",bun:"^1.3.6",esbuild:"^0.27.2",execa:"^9.6.1",typescript:"^5"},peerDependencies:{typescript:"^5"}};F();w();import{join as n,basename as g6,dirname as m6}from"path";F();w();k();import{join as L6}from"path";function Y1($,q){if($==="darwin"){if(q==="arm64")return"@oven/bun-darwin-aarch64";if(q==="x64")return"@oven/bun-darwin-x64"}if($==="linux"){if(q==="arm64")return"@oven/bun-linux-aarch64";if(q==="x64")return"@oven/bun-linux-x64"}if($==="win32"){if(q==="x64")return"@oven/bun-windows-x64"}return null}function Z1($,q,Q){let J=Q;if(!J.endsWith("/"))J+="/";let Y=$.startsWith("@"),K=$;if(Y){let X=$.split("/");if(X.length===2)K=X[1]}let b=`${K}-${q}.tgz`;return`${J}${$}/-/${b}`}k();async function d($,q={}){let{cwd:Q,env:J,prependPath:Y,stdin:K="inherit",stdout:b="inherit",stderr:X="inherit"}=q,H={...process.env,...J};if(Y){let W=H.PATH||"",A=process.platform==="win32"?";":":";H.PATH=`${Y}${A}${W}`}let C=await Bun.spawn({cmd:$,cwd:Q,env:H,stdin:K,stdout:b,stderr:X}).exited;if((C??0)!==0)throw Error(`${Z.red("Command failed")}: ${$.join(" ")} (code ${C})`);return C??0}async function a($,q={}){let{timeout:Q=5000,...J}=q,Y=new AbortController,K=setTimeout(()=>Y.abort(),Q);try{let b=await fetch($,{...J,signal:Y.signal});return clearTimeout(K),b}catch(b){if(clearTimeout(K),b.name==="AbortError")throw Error(`Request to ${$} timed out after ${Q}ms`);throw b}}async function O6($,q=2000){if($.length===0)throw Error("No URLs to race");if($.length===1)return $[0];return new Promise((Q,J)=>{let Y=0,K=!1;$.forEach((b)=>{a(b,{method:"HEAD",timeout:q}).then((X)=>{if(X.ok&&!K)K=!0,Q(b);else if(!K){if(Y++,Y===$.length)J(Error("All requests failed"))}}).catch(()=>{if(!K){if(Y++,Y===$.length)J(Error("All requests failed"))}})})})}async function z6(){try{let $=await a("https://1.1.1.1/cdn-cgi/trace",{timeout:500});if(!$.ok)return null;let Q=(await $.text()).match(/loc=([A-Z]{2})/);return Q?Q[1]:null}catch($){return null}}var I$=null,w$={NPM:"https://registry.npmjs.org",TAOBAO:"https://registry.npmmirror.com",TENCENT:"https://mirrors.cloud.tencent.com/npm/"};async function e(){if(I$)return I$;let $=await z6(),q=[];if($==="CN")q=[w$.TAOBAO,w$.TENCENT,w$.NPM];else q=[w$.NPM,w$.TAOBAO];try{let Q=await O6(q,2000);return I$=Q,Q}catch(Q){return q[0]}}w();var w6="bun-versions.json",x6=3600000;async function k6(){if(D)return[...Z$];let $=L6(_,w6);try{if(await G($)){let Y=await f($),K=JSON.parse(Y);if(Date.now()-K.timestamp<x6&&Array.isArray(K.versions))return K.versions}}catch(Y){}let q=await e(),Q=[q];if(q!=="https://registry.npmjs.org")Q.push("https://registry.npmjs.org");let J=null;for(let Y of Q){let K=`${Y.replace(/\/$/,"")}/bun`;try{let b=await a(K,{headers:{"User-Agent":X$,Accept:"application/vnd.npm.install-v1+json"},timeout:1e4});if(!b.ok)throw Error(`Status ${b.status}`);let X=await b.json();if(!X.versions)throw Error("Invalid response (no versions)");let H=Object.keys(X.versions);try{await N(_),await V($,JSON.stringify({timestamp:Date.now(),versions:H}))}catch(z){}return H}catch(b){J=b}}throw J||Error("Failed to fetch versions from any registry.")}async function C6(){if(D)return[...Z$];return new Promise(($,q)=>{let Q=[];try{let J=Bun.spawn(["git","ls-remote","--tags","https://github.com/oven-sh/bun.git"],{stdout:"pipe",stderr:"pipe"}),Y=setTimeout(()=>{J.kill(),q(Error("Git operation timed out"))},1e4);new Response(J.stdout).text().then((b)=>{clearTimeout(Y);let X=b.split(`
5
+ `)}render($){let q=Math.min(1,this.current/this.total),Q=Math.round(this.width*q),J=this.width-Q,Y=process.platform==="win32",K=Y?"=":"\u2588",b=Y?"-":"\u2591",X=Z.green(K.repeat(Q))+Z.gray(b.repeat(J)),H=(q*100).toFixed(0).padStart(3," "),z=(this.current/1048576).toFixed(1),C=(this.total/1048576).toFixed(1),W=$?` ${$.speed}KB/s`:"";process.stdout.write(`\r\x1B[2K ${X} ${H}% | ${z}/${C}MB${W}`)}}var U6,y6=($)=>$.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),i=($,q,Q=$)=>(J)=>U6?$+J.replace(new RegExp(y6(q),"g"),Q)+q:J,Z;var k=y$(()=>{U6=!process.env.NO_COLOR,Z={red:i("\x1B[1;31m","\x1B[39m"),green:i("\x1B[1;32m","\x1B[39m"),yellow:i("\x1B[1;33m","\x1B[39m"),blue:i("\x1B[1;34m","\x1B[39m"),magenta:i("\x1B[1;35m","\x1B[39m"),cyan:i("\x1B[1;36m","\x1B[39m"),gray:i("\x1B[90m","\x1B[39m"),bold:i("\x1B[1m","\x1B[22m","\x1B[22m\x1B[1m"),dim:i("\x1B[2m","\x1B[22m","\x1B[22m\x1B[2m")}});async function y($,q,Q){if(process.platform==="win32"){console.log(Z.cyan(`> ${$}`));let K={start:(b)=>{if(b)console.log(Z.cyan(`> ${b}`))},stop:()=>{},succeed:(b)=>console.log(Z.green(` \u2713 ${b}`)),fail:(b)=>console.log(Z.red(` \u2716 ${b}`)),info:(b)=>console.log(Z.cyan(` \u2139 ${b}`)),update:(b)=>console.log(Z.dim(` ... ${b}`)),isSpinning:!1};try{return await q(K)}catch(b){let X=y1(b,Q?.failMessage);if(console.log(Z.red(` \u2716 ${X}`)),process.env.BVM_DEBUG,console.log(Z.dim(` Details: ${b.message}`)),b.code)console.log(Z.dim(` Code: ${b.code}`));throw b.reported=!0,b}}let Y=new R$($);Y.start();try{let K=await q(Y);return Y.stop(),K}catch(K){let b=y1(K,Q?.failMessage);throw Y.fail(Z.red(b)),K.reported=!0,K}}function y1($,q){let Q=$ instanceof Error?$.message:String($);if(!q)return Q;if(typeof q==="function")return q($);return`${q}: ${Q}`}var R=y$(()=>{k()});var O1={};i$(O1,{resolveLocalVersion:()=>g,displayVersion:()=>d$});import{join as P6}from"path";async function g($){if($==="current"){let{version:Y}=await l();return Y}if($==="latest"){let Y=await P();if(Y.length>0)return Y[0];return null}let q=P6(x,$);if(await G(q))try{let Y=(await f(q)).trim();return U(Y)}catch{return null}let Q=U($),J=await P();return r($,J)}async function d$($){await y(`Resolving version '${$}'...`,async()=>{let q=await g($);if(q)console.log(q);else throw Error("N/A")},{failMessage:`Failed to resolve version '${$}'`})}var q$=y$(()=>{F();w();R()});import{parseArgs as Y4}from"util";var Y$={name:"bvm-core",version:"1.1.18",description:"The native version manager for Bun. Cross-platform, shell-agnostic, and zero-dependency.",main:"dist/index.js",bin:{bvm:"bin/bvm-npm.js"},publishConfig:{access:"public"},scripts:{dev:"bun run src/index.ts",build:"bun build src/index.ts --target=bun --outfile dist/index.js --minify && bun run scripts/sync-runtime.ts",test:"bun test",bvm:"bun run src/index.ts","bvm:sandbox":'mkdir -p "$PWD/.sandbox-home" && HOME="$PWD/.sandbox-home" bun run src/index.ts',release:"bun run scripts/release.ts","test:e2e:npm":"bun run scripts/verify-e2e-npm.ts","sync-runtime":"bun run scripts/sync-runtime.ts",postinstall:"node scripts/postinstall.js"},repository:{type:"git",url:"git+https://github.com/EricLLLLLL/bvm.git"},keywords:["bun","version-manager","cli","bvm","nvm","nvm-windows","fnm","nodenv","bun-nvm","version-switching"],files:["dist/index.js","dist/bvm-shim.sh","dist/bvm-shim.js","bin/bvm-npm.js","scripts/postinstall.js","install.sh","install.ps1","README.md"],author:"EricLLLLLL",license:"MIT",type:"commonjs",dependencies:{"cli-progress":"^3.12.0"},optionalDependencies:{"@oven/bun-darwin-aarch64":"^1.3.6","@oven/bun-darwin-x64":"^1.3.6","@oven/bun-linux-aarch64":"^1.3.6","@oven/bun-linux-x64":"^1.3.6","@oven/bun-windows-x64":"^1.3.6"},devDependencies:{"@types/bun":"^1.3.4","@types/cli-progress":"^3.11.6","@types/node":"^24.10.2",bun:"^1.3.6",esbuild:"^0.27.2",execa:"^9.6.1",typescript:"^5"},peerDependencies:{typescript:"^5"}};F();w();import{join as n,basename as g6,dirname as m6}from"path";F();w();k();import{join as L6}from"path";function Y1($,q){if($==="darwin"){if(q==="arm64")return"@oven/bun-darwin-aarch64";if(q==="x64")return"@oven/bun-darwin-x64"}if($==="linux"){if(q==="arm64")return"@oven/bun-linux-aarch64";if(q==="x64")return"@oven/bun-linux-x64"}if($==="win32"){if(q==="x64")return"@oven/bun-windows-x64"}return null}function Z1($,q,Q){let J=Q;if(!J.endsWith("/"))J+="/";let Y=$.startsWith("@"),K=$;if(Y){let X=$.split("/");if(X.length===2)K=X[1]}let b=`${K}-${q}.tgz`;return`${J}${$}/-/${b}`}k();async function d($,q={}){let{cwd:Q,env:J,prependPath:Y,stdin:K="inherit",stdout:b="inherit",stderr:X="inherit"}=q,H={...process.env,...J};if(Y){let W=H.PATH||"",A=process.platform==="win32"?";":":";H.PATH=`${Y}${A}${W}`}let C=await Bun.spawn({cmd:$,cwd:Q,env:H,stdin:K,stdout:b,stderr:X}).exited;if((C??0)!==0)throw Error(`${Z.red("Command failed")}: ${$.join(" ")} (code ${C})`);return C??0}async function a($,q={}){let{timeout:Q=5000,...J}=q,Y=new AbortController,K=setTimeout(()=>Y.abort(),Q);try{let b=await fetch($,{...J,signal:Y.signal});return clearTimeout(K),b}catch(b){if(clearTimeout(K),b.name==="AbortError")throw Error(`Request to ${$} timed out after ${Q}ms`);throw b}}async function O6($,q=2000){if($.length===0)throw Error("No URLs to race");if($.length===1)return $[0];return new Promise((Q,J)=>{let Y=0,K=!1;$.forEach((b)=>{a(b,{method:"HEAD",timeout:q}).then((X)=>{if(X.ok&&!K)K=!0,Q(b);else if(!K){if(Y++,Y===$.length)J(Error("All requests failed"))}}).catch(()=>{if(!K){if(Y++,Y===$.length)J(Error("All requests failed"))}})})})}async function z6(){try{let $=await a("https://1.1.1.1/cdn-cgi/trace",{timeout:500});if(!$.ok)return null;let Q=(await $.text()).match(/loc=([A-Z]{2})/);return Q?Q[1]:null}catch($){return null}}var I$=null,w$={NPM:"https://registry.npmjs.org",TAOBAO:"https://registry.npmmirror.com",TENCENT:"https://mirrors.cloud.tencent.com/npm/"};async function e(){if(I$)return I$;let $=await z6(),q=[];if($==="CN")q=[w$.TAOBAO,w$.TENCENT,w$.NPM];else q=[w$.NPM,w$.TAOBAO];try{let Q=await O6(q,2000);return I$=Q,Q}catch(Q){return q[0]}}w();var w6="bun-versions.json",x6=3600000;async function k6(){if(D)return[...Z$];let $=L6(_,w6);try{if(await G($)){let Y=await f($),K=JSON.parse(Y);if(Date.now()-K.timestamp<x6&&Array.isArray(K.versions))return K.versions}}catch(Y){}let q=await e(),Q=[q];if(q!=="https://registry.npmjs.org")Q.push("https://registry.npmjs.org");let J=null;for(let Y of Q){let K=`${Y.replace(/\/$/,"")}/bun`;try{let b=await a(K,{headers:{"User-Agent":X$,Accept:"application/vnd.npm.install-v1+json"},timeout:1e4});if(!b.ok)throw Error(`Status ${b.status}`);let X=await b.json();if(!X.versions)throw Error("Invalid response (no versions)");let H=Object.keys(X.versions);try{await N(_),await V($,JSON.stringify({timestamp:Date.now(),versions:H}))}catch(z){}return H}catch(b){J=b}}throw J||Error("Failed to fetch versions from any registry.")}async function C6(){if(D)return[...Z$];return new Promise(($,q)=>{let Q=[];try{let J=Bun.spawn(["git","ls-remote","--tags","https://github.com/oven-sh/bun.git"],{stdout:"pipe",stderr:"pipe"}),Y=setTimeout(()=>{J.kill(),q(Error("Git operation timed out"))},1e4);new Response(J.stdout).text().then((b)=>{clearTimeout(Y);let X=b.split(`
6
6
  `);for(let H of X){let z=H.match(/refs\/tags\/bun-v?(\d+\.\d+\.\d+.*)$/);if(z)Q.push(z[1])}$(Q)}).catch((b)=>{clearTimeout(Y),q(b)})}catch(J){q(Error(`Failed to run git: ${J.message}`))}})}async function b1(){if(D)return[...Z$];try{let q=(await k6()).filter((Q)=>m(Q)).map((Q)=>({v:Q,parsed:J$(Q)}));return q.sort((Q,J)=>T$(J.parsed,Q.parsed)),q.map((Q)=>Q.v)}catch($){try{let q=await C6();if(q.length>0)return Array.from(new Set(q.filter((J)=>m(J)))).sort(K$);throw Error("No versions found via Git")}catch(q){throw Error(`Failed to fetch versions. NPM: ${$.message}. Git: ${q.message}`)}}}async function X1($){if(D)return Z$.includes($)||$==="latest";let q=await e(),Q=$.replace(/^v/,""),J=`${q}/bun/${Q}`,Y=B==="win32"?"curl.exe":"curl",K=async()=>{try{return await d([Y,"-I","-f","-m","5","-s",J],{stdout:"ignore",stderr:"ignore"}),!0}catch(X){return!1}},b=new Promise((X)=>setTimeout(()=>X(!1),1e4));return Promise.race([K(),b])}async function G1(){if(D)return{latest:"1.1.20"};let q=`${await e()}/-/package/bun/dist-tags`;try{let Q=await a(q,{headers:{"User-Agent":X$},timeout:5000});if(Q.ok)return await Q.json()}catch(Q){}return{}}async function H1($){let q=U($);if(!m(q))return console.error(Z.red(`Invalid version provided to findBunDownloadUrl: ${$}`)),null;if(D)return{url:`https://example.com/${f$(q)}`,foundVersion:q};let Y=Y1(B==="win32"?"win32":B,z$==="arm64"?"arm64":"x64");if(!Y)throw Error(`Unsupported platform/arch for NPM download: ${B}-${z$}`);let K="";if(process.env.BVM_REGISTRY)K=process.env.BVM_REGISTRY;else if(process.env.BVM_DOWNLOAD_MIRROR)K=process.env.BVM_DOWNLOAD_MIRROR;else K=await e();let b=q.replace(/^v/,"");return{url:Z1(Y,b,K),foundVersion:q}}async function F$(){try{let q=(await e()).replace(/\/$/,""),Q=await a(`${q}/-/package/bvm-core/dist-tags`,{headers:{"User-Agent":X$},timeout:5000});if(!Q.ok)return null;let Y=(await Q.json()).latest;if(!Y)return null;let K=await a(`${q}/bvm-core/${Y}`,{headers:{"User-Agent":X$},timeout:5000});if(K.ok){let b=await K.json();return{version:Y,tarball:b.dist.tarball,integrity:b.dist.integrity,shasum:b.dist.shasum}}}catch($){}return null}k();F();import{spawn as F6}from"child_process";async function W1($,q){if($.endsWith(".zip"))if(B==="win32")await d(["powershell","-Command",`Expand-Archive -Path "${$}" -DestinationPath "${q}" -Force`],{stdout:"ignore",stderr:"inherit"});else await d(["unzip","-o","-q",$,"-d",q],{stdout:"ignore",stderr:"inherit"});else if($.endsWith(".tar.gz")||$.endsWith(".tgz"))await new Promise((Q,J)=>{let Y=F6("tar",["-xzf",$,"-C",q],{stdio:"inherit",shell:!1});Y.on("close",(K)=>{if(K===0)Q();else J(Error(`tar exited with code ${K}`))}),Y.on("error",(K)=>J(K))});else throw Error(`Unsupported archive format: ${$}`)}import{chmod as m$}from"fs/promises";w();F();k();import{join as T,dirname as R6,delimiter as D6}from"path";import{homedir as $$}from"os";import{chmod as x$}from"fs/promises";var E$=`#!/bin/bash
7
7
 
8
8
  # bvm-init.sh: Initializes bvm default version on shell startup
package/install.ps1 CHANGED
@@ -46,7 +46,7 @@ function Detect-NetworkZone {
46
46
  $BVM_REGION = Detect-NetworkZone
47
47
  $REGISTRY = if ($BVM_REGION -eq "cn") { "registry.npmmirror.com" } else { "registry.npmjs.org" }
48
48
 
49
- $DEFAULT_BVM_VER = "v1.1.16"
49
+ $DEFAULT_BVM_VER = "v1.1.18"
50
50
  $BVM_VER = if ($env:BVM_INSTALL_VERSION) { $env:BVM_INSTALL_VERSION } else { "" }
51
51
  if (-not $BVM_VER) {
52
52
  try {
@@ -165,7 +165,7 @@ foreach ($name in $CMD_NAMES) {
165
165
  $RawPath = [Environment]::GetEnvironmentVariable("Path", "User")
166
166
  $PathList = if ($RawPath) { $RawPath.Split(';') } else { @() }
167
167
  $NewPathList = @()
168
- foreach ($p in $PathList) { if ($p -notlike "*\.bvm\shims*" -and $p -notlike "*\.bvm\bin*" -and [string]::IsNotNullOrEmpty($p)) { $NewPathList += $p } }
168
+ foreach ($p in $PathList) { if ($p -notlike "*\.bvm\shims*" -and $p -notlike "*\.bvm\bin*" -and -not [string]::IsNullOrEmpty($p)) { $NewPathList += $p } }
169
169
  $FinalPath = "$BVM_SHIMS_DIR;$BVM_BIN_DIR;" + ($NewPathList -join ';')
170
170
  [Environment]::SetEnvironmentVariable("Path", $FinalPath, "User")
171
171
  $env:Path = "$BVM_SHIMS_DIR;$BVM_BIN_DIR;$env:Path"
package/install.sh CHANGED
@@ -2,7 +2,7 @@
2
2
  set -e
3
3
 
4
4
  # --- Configuration ---
5
- DEFAULT_BVM_VERSION="v1.1.16" # Fallback
5
+ DEFAULT_BVM_VERSION="v1.1.18" # Fallback
6
6
  FALLBACK_BUN_VERSION="1.3.6"
7
7
  BVM_SRC_VERSION="${BVM_INSTALL_VERSION}" # If empty, will resolve dynamically
8
8
 
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "bvm-core",
3
- "version": "1.1.16",
3
+ "version": "1.1.18",
4
4
  "description": "The native version manager for Bun. Cross-platform, shell-agnostic, and zero-dependency.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
7
- "bvm": "dist/index.js"
7
+ "bvm": "bin/bvm-npm.js"
8
8
  },
9
9
  "publishConfig": {
10
10
  "access": "public"
@@ -40,6 +40,7 @@
40
40
  "dist/index.js",
41
41
  "dist/bvm-shim.sh",
42
42
  "dist/bvm-shim.js",
43
+ "bin/bvm-npm.js",
43
44
  "scripts/postinstall.js",
44
45
  "install.sh",
45
46
  "install.ps1",
@@ -71,10 +72,10 @@
71
72
  "typescript": "^5"
72
73
  },
73
74
  "bvm_fingerprints": {
74
- "cli": "bcea0910bcd102d19d4d25d2611eff4a",
75
+ "cli": "302bb3dee56d88ad9c506d2d3638c07c",
75
76
  "shim_win": "c6f43afddfb205e130633fe00ee860d8",
76
77
  "shim_unix": "8aa89a0324b52c9c81b96c0c03afe36c",
77
- "install_sh": "b1de24746d010effa2df0cfc4becbd73",
78
- "install_ps1": "96e267d6216f6153b58902e87983d52f"
78
+ "install_sh": "2f4a13e4e0837640678c6d5178eae4ed",
79
+ "install_ps1": "b9df4fa27f183bcfbb1342bc1f530ded"
79
80
  }
80
81
  }
@@ -1,13 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * BVM Post-install Script
5
- * Standardized Installation Workflow:
6
- * 1. Init & Deploy Assets
7
- * 2. Detect System Bun
8
- * 3. Smoke Test System Bun
9
- * 4. Configure Runtime (Reuse or Download)
10
- * 5. Setup Shell Environment
4
+ * BVM Post-install Script (Smart Edition)
5
+ * Optimized for Speed: Priority given to npmmirror (China) and npmjs (Official).
11
6
  */
12
7
 
13
8
  const fs = require('fs');
@@ -15,285 +10,194 @@ const path = require('path');
15
10
  const { spawnSync } = require('child_process');
16
11
  const os = require('os');
17
12
 
18
- // --- Constants & Config ---
19
- const ASCII_LOGO = `
20
- \x1b[36m
21
- __________
22
- \______ \__ _______
23
- | | _| \/ / \
24
- | | \\ / Y Y \
25
- |______ / \_/|__|_| /
26
- \/ \/
27
- \x1b[0m`;
28
-
13
+ const IS_WINDOWS = process.platform === 'win32';
29
14
  const HOME = process.env.HOME || os.homedir();
30
15
  const BVM_DIR = process.env.BVM_DIR || path.join(HOME, '.bvm');
31
16
  const BVM_SRC_DIR = path.join(BVM_DIR, 'src');
32
17
  const BVM_BIN_DIR = path.join(BVM_DIR, 'bin');
33
18
  const PKG_ROOT = path.resolve(__dirname, '..');
34
19
  const DIST_DIR = path.join(PKG_ROOT, 'dist');
35
- const LOG_FILE = path.join(os.tmpdir(), 'bvm-install.log');
36
20
 
37
- // --- Helpers ---
38
- function log(msg) {
39
- const line = `[bvm] ${msg}`;
40
- process.stdout.write(line + '\n');
41
- try { fs.appendFileSync(LOG_FILE, line + '\n'); } catch(e) {}
42
- }
21
+ function log(msg) { console.log(`[bvm] ${msg}`); }
22
+ function error(msg) { console.error(`\x1b[31m[bvm] ERROR: ${msg}\x1b[0m`); }
43
23
 
44
- function error(msg) {
45
- const line = `[bvm] ERROR: ${msg}`;
46
- process.stderr.write(`\x1b[31m${line}\x1b[0m\n`);
47
- try { fs.appendFileSync(LOG_FILE, line + '\n'); } catch(e) {}
24
+ function run(cmd, args, opts = {}) {
25
+ return spawnSync(cmd, args, Object.assign({ encoding: 'utf-8', shell: IS_WINDOWS }, opts));
48
26
  }
49
27
 
50
- function ensureDir(dir) {
51
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
52
- }
53
-
54
- function ensureVersionPrefix(version) {
55
- if (!version) return 'v0.0.0';
56
- return version.startsWith('v') ? version : `v${version}`;
57
- }
58
-
59
- // --- Core Logic ---
60
-
61
- function main() {
62
- // Force a leading newline to break from npm's output
63
- process.stdout.write('\n' + ASCII_LOGO + '\n');
64
- log('Starting BVM post-install setup...');
65
-
66
- // 1. Conflict Detection
67
- const BVM_EXEC = path.join(BVM_BIN_DIR, 'bvm');
68
- if (fs.existsSync(BVM_EXEC) && !process.env.BVM_FORCE_INSTALL) {
69
- try {
70
- const content = fs.readFileSync(BVM_EXEC, 'utf-8');
71
- if (!content.includes('BVM_INSTALL_SOURCE="npm"')) {
72
- log('Native BVM installation detected. Proceeding to update assets...');
73
- }
74
- } catch (e) {}
75
- }
76
-
77
- // 2. Deploy Assets
78
- log(`Deploying to ${BVM_DIR}...`);
79
- ensureDir(BVM_SRC_DIR);
80
- ensureDir(BVM_BIN_DIR);
81
-
82
- const filesToCopy = [
83
- { src: 'index.js', destDir: BVM_SRC_DIR, name: 'index.js' },
84
- { src: 'bvm-shim.sh', destDir: BVM_BIN_DIR, name: 'bvm-shim.sh', mode: 0o755 },
85
- { src: 'bvm-shim.js', destDir: BVM_BIN_DIR, name: 'bvm-shim.js' }
86
- ];
87
-
88
- for (const file of filesToCopy) {
89
- const srcPath = path.join(DIST_DIR, file.src);
90
- const destPath = path.join(file.destDir, file.name);
91
-
92
- if (fs.existsSync(srcPath)) {
93
- fs.copyFileSync(srcPath, destPath);
94
- if (file.mode) fs.chmodSync(destPath, file.mode);
95
- } else {
96
- error(`Source file not found: ${srcPath}`);
28
+ function findBinary(dir, name) {
29
+ const files = fs.readdirSync(dir);
30
+ for (const file of files) {
31
+ const full = path.join(dir, file);
32
+ const stat = fs.statSync(full);
33
+ if (stat.isDirectory()) {
34
+ const res = findBinary(full, name);
35
+ if (res) return res;
36
+ } else if (file === name || (IS_WINDOWS && file === name + '.exe')) {
37
+ return full;
97
38
  }
98
39
  }
40
+ return null;
41
+ }
99
42
 
100
- // 3. Detect & Configure Runtime
101
- let activeRuntimePath = null;
102
- let activeVersion = null;
103
-
104
- const systemBun = detectSystemBun();
105
- let systemBunCompatible = false;
106
-
107
- if (systemBun) {
108
- log(`Found system Bun at ${systemBun.path} (v${systemBun.version})`);
109
- if (runSmokeTest(systemBun.path)) {
110
- log('Smoke Test passed: System Bun is compatible.');
111
- systemBunCompatible = true;
112
- activeVersion = ensureVersionPrefix(systemBun.version);
113
- const versionDir = path.join(BVM_DIR, 'versions', activeVersion);
114
- registerBunVersion(systemBun.path, versionDir);
115
- activeRuntimePath = versionDir;
116
- } else {
117
- log('Smoke Test failed: System Bun cannot run BVM core.');
118
- const versionDir = path.join(BVM_DIR, 'versions', ensureVersionPrefix(systemBun.version));
119
- registerBunVersion(systemBun.path, versionDir);
120
- }
121
- } else {
122
- log('No system Bun detected.');
123
- }
43
+ /**
44
+ * Prioritized registry sniffing for speed and reliability
45
+ */
46
+ function getPackageInfo(pkgName) {
47
+ const registries = [
48
+ 'https://registry.npmmirror.com', // High speed for China
49
+ 'https://registry.npmjs.org' // Official reliability
50
+ ];
124
51
 
125
- if (!systemBunCompatible) {
126
- log('Downloading compatible Bun runtime...');
127
- try {
128
- const installedVersion = downloadAndInstallRuntime(BVM_DIR);
129
- if (installedVersion) {
130
- activeVersion = installedVersion;
131
- activeRuntimePath = path.join(BVM_DIR, 'versions', activeVersion);
132
- } else {
133
- throw new Error('Download failed');
134
- }
135
- } catch (e) {
136
- error(`Failed to download runtime: ${e.message}`);
52
+ for (const registry of registries) {
53
+ log(`Checking ${pkgName} info from ${registry}...`);
54
+ const res = run('npm', ['info', pkgName, '--json', '--registry', registry]);
55
+ if (res.status === 0 && res.stdout) {
56
+ try {
57
+ const info = JSON.parse(res.stdout);
58
+ if (info.dist && info.dist.tarball) {
59
+ return { url: info.dist.tarball, version: info.version };
60
+ }
61
+ } catch (e) {}
137
62
  }
138
63
  }
64
+ return null;
65
+ }
139
66
 
140
- // 4. Link Runtime & Default Alias
141
- if (activeRuntimePath && activeVersion) {
142
- const legacyCurrentLink = path.join(BVM_DIR, 'current');
143
- const privateRuntimeLink = path.join(BVM_DIR, 'runtime', 'current');
144
- ensureDir(path.join(BVM_DIR, 'runtime'));
145
-
146
- try { if (fs.existsSync(privateRuntimeLink)) fs.unlinkSync(privateRuntimeLink); } catch(e) {}
147
- try { fs.symlinkSync(activeRuntimePath, privateRuntimeLink, 'dir'); } catch(e) {}
148
-
149
- try { if (fs.existsSync(legacyCurrentLink)) fs.unlinkSync(legacyCurrentLink); } catch(e) {}
150
- try { fs.symlinkSync(activeRuntimePath, legacyCurrentLink, 'dir'); } catch(e) {}
151
-
152
- const aliasDir = path.join(BVM_DIR, 'aliases');
153
- ensureDir(aliasDir);
154
- fs.writeFileSync(path.join(aliasDir, 'default'), activeVersion);
155
- log(`Active runtime set to ${activeVersion}`);
156
- }
157
-
158
- // 5. Create BVM Wrapper
159
- createBvmWrapper();
67
+ function setupRuntimeLink(verDir, ver) {
68
+ const runtimeDir = path.join(BVM_DIR, 'runtime');
69
+ if (!fs.existsSync(runtimeDir)) fs.mkdirSync(runtimeDir, { recursive: true });
70
+ const currentLink = path.join(runtimeDir, 'current');
71
+ const userCurrentLink = path.join(BVM_DIR, 'current');
72
+ const linkType = IS_WINDOWS ? 'junction' : 'dir';
160
73
 
161
- // 6. Configure Shell (bvm setup)
162
- log('Configuring shell environment...');
163
- spawnSync(path.join(BVM_BIN_DIR, 'bvm'), ['setup', '--silent'], {
164
- stdio: 'inherit',
165
- env: Object.assign({}, process.env, { BVM_DIR })
74
+ [currentLink, userCurrentLink].forEach(link => {
75
+ try { if (fs.existsSync(link)) fs.unlinkSync(link); } catch(e) {}
76
+ try { fs.symlinkSync(verDir, link, linkType); } catch(e) {}
166
77
  });
167
-
168
- // 7. Final Success Message
169
- printSuccessMessage(!!systemBun);
78
+
79
+ const aliasDir = path.join(BVM_DIR, 'aliases');
80
+ if (!fs.existsSync(aliasDir)) fs.mkdirSync(aliasDir, { recursive: true });
81
+ fs.writeFileSync(path.join(aliasDir, 'default'), ver);
170
82
  }
171
83
 
172
- // --- Implementation Details ---
173
-
174
- function detectSystemBun() {
175
- const which = spawnSync('which', ['bun'], { encoding: 'utf-8' });
176
- if (which.status === 0 && which.stdout) {
177
- const binPath = which.stdout.trim();
178
- const ver = getBunVersion(binPath);
179
- if (ver) return { path: binPath, version: ver };
84
+ function downloadAndInstall() {
85
+ const platform = process.platform === 'win32' ? 'windows' : process.platform;
86
+ const arch = process.arch === 'arm64' ? 'aarch64' : 'x64';
87
+ const pkgName = `@oven/bun-${platform}-${arch}`;
88
+
89
+ const info = getPackageInfo(pkgName);
90
+ if (!info) {
91
+ error(`Failed to locate ${pkgName} on both npmmirror and npmjs.`);
92
+ return false;
180
93
  }
181
- return null;
182
- }
183
94
 
184
- function getBunVersion(binPath) {
185
- try {
186
- const proc = spawnSync(binPath, ['--version'], { encoding: 'utf-8' });
187
- if (proc.status === 0) return proc.stdout.trim().replace(/^v/, '');
188
- } catch (e) {}
189
- return null;
190
- }
191
-
192
- function runSmokeTest(binPath) {
193
- try {
194
- const indexJs = path.join(BVM_SRC_DIR, 'index.js');
195
- const proc = spawnSync(binPath, [indexJs, '--version'], {
196
- encoding: 'utf-8',
197
- env: Object.assign({}, process.env, { BVM_DIR })
198
- });
199
- return proc.status === 0;
200
- } catch (e) {
95
+ const { url, version } = info;
96
+ const tempTgz = path.join(os.tmpdir(), `bvm-bun-${Date.now()}.tgz`);
97
+ log(`Downloading Bun v${version} from: ${url}`);
98
+
99
+ const dl = run('curl', ['-L', '-s', '-o', tempTgz, url]);
100
+ if (dl.status !== 0) {
101
+ error('Download failed. Please check your internet connection.');
201
102
  return false;
202
103
  }
104
+
105
+ const extractDir = path.join(os.tmpdir(), `bvm-ext-${Date.now()}`);
106
+ fs.mkdirSync(extractDir, { recursive: true });
107
+ const ex = run('tar', ['-xzf', tempTgz, '-C', extractDir]);
108
+ if (ex.status !== 0) { error('Extraction failed.'); return false; }
109
+
110
+ const exeName = IS_WINDOWS ? 'bun.exe' : 'bun';
111
+ const foundBin = findBinary(extractDir, exeName);
112
+
113
+ if (foundBin) {
114
+ const verName = 'v' + version.replace(/^v/, '');
115
+ const verDir = path.join(BVM_DIR, 'versions', verName);
116
+ const binDir = path.join(verDir, 'bin');
117
+ if (!fs.existsSync(binDir)) fs.mkdirSync(binDir, { recursive: true });
118
+ fs.copyFileSync(foundBin, path.join(binDir, IS_WINDOWS ? 'bun.exe' : 'bun'));
119
+ setupRuntimeLink(verDir, verName);
120
+ log(`Successfully installed Bun ${verName} as BVM runtime.`);
121
+ return true;
122
+ }
123
+ return false;
203
124
  }
204
125
 
205
- function registerBunVersion(sourceBin, targetDir) {
206
- const targetBinDir = path.join(targetDir, 'bin');
207
- ensureDir(targetBinDir);
208
- const targetBin = path.join(targetBinDir, 'bun');
209
- if (!fs.existsSync(targetBin)) {
210
- fs.copyFileSync(sourceBin, targetBin);
211
- fs.chmodSync(targetBin, 0o755);
126
+ function createWrappers() {
127
+ const bvmBin = path.join(BVM_BIN_DIR, IS_WINDOWS ? 'bvm.cmd' : 'bvm');
128
+ const bvmDirWin = BVM_DIR.replace(/\//g, '\\');
129
+ const entryPath = path.join(PKG_ROOT, 'bin', 'bvm-npm.js');
130
+
131
+ if (IS_WINDOWS) {
132
+ const content = `@echo off\r\nset "BVM_DIR=${bvmDirWin}"\r\nnode "${entryPath}" %*`;
133
+ fs.writeFileSync(bvmBin, content);
134
+ fs.writeFileSync(path.join(BVM_BIN_DIR, 'bvm'), '# BVM Windows Shim\n');
135
+ } else {
136
+ const content = `#!/bin/bash\nexport BVM_DIR="${BVM_DIR}"\nnode "${entryPath}" "$@"`;
137
+ fs.writeFileSync(bvmBin, content);
138
+ fs.chmodSync(bvmBin, 0o755);
212
139
  }
213
140
  }
214
141
 
215
- function downloadAndInstallRuntime(bvmDir) {
216
- const platform = process.platform;
217
- const arch = process.arch;
142
+ function main() {
143
+ log('Starting BVM post-install setup...');
218
144
 
219
- let pkgPlatform = '';
220
- if (platform === 'darwin') pkgPlatform = 'darwin';
221
- else if (platform === 'linux') pkgPlatform = 'linux';
222
- else if (platform === 'win32') pkgPlatform = 'windows';
223
- else return null;
224
-
225
- const pkgArch = arch === 'arm64' ? 'aarch64' : 'x64';
226
- const bunVer = '1.1.13';
227
- const vBunVer = ensureVersionPrefix(bunVer);
228
- const registry = 'https://registry.npmmirror.com';
229
- const pkgName = `@oven/bun-${pkgPlatform}-${pkgArch}`;
230
- const url = `${registry}/${pkgName}/-/${pkgName.split('/').pop()}-${bunVer}.tgz`;
145
+ [BVM_SRC_DIR, BVM_BIN_DIR].forEach(d => { if (!fs.existsSync(d)) fs.mkdirSync(d, { recursive: true }); });
146
+ const assets = [
147
+ { src: 'index.js', dest: path.join(BVM_SRC_DIR, 'index.js') },
148
+ { src: 'bvm-shim.sh', dest: path.join(BVM_BIN_DIR, 'bvm-shim.sh') },
149
+ { src: 'bvm-shim.js', dest: path.join(BVM_BIN_DIR, 'bvm-shim.js') }
150
+ ];
151
+ assets.forEach(a => {
152
+ const srcPath = path.join(DIST_DIR, a.src);
153
+ if (fs.existsSync(srcPath)) {
154
+ fs.copyFileSync(srcPath, a.dest);
155
+ if (!a.dest.endsWith('.js')) fs.chmodSync(a.dest, 0o755);
156
+ }
157
+ });
231
158
 
232
- const tempTgz = path.join(os.tmpdir(), `bun-${Date.now()}.tgz`);
233
- const extractDir = path.join(os.tmpdir(), `bun-ext-${Date.now()}`);
159
+ let hasValidBun = false;
160
+ const whichCmd = IS_WINDOWS ? 'where' : 'which';
161
+ const checkBun = run(whichCmd, ['bun']);
162
+
163
+ if (checkBun.status === 0 && checkBun.stdout) {
164
+ const binPath = checkBun.stdout.trim().split('\n')[0].trim();
165
+ log(`System Bun detected at: ${binPath}. Running Smoke Test...`);
166
+
167
+ const verRes = run(binPath, ['--version']);
168
+ const ver = 'v' + (verRes.stdout || '1.3.6').trim().replace(/^v/, '');
169
+ const verDir = path.join(BVM_DIR, 'versions', ver);
170
+
171
+ // Register anyway to preserve user's version
172
+ if (!fs.existsSync(path.join(verDir, 'bin'))) {
173
+ fs.mkdirSync(path.join(verDir, 'bin'), { recursive: true });
174
+ fs.copyFileSync(binPath, path.join(verDir, 'bin', IS_WINDOWS ? 'bun.exe' : 'bun'));
175
+ }
234
176
 
235
- try {
236
- log(`Downloading Bun ${bunVer}...`);
237
- spawnSync('curl', ['-L', '-s', '-o', tempTgz, url]);
238
- ensureDir(extractDir);
239
- spawnSync('tar', ['-xzf', tempTgz, '-C', extractDir]);
240
- const exeName = platform === 'win32' ? 'bun.exe' : 'bun';
241
- const foundBinProc = spawnSync('find', [extractDir, '-name', exeName], { encoding: 'utf-8' });
242
- const binPath = foundBinProc.stdout ? foundBinProc.stdout.trim().split('\n')[0] : null;
243
- if (binPath && fs.existsSync(binPath)) {
244
- const versionDir = path.join(bvmDir, 'versions', vBunVer);
245
- registerBunVersion(binPath, versionDir);
246
- return vBunVer;
177
+ const test = run(binPath, [path.join(BVM_SRC_DIR, 'index.js'), '--version'], { env: { BVM_DIR } });
178
+ if (test.status === 0) {
179
+ log('Smoke test passed. Reusing system Bun.');
180
+ setupRuntimeLink(verDir, ver);
181
+ hasValidBun = true;
182
+ } else {
183
+ log('Smoke test failed. System Bun is incompatible with BVM core.');
247
184
  }
248
- } catch (e) {
249
- error(`Download failed: ${e.message}`);
250
- } finally {
251
- try {
252
- if (fs.existsSync(tempTgz)) fs.unlinkSync(tempTgz);
253
- if (fs.existsSync(extractDir)) fs.rmSync(extractDir, { recursive: true });
254
- } catch(e) {}
255
185
  }
256
- return null;
257
- }
258
-
259
- function createBvmWrapper() {
260
- const wrapperContent = `#!/bin/bash
261
- export BVM_DIR="${BVM_DIR}"
262
- export BVM_INSTALL_SOURCE="npm"
263
- if [ -x "\${BVM_DIR}/runtime/current/bin/bun" ]; then
264
- exec "\${BVM_DIR}/runtime/current/bin/bun" "\${BVM_DIR}/src/index.js" "$@"
265
- elif command -v bun >/dev/null 2>&1; then
266
- exec bun "\${BVM_DIR}/src/index.js" "$@"
267
- else
268
- echo "Error: BVM requires Bun. Please ensure setup completed correctly."
269
- exit 1
270
- fi
271
- `;
272
- const bvmExec = path.join(BVM_BIN_DIR, 'bvm');
273
- fs.writeFileSync(bvmExec, wrapperContent);
274
- fs.chmodSync(bvmExec, 0o755);
275
- }
276
-
277
- function printSuccessMessage(hasSystemBun) {
278
- const shell = process.env.SHELL || '';
279
- let configFile = '~/.zshrc';
280
- if (shell.includes('bash')) configFile = '~/.bashrc';
281
- else if (shell.includes('fish')) configFile = '~/.config/fish/config.fish';
282
186
 
283
- process.stdout.write('\n\x1b[32m\x1b[1m🎉 BVM (Bun Version Manager) installed successfully!\x1b[0m\n');
284
- if (hasSystemBun) {
285
- process.stdout.write('\x1b[33mBVM has been added to the END of your PATH configuration to ensure priority.\x1b[0m\n');
286
- process.stdout.write('\x1b[33mYour existing Bun installations were NOT deleted and will coexist.\x1b[0m\n');
287
- } else {
288
- process.stdout.write('\x1b[33mBVM has installed a compatible Bun runtime for you.\x1b[0m\n');
187
+ if (!hasValidBun) {
188
+ log('No compatible system Bun found. Performing smart download...');
189
+ hasValidBun = downloadAndInstall();
289
190
  }
290
- process.stdout.write('\n\x1b[1mTo finalize the setup, please restart your terminal or run:\x1b[0m\n');
291
- process.stdout.write(` source ${configFile}\n\n`);
191
+
192
+ createWrappers();
193
+ const bvmBin = path.join(BVM_BIN_DIR, IS_WINDOWS ? 'bvm.cmd' : 'bvm');
194
+ run(bvmBin, ['setup', '--silent'], { env: { BVM_DIR } });
195
+ log('🎉 BVM initialized successfully.');
292
196
  }
293
197
 
294
- // Execute
295
198
  try {
296
199
  main();
297
200
  } catch (e) {
298
201
  error(e.message);
202
+ process.exit(1);
299
203
  }