#!/usr/bin/env bash set -euo pipefail command -v node >/dev/null 2>&1 || { echo 'Please install Node.js 24 LTS with npm: https://nodejs.org/' >&2; exit 1; } command -v npm >/dev/null 2>&1 || { echo 'npm is required. Install Node.js 24 LTS: https://nodejs.org/' >&2; exit 1; } node -e 'const [a,b]=process.versions.node.split(".").map(Number);if(!((a===22&&b>=19)||a>=24)){console.error("Requires Node.js 22.19+ (22.x) or 24+. Install Node.js 24 LTS.");process.exit(1)}' vimox_prefix="${VIMOX_INSTALL_PREFIX:-$HOME/.local}" case "$vimox_prefix" in /*) ;; *) echo 'VIMOX_INSTALL_PREFIX must be an absolute path.' >&2; exit 1 ;; esac export VIMOX_INSTALL_PREFIX="$vimox_prefix" export PATH="$vimox_prefix/bin:$PATH" node --input-type=module <<'VIMOX_INSTALL' import { createHash } from 'node:crypto'; import { mkdtemp, writeFile, readFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { spawnSync } from 'node:child_process'; const base = 'https://tui.vimox.cn'; const response = await fetch(`${base}/downloads/latest.json`, { signal: AbortSignal.timeout(30000) }); if (!response.ok) throw new Error(`Cannot download release information (${response.status})`); const release = await response.json(); if (!/^\d+\.\d+\.\d+(?:-[A-Za-z0-9.-]+)?$/.test(release.version) || !/^[a-f0-9]{64}$/.test(release.sha256)) throw new Error('Invalid release information'); const url = `${base}/downloads/vimox-${release.version}.tgz`; const archive = await fetch(url, { signal: AbortSignal.timeout(120000) }); if (!archive.ok) throw new Error(`Cannot download Vimox (${archive.status})`); const data = Buffer.from(await archive.arrayBuffer()); if (createHash('sha256').update(data).digest('hex') !== release.sha256) throw new Error('Package SHA-256 verification failed'); const directory = await mkdtemp(join(tmpdir(), 'vimox-download-')); const path = join(directory, `vimox-${release.version}.tgz`); await writeFile(path, data, { flag: 'wx', mode: 0o600 }); console.log(`Installing Vimox ${release.version} to ${process.env.VIMOX_INSTALL_PREFIX} ...`); const registries = process.env.VIMOX_NPM_REGISTRY ? [process.env.VIMOX_NPM_REGISTRY.replace(/\/$/, '')] : ['https://registry.npmmirror.com', 'https://registry.npmjs.org']; if (registries.some(registry => !/^https:\/\/[A-Za-z0-9.-]+(?::[0-9]+)?$/.test(registry))) throw new Error('VIMOX_NPM_REGISTRY must be a single HTTPS registry URL'); let registry = registries[0]; if (registries.length > 1) { const attempts = registries.map(async candidate => { const probe = await fetch(`${candidate}/@deepseek-ai%2fdsh`, { signal: AbortSignal.timeout(5000) }); if (!probe.ok) throw new Error(`${candidate} returned ${probe.status}`); if ((await probe.json()).name !== '@deepseek-ai/dsh') throw new Error('Invalid registry response'); return candidate; }); try { registry = await Promise.any(attempts); } catch { registry = 'https://registry.npmjs.org'; } } console.log(`Using npm registry: ${registry}`); const args = ['install', '--global', '--prefix', process.env.VIMOX_INSTALL_PREFIX, '--prefer-offline', '--no-audit', '--no-fund', '--fetch-timeout=30000', '--fetch-retries=1', '--fetch-retry-mintimeout=1000', '--fetch-retry-maxtimeout=3000', '--allow-scripts=vimox,@deepseek-ai/dsh-subprocess-local,koffi,node-pty,@google/genai,protobufjs', path]; let result = spawnSync('npm', [...args, `--registry=${registry}`], { stdio: 'inherit' }); if ((result.error || result.status !== 0) && registries.length > 1) { registry = registries.find(candidate => candidate !== registry); console.log(`Retrying with npm registry: ${registry}`); result = spawnSync('npm', [...args, `--registry=${registry}`], { stdio: 'inherit' }); } if (result.error || result.status !== 0) throw new Error('Vimox installation failed'); const installed = JSON.parse(await readFile(join(process.env.VIMOX_INSTALL_PREFIX, 'lib/node_modules/vimox/package.json'), 'utf8')); if (installed.version !== release.version) throw new Error('Installed version does not match the downloaded release'); VIMOX_INSTALL if [ "${VIMOX_INSTALL_NO_PATH:-0}" != 1 ]; then # Quote paths before writing a persistent, user-owned shell PATH entry. printf -v vimox_path_line 'export PATH=%q:"$PATH"' "$vimox_prefix/bin" for vimox_profile in "$HOME/.profile" "$HOME/.bashrc" "${ZDOTDIR:-$HOME}/.zshrc"; do if ! test -f "$vimox_profile" || ! grep -Fqx "$vimox_path_line" "$vimox_profile"; then printf '\n# Vimox TUI\n%s\n' "$vimox_path_line" >> "$vimox_profile" fi done fi "$vimox_prefix/bin/vimox" --version printf '\nInstalled. Reopen your terminal, enter your project directory and run: vimox\n'