refactor(cli): remove legacy qwen auth CLI subcommand, redirect to /auth TUI dialog#3959
Conversation
…/auth TUI dialog The `qwen auth` CLI subcommand (with subcommands like qwen-oauth, coding-plan, api-key, openrouter, status) has been superseded by the richer /auth TUI dialog introduced in the provider-first auth registry (#3864). Running `qwen auth` now prints a deprecation notice pointing users to the /auth TUI dialog (interactive), env vars (CI/headless), or /doctor (status check). Changes: - Replace auth.ts with a stub that prints a removal notice and exits - Delete handler.ts (734 lines), interactiveSelector.ts, and their tests (interactiveSelector.test.ts, openrouter.test.ts, status.test.ts) - Update /auth slash command to handle non-interactive/ACP modes gracefully - Enrich /doctor auth check with provider-aware diagnostics using findProviderByCredentials - Mark `auth` as a subcommand that handles its own exit in config.ts Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
qwen auth CLI subcommand, redirect to /auth TUI dialog
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
wenshao
left a comment
There was a problem hiding this comment.
Solid cleanup overall — ~2400 lines of dead code removed, the stub/guard/enrichment all read cleanly, and Lint/Linux CI is green. Two things before merge:
🔴 Documentation needs to ship in this PR
31 references to qwen auth remain in docs/. The most painful:
docs/users/configuration/auth.md:315-348— entire "qwen authCLI command" section with subcommand tabledocs/users/configuration/auth.md:13— auth-blocker banner says "Runqwen authto configure"docs/users/quickstart.md:82,225-227— quickstart command table listsqwen auth/api-key/statusdocs/design/openrouter-auth-and-models.md:32-33
A breaking removal landing while the official docs still tell users to run the removed command is the worst kind of UX cliff. These should be updated in this PR.
🟡 Test coverage for new behavior
Three test files were deleted, but no tests were updated/added for the new code paths — non-interactive /auth, the doctor enrichment, and the deprecation stub itself. The PR description's "330 files, 5255 tests passed" is true but elides that none of those tests cover this PR's behavior changes. See inline comments.
Other observations (minor, see inline)
- i18n regression in the deprecation notice
- Hardcoded
v0.15.8in the message - ANSI escapes without TTY /
NO_COLORguard process.exit(0)flush risk on pipes
Stale i18n entries in pt.js / ca.js / ru.js / ja.js for the deleted handler strings are dead but harmless — cleanup optional.
| describe: 'Configure authentication (removed since v0.15.8)', | ||
| builder: (yargs) => yargs.version(false), | ||
| handler: () => { | ||
| writeStdoutLine( | ||
| '\n' + | ||
| '\x1b[33m⚠ qwen auth has been removed since v0.15.8\x1b[0m\n' + | ||
| '\n' + | ||
| ' \x1b[36mInteractive\x1b[0m → run qwen and use /auth to configure (8+ providers, guided setup)\n' + | ||
| ' \x1b[36mCI / Headless\x1b[0m → set OPENAI_API_KEY + OPENAI_BASE_URL + OPENAI_MODEL env vars\n' + | ||
| ' or pass --openai-api-key, --openai-base-url, --model\n' + | ||
| ' \x1b[36mScripted\x1b[0m → edit \x1b[36m~/.qwen/settings.json\x1b[0m, or run qwen interactively once\n' + | ||
| '\n' + | ||
| ' Check auth status → \x1b[36m/doctor\x1b[0m\n', | ||
| ); | ||
| process.exit(0); |
There was a problem hiding this comment.
This stub bundles four small concerns worth addressing before merge:
-
i18n regression — the original handler used
t()everywhere; bothdescribe(line 19) and thewriteStdoutLinepayload (lines 24-31) are now English-only. zh / ja / ru / pt / ca / de / fr users lose the localized message. Suggest wrapping witht()(cf.useProviderUpdates.ts:310for the same pattern). -
Hardcoded version "v0.15.8" (lines 19, 24) —
package.jsonis0.15.8today, but if this ships in0.15.9/0.16.0the notice misleads. Source frompackage.jsonat runtime, or drop the version from the message. -
ANSI escapes without TTY /
NO_COLORguard (lines 24-31) — output redirected to a file or captured by CI tooling will contain literal\x1b[...mbytes, andNO_COLOR=1is ignored. Guard withprocess.stdout.isTTYor use the chalk-style helper used elsewhere. -
process.exit(0)flush (line 33) —process.stdout.writeis async on non-TTY, so on a pipe the message can be truncated. Useprocess.stdout.write(msg, () => process.exit(0))for safety (config.ts:640 marksauthas self-exiting, so an explicit exit is still needed).
| action: ( | ||
| context, | ||
| _args, | ||
| ): OpenDialogActionReturn | SlashCommandActionReturn => { | ||
| const executionMode = context.executionMode ?? 'interactive'; | ||
| if (executionMode !== 'interactive') { | ||
| return { | ||
| type: 'message', | ||
| messageType: 'info', | ||
| content: t( | ||
| 'Authentication configuration is only available in interactive mode. To configure authentication, run Qwen Code interactively and use /auth, or set environment variables: OPENAI_API_KEY, OPENAI_BASE_URL, OPENAI_MODEL.', | ||
| ), | ||
| }; | ||
| } | ||
| return { | ||
| type: 'dialog', | ||
| dialog: 'auth', | ||
| }; | ||
| }, |
There was a problem hiding this comment.
New non-interactive / ACP branch isn't covered by authCommand.test.ts (still 38 lines, no executionMode mention). Suggest adding tests asserting:
executionMode: 'non_interactive'returns{ type: 'message', messageType: 'info', content: ... }executionMode: 'acp'returns the sameexecutionMode: 'interactive'(or undefined) returns the dialog-open action
Also worth a small assertion that supportedModes includes all three values, since ['interactive'] was the previous contract.
| // Build enriched diagnostic information | ||
| const cgConfig = | ||
| typeof config?.getContentGeneratorConfig === 'function' | ||
| ? config.getContentGeneratorConfig() | ||
| : undefined; | ||
| const model = cgConfig?.model ?? config?.getModel(); | ||
| const baseUrl = cgConfig?.baseUrl; | ||
| const envKey = cgConfig?.apiKeyEnvKey; | ||
|
|
||
| const provider = findProviderByCredentials(baseUrl, envKey); | ||
|
|
||
| const detailParts: string[] = []; | ||
| if (provider) { | ||
| detailParts.push( | ||
| t('Provider: {{provider}}', { provider: t(provider.label) }), | ||
| ); | ||
| } | ||
| if (baseUrl) { | ||
| detailParts.push(t('Base URL: {{baseUrl}}', { baseUrl })); | ||
| } | ||
| if (model) { | ||
| detailParts.push(t('Model: {{model}}', { model })); | ||
| } | ||
| if (envKey) { | ||
| const hasKey = !!process.env[envKey]; | ||
| detailParts.push( | ||
| hasKey | ||
| ? t('API key: configured ({{envKey}})', { envKey }) | ||
| : t('API key: {{envKey}} not set', { envKey }), | ||
| ); | ||
| } | ||
|
|
||
| return { | ||
| category: t('Authentication'), | ||
| name: t('API key'), | ||
| status: 'pass', | ||
| message: t('configured ({{authType}})', { authType }), | ||
| detail: detailParts.length > 0 ? detailParts.join('\n') : undefined, |
There was a problem hiding this comment.
Enrichment block is untested — doctorChecks.test.ts doesn't cover:
findProviderByCredentialsreturning a known provider vs.undefined(e.g. user with a custom OpenAI-compatible base URL not in the registry)process.env[envKey]set vs. unset vs. empty string (!!process.env[envKey]correctly maps empty-string → "not set", but worth pinning down)cgConfigundefined fallback toconfig.getModel()detailPartsempty case (no provider, no baseUrl, no model, no envKey) falling through todetail: undefined
This is the new user-visible /doctor output, so a couple of focused cases would catch regressions.
tanzhenxin
left a comment
There was a problem hiding this comment.
Review
The structural cleanup here is real — the provider-first registry already supersedes everything the deleted handler did, and there are no orphaned imports left behind. But the migration UX the PR is built around is broken for every legacy invocation form this same repo's docs advertise. Two issues should land before merge; the third is a smaller polish on the deprecation notice itself.
1. qwen auth <subcommand> exits 1 with a yargs help dump instead of the deprecation notice (severity: high · confidence: very high)
The new stub registers a bare auth command with no nested subcommands, but the root parser is .strict(). So while the bare qwen auth correctly prints the friendly notice and exits 0, every documented legacy form — qwen auth status, qwen auth api-key, qwen auth qwen-oauth, qwen auth coding-plan --region china --key …, qwen auth openrouter --key …, even qwen auth --key foo — fails parsing first and prints a multi-screen yargs option dump followed by Unknown argument: status (or whichever token), exiting 1.
That means CI scripts and shell aliases that ran any of the documented subcommand forms now fail noisily with no migration guidance. The friendly notice is unreachable for exactly the users who would type the old commands. Either declare the legacy names as a catch-all positional or list each removed subcommand pointing at the same handler.
2. The user-facing docs still tell users to run the removed commands (severity: high · confidence: very high)
This PR touches zero docs. After it lands, the README, the quickstart, the auth configuration page, the commands reference, and the OpenRouter design doc all still instruct users to run qwen auth, qwen auth status, qwen auth api-key, qwen auth coding-plan --region china --key sk-sp-…, and qwen auth openrouter --key …. The auth configuration page in particular has an entire "qwen auth CLI command" section with usage tables.
Combined with #1, a user who follows the docs hits the broken path before they ever see the new "removed since v0.15.8" notice — and the broken path's output gives them no hint that the removal was intentional. The doc updates need to land in the same PR.
3. The deprecation notice has no real replacement for the scripted Coding Plan / OpenRouter / qwen-oauth flows (severity: medium · confidence: high)
The notice tells CI/headless users to set OPENAI_API_KEY + OPENAI_BASE_URL + OPENAI_MODEL. That's fine for plain API-key setups, but the deleted scripted flows did more than that: qwen auth coding-plan --region china --key … configured DashScope's region-specific endpoint and model defaults atomically; qwen auth openrouter --key … configured OpenRouter's endpoint and model; qwen-oauth was a non-interactive token bootstrap with no env-var equivalent. The notice doesn't mention OpenRouter at all, doesn't tell Coding Plan users which OPENAI_BASE_URL to use for China vs international, and doesn't acknowledge that OAuth flows can't reduce to env vars. The "Scripted → edit ~/.qwen/settings.json" pointer is correct but offers no schema or example.
Verdict
REQUEST_CHANGES — issues 1 and 2 together break the migration UX the PR is built around for every documented legacy invocation. The fix is small (catch-all the subcommand parser, update the docs in the same PR), and the underlying cleanup is good.
Register old subcommands (status, coding-plan, openrouter, api-key, qwen-oauth) as no-op redirects so users running 'qwen auth status' etc. see the removal notice instead of a raw yargs 'Unknown argument' error.
wenshao
left a comment
There was a problem hiding this comment.
LGTM! ✅
发现 1 个问题已自动修复并推送:
auth.ts— 旧子命令(qwen auth status等)在全局 yargs strict 模式下静默失效。修复:注册了 5 个旧子命令为 no-op 重定向,所有变体都正确显示弃用提示。
— deepseek-v4-pro via Qwen Code /review
The removal notice now wraps text with i18n, guards ANSI escapes behind TTY/NO_COLOR checks, uses a flush-safe process.exit path, and includes provider-specific migration guidance for Coding Plan, OpenRouter, and OAuth flows. Legacy subcommands register a catch-all positional so old invocations like `qwen auth status --key` reach the notice instead of a yargs strict-mode error. Docs replace every `qwen auth` reference with `/auth` or `/doctor` migration guidance. Adds tests for: - auth.ts stub and legacy subcommand routing - /auth non-interactive and ACP execution modes - /doctor provider-aware diagnostics (known provider, unknown credentials, missing env key, empty env key, fallback to config model, empty detail)
|
感谢 @wenshao 和 @tanzhenxin 的详细 review。已推送新 commit 处理如下点: 1. 弃用提示增强
2. 文档更新
3. 测试补充
|
tanzhenxin
left a comment
There was a problem hiding this comment.
Review
Both response commits land cleanly. Per-subcommand yargs registrations with .strict(false) on both outer and inner builders mean every legacy invocation form now exits 0 with the deprecation notice instead of dumping yargs help — verified end-to-end with the real CLI for auth, auth status, auth api-key, auth qwen-oauth, auth coding-plan --region china --key …, auth openrouter --key …, auth --key foo, and auth unknown-subcommand. The notice text now covers Coding Plan (both Beijing and international endpoints), OpenRouter, Qwen OAuth, and the Scripted path. Test coverage for the stub, the non-interactive/ACP guard, and the doctor enrichment branches all landed.
One small polish item worth folding in before merge: the top-of-README News bullet still ends with "Run qwen auth to configure," which is internally contradictory with the auth and commands docs that now mark the command as removed. Functionally non-breaking — qwen auth does print the deprecation notice — but a one-line change to "Run qwen and use /auth to configure" closes it.
Verdict
APPROVE — prior blockers are closed end-to-end.
…y-cli-auth # Conflicts: # packages/core/src/tools/syntheticOutput.test.ts
…/auth TUI dialog (#3959) The `qwen auth` CLI subcommand (with subcommands like qwen-oauth, coding-plan, api-key, openrouter, status) has been superseded by the richer /auth TUI dialog introduced in the provider-first auth registry (#3864). Running `qwen auth` now prints a deprecation notice pointing users to the /auth TUI dialog (interactive), env vars (CI/headless), or /doctor (status check). Changes: - Replace auth.ts with a stub that prints a removal notice and exits - Delete handler.ts (734 lines), interactiveSelector.ts, and their tests (interactiveSelector.test.ts, openrouter.test.ts, status.test.ts) - Update /auth slash command to handle non-interactive/ACP modes gracefully - Enrich /doctor auth check with provider-aware diagnostics using findProviderByCredentials - Mark `auth` as a subcommand that handles its own exit in config.ts Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Summary
The
qwen authCLI subcommand — with its five sub-subcommands (qwen-oauth,coding-plan,api-key,openrouter,status) plus an interactive selector — was the original way to configure authentication. It worked, but it was a 734-line handler that hardcoded every provider's auth flow as if/else branches, couldn't keep up as we added new providers, and gave users a narrow, text-only experience.That handler was superseded by the provider-first auth registry (#3864), which introduced 8 declarative provider configs and a rich
/authTUI dialog. The old CLI subcommand was still sitting there, duplicating logic and confusing users who ranqwen authinstead of/auth.This PR removes the legacy
qwen authsubcommand entirely. Runningqwen authnow prints a clear deprecation notice with migration paths, then exits. The/authslash command is also updated to handle non-interactive and ACP execution modes gracefully, and/doctornow shows provider-aware diagnostics instead of a bare auth-type string.handler.ts(734 lines),interactiveSelector.ts, and 3 test files; replacedauth.tswith a stub; updated/authslash command for non-interactive/ACP modes; enriched/doctorauth check with provider info fromfindProviderByCredentials.auth.ts(deprecation notice + exit), theauthCommand.tsnon-interactive guard, and thedoctorChecks.tsenrichment logic. All 330 test files pass.User Perspective / Usage Steps
Before this PR: Running
qwen authlaunched an interactive CLI menu — or one of its sub-subcommands. The experience was text-only, limited to 5 hardcoded paths, and had no way to discover the newer providers (Token Plan, Alibaba Standard, DeepSeek, MiniMax, Z.AI, custom OpenAI-compatible).After this PR:
qwen authprints a removal notice and exits. Users are guided to the right path based on their scenario:For interactive users,
/authinside the TUI opens a 3-level wizard with 8+ providers grouped by category (Alibaba ModelStudio, Third-party, OAuth, Custom). For CI/headless users, the notice tells them exactly which env vars to set. For scripted setups, it points tosettings.json.In non-interactive/ACP mode, running
/authreturns a message instead of trying to open a dialog:/doctornow shows enriched auth diagnostics — provider name, base URL, model, and API key status — instead of just "configured (USE_OPENAI)".Validation
qwen authprints a deprecation notice and exits cleanly;/authin non-interactive mode returns an informational message;/doctorshows provider-aware auth diagnostics.node dist/cli.js authto see the deprecation notice, then checkdoctorChecks.tsfor the enrichment logic.Scope / Risk
qwen authas a CLI command (e.g., in CI scripts or shell aliases) will see a deprecation notice instead of the old interactive menu. The notice provides clear alternatives, so the migration path is explicit./authTUI dialog is unchanged — only tested via existing unit tests. No E2E test for the TUI flow in this PR.qwen authis removed. Users should switch to/auth(interactive), env vars (CI/headless), orsettings.json(scripted). This is a breaking change for anyone scriptingqwen authsubcommands.Testing Matrix
Testing matrix notes:
Linked Issues / Bugs
No linked issues
中文说明
背景
qwen authCLI 子命令(包含qwen-oauth、coding-plan、api-key、openrouter、status五个子命令以及交互式选择器)曾是配置认证的唯一入口。它的 handler.ts 有 734 行,用 if/else 分支硬编码每个 provider 的认证流程,无法跟上新 provider 的扩展节奏,且只提供纯文本的窄体验。provider-first auth registry (#3864) 引入了 8 个声明式 provider 配置和丰富的
/authTUI 对话框,彻底取代了这套逻辑。但旧的 CLI 子命令仍然残留,与/auth对话框逻辑重复,也给用户造成混淆——有人跑qwen auth而不是/auth,走进了更差的路。变更内容
handler.ts(734 行)、interactiveSelector.ts及 3 个测试文件auth.ts替换为 stub:打印移除提示 + 退出/auth斜杠命令增加 non-interactive/ACP 模式守卫(返回提示消息而非尝试打开对话框)/doctor认证检查增加 provider-aware 诊断信息(provider 名称、base URL、模型、API key 状态)用户视角
运行
qwen auth后的输出:交互模式下
/auth打开 3 级向导,支持 8+ 个 provider(阿里百炼、第三方、OAuth、自定义)。CI/headless 用户通过环境变量配置。脚本化用户直接编辑settings.json。非交互/ACP 模式下
/auth返回提示消息,不再尝试打开对话框。/doctor认证检查现在显示 provider 名称、base URL、模型和 API key 状态,而非仅显示 "configured (USE_OPENAI)"。验证
Breaking Change
qwen auth命令被移除。 依赖该命令的 CI 脚本或 shell alias 需迁移至/auth(交互)、环境变量(CI/headless)或settings.json(脚本化)。移除提示给出了明确的三条替代路径。🤖 Generated with Qwen Code