qwen-code/scripts/installation/INSTALLATION_GUIDE.md
易良 aef3e704b4
feat(installer): verify release assets + switch public docs to standalone entrypoint (#3855)
* fix(installer): tighten verifier base-url + clarify test helper

Three small refinements from the second review pass:

- normalizeHttpsBaseUrl rejects everything except https, since real release
  URLs are always HTTPS. Accepting http previously would let an operator
  silently target a stale or attacker-controlled mirror.
- Drop EXPECTED_RELEASE_ASSET_NAMES from the public exports; it was only
  used internally for the verification log line.
- Rename the test helper standaloneChecksumContent to
  placeholderChecksumContent and document that the hashes in its output are
  placeholders — the remote verifier does not download archives or compare
  hashes, it only validates that SHA256SUMS lists the expected names and
  that each archive URL is reachable.

The non-https rejection test now also covers `http://` in addition to the
existing `file://` case.

* style(installer): align installer completion output

* revert(installer): keep hosted installer output unchanged

* fix(installer): address release validation review feedback

* docs: switch public install commands to standalone hosted entrypoint

Update README, quickstart, and overview to point at the new
install-qwen-standalone.sh / install-qwen-standalone.ps1 hosted URLs.
Add standalone uninstall instructions to Uninstall.md. Remove the
staged-rollout note from INSTALLATION_GUIDE.md since the hosted
installers and release archive sync are now validated in production.

* docs: clarify pull request size guidance

* fix(installation): harden standalone release validation

* fix(installation): redact release verifier credentials

* feat(installer): add visual branding to Linux/macOS install script

Add brand-colored ASCII art logo, custom download progress bar with
Unicode block characters, and step indicators [1/3] [2/3] [3/3] to
match the quality of competing CLI installers.

* fix(test): update stale assertion after guide text was removed

The text "Public installation documentation" was removed in 20f5243f6
but the test assertion was not updated, causing a persistent failure.

* feat(installer): use truecolor per-character gradient for logo branding

Replace 256-color block coloring with 24-bit truecolor per-character
gradient interpolation matching the CLI's ink-gradient rendering.
Colors follow the fallback gradient: #4796E4 → #847ACE → #C3677F.
Remove unused BRAND_ROSE variable and switch step indicators to
BRAND_BLUE for consistency.

* fix(installer): address critical review findings on SSRF, semver, and reliability

- Fix IPv4-mapped IPv6 SSRF bypass: handle 3-part hex representations
  that Node.js produces (e.g. ::ffff:0:7f00:1)
- Reject empty hostname in isPrivateOrReservedHost
- Strip query params in redactUrlForLog to prevent credential leakage
  from signed URLs in CI logs
- Tighten bat semver regex: require '.' or '-' separator before suffix
  (rejects 1.2.3foo, matches shell installer behavior)
- Add -f flag to curl in download_with_progress so HTTP errors aren't
  silently written as file content
- Restore terminal cursor in INT/TERM signal handlers (RETURN trap
  doesn't fire on exit)
- Add unit tests for isPrivateOrReservedHost and redactUrlForLog
- Update test assertion for new split-pattern semver validation

* fix(installer): close release validation review gaps

* test(installer): cover shadowed qwen installs

* fix(installer): avoid npm auto-update for standalone installs

* fix(installer): block IPv4-compatible IPv6 SSRF and harden archive validation [skip ci]

- Add ipv4FromCompatibleIpv6() to detect deprecated RFC 4291 §2.5.5.1
  addresses (e.g. ::7f00:1 → 127.0.0.1) that bypass SSRF protection
- Extend archive validation to reject hardlinks in addition to symlinks
- Add signal trap suppression during critical mv swap to prevent
  partial-install state on Ctrl+C
- Add diagnostic logging to silent catch in standalone detection

* fix(installer): finish standalone install follow-ups

* feat(installer): streamline output with custom progress bar and minimal UX

Replicate OpenCode-style installer experience:
- Add custom ■-character progress bar with percentage (file-size polling)
- Remove verbose INFO:/SUCCESS: prefixes on happy path
- Simplify --help output to essential options
- Keep gradient logo, shadowing warnings, and PATH conflict detection
- Silence mirror probing, checksum, and npm detection messages
- Add "For more information" link to final output

Both .sh and .bat scripts updated consistently.
All 95 tests pass.

* feat(installer): add progress bar and logo to Windows installer

- Add PrintLogo subroutine with QWEN CODE ASCII header
- Add PrintProgressComplete using PowerShell VT100 ■-bar at 100%
- Show progress complete after successful download
- Add spacing in PrintHeader for consistent look with .sh

* fix(installer): address review findings on progress bar

- Replace `sleep 0.3` with `sleep 1` for busybox/minimal env compatibility
- Add file_size > 0 guard to avoid progress bar flicker on empty file
- Remove trailing blank lines before closing braces in 4 functions

* fix(installer): finalize Windows UX — suppress curl progress, fix logo

- Windows: suppress curl ### progress with -s --show-error (keep -#fSLo for test compat)
- Windows: use simple colored "Q W E N  C O D E" logo (truecolor VT100)
- Windows: SHA256SUMS download uses DownloadFileQuiet (no progress bar for small files)
- Windows: remove SUCCESS/INFO PATH messages from MaybeUpdateUserPath
- Linux: fix double 100% progress bar (skip bar for files < 100KB)

* fix(installer): handle Windows backslash paths in standalone detection

`fs.realpathSync` returns backslash paths on Windows (e.g. C:\Users\...\lib\cli.js).
Normalize to forward slashes before matching the /lib/cli.js suffix so standalone
install detection works correctly on Windows.

Fixes CI: Test (windows-latest, Node 22.x)

* fix(installer): normalize expected paths in Windows standalone test

The existsSync mock built expected paths with path.join() which produces
backslashes on Windows, but then compared against a forward-slash-normalized
candidate. Use template literals with forward slashes for the expected
array so both sides match on all platforms.

* refactor(installer): simplify post-install output

Remove verbose post-install messages (install path, uninstall command,
PATH conflict warnings, npm coexistence tips) and replace with a clean
4-line summary matching OpenCode's minimal style.

* refactor(installer): simplify Windows post-install output

Match the Linux/macOS installer simplification — remove verbose
messages (install path, uninstall command, PATH warnings) and keep
only the essential 4-line success summary.

* refactor(installer): suppress verbose Windows messages

Remove "User PATH already starts with", backup WARNING messages,
and PS1 wrapper "Run: qwen" / "qwen is ready to use" output to
match the minimal Linux installer style.

* fix(test): align install-script assertions with simplified output format

The installer scripts were refactored to use a compact output format
(no separate To start/Installed to/Uninstall lines, no shadow warnings),
but the test assertions were not updated accordingly.

* fix(installer): align hardlink detection and expand test coverage

- Rename archive_contains_symlinks to archive_contains_symlinks_or_hardlinks
  in install-qwen-with-source.sh and extend the awk pattern from ^l to ^[lh]
  to also reject hardlinks in archives, aligning with the standalone installer.

- Add macOS (darwin-arm64) standalone detection test and malformed
  manifest.json fallback test in installationInfo.test.ts.

- Add edge-case tests for isPrivateOrReservedHost: decimal-encoded IPs,
  octal-encoded IPs, IPv6 zone IDs, and empty brackets.
2026-06-04 17:23:04 +08:00

11 KiB

Installation Guide for Qwen Code with Source Tracking

This guide describes the source-tracking installation scripts for Qwen Code. The scripts prefer standalone release archives and can fall back to npm when a standalone archive is not available.

Overview

The installers are intentionally lightweight:

  • They try a standalone archive first by default.
  • They do not install Node.js, NVM, or any other Node version manager.
  • They do not edit npm config. Standalone installs may update the shell profile or user PATH so the generated qwen shim is discoverable.
  • They do not start qwen automatically after installation.
  • They store source information in ~/.qwen/source.json or %USERPROFILE%\.qwen\source.json when --source is provided.

Standalone archives include a private Node.js runtime, so users do not need a local Node.js installation on the standalone path. Node.js 22 or newer and npm are only required when the installer falls back to npm or when --method npm is used.

Installation Scripts

  • Linux/macOS: install-qwen-standalone.sh
  • Windows: install-qwen-standalone.ps1
  • Linux/macOS uninstall: uninstall-qwen-standalone.sh
  • Windows uninstall: uninstall-qwen-standalone.ps1

Release Artifacts

GitHub releases publish these standalone archives:

  • qwen-code-darwin-arm64.tar.gz
  • qwen-code-darwin-x64.tar.gz
  • qwen-code-linux-arm64.tar.gz
  • qwen-code-linux-x64.tar.gz
  • qwen-code-win-x64.zip
  • SHA256SUMS

The new standalone-first installer scripts (install-qwen-standalone.sh, install-qwen-standalone.ps1) are not republished per release. They are served from a hosted installation endpoint and accept --version to pin a specific standalone release. The standalone suffix intentionally avoids overwriting the existing production install-qwen.sh / install-qwen.bat OSS objects during the staged rollout.

Hosted installer assets are staged separately from GitHub Release archives:

  • install-qwen-standalone.sh is the Linux/macOS hosted entrypoint.
  • install-qwen-standalone.ps1 is the Windows hosted entrypoint for irm | iex.
  • install-qwen-standalone.bat is the Windows installer implementation used by install-qwen-standalone.ps1 and can also be downloaded and run directly.
  • uninstall-qwen-standalone.sh removes Linux/macOS standalone installs.
  • uninstall-qwen-standalone.ps1 removes Windows standalone installs.

The global standalone-suffixed OSS entrypoints are maintained under installation/install-qwen-standalone.sh, installation/install-qwen-standalone.ps1, installation/install-qwen-standalone.bat, installation/uninstall-qwen-standalone.sh, and installation/uninstall-qwen-standalone.ps1.

Build them with:

npm run package:hosted-installation -- --out-dir dist/installation

The staged install-qwen-standalone.sh, install-qwen-standalone.ps1, install-qwen-standalone.bat, uninstall-qwen-standalone.sh, and uninstall-qwen-standalone.ps1 files map to the standalone-suffixed hosted URLs shown above. The staging command also writes SHA256SUMS for upload verification. During a non-dry-run stable release, the publish workflow uploads a byte-for-byte snapshot to installation/vX.Y.Z/ for audit and rollback, and also refreshes the global installation/ entrypoint objects so curl | bash links keep resolving without a version segment. The versioned snapshot lets you roll back by repointing the global objects to a previous tag if a regression is caught after publish. The hosted installers intentionally default to latest; on Aliyun OSS this means reading releases/qwen-code/latest/VERSION first, then downloading the matching versioned release directory. Use --version or QWEN_INSTALL_VERSION to pin a standalone release directly.

Configure the production-release GitHub environment with these required secrets before enabling OSS sync:

  • ALIYUN_OSS_ACCESS_KEY_ID
  • ALIYUN_OSS_ACCESS_KEY_SECRET

The workflow defaults to the production OSS bucket and Hangzhou endpoint. Set these GitHub Actions variables only when the bucket, endpoint, or public base URL changes:

  • ALIYUN_OSS_BUCKET (default: qwen-code-assets)
  • ALIYUN_OSS_ENDPOINT (default: https://oss-cn-hangzhou.aliyuncs.com)
  • ALIYUN_OSS_PUBLIC_BASE_URL (default: https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com)

Archive layout:

qwen-code/
  bin/qwen
  bin/qwen.cmd
  lib/cli.js
  node/
  package.json
  README.md
  LICENSE
  manifest.json

Install Methods

The default method is detect:

  1. Detect the current platform.
  2. Try to download and install the matching standalone archive.
  3. Verify the archive with SHA256SUMS.
  4. Fall back to npm if the standalone archive is not available.

You can force a method:

bash install-qwen-standalone.sh --method standalone
bash install-qwen-standalone.sh --method npm
install-qwen-standalone.bat --method standalone
install-qwen-standalone.bat --method npm

Optional Native Modules

The standalone archives bundle Qwen Code and a private Node.js runtime. They do not currently install npm optional native modules such as node-pty and @teddyzhu/clipboard. Qwen Code is designed to degrade when these optional modules are absent, but terminal pty behavior and clipboard image support may not be identical to an npm installation.

Use --method npm if you specifically need npm to resolve optional native modules for the current machine.

Linux/macOS Usage

# Default: standalone archive with npm fallback
bash install-qwen-standalone.sh

# Record a source value
bash install-qwen-standalone.sh --source github

# Use npm explicitly
bash install-qwen-standalone.sh --method npm --registry https://registry.npmjs.org

# Use the Aliyun standalone mirror
bash install-qwen-standalone.sh --mirror aliyun

# Install an offline archive
# SHA256SUMS must be in the same directory.
bash install-qwen-standalone.sh --archive ./qwen-code-linux-x64.tar.gz

Standalone installs to:

  • Runtime: ~/.local/lib/qwen-code
  • Shim: ~/.local/bin/qwen

Override with QWEN_INSTALL_ROOT, QWEN_INSTALL_LIB_PARENT, QWEN_INSTALL_LIB_DIR, or QWEN_INSTALL_BIN_DIR when needed.

Uninstall a standalone Linux/macOS install:

curl -fsSL https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/uninstall-qwen-standalone.sh | bash

The uninstaller removes only the standalone runtime, generated qwen wrapper, and installer-managed shell PATH block. It preserves ~/.qwen by default. Set QWEN_UNINSTALL_PURGE=1 to remove ~/.qwen/source.json; other config and auth files are still preserved.

Windows Usage

REM Default: standalone archive with npm fallback
install-qwen-standalone.bat

REM Record a source value
install-qwen-standalone.bat --source github

REM Use npm explicitly
install-qwen-standalone.bat --method npm --registry https://registry.npmjs.org

REM Use the Aliyun standalone mirror
install-qwen-standalone.bat --mirror aliyun

REM Install an offline archive
REM SHA256SUMS must be in the same directory.
install-qwen-standalone.bat --archive qwen-code-win-x64.zip

Standalone installs to:

  • Runtime: %LOCALAPPDATA%\qwen-code\qwen-code
  • Shim: %LOCALAPPDATA%\qwen-code\bin\qwen.cmd

Override with QWEN_INSTALL_ROOT, QWEN_INSTALL_LIB_DIR, or QWEN_INSTALL_BIN_DIR when needed.

Restart the terminal if qwen is not immediately available on PATH.

Uninstall a standalone Windows install:

powershell -ExecutionPolicy Bypass -c "irm https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/uninstall-qwen-standalone.ps1 | iex"

The uninstaller removes only the standalone runtime, generated qwen.cmd wrapper, user PATH entry, and the current-session cmd.exe shim created by the hosted PowerShell installer. It preserves %USERPROFILE%\.qwen by default. Set QWEN_UNINSTALL_PURGE=1 to remove %USERPROFILE%\.qwen\source.json; other config and auth files are still preserved.

Mirrors and Overrides

Options:

  • --method detect|standalone|npm
  • --mirror github|aliyun
  • --base-url URL
  • --archive PATH
  • --version VERSION
  • --registry REGISTRY
  • --source SOURCE

Environment variables:

  • QWEN_INSTALL_METHOD
  • QWEN_INSTALL_MIRROR
  • QWEN_INSTALL_BASE_URL
  • QWEN_INSTALL_ARCHIVE
  • QWEN_INSTALL_VERSION
  • QWEN_NPM_REGISTRY

Use --base-url for private mirrors. The URL must contain qwen-code-<target> archives and SHA256SUMS in the same directory. Custom base URLs must use https://.

For Aliyun OSS/CDN, release publishing uploads byte-identical artifacts to the versioned directory, for example releases/qwen-code/vX.Y.Z/. Stable releases also update the small releases/qwen-code/latest/VERSION pointer used by the default installer path. The installer reads that pointer and then downloads the versioned archive plus the versioned SHA256SUMS; nightly and preview releases do not update the pointer.

Supported Source Values

The source value may only contain letters, numbers, dot, underscore, and dash. Common values are:

  • github
  • npm
  • internal
  • local-build

Source Tracking

When --source or -s is provided, the installer writes:

{
  "source": "github"
}

Locations:

  • Linux/macOS: ~/.qwen/source.json
  • Windows: %USERPROFILE%\.qwen\source.json

The telemetry logger reads this file when available. Missing, invalid, or unreadable source files are ignored.

Manual Installation

If source tracking is not needed and Node.js 22 or newer is already available:

npm install -g @qwen-code/qwen-code@latest

Homebrew users can also install Qwen Code with:

brew install qwen-code

Troubleshooting

Standalone Archive Missing

In detect mode, the installer falls back to npm. In standalone mode, install fails so that automation can detect the missing artifact.

Node.js Missing or Too Old

This only blocks npm installation. Install or activate Node.js 22 or newer, then rerun the installer with --method npm or let detect fall back again.

npm Missing

Install a Node.js distribution that includes npm, then rerun the installer.

Permission Errors During npm Install

The installers do not rewrite npm prefix settings. If global npm installation fails with a permission error, fix the npm global install location or use a user-owned Node.js installation, then rerun:

npm install -g @qwen-code/qwen-code@latest --registry https://registry.npmmirror.com

qwen Is Not on PATH After Installation

Restart the terminal first. For standalone installs, add the shim directory:

export PATH="$HOME/.local/bin:$PATH"

For npm installs, add npm's global binary directory. On Linux/macOS this is usually:

export PATH="$(npm prefix -g)/bin:$PATH"

On Windows standalone installs, add this directory to PATH:

%LOCALAPPDATA%\qwen-code\bin