* fix(ci): require maintainer-applied `autofix/approved` label for tier-1 fast-path
The scheduled scan and `issues:labeled` event paths in `qwen-autofix.yml`
previously trusted the `status/ready-for-agent` label alone, which is
applied by an LLM reading untrusted issue text. An attacker could craft
issue content to trick the triage LLM into labeling it ready, bypassing
human review.
Introduce an `autofix/approved` label that only maintainers (write+
permission) can apply. Both the cron scan and event-triggered paths now
require this label alongside `status/ready-for-agent` before entering the
autonomous fix pipeline (dual-factor: LLM signal + human confirmation).
Also:
- `release.yml` automatically adds `autofix/approved` on workflow-created
release-failure issues (trusted source, preserves existing automation)
- Forced-issue path (non-dispatch) also checks for the new label
- Claim step strips `autofix/approved` to keep processed issues clean
Closes#5634
* fix(ci): address autofix approval review
* fix(ci): address autofix approval follow-ups
* test(ci): cover autofix approval gate
* fix(ci): harden autofix approval revalidation
* fix(ci): avoid autofix approval retry loop
* fix(ci): harden autofix claim handling
* fix(ci): clarify autofix reapproval label
* fix(ci): make autofix claim label update non-destructive
* Upgrade GitHub Actions to latest versions
Signed-off-by: Salman Muin Kayser Chishti <13schishti@gmail.com>
* ci: restore ratchet annotations on upgraded actions
The action version bumps had replaced '# ratchet:owner/repo@<ver>'
annotations with bare '# vX.Y.Z' comments, which silently breaks
ratchet's ability to track and update these SHA pins. Restore the
annotations on every changed line (updated to the new major), matching
each line's pre-existing convention on main.
---------
Signed-off-by: Salman Muin Kayser Chishti <13schishti@gmail.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
Co-authored-by: yiliang114 <effortyiliang@gmail.com>
* ci(release): drop --delete-branch from auto-merge (incompatible with merge queue)
main now has a merge queue, and gh pr merge rejects --delete-branch when one is enabled, failing the release workflow's final step. The queue owns the merge, so head-branch deletion is governed by the repo's auto-delete setting instead.
* ci(release): drop --delete-branch from remaining auto-merge workflows
release-sdk, release-sdk-python and desktop-release all run
'gh pr merge --auto --delete-branch' against main, which now has a merge
queue. gh rejects --delete-branch when a queue is enabled, so each would
fail at the auto-merge step on its next real release. Mirror the
release.yml fix: drop the flag and let the repo's 'Automatically delete
head branches' setting govern cleanup.
* ci(release): label release PRs skip-changelog to keep them out of notes
Every release workflow opens a 'chore(release): …' PR against main (version
bump + changelog sync). Those PRs merge before the next release, so GitHub's
--generate-notes picks them up as line items in the next release's notes.
.github/release.yml already excludes the 'skip-changelog' label, but the
release PRs were never tagged with it. Add --label 'skip-changelog' to each
'gh pr create' so the automated release PRs drop out of generated notes.
* ci(release): use --squash for Python SDK auto-merge
main's ruleset is squash-only; --merge would fail at the merge-queue
stage. Aligns release-sdk-python.yml with the other three release
workflows.
---------
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
* ci(release): dispatch autofix for release failures
* ci(release): harden autofix dispatch
* ci(release): tighten autofix review safeguards
* ci(release): harden release autofix safeguards
* ci(release): keep autofix dispatch resilient
* ci(release): keep dispatch failure comments best-effort
* ci(release): keep autofix trigger scoped
* ci(release): queue release failures for autofix
* ci(release): avoid event-triggered autofix
* ci(release): dispatch autofix after failure issue
* fix(ci): filter unattended autofix candidates
* ci(release): deduplicate failure issues
* ci(release): respect autofix exclusion labels
* ci(release): harden autofix dispatch
* ci(autofix): drop unused comment fetch in ready-for-agent scan
Tier-1 scans type/bug + status/ready-for-agent and takes candidates
directly without the unattended (human-engagement) filter, so the
`comments` field was fetched and immediately discarded. Drop it from the
issue-list `--json` and the now-redundant `del(.comments)`. Tier-2 still
fetches comments for its own filter.
* ci(release): harden release-failure issue dedup
- Anchor reuse on the exact "Release Failed for <tag> on " title prefix
so a tag that is a prefix of another (v0.18.1 vs v0.18.10) can't reuse
the wrong release's issue (in:title is a fuzzy full-text search).
- Prefer a github-actions[bot]-owned match so a same-titled human/foreign
issue sorting first can't make us skip an existing bot issue and open
duplicates.
- Emit a :⚠️: when an existing issue carries an autofix exclusion
label so the skipped dispatch is visible instead of a silent green job.
Standalone archives shipped only curated dist/ entries, so the esbuild-external
@qwen-code/audio-capture addon couldn't be resolved at runtime — streaming voice
was unavailable in standalone installs (batch only, and only with SoX on PATH).
create-standalone-package.js now bundles the addon into lib/node_modules (where
the bundled lib/cli.js resolves bare specifiers): the trimmed package.json
(install hook removed; type/exports kept for ESM resolution) + dist + only this
target's prebuild (win-x64 -> win32-x64) + its zero-dep runtime dependency
node-gyp-build. Targets without a matching prebuild (e.g. local builds) ship
without it and degrade to SoX/arecord as before (warns, doesn't fail). The
release pipeline already downloads prebuilds before packaging.
Refs: #5502, #5590.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* feat(voice): voice dictation with native capture, streaming, and biasing
Add voice dictation for the prompt input:
- /voice [hold|tap|off|status] command + general.voice.{enabled,mode,language,protocol} settings; push-to-talk via Space, /model --voice to pick the model
- Native microphone capture (@qwen-code/audio-capture, miniaudio N-API) with arecord/SoX fallback, silence auto-stop, cold-start warm-up, and macOS permission query
- Batch transcription via DashScope Qwen-ASR (OpenAI-compatible chat/completions + input_audio) with language + keyterm biasing and an echo guard
- Live streaming over the DashScope realtime WebSocket (fun-asr-realtime / paraformer-realtime-v2) with interim text and an input-level waveform, behind voice.protocol=dashscope-realtime
- Rich VoiceIndicator UI (state, level meter, live partial transcript)
- Cross-platform prebuilds via prebuildify + node-gyp-build and a CI matrix
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* feat(cli): route voice ASR by model
* feat(cli): polish voice realtime parity
* ci: fix voice workflow checks
* fix(cli): address voice review blockers
* fix(cli): harden voice transcription failures
* fix(cli): address voice PR review blockers
* fix(cli): harden voice review follow-ups
* fix(voice): handle realtime review blockers
* fix(cli): add voice command i18n keys
* fix(cli): address voice review blockers
* fix(cli): address voice review follow-ups
* fix(voice): harden review edge cases
* fix(voice): address release and realtime review blockers
* fix(voice): address realtime suggestion followups
* fix(voice): handle realtime review followups
* fix(voice): address realtime review blockers
* fix(voice): address review feedback
* fix(voice): address recorder review feedback
* docs(voice): document ssrf guard boundary
* fix(voice): address review feedback
* fix(voice): preserve warm recorder session safety
* fix(voice): address stream review suggestions
* fix(voice): address multi-round review findings
Realtime/streaming:
- Salvage an already-committed transcript when the WebSocket closes right
after finish() instead of rejecting the whole dictation
(qwenAsrRealtimeSession, voiceStreamSession) + regression tests.
useVoiceInput state machine:
- Single-shot finalize guard so a tap-stop racing the silence auto-stop can't
double-stop the recorder and surface a spurious failure.
- Reset mountedRef on (re)mount so StrictMode (DEBUG) can't freeze the voice UI.
- Widen the hold-mode first-press release window above common key-repeat delays.
Model selection:
- Reject ids with no ASR transport at /model --voice and in the model dialog via
a new isSelectableVoiceModel; move resolveVoiceTransport into voiceModel so the
record-time config resolver stays transport-agnostic (+ tests).
macOS mic permission:
- Surface the not-determined state in voice warmup so the first dictation isn't
silently lost behind the TCC dialog.
Native packaging:
- Make the audio-capture native install non-fatal (falls back to SoX/arecord) so
a voice-only build failure can't break npm ci.
- Download audio-capture prebuilds before building standalone release archives.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(voice): address review blockers
* fix(voice): update batch recording audio level
* fix(voice): tap-mode transcript loss, stream leaks, and dead keyterm cleanup
- Tap-mode dictation submitted a stale empty buffer.text, wiping the just-
inserted transcript and sending nothing: thread the resulting prompt text
through onSubmit(text) instead of reading buffer.text back synchronously
after the async insert (useVoiceInput, InputPrompt).
- Streaming finalize leaked the WebSocket session when recorder.drain() threw:
abort the session before propagating the error.
- voiceStreamSession: reject the connect promise when 'task-finished' arrives
before 'task-started' instead of hanging forever in 'transcribing'.
- Remove dead keyterm enrichment (project/branch/recent-file paths were
unreachable after the privacy fix) and the now-inert CJK echo guard, plus the
tests that asserted that removed behavior; fix the misleading "OpenAI prompt
field" comment.
- Add the missing 'Voice Model' and macOS mic-permission i18n keys to
en/zh/zh-TW (they were falling back to English; check-i18n doesn't flag keys
absent from en).
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(voice): reject partial stream transcripts
* fix(cli): let dialogs consume voice keys first
* fix(voice): reject incomplete qwen realtime transcripts
* fix(voice): handle stream review blockers
* fix(voice): salvage qwen realtime transcript on close
* fix(voice): sanitize streamed transcript text
* test(audio): run audio capture tests in CI
* fix(voice): address review blockers
* fix(voice): report stream close while recording
* fix(voice): reduce keyterm echo false positives
* fix(voice): handle realtime close diagnostics
* fix(voice): address realtime review blockers
* fix(lint): allow legacy voice filenames
* chore(cli): rename voice files to kebab-case
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* ci(release): report required Test checks on release PRs and auto-approve
Release-sync PRs skipped the matrix Test job at job level, so the required "Test (os, Node 22.x)" contexts were never reported and the PR could not merge. Move the skip to step level so the job still expands its matrix and concludes as a work-free pass. Also add a CI_DEV_BOT_PAT approval on the release PR to cover one of the two required reviews.
* ci: single-quote the test job if expression for yamllint
After dropping the inner 'true' comparison, double quotes are no longer required, so yamllint's quoted-strings rule wants single quotes.
* feat(ci): add auto-generated CHANGELOG.md synced from releases (#4872)
Add a Keep a Changelog CHANGELOG.md that is fully derived from the project's
GitHub Releases, so users no longer have to dig through commit history to see
what changed between versions.
- scripts/generate-changelog.js: fetch releases via the gh CLI, keep only
stable vX.Y.Z tags (nightly/preview omitted), and re-group each release's
auto-generated "What's Changed" list into Added/Changed/Fixed/Performance/
Documentation/Other sections by the conventional-commit prefix every PR title
uses. Zero runtime deps (node builtins + gh).
- release.yml: regenerate and commit CHANGELOG.md on stable releases; the
commit rides the existing release-branch PR into main.
- package.json: add 'npm run changelog'.
- .prettierignore: skip the generated file.
- Seed CHANGELOG.md from the current stable release history.
Closes#4872
* ci(release): make CHANGELOG regeneration non-blocking
Add continue-on-error to the CHANGELOG step. Its only realistic failures
(the gh API read and the git push) are transient, and the generator rebuilds
from the full release history each run, so a skipped update self-heals on the
next stable release. This keeps a changelog hiccup from blocking the
version-bump PR to main that follows.
* fix(ci): harden changelog generator per review
Address review findings on the changelog generator:
- Command injection: fetchReleasesJsonl built a shell string with the repo
interpolated; switch to execFileSync (no shell) plus an owner/name format
check so --repo can never be a shell payload.
- Empty-response clobber: if the API returns zero stable releases (rate limit,
auth, 5xx), refuse to overwrite CHANGELOG.md and exit 1 instead of committing
a header-only stub. Paired with set -euo pipefail in the workflow so the
failure is visible and the non-blocking step self-heals next release.
- Arg parsing: switch getArgs() to parseArgs() (already imported) so a --dryrun
typo errors instead of silently overwriting, and -h works as documented.
- Breaking changes: capture the conventional-commit ! marker and prefix the
entry with **BREAKING** (the repo uses feat()!:/refactor()!: in practice).
- Bot authors: ENTRY_RE now accepts a trailing [bot] so GitHub App authors
(e.g. @dependabot[bot]) are not dropped from release notes.
Regenerates CHANGELOG.md (two entries now flagged **BREAKING**).
* refactor(ci): simplify changelog generator
Quality-only cleanup (output is byte-identical; 23 tests green):
- Single source of truth for sections: derive TYPE_TO_SECTION and
SECTION_ORDER from one SECTIONS list so they can't drift.
- Parse each entry once: formatRelease now reuses the categorize() result for
the noise check, section lookup, and formatEntry (was parsed up to 3x).
- Drop the redundant sortKey field; sort directly from version.
- Collapse the duplicated double-.map() setup in the selectStableReleases test.
* ci: split Aliyun OSS sync into a separate post-release workflow
The OSS upload and verification steps were adding significant time to the
release workflow's critical path. Move them into a new `sync-release-to-oss.yml`
workflow that triggers on `release: published`, running asynchronously after
the release completes.
Key changes:
- Extract all OSS steps (ossutil install, credential config, asset upload,
verification, hosted installation sync, latest VERSION pointer) into
`sync-release-to-oss.yml`
- Switch `gh release create` to use CI_BOT_PAT so the release event can
trigger the new downstream workflow (GITHUB_TOKEN events don't trigger
other workflows)
- Add `workflow_dispatch` input for manual re-runs on failure
- New workflow downloads release assets from GitHub Release instead of
rebuilding them
This decouples publishing from CDN distribution: the release finishes as
soon as npm publish + GitHub Release are done, and China CDN sync happens
in parallel without blocking.
* fix(test): update install-script test to check sync-release-to-oss.yml
The test asserts OSS sync steps exist in the workflow. Now that these
steps live in sync-release-to-oss.yml instead of release.yml, update the
test to read from the correct file and add assertions that release.yml
no longer contains OSS logic.
* fix(ci): address review feedback for OSS sync split
- Add 'Verify Standalone Archives' step before gh release create in
release.yml as a pre-publish safety gate (wenshao)
- Add concurrency group to sync-release-to-oss.yml to prevent race
conditions when multiple releases publish close together (wenshao)
- Update test to assert verify step exists in release.yml
* chore: add comment explaining CI_BOT_PAT requirement [skip ci]
* feat(installer): add standalone archive installation
* fix(installer): harden standalone archive installs
* fix(installer): address standalone review findings
* chore(installer): clarify review followups
* fix(installer): stabilize standalone script checks
* chore(installer): remove internal planning docs
* chore(installer): simplify standalone release review fixes
* test(installer): add Windows batch install smoke
* test(installer): fix Windows batch smoke quoting
* test(installer): preserve Windows cmd quotes
* fix(installer): use robust Windows checksum hashing
* ci: narrow installer debug matrix
* fix(installer): address standalone review hardening
* fix(installer): avoid Windows validation parse errors
* fix(installer): simplify Windows option validation
* fix(installer): harden standalone review fixes
* feat(installer): publish release installer assets
* fix(installer): address release asset review feedback
* fix(installer): avoid prerelease installer asset links
* test(installer): isolate standalone dist fixture
* feat(installer): add hosted install release alias
* chore: no changes - code review requested
Agent-Logs-Url: https://github.com/QwenLM/qwen-code/sessions/38467aec-15b9-4b76-9139-0b2cfe40477a
* fix(installer): pin versioned installer assets
* fix: parallelize Node.js binary downloads in standalone release build
Use Promise.all instead of sequential for...of+await for
the 5 independent Node.js runtime downloads, reducing CI
release build time by ~4-5x.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(installer): address release asset review followups
* refactor(installer): share release CLI parsing
* fix(installer): address release asset review followups
- sh: reject CR/LF in archive entry names before the literal `..` glob so
a `..\r` entry cannot bypass path validation.
- bat: prefer Tls12+Tls13 in PowerShell helpers, fall back to Tls12 alone
on older .NET Framework where the Tls13 enum is missing.
- bat: document the implicit `:ValidateOptions` dependency next to the
qwen.cmd wrapper writer so loosening the validator stays a conscious
choice.
- build-standalone-release: surface the `xz-utils` host requirement for
Linux Node downloads in `--help`.
- release-script-utils: support `--key=value` form in `parseCliArgs`.
- tests: cover the new CRLF message, TLS string, and `--key=value` parsing;
register process-level signal/exit handlers in `ensureMinimalDist` so a
crashed test still restores `dist/`.
* fix(installer): unblock Windows CI for standalone install path
Three CI failures and a few review followups in one pass.
- ensureMinimalDist places its dist/ backup beside dist/ instead of
under os.tmpdir(). On Windows GitHub runners the workspace lives on
D: while os.tmpdir() is on C:, so renameSync raised EXDEV for every
test that needed to swap dist/ in.
- create-standalone-package.js and the matching test fixture build
win-x64 zips with [IO.Compression.ZipFile]::CreateFromDirectory.
Compress-Archive emits backslash entry names that the .bat
installer's path-traversal guard then rejected, so every freshly
built archive failed the standalone install path on Windows.
- :ValidateArchiveContents normalizes entry separators to '/' before
checking for '..', absolute paths, and drive prefixes - archives
from any Windows zip tool still install while real traversal
entries remain rejected.
- createWindowsTraversalStandaloneArchive runs PowerShell via -File
instead of a single -Command line; the joined-with-'; ' form had a
function definition the runner's PowerShell refused to parse.
Drive-by review followups:
- replaceRequired uses replaceAll so a future duplicate placeholder
cannot silently keep the trailing copy as 'latest'.
- :ValidateOptions runs the unsafe-character check on SOURCE
alongside the other variables.
- build-installation-assets.js drops a dead INSTALLATION_ASSETS
re-export; consumers already import from release-asset-config.js.
- .gitignore covers the new sibling .qwen-dist-backup-* directory.
* fix(installer): address release asset review findings
* fix(installer): keep installer entrypoint hosted
* fix(installer): reject stale hosted assets
* fix(installer): refine hosted asset staging
* fix(installer): tighten hosted default-version check, flag legacy URL
- Replace the loose `latest` fragment check with per-format regex patterns
in HOSTED_INSTALLER_DEFAULT_VERSION_PATTERNS so an unrelated occurrence
of `latest` (comment, help text) cannot satisfy the staging guard. The
patterns still tolerate whitespace variation, only the default-version
assignment itself must be intact.
- Add a "Hosted endpoint status" callout in INSTALLATION_GUIDE.md before
the curl examples. The documented `--version` flow does not work against
the OSS URL today because it currently serves the legacy NVM-based
installer; the callout points users at a local checkout until the next
release sync.
- Tests: drop `latest` from the fragments equality assertion, add positive
and negative regex coverage, add a failure-path case for sources whose
default version is not `latest`, and pin the new guide markers so the
callout cannot silently disappear.
* feat(installer): verify installation release assets
Adds `npm run verify:installation-release` and wires it into the release
workflow after `Build Standalone Archives`, so a broken release directory
fails CI before publishing.
Local mode (`--dir PATH`) checks:
- All five `qwen-code-{platform}.{ext}` standalone archives exist.
- `SHA256SUMS` covers exactly those five — missing or unexpected entries fail.
- Each archive's actual SHA256 matches its `SHA256SUMS` entry.
Remote mode (`--base-url URL`) checks:
- `SHA256SUMS` is downloadable, parseable, and contains exactly the expected
archive entries.
- Each archive URL is reachable via HEAD, with a 1-byte ranged GET fallback
for hosts that disable HEAD.
Hosted installer scripts (`install-qwen.sh` / `install-qwen.bat`) are
intentionally out of scope here — they are served from the hosted endpoint
prepared by `package:hosted-installation` (PR #3853), not from the GitHub
Release surface this verifier targets.
* 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.
* fix(installer): address standalone review follow-ups
* fix(installer): repair Windows installer tests
* fix(release): tighten standalone asset checks
* fix(installer): stabilize Windows managed install checks
* test(installer): relax Windows installer timeout
* fix(test): escape release asset regex
* test(cli): avoid POSIX node path in relaunch test
* fix(installer): align npm fallback node gate with engines
* test(installer): allow Windows archive validation more time
* fix(installer): remove stale node 20 installer references
* docs(installer): clarify hosted endpoint sync requirement
* refactor(installer): reuse standaloneArchiveName in release verifier
The verify-installation-release script was duplicating the archive name
derivation logic with a hardcoded ternary instead of reusing the
standaloneArchiveName helper from build-standalone-release. Export the
helper and import it so the extension mapping lives in one place.
* fix(scripts): address release verifier review feedback
* feat(installer): add standalone archive installer with multi-platform release workflow
- Add standalone archive installer (bat/sh) that downloads platform binaries
from GitHub/Aliyun without requiring Node.js or npm on the target machine
- Add fork-friendly release-test workflow for manual GitHub Release creation
covering all 5 platforms (darwin-arm64/x64, linux-arm64/x64, win-x64)
- Add OSS upload/mirror tools for staging and release distribution
- Update .gitignore to exclude generated build artifacts (release-staging/,
hosted-staging/)
- Fix Windows PowerShell test command in copy-release-to-latest tool
* feat(installer): support QWEN_INSTALL_GITHUB_REPO env var for custom repo
* chore(installer): exclude local-only staging tools from PR
The tools/ directory contained personal staging-OSS upload helpers
(upload-staging, upload-release-mirror, copy-release-to-latest,
test-upload-one) that should not ship in the public PR. They reference
a personal staging bucket and only exist to validate the installer
end-to-end before production release.
Removes them from git tracking via `git rm --cached` (files stay on
disk for the author's local use) and adds /tools/ to root .gitignore
so they cannot be re-added accidentally.
No runtime / installer code change. Production CI on ubuntu-latest is
unaffected.
* fix(installer): enforce CRLF line endings for .bat files via gitattributes
cmd.exe requires CRLF in batch scripts; the global eol=lf was causing
every line to be misparsed on Windows, producing errors like
'QWEN_VALIDATE_METHOD=detect is not recognized as a command'.
* fix(installer): store .bat files with CRLF in git blob for raw GitHub downloads
GitHub raw file serving bypasses gitattributes eol conversion and serves
blob bytes directly, so eol=crlf alone was not enough. Use -text to disable
normalization and commit with actual CRLF so raw downloads work on Windows.
* fix(installer): follow HTTP redirects in UrlExists and RaceMirrorHead probes
GitHub release asset URLs return HTTP 302 to objects.githubusercontent.com.
[Net.WebRequest] with HEAD does not auto-redirect by default, so the
existence check and mirror-race probe both incorrectly reported the file
as missing. Set AllowAutoRedirect=true on HttpWebRequest instances.
* fix(installer): surface download errors and add MaximumRedirection 10
* feat(installer): add hosted install-qwen.ps1 shim for irm|iex one-liner
The previous Windows quick-install one-liner used `Invoke-WebRequest -OutFile
(Join-Path $env:TEMP 'install-qwen.bat'); & (Join-Path …)`. When pasted into a
narrow terminal, line wrap could land on `-OutFile`, orphaning the parameter
from its value and producing the "missing argument for OutFile" failure
followed by a "file not found" when the second `&` ran. PowerShell's line
continuation rules cannot resolve this for parameter-name-at-EOL.
Add `install-qwen.ps1` as a thin hosted entrypoint that downloads
`install-qwen.bat` into TEMP, runs it, and cleans up. Documented one-liner
becomes the standard pattern used by bun, uv, scoop, deno, pnpm:
powershell -ExecutionPolicy Bypass -c "irm <url>/install-qwen.ps1 | iex"
The `.bat` remains the source of truth for installer behavior; `.ps1` is just
the modern hosted entrypoint. Version pinning via `$env:QWEN_INSTALL_VERSION`
flows through unchanged. Stored with `*.ps1 -text` so CRLF survives both
GitHub raw and OSS uploads, matching the existing `.bat` handling.
* fix(installer): stage direct hosted install scripts
* chore(installer): trim hosted release diff scope
* chore(installer): narrow hosted release diff
* feat(installer): restore hosted PowerShell entrypoint
* chore(installer): stage standalone hosted entrypoints
* fix(installer): address hosted installer review followups
* fix(installer): stabilize Windows installer tests
* fix(installer): make Windows option validation readable
* feat(installer): wire Aliyun OSS sync, address review followups
- Add Aliyun OSS sync steps to release workflow: package hosted assets,
install pinned ossutil, configure credentials, upload versioned and
latest paths, and verify upload via verify:installation-release plus
curl probes against the hosted installer endpoint.
- Document required production-release environment secrets and bucket
variables in INSTALLATION_GUIDE.md.
- Restructure hosted endpoint guidance to lead with the pre-sync
warning, splitting "Run today" (local checkout) from "After the OSS
sync" (hosted one-liners) so users no longer copy a one-liner that
silently installs latest.
- Distinguish mirror auto-selection timeout from successful selection
in install-qwen-standalone.sh and install-qwen-standalone.bat: emit
a "timed out; defaulting to github" log instead of pretending the
HEAD probe picked github.
- Support QWEN_INSTALLER_BAT_URL override (https only) in the
PowerShell shim so staging mirrors can be exercised without forking
the file.
- Strip a leading UTF-8 BOM in verify-installation-release.js
parseSha256Sums so BOM-prefixed SHA256SUMS reports a useful
"Missing checksum entry" error instead of "Malformed SHA256SUMS
line 1".
- Add tests for verifier HEAD→Range fallback, partial-failure
formatting, all-failure wording, and BOM tolerance.
* ci(installer): add temporary OSS smoke test
* fix(installer): make OSS release assets public-readable
* chore(installer): remove temporary OSS smoke workflow
* fix(installer): address hosted installer review gaps
* feat(installer): refactor argument parsing and utility functions for release scripts
* fix(installer): harden hosted release script checks
* fix(installer): suppress PowerShell progress bar in hosted entrypoint shim
Add $ProgressPreference = 'SilentlyContinue' to the .ps1 wrapper so
Invoke-WebRequest downloads don't render a progress bar when invoked
via the irm | iex one-liner.
* fix(installer): suppress PowerShell progress bar in bat installer downloads
Add $ProgressPreference = 'SilentlyContinue' to DownloadFile so the
full-screen progress UI does not appear during archive downloads in
interactive PowerShell sessions, consistent with the .ps1 shim.
* fix(installer): use curl.exe -# progress bar in Windows downloads
Prefer curl.exe with -# (hash-mark progress bar) for archive and installer
downloads on Windows 10+. Falls back to Invoke-WebRequest (which shows its
own progress bar) when curl.exe is unavailable. Matches the approach used
by code-server (curl -#fL) and bun.sh (curl.exe -#SfLo).
* fix(installer): suppress progress bars for small downloads and Expand-Archive
- .ps1: replace curl.exe -# with silent mode, suppress Invoke-WebRequest
progress bar; save/restore $global:ProgressPreference
- .bat: add $ProgressPreference = 'SilentlyContinue' before Expand-Archive
to prevent full-screen extraction progress UI
- .sh: remove --progress-bar / --show-progress from download_file, always
use silent curl/wget
* fix(installer): auto-backup non-qwen directories and simplify output
- ensure_managed_install_dir / :EnsureManagedInstallDir now back up
non-qwen directories instead of refusing to install, so users
upgrading from npm or old installers don't hit a hard error
- Simplify header/footer output: remove banner bars, verbose INFO
lines, and redundant "Installation completed!" message
- Match bun.sh / code-server style: minimal, to the point
* fix(installer): revert Expand-Archive progress suppression in bat
The inline $ProgressPreference = 'SilentlyContinue' caused a cmd.exe
parsing error ("此时不应有 >") on Chinese Windows. Revert to the
original Expand-Archive invocation.
* fix(installer): fix cmd.exe parsing error in backup fallback code
The %s in the for /f fallback command string was interpreted as a variable
reference by cmd.exe, causing "此时不应有 >" on Chinese Windows. Replace
with a safe fallback and re-enable Expand-Archive progress suppression.
* fix(installer): always persist install bin to user PATH
Previously MaybeUpdateUserPath was only called when shadow qwen
executables were detected. When no shadow was found, the PATH update
was skipped entirely, leaving the user without qwen on PATH after
restarting their terminal.
Now always persist the bin directory to PATH (unless --no-modify-path
is set), regardless of whether other qwen installations exist.
* fix(installer): persist PATH to current terminal session on Windows
Use the `endlocal & set` trick (same as bun/Rust installers) to export
the install bin directory from the setlocal scope to the current cmd
session. qwen is now usable immediately without restarting the terminal.
* docs(installer): document cmd.exe one-liner for immediate PATH availability
Add curl-based one-liner for cmd.exe users. Running the .bat directly
in the current cmd session makes `qwen` available immediately via the
`endlocal & set` trick. The `powershell -c "irm | iex"` path creates
a child process so PATH changes cannot propagate to the parent.
* feat(installer): make qwen usable immediately from PowerShell after install
- .ps1: detect parent process, update current session PATH, and for
cmd.exe parents emit a `set PATH=...` command
- .bat: skip final instructions when called from PowerShell to avoid
duplicate "Run: qwen" output
* fix(installer): remove non-functional doskey approach for cmd parent
doskey /exename from a child PowerShell process cannot modify the
parent cmd.exe session. Replace with a simple set PATH=... command
that the user can copy-paste.
* fix(installer): make Windows standalone shim available in cmd
* feat(installer): add standalone uninstall scripts
* fix(uninstall): match shell-quoted paths when removing the wrapper
The installer's write_unix_wrapper shell-quotes the binary path, so
paths containing single quotes (or other shell metacharacters) appear
as shell-quoted strings in the generated wrapper file. The uninstall
script's literal grep -qF missed these, leaving the wrapper orphaned.
Add shell_quote to the uninstall script and match against both the raw
and shell-quoted forms before removing the wrapper.
* fix(installer): update download commands to use progress indicators for curl and wget
* fix(installer): resolve Aliyun latest via version pointer
* fix(installer): cleanup mirror probe temp dirs
* fix(installer): harden standalone release fallback
* fix(installer): address standalone review feedback
* style(installer): align standalone install output
* fix(installer): print standalone uninstall commands
* fix(installer): address release review follow-ups
* fix(installer): harden Windows target detection
* test(installer): stabilize Windows fake tool path
* fix(installer): allow explicit Windows curl path
* test(installer): use cmd fake curl on Windows
* test(installer): cover Windows fake curl helper
* test(installer): inject Windows arch overrides in cmd
* test(cli): wait for prompt suggestion render
* test(cli): revert prompt suggestion wait tweak
* fix(installer): harden hosted release publishing
* fix(installer): harden Windows latest pointer parsing
* fix(installer): bound Windows download timeouts
* fix(installer): bound hosted installer probes
* fix(release): make ossutil download configurable
* fix(installer): address hosted release review feedback
* test(installer): keep dist backup on same filesystem
* fix(installer): address remaining review feedback on PR #3828
- Remove REQUIRE_CHECKSUM dead code, always hard-fail on checksum issues
- Add JSDoc to HOSTED_INSTALLER_BEHAVIOR_PATTERNS explaining its purpose
- Add credential cleanup trap for ossutilconfig in release workflow
- Add 3-attempt retry with exponential backoff for OSS uploads
- Tighten findstr SOURCE regex to require leading letter
* fix(release): correct OSS credentials lifetime and mirror probe fallback
- release.yml: remove `trap EXIT` inside the Configure step; it deleted
${RUNNER_TEMP}/.ossutilconfig as soon as the configure shell exited,
so every subsequent step (publish/sync/verify) lost the credentials.
Move credential cleanup to a final `if: always()` step at the job tail.
- install-qwen-standalone.sh: drop the predictable PID-based mktemp -d
fallback in race_mirror_head; if mktemp fails, return "github" instead
of using /tmp/qwen-mirror.$$ which a local attacker could pre-create
to bias mirror selection.
* fix(installer): address review feedback round 2
Workflow:
- Move 'Publish Aliyun OSS Latest VERSION' to run after the hosted installer
assets are uploaded and verified, so the latest/VERSION pointer only flips
once every release artifact is in place. Previously a hosted-sync failure
could leave the pointer ahead of the actual installer scripts.
upload-aliyun-oss-assets.js:
- Replace `spawnSync('sleep', ...)` retry backoff with an Atomics.wait-based
cross-platform sleep so retries also work on Windows runners.
install-qwen-standalone.bat:
- :DetectTarget no longer emits TARGET=win-arm64 because RELEASE_TARGETS has
no win-arm64 archive; ARM64 hosts now fall through to the unsupported-arch
branch and (in detect mode) get the npm fallback instead of a 404.
- Add QWEN_INSTALL_CURL_EXE to :ValidateRawEnvironmentOptions so this curl
override is checked for shell metacharacters like every other knob.
- Replace `call echo %%i>>...` with plain `echo %%i>>...` when capturing
pre-install qwen.cmd paths; `call` triggered an extra parse pass that
could interpret &/|/<,>/etc. inside a directory name as command separators.
- Add `--retry 2` to curl.exe downloads (`:DownloadFile` / `:DownloadFileQuiet`)
to match the shell installer.
- Include expected vs actual hash in the checksum-mismatch error message.
install-qwen-standalone.ps1:
- Stage the downloaded installer at a cryptographically random temp path
(`qwen-installer-<random>.bat`) so a same-user attacker cannot pre-stage a
malicious .bat at a predictable path and race the verify/execute window.
- Atomically install the current-session cmd shim by writing to a sibling
`.new` temp file then renaming, so a partial write cannot leave a
half-written shim on PATH.
- Add `--retry 2` to the curl.exe download path.
- Include expected vs actual hash in the checksum-mismatch error message.
install-qwen-standalone.sh:
- Include expected vs actual hash in the checksum-mismatch error message.
uninstall-qwen-standalone.ps1:
- Accept `-Purge` and `-Help` parameters; previously every CLI flag was
silently dropped, so users running with `-Purge` got no purge and no error.
`-Purge` maps to `QWEN_UNINSTALL_PURGE=1`.
uninstall-qwen-standalone.sh:
- `remove_install_wrapper` additionally requires the wrapper file to start
with a `#!` shebang before it deletes it; a user-authored script that just
happens to mention the install path now stays untouched.
verify-installation-release.js, build-hosted-installation-assets.js:
- Include expected vs actual hash in the checksum-mismatch error messages.
scripts/tests/install-script.test.js:
- Update assertions for the new error wording, the curl `--retry 2` flag,
the dropped ARM64 detection, and the new release-step ordering.
* fix(installer): address review feedback round 3
Workflow:
- Configure Aliyun OSS Credentials: write the ossutil config file directly
with restricted umask instead of invoking `ossutil config -k <secret>`.
Passing the access-key secret via argv made it visible in /proc/<pid>/cmdline
for the lifetime of that step; writing the INI file in-process keeps the
secret out of the process table.
upload-aliyun-oss-assets.js:
- Upload assets in parallel with `Promise.all` + async `spawn` instead of a
sequential `spawnSync` loop. Each asset keeps its own retry budget; failures
are aggregated so one flaky upload does not mask a separate failure.
- Replace the bespoke `Atomics.wait` retry sleep with `timers/promises#setTimeout`
now that the loop is async.
INSTALLATION_GUIDE.md:
- Drop the misleading "instead of overwriting the global installation/
entrypoint objects" sentence; the workflow has always also refreshed the
global versionless objects so curl|bash links keep resolving without a
version segment. Document the rollback story instead.
* test(installer): add parseUploadArgs unit tests and align verify derivation
- scripts/tests/upload-aliyun-oss-assets.test.js: cover --help short-circuit,
required-option validation (--bucket/--config/--prefix/empty assets),
unknown options, missing option values, and trailing-slash prefix
normalization.
- scripts/verify-installation-release.js: switch the win-only zip branch
from `startsWith('win-')` to the strict `=== 'win-x64'` check used by
build-standalone-release.js, and add a comment recording that the two
derivations must stay aligned. Without this the helpers would diverge
the moment a non-x64 win target gets added.
* test(installer): add uploadAssets integration tests with fake ossutil
Add two integration tests that route a temp-directory ossutil shim onto
PATH so uploadAssets actually spawns the real binary with the real cp
argv:
- happy-path test asserts the destination URI, `-c <config>`, `--acl
public-read`, and per-asset cp invocations land for both inputs.
- failure-path test asserts non-zero ossutil exits surface as an
aggregate `asset uploads failed` error after the retry budget runs out.
* revert(installer): drop over-engineered ossutil/upload changes
Roll back two changes from a1ef8697b/0a5d308c9 that were not justified
by the actual threat model or release-pipeline needs:
- .github/workflows/release.yml: restore the supported `ossutil config -k`
invocation. The earlier switch to writing the .ossutilconfig INI file
in-process was meant to keep the access-key out of /proc/<pid>/cmdline,
but GitHub-hosted runners are single-tenant ephemeral VMs where no other
user can read that namespace. The benefit was theoretical; the cost was
taking on a brittle dependency on ossutil's undocumented config format.
- scripts/upload-aliyun-oss-assets.js: revert the uploadAssets parallel
rewrite (Promise.all + spawn + setTimeout) back to the original sync
spawnSync loop with retry. Release-time uploads of ~6 small files do
not need parallelism, and the async refactor changed the public
contract (sync→async) for no real wall-clock win.
Kept from those commits:
- The cleanup `if: always()` step that removes RUNNER_TEMP/.ossutilconfig
at the end of the publish job.
- The cross-platform sleepSync(ms) helper, since `spawnSync('sleep', ...)`
still does not work on Windows runners.
- The INSTALLATION_GUIDE.md doc fix.
- All other round-2 fixes.
Test assertions updated for the restored sync uploadAssets contract.
* test(installer): cover Windows release script regressions
* test(release): avoid Windows shim lookup in oss upload tests
* test(installer): use stable fake Aliyun version on Windows
* fix(installer): parse Aliyun latest version in batch
* fix(installer): validate Aliyun latest version without findstr
* fix(installer): normalize Aliyun latest version via PowerShell
* fix(installer): avoid captured PowerShell output in batch latest parsing
* fix(installer): normalize Aliyun latest pointer from file
* test(installer): fix fake Windows curl output parsing
* fix(installer): print checksum path on miss, gate hardcoded version pin in ps1 [skip ci]
Address two narrow follow-ups from PR #3828 review:
- build-hosted-installation-assets.js: add a HOSTED_INSTALLER_FORBIDDEN_PATTERNS guard for install-qwen-standalone.ps1. The ps1 shim has no VERSION variable of its own (it forwards @args to the .bat), so the existing default-version positive-match patterns don't apply. The new guard fails the build if a $env:QWEN_INSTALL_VERSION assignment or a --version flag prepended to the forwarded argument list ever lands in the shim. Patterns are line-anchored with /m so the documented usage examples in the header docstring stay valid. Two vitest cases cover the reject and allow paths.
- install-qwen-standalone.sh / .bat: include the searched checksum-file path in the "SHA256SUMS not found" error. Operators triaging --archive failures could not tell from the prior message whether the fallback path (next to the archive) or the remote URL was being looked up. Existing test assertions updated to match the new wording.
Local validation: npm run test:scripts -> 160 passed | 9 skipped (was 158 | 9).
* fix: stamp release version in hosted installers and add Zip Slip protection [skip ci]
1. The hosted installation asset build now accepts --version and stamps it
into the copied .sh/.bat installers so they default to the tagged release
version instead of 'latest'. The release workflow passes the version.
2. install-qwen-with-source.bat now validates archive entries before calling
Expand-Archive, rejecting paths with '..', leading '/', drive-rooted
paths, empty names, or control characters — matching the protection
already present in install-qwen-standalone.bat and the .sh installer.
* fix(installer): add SOURCE to PowerShell unsafe-character validation [skip ci]
The SOURCE variable is user-provided and used in path operations but was
not included in the :ValidateOptions unsafe-character check. Add it
alongside the other validated variables.
* fix: correct copyright year 2025 -> 2026 in new files [skip ci]
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: yiliang114 <effortyiliang@gmail.com>
GitHub Actions deprecates the Node 20 runtime; older versions of every
docker/* action run on Node 20 and emit the "Node.js 20 actions are
deprecated" warning in every release / e2e / image-build run today.
Each action shipped a "Node 24 as default runtime" major:
- docker/setup-buildx-action v3 → v4 (2026-03-05)
- docker/setup-qemu-action v3 → v4
- docker/metadata-action v5 → v6
- docker/login-action v3 → v4
- docker/build-push-action v6 → v7
None of our usages touch the deprecated inputs removed in the bumps —
release.yml / e2e.yml call setup-buildx with no `with:` block, and
build-and-publish-image.yml only passes the universally-supported
`images` / `tags` / `registry` / `username` / `password` / `context` /
`platforms` / `push` / `labels` / `build-args` inputs. ESM internal
refactor of each action is transparent to consumers.
Ratchet-pinned bumps use the v4.0.0 commit SHA
`4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd` (release.yml + e2e.yml).
Other action references in build-and-publish-image.yml use
`# ratchet:exclude` per existing convention, so version-string bumps
suffice there.
Verified runtime hosts (`actions/runner` v2.327.1+) are already in use
on github-hosted runners as of 2026-03; no infra bump required.
Co-authored-by: 秦奇 <gary.gq@alibaba-inc.com>
* ci(release): skip CI on auto-generated version-bump PRs
Append `[skip ci]` to both the bump commit message and the
release-PR title in `release.yml` and `release-sdk.yml`.
The PR title doubles as the squash-merge commit message on `main`,
so adding `[skip ci]` there is required to skip CI when the release
PR is auto-merged into `main`.
Affected flows:
- release.yml -> chore(release): vX.Y.Z
- release-sdk.yml -> chore(release): sdk-typescript vX.Y.Z
These PRs only bump `package.json` / `package-lock.json` versions
and do not need the full lint/test/CodeQL matrix to run on PR open,
on the release branch push, or on the post-merge push to `main`.
* ci(release): make skip-ci squash subject explicit
* ci(release): scope skip-ci to the squash commit and bind RELEASE_TAG
Two follow-ups on the release-PR skip-ci change:
- Drop `[skip ci]` from the release-branch commit. Putting it there
also suppressed `push: tags: [v*]` workflows because the release
tag points at the release-branch tip, so `Build and Publish Docker
Image` would silently skip when the release was tagged. Keeping the
marker only on the squash subject preserves the "do not re-run CI
on main" goal without affecting tag pushes.
- Export `RELEASE_TAG` to the `Enable auto-merge for release PR` step
in both workflows. The step runs under `set -euo pipefail`, so the
new `--subject` referencing `${RELEASE_TAG}` would otherwise crash
with `unbound variable` and abort the merge.
After a stable/patch CLI release, the release branch version bump was
never synced back to main, causing nightly versions to fall behind
stable (e.g. nightly 0.15.3 < stable 0.15.5). Add automated PR
creation and auto-merge steps (matching the existing release-sdk.yml
pattern) so package.json on main stays in sync after each release.
Resolves#3756
- Mark @qwen-code/channel-plugin-example as private in package.json
- Remove publish step from release workflow
- Remove file: to semver rewrite logic in version script
- Use file: reference for @qwen-code/channel-base dependency
This change prevents the example plugin from being published to npm, as it's only intended for internal/development use.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Move non-interactive tests to cli/, interactive tests to interactive/.
Add cron-interactive.test.ts wrapping terminal-capture E2E in vitest.
Update npm scripts and release workflow for new directory layout.
- Bump channel package versions to 0.13.0
- Add publish steps for @qwen-code/channel-base and @qwen-code/channel-plugin-example
- Update version script to convert file: references to semver for published packages
This enables proper npm publishing of channel packages during the release process.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Fix release-sdk.yml: Use file-based approach with --notes-file instead of
complex inline string interpolation to avoid shell parsing errors with
backticks and special characters
- Fix release.yml: Add --prerelease flag for nightly and preview releases
to properly mark them as pre-releases on GitHub
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>