qwen-code/docs/developers/development/deployment.md
ChiGao e09c461a92
Some checks failed
Qwen Code CI / Classify PR (push) Waiting to run
Qwen Code CI / Test (ubuntu-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Lint & Static (ubuntu-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / web-shell E2E Smoke (ubuntu-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (macos-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (windows-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Integration Tests (no-AK, No Sandbox) (push) Blocked by required conditions
Qwen Code CI / Integration Tests (CLI, No Sandbox) (push) Blocked by required conditions
Qwen Code CI / Desktop Shell (ubuntu-22.04) (push) Blocked by required conditions
Qwen Code CI / Desktop Shell (windows-2022) (push) Blocked by required conditions
E2E Tests / E2E Test - macOS - shard 1/2 (push) Blocked by required conditions
E2E Tests / E2E Test - macOS - shard 2/2 (push) Blocked by required conditions
E2E Tests / Build for E2E (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 1/1 (push) Blocked by required conditions
E2E Tests / E2E Interactive - OpenTUI renderer (bun) (push) Blocked by required conditions
E2E Tests / channel-plugin E2E (nightly) (push) Blocked by required conditions
pnpm Worktree Smoke / Install (macos-latest) (push) Waiting to run
pnpm Worktree Smoke / Install (ubuntu-latest) (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 1/1 (push) Blocked by required conditions
E2E Tests / cron-interactive E2E (nightly) (push) Blocked by required conditions
E2E Tests / web-shell Browser Regression (push) Waiting to run
npm cache producer / Save npm cache (push) Waiting to run
pnpm Worktree Smoke / Install (windows-latest) (push) Waiting to run
SDK Java / ubuntu-latest / Java 11 (push) Waiting to run
SDK Java / ubuntu-latest / Java 17 (push) Waiting to run
SDK Java / macos-latest / Java 21 (push) Waiting to run
SDK Java / ubuntu-latest / Java 21 (push) Waiting to run
SDK Java / windows-latest / Java 21 (push) Waiting to run
SDK Java / Real daemon E2E / Java 11 (push) Waiting to run
Update ECS Runner Qwen / Resolve version (push) Has been cancelled
Update ECS Runner Qwen / Update Qwen on ecs-update-hk-1 (push) Has been cancelled
Update ECS Runner Qwen / Update Qwen on ecs-update-hk-2 (push) Has been cancelled
Update ECS Runner Qwen / Update Qwen on ecs-update-hk-3 (push) Has been cancelled
Update ECS Runner Qwen / Update Qwen on ecs-update-hk-4 (push) Has been cancelled
Update ECS Runner Qwen / Update Qwen on ecs-update-hk-5 (push) Has been cancelled
Update ECS Runner Qwen / Report a stale fleet (push) Has been cancelled
fix(cli): restore OpenTUI banner, footer, and loading states; fix the OSS mirror flavor (#11437)
* fix(cli): restore banner, footer, and loading states in the OpenTUI shell

The batched OpenTUI migration restructured the original single-file backend
into modules and dropped three user-visible pieces of the chrome along the
way: the startup banner, the bottom status line, and the responding
spinner. The OpenTUI renderer therefore booted into a screen that looked
nothing like the ink one it was meant to replace.

Rebuild all three as their own modules and mount them in the app shell,
following the ink layout: banner at the top, loading indicator above the
composer, status line at the bottom.

Two defects carried over from the original implementation are fixed rather
than copied. The context-usage label was missing its percent sign, and the
approval-mode colour check compared against a label that never matched, so
the auto-edit mode fell through to the default colour.

The context-file count is intentionally not restored: the shell no longer
tracks it and the current ink footer dropped it too.

* fix(cli): tick the OpenTUI spinner at the rate ink uses

The restored spinner ran at 120ms, carried over from the pre-batch
implementation, while the ink responding spinner it mirrors runs the same
frame set at 80ms and the existing OpenTUI compaction spinner does too. The
same animation therefore looked visibly slower under one renderer.

Name the interval and set it to the shared rate. The frame set itself is
unchanged.

* fix(release): sync the OSS mirror by what the tag shipped, not by a variable

The workflow that mirrors a release to Aliyun OSS decided whether to expect
the bun/OpenTUI preview archives from a repository variable. A repository
variable describes the default branch, while the checkout and the downloaded
assets come from the tag being synced, and the workflow can be re-dispatched
for any tag. Re-syncing a tag cut before the preview flavor existed therefore
asked that tag's own verify script for archives the release never shipped:
v0.22.3 fails because its script does not know the flag at all, and v0.23.0
fails on a missing checksum entry.

Derive the flag once from the archives actually downloaded and let all three
consumers read it, so the mirror follows the release instead of the branch.

Two related gaps in the same gate are closed here. The test covering it
asserted only the variable name, so inverting the comparison and silently
changing the flavor's default polarity stayed green; it now pins the
operator. And the variable appeared in no documentation, so an operator had
no way to learn that leaving it unset is what keeps the flavor off.

* fix(cli): source the OpenTUI chrome from main's shared helpers

The restored banner, footer, and responding indicator had grown private
copies of mappings main already owns, so the two renderers could drift.
Route the approval-mode label through formatApprovalModeName, extract the
context-usage format and version-label rules into ink-free helpers both
trees consume, and take the spinner frames and loading phrases from the
shared constants and cycler.

Also truncate the footer rows and the indicator phrase to the terminal
width so a long branch, path, or phrase cannot wrap the chrome mid-turn,
and read typed Config accessors in the banner instead of structural
casts and a manual package.json read.
2026-09-09 09:26:07 +00:00

4.9 KiB

Qwen Code Execution and Deployment

This document describes how to run Qwen Code and explains the deployment architecture that Qwen Code uses.

Running Qwen Code

There are several ways to run Qwen Code. The option you choose depends on how you intend to use it.


This is the recommended way for end-users to install Qwen Code. It involves downloading the Qwen Code package from the NPM registry.

  • Global install:

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

    Then, run the CLI from anywhere:

    qwen
    
  • NPX execution:

    # Execute the latest version from NPM without a global install
    npx @qwen-code/qwen-code
    

2. Running in a sandbox (Docker/Podman)

For security and isolation, Qwen Code can be run inside a container. This is the default way that the CLI executes tools that might have side effects.

  • Directly from the Registry: You can run the published sandbox image directly. This is useful for environments where you only have Docker and want to run the CLI.
    # Run the published sandbox image
    docker run --rm -it ghcr.io/qwenlm/qwen-code:0.0.11
    
  • Using the --sandbox flag: If you have Qwen Code installed locally (using the standard installation described above), you can instruct it to run inside the sandbox container.
    qwen --sandbox -y -p "your prompt here"
    

Contributors to the project will want to run the CLI directly from the source code.

  • Development Mode: This method provides hot-reloading and is useful for active development.

    # From the root of the repository
    npm run start
    
  • Production-like mode (Linked package): This method simulates a global installation by linking your local package. It's useful for testing a local build in a production workflow.

    # Link the local cli package to your global node_modules
    npm link packages/cli
    
    # Now you can run your local version using the `qwen` command
    qwen
    

4. Running the latest Qwen Code commit from GitHub

You can run the most recently committed version of Qwen Code directly from the GitHub repository. This is useful for testing features still in development.

# Execute the CLI directly from the main branch on GitHub
npx https://github.com/QwenLM/qwen-code

Deployment architecture

The execution methods described above are made possible by the following architectural components and processes:

NPM packages

Qwen Code project is a monorepo that publishes core packages to the NPM registry:

  • @qwen-code/qwen-code-core: The backend, handling logic and tool execution.
  • @qwen-code/qwen-code: The user-facing frontend.

These packages are used when performing the standard installation and when running Qwen Code from the source.

Build and packaging processes

There are two distinct build processes used, depending on the distribution channel:

  • NPM publication: For publishing to the NPM registry, the TypeScript source code in @qwen-code/qwen-code-core and @qwen-code/qwen-code is transpiled into standard JavaScript using the TypeScript Compiler (tsc). The resulting dist/ directory is what gets published in the NPM package. This is a standard approach for TypeScript libraries.

  • GitHub npx execution: When running the latest version of Qwen Code directly from GitHub, a different process is triggered by the prepare script in package.json. This script uses esbuild to bundle the entire application and its dependencies into a single, self-contained JavaScript file. This bundle is created on-the-fly on the user's machine and is not checked into the repository.

Docker sandbox image

The Docker-based execution method is supported by the qwen-code-sandbox container image. This image is published to a container registry and contains a pre-installed, global version of Qwen Code.

Release process

The release process is automated through GitHub Actions. The release workflow performs the following actions:

  1. Build the NPM packages using tsc.
  2. Publish the NPM packages to the artifact registry.
  3. Create GitHub releases with bundled assets.

OpenTUI preview flavor

Standalone archives ship in the classic Node.js flavor by default. Setting the OPENTUI_PREVIEW_RELEASE_ENABLED repository variable to true additionally builds the bun/OpenTUI preview flavor, whose archives carry an -opentui-preview suffix and whose launcher defaults QWEN_TUI_RENDERER to opentui. Leaving the variable unset keeps that flavor off.

The variable decides only what a new release builds. The workflow that mirrors a release to Aliyun OSS does not read it: that workflow can be re-dispatched for any tag, and a tag cut before the flavor existed has no preview archives to verify, so it derives the flavor from the archives the release actually shipped.