Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ npm install -g bvm-core@latest
- **🚀 Zero Latency**: Shim-based design ensures ~0ms shell startup overhead.
- **🛡️ Bunker Architecture**: BVM manages its own isolated Bun runtime, ensuring stability even if your system Bun is broken or missing.
- **🛡️ Atomic Isolation**: Each Bun version has its own global package directory. No more conflicts.
- **🌏 Smart Mirroring**: Automatically detects your region and picks the fastest registry (npmmirror/npmjs).
- **🌏 Smart Mirroring & Auto-Config**: Automatically selects the fastest registry for downloads AND auto-configures `bunfig.toml` for instant, "no-magic" `bun install` speeds.
- **📦 Zero Dependency**: BVM bootstraps itself. No pre-requisites required (it can reuse your system Bun or download its own).

---
Expand Down
2 changes: 1 addition & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ npm install -g bvm-core@latest
- **🚀 零延迟启动**:采用 Shim 架构设计,Shell 启动耗时约为 0ms。
- **🛡️ 地堡架构 (Bunker Architecture)**:BVM 拥有独立的私有运行环境,即使卸载系统 Bun,BVM 依然能稳定工作并自愈。
- **🛡️ 原子化隔离**:每个 Bun 版本拥有独立的全局包目录,彻底告别依赖冲突。
- **🌏 智能镜像加速**:基于 GeoIP 自动识别地理位置,智能选择 NPM 官方源或淘宝镜像,下载飞快
- **🌏 零魔法全网通**:不仅安装 Bun 时飞快,更能自动检测并配置最佳镜像源(如 npmmirror),确保后续的 `bun install` 开箱即用,无需手动配置
- **📦 零依赖自举**:BVM 自身能够实现环境自举。安装无需预设环境(它会自动复用系统 Bun 或按需下载)。

---
Expand Down
2 changes: 1 addition & 1 deletion conductor/product.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ BVM (Bun Version Manager) 是一个使用 Bun 开发、专为 Bun 设计的原
- **地堡架构 (Bunker Architecture)**:通过双重链接机制维护一个绝对隔离的私有运行时(Private Host),确保 BVM 自身的运行与用户环境完全解耦,即便系统 Bun 损坏或缺失,BVM 依然能自愈并稳定工作。
- **混合路径路由**:兼顾全局模式的 OS 原生性能(物理软链接)和项目模式的灵活性(递归搜索 .bvmrc)。
- **环境级原子隔离**:通过直接注入 `BUN_INSTALL`,确保每个版本的全局包物理隔离,杜绝冲突。
- **全球化高速分发**:基于 IP 地理位置与实时竞速策略(Race Strategy),智能选择官方、淘宝或腾讯镜像源,显著提升全球下载速度
- **零魔法全网通 (No-Magic Connectivity)**:不仅安装 Bun 时智能竞速,更能在安装后自动检测并配置最佳 Registry(如自动切换 npmmirror),确保用户后续的 `bun install` 在任何网络环境下都具备“开箱即用”的极速体验
- **智能自愈升级**:基于 NPM Registry 的原子化 Tarball 更新机制,彻底摆脱 CDN 依赖,确保管理器升级的绝对一致性与稳定性。配合组件指纹校验(可选),确保管理器始终处于最优状态。
- **智能工具链对齐**:自动处理 `yarn`、`npm`、`pnpm` 等兼容性链接。

Expand Down
7 changes: 6 additions & 1 deletion conductor/tracks.md
Original file line number Diff line number Diff line change
Expand Up @@ -397,4 +397,9 @@ This file tracks all major tracks for the project. Each track has its own detail



\n---\n\n- [~] **Track: Release BVM v1.1.32**\n*Link: [./conductor/tracks/release_v1_1_32_20260126/](./conductor/tracks/release_v1_1_32_20260126/)*
\n---\n\n- [~] **Track: Release BVM v1.1.35**\n*Link: [./conductor/tracks/release_v1_1_32_20260126/](./conductor/tracks/release_v1_1_32_20260126/)*

---

- [x] **Track: Smart Registry Auto-Configuration (No-Magic Bun Install)**
*Link: [./conductor/tracks/smart_registry_auto_config_20260127/](./conductor/tracks/smart_registry_auto_config_20260127/)*
28 changes: 16 additions & 12 deletions conductor/tracks/release_v1_1_32_20260126/plan.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
# Implementation Plan: Release BVM v1.1.32
# Implementation Plan: Release BVM v1.1.35

## Phase 1: Update Version
- [ ] Task: Bump version to 1.1.32
- [ ] Update `package.json`.
- [ ] Run `sync-runtime`.
- [ ] Task: Conductor - User Manual Verification 'Phase 1'
- [~] Task: Bump version to 1.1.35
- [x] Update `package.json`.
- [ ] Run `sync-runtime` to update install scripts.
- [ ] Verify `install.sh` and `install.ps1` contain `DEFAULT_BVM_VERSION="v1.1.35"`.

## Phase 2: Build & Verify
- [ ] Task: Build
- [ ] Run `npm run build`.
- [ ] Run `check-integrity`.
- [ ] Task: Conductor - User Manual Verification 'Phase 2'
- [ ] Task: Verification
- [ ] Run `test:e2e:npm` (Simulate install from local build).
- [ ] Verify `bin/bvm-npm.js` logic if changed (Shim logic).

## Phase 3: Finalize
- [ ] Task: Commit & Push
- [ ] Commit changes.
- [ ] Tag `v1.1.32`.
- [ ] Push to `test`.
- [ ] Task: Conductor - User Manual Verification 'Phase 3'
## Phase 3: Release
- [ ] Task: Commit & Tag
- [ ] Commit version bump.
- [ ] Create tag `v1.1.35`.
- [ ] Push to main.
- [ ] Task: NPM Publish
- [ ] Ensure clean registry state.
- [ ] Publish to NPM.
34 changes: 34 additions & 0 deletions conductor/tracks/smart_registry_auto_config_20260127/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Implementation Plan: Smart Registry Auto-Configuration

## Phase 1: Research & Core Logic
- [x] Task: Research `bunfig.toml`
- [x] Verified: Bun looks for global config at `~/.bunfig.toml` (and `$XDG_CONFIG_HOME/.bunfig.toml`).
- [x] Verified: Local config overrides global, and CLI flags override all.
- [x] Task: Implement Registry Speed Test (`src/utils/registry-check.ts`)
- [x] Create `RegistrySpeedTester` class.
- [x] Implement `raceRegistries()` using HEAD requests (timeout: 3s).
- [x] Define default registries: Official (`registry.npmjs.org`) vs Mirror (`registry.npmmirror.com`).
- [x] Task: Implement Config Manager (`src/utils/bunfig.ts`)
- [x] Implement `readGlobalBunfig()`.
- [x] Implement `writeGlobalBunfig()`.
- [x] Implement `setGlobalRegistry(url)`.

## Phase 2: Integration & CLI
- [x] Task: Integrate into `installBunVersion`
- [x] In `src/commands/install.ts`, trigger registry check after installation.
- [x] Logic: If `~/.bunfig.toml` doesn't exist or doesn't specify a registry -> Race -> Auto-write if Mirror wins.
- [x] UX: Show a friendly message: "⚡ Auto-configured global bunfig.toml..."
- [x] Task: Add `bvm config` command
- [x] `bvm config registry [url|auto]`
- [x] `bvm config ls` (Show current config)
- [x] Task: Add to `bvm doctor`
- [x] Update `src/commands/doctor.ts` to report current registry.

## Phase 3: Verification
- [x] Task: Unit Tests
- [x] Mock network requests to test race logic.
- [x] Mock file system to test TOML writing.
- [x] Task: Manual Verification
- [x] Verified `bvm config registry auto` works.
- [x] Verified `bvm doctor` shows config.
- [x] Verified `bvm install` triggers auto-config.
42 changes: 42 additions & 0 deletions conductor/tracks/smart_registry_auto_config_20260127/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Specification: Smart Registry Auto-Configuration (No-Magic Bun Install)

## 1. Background
Bun users in mainland China often face slow download speeds or connection timeouts when running `bun install` due to the official npm registry being blocked or throttled. "Magic" (VPNs/Proxies) is a high barrier to entry. BVM aims to lower this barrier by providing an out-of-the-box solution that automatically optimizes network settings based on the user's location.

## 2. Objectives
- **Zero Configuration**: Users should not need to manually edit `.npmrc` or `bunfig.toml` to get fast install speeds.
- **Smart Detection**: Automatically detect if the user is in a region that requires a mirror (e.g., Mainland China).
- **Non-Destructive**: Respect existing user configurations. Do not overwrite if a custom registry is already set.
- **Transparency**: Inform the user when auto-configuration is applied.

## 3. Implementation Details

### 3.1 Network Detection
- Implement a utility to detect network conditions.
- **Method**: Race specific URLs (e.g., `registry.npmjs.org` vs `registry.npmmirror.com`) or check public IP geolocation (via a lightweight API or DNS check).
- **Preference**: Use a race strategy (Head request latency) to determine the fastest registry, rather than strict IP geolocation, as this covers more edge cases (e.g., VPN users).

### 3.2 Configuration Management (`bunfig.toml`)
- **Target**: Global `bunfig.toml` (usually at `~/.bunfig.toml` or similar, depending on OS) or a per-version config if BVM isolates it.
- **Action**: If `registry.npmmirror.com` is significantly faster (> 2x) or the official registry is unreachable:
1. Check if `bunfig.toml` exists.
2. Read current config.
3. If `install.registry` is not set, set it to the fast mirror.
4. Write back the config.

### 3.3 Integration Points
- **Post-Install Hook**: After `bvm install <version>` succeeds, run the detection and auto-config logic.
- **Explicit Command**: Add `bvm doctor` or `bvm speed` to trigger this check manually.

### 3.4 User Experience
- **Output**:
```
[BVM] Detected slow connection to official registry.
[BVM] Auto-configured global bunfig.toml to use npmmirror.com for better performance.
[BVM] (You can revert this by editing ~/.bunfig.toml)
```

## 4. Constraints
- Must function correctly on Windows, macOS, and Linux.
- Must not block the installation process for too long (set reasonable timeouts).
- Must handle read-only file system errors gracefully.
2 changes: 1 addition & 1 deletion install.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function Detect-NetworkZone {
$BVM_REGION = Detect-NetworkZone
$REGISTRY = if ($BVM_REGION -eq "cn") { "registry.npmmirror.com" } else { "registry.npmjs.org" }

$DEFAULT_BVM_VER = "v1.1.33"
$DEFAULT_BVM_VER = "v1.1.35"
$BVM_VER = if ($env:BVM_INSTALL_VERSION) { $env:BVM_INSTALL_VERSION } else { "" }
if (-not $BVM_VER) {
try {
Expand Down
2 changes: 1 addition & 1 deletion install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
set -e

# --- Configuration ---
DEFAULT_BVM_VERSION="v1.1.33" # Fallback
DEFAULT_BVM_VERSION="v1.1.35" # Fallback
FALLBACK_BUN_VERSION="1.3.6"
BVM_SRC_VERSION="${BVM_INSTALL_VERSION}" # If empty, will resolve dynamically

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "bvm-core",
"version": "1.1.33",
"version": "1.1.35",
"description": "The native version manager for Bun. Cross-platform, shell-agnostic, and zero-dependency.",
"main": "dist/index.js",
"bin": {
Expand Down
40 changes: 40 additions & 0 deletions src/commands/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { BunfigManager } from '../utils/bunfig';
import { RegistrySpeedTester, REGISTRIES } from '../utils/registry-check';
import { colors } from '../utils/ui';
import { withSpinner } from '../command-runner';

export async function handleConfigCommand(args: string[]) {
const [subcommand, key, value] = args;
const bunfig = new BunfigManager();

if (subcommand === 'ls' || !subcommand) {
console.log(colors.bold('\nBVM Configuration (via ~/.bunfig.toml)'));
console.log(`Path: ${colors.dim(bunfig.getPath())}`);
const registry = bunfig.getRegistry();
console.log(`Registry: ${registry ? colors.green(registry) : colors.yellow('(not set, using Bun default)')}`);
return;
}

if (subcommand === 'registry') {
if (key === 'auto') {
await withSpinner('Racing registries for optimal speed...', async (spinner) => {
const tester = new RegistrySpeedTester();
const fastest = await tester.getFastestRegistry();
bunfig.setRegistry(fastest);
spinner.succeed(colors.green(`✓ Set registry to ${fastest}`));
});
} else if (key) {
bunfig.setRegistry(key);
console.log(colors.green(`✓ Registry set to ${key}`));
} else {
const current = bunfig.getRegistry();
console.log(`Current registry: ${current || 'default'}`);
}
return;
}

console.log(colors.red(`Unknown config command: ${subcommand}`));
console.log('Usage:');
console.log(' bvm config ls');
console.log(' bvm config registry <url|auto>');
}
11 changes: 11 additions & 0 deletions src/commands/doctor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,19 @@ import {
getActiveVersion, // New
} from '../utils';
import { withSpinner } from '../command-runner';
import { BunfigManager } from '../utils/bunfig';

interface DoctorReport {
currentVersion: string | null;
installedVersions: string[];
aliases: Array<{ name: string; target: string }>
env: Record<string, string | undefined>;
bunfig: { path: string; registry: string | null };
}

export async function doctor(): Promise<void> {
await withSpinner('Gathering BVM diagnostics...', async () => {
const bunfigManager = new BunfigManager();
const report: DoctorReport = {
currentVersion: (await getActiveVersion()).version,
installedVersions: await getInstalledVersions(),
Expand All @@ -41,6 +44,10 @@ export async function doctor(): Promise<void> {
BVM_TEST_MODE: process.env.BVM_TEST_MODE,
HOME: process.env.HOME || homedir(),
},
bunfig: {
path: bunfigManager.getPath(),
registry: bunfigManager.getRegistry(),
},
};

printReport(report);
Expand Down Expand Up @@ -94,6 +101,10 @@ function printReport(report: DoctorReport): void {
});
}

console.log(colors.bold('\nConfiguration'));
console.log(` Bunfig: ${colors.cyan(report.bunfig.path)}`);
console.log(` Registry: ${report.bunfig.registry ? colors.green(report.bunfig.registry) : colors.dim('default')}`);

console.log(colors.bold('\nAliases'));
if (report.aliases.length === 0) {
console.log(' (no aliases configured)');
Expand Down
25 changes: 25 additions & 0 deletions src/commands/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { runCommand } from '../helpers/process';
import { useBunVersion } from './use';
import { rehash } from './rehash';
import { rename, rm } from 'fs/promises';
import { RegistrySpeedTester, REGISTRIES } from '../utils/registry-check';
import { BunfigManager } from '../utils/bunfig';

async function safeRename(src: string, dest: string) {
try {
Expand Down Expand Up @@ -291,6 +293,29 @@ Debug: ${error.message}`)); // Visible if spinner fails

await rehash();

// --- Smart Registry Auto-Config ---
if (installedVersion && !IS_TEST_MODE) {
try {
const bunfig = new BunfigManager();
// Only auto-configure if no registry is explicitly set
if (!bunfig.getRegistry()) {
await withSpinner('Checking network speed for registry optimization...', async (spinner) => {
const tester = new RegistrySpeedTester(3000); // 3s timeout
const fastest = await tester.getFastestRegistry();

if (fastest === REGISTRIES.NPM_MIRROR) {
bunfig.setRegistry(REGISTRIES.NPM_MIRROR);
spinner.succeed(colors.green('⚡ Auto-configured global bunfig.toml to use npmmirror.com for faster installs.'));
} else {
spinner.stop(); // Official is fast enough or wins
}
}, { failMessage: 'Registry check failed (harmless)' });
}
} catch (e) {
// Ignore errors silently
}
}

// Final success messages (moved here to appear after Rehash log)
if (installedVersion) {
console.log(colors.cyan(`\n✓ Bun ${installedVersion} installed and active.`));
Expand Down
4 changes: 4 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { cacheCommand } from './commands/cache';
import { configureShell } from './commands/setup';
import { upgradeBvm } from './commands/upgrade';
import { doctor } from './commands/doctor';
import { handleConfigCommand } from './commands/config';
import { rehash } from './commands/rehash';
import { printCompletion } from './commands/completion';
import { colors } from './utils/ui';
Expand Down Expand Up @@ -217,6 +218,9 @@ async function main() {
app.command('upgrade', 'Upgrade bvm to the latest version', { aliases: ['self-update'] })
.action(async () => { await upgradeBvm(); });

app.command('config <subcommand>', 'Manage BVM configuration (registry)')
.action(async (args) => { await handleConfigCommand(args); });

app.command('doctor', 'Show diagnostics for Bun/BVM setup')
.action(async () => { await doctor(); });

Expand Down
71 changes: 71 additions & 0 deletions src/utils/bunfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { existsSync, readFileSync, writeFileSync } from 'fs';
import { homedir } from 'os';
import { join } from 'path';

export class BunfigManager {
private configPath: string;

constructor(customPath?: string) {
this.configPath = customPath || join(homedir(), '.bunfig.toml');
}

getPath(): string {
return this.configPath;
}

getRegistry(): string | null {
if (!existsSync(this.configPath)) return null;
const content = readFileSync(this.configPath, 'utf-8');

// Find [install] section
const installIndex = content.indexOf('[install]');
if (installIndex === -1) return null;

// Find end of section (next [ or EOF)
const nextSectionIndex = content.indexOf('[', installIndex + 1);
const sectionBody = content.substring(
installIndex,
nextSectionIndex === -1 ? undefined : nextSectionIndex
);

const match = sectionBody.match(/registry\s*=\s*\"(.*?)\"/);
return match ? match[1] : null;
}

setRegistry(url: string): void {
let content = '';
if (existsSync(this.configPath)) {
content = readFileSync(this.configPath, 'utf-8');
}

const installHeader = '[install]';
const installIndex = content.indexOf(installHeader);

if (installIndex === -1) {
// Append new section
const prefix = content.length > 0 && !content.endsWith('\n') ? '\n' : '';
content += `${prefix}${installHeader}\nregistry = \"${url}\"\n`;
} else {
// Section exists
const nextSectionIndex = content.indexOf('[', installIndex + 1);
const endOfSection = nextSectionIndex === -1 ? content.length : nextSectionIndex;

const preSection = content.substring(0, installIndex);
const sectionBody = content.substring(installIndex, endOfSection);
const postSection = content.substring(endOfSection);

if (sectionBody.match(/registry\s*=/)) {
// Replace existing key
const newBody = sectionBody.replace(/registry\s*=\s*".*?"/, `registry = \"${url}\"`);
content = preSection + newBody + postSection;
} else {
// Append key to section
// We insert after [install] line
const newBody = sectionBody.replace(installHeader, `${installHeader}\nregistry = \"${url}\"`);
content = preSection + newBody + postSection;
}
}

writeFileSync(this.configPath, content, 'utf-8');
}
}
Loading