feat(vscode): replace OAuth with Coding Plan / API Key provider setup#3398
Conversation
Extract Coding Plan region configs, model templates, and utility functions into packages/core/src/constants/ so both CLI and VSCode extension can import from a shared source of truth.
… path Update all CLI files to import CodingPlanRegion, CODING_PLAN_ENV_KEY, and related utilities from @qwen-code/qwen-code-core, replacing the local ../../constants/codingPlan.js imports.
…a VSCode Settings Replace the OAuth-based login command with a settings-driven provider configuration flow. Users now configure Coding Plan or API Key providers through VSCode Settings (qwen-code.*), which auto-syncs to ~/.qwen/settings.json. - Rename login command to auth, opening VSCode Settings panel - Add /auth2 interactive flow (QuickPick + InputBox) - Add ProviderSetupForm onboarding component with inline config - Add bidirectional sync between VSCode settings and ~/.qwen/settings.json - Add settingsWriter service for direct settings.json read/write - Add VSCode configuration schema (provider, apiKey, region, model, etc.) - Update all login/session messages to use auth terminology
…fix sync guard - Rename auth2 to auth for all message types, handlers, and slash command - Remove unused InfoBanner.tsx (128 lines, no references) - Remove dead openProviderSettings handler (no callers) - Remove redundant qwen-code.baseUrl VSCode setting (already in modelProviders) - Replace unreliable setTimeout(500) sync guard with await Promise.all + finally - Clean up old authHandler/setAuthHandler in favor of authInteractiveHandler
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. |
… simplify sync - Remove qwen-code.modelProviders and qwen-code.model from package.json (model switching handled by chat UI's /model command, not VSCode Settings) - Remove connectWithSettings message handler and plumbing (no webview component sends this message type) - Remove handleConnectWithSettings method from WebViewProvider - Simplify syncVSCodeSettingsToQwenConfig: only sync provider/apiKey/region - Simplify syncQwenConfigToVSCodeSettings: only populate provider/apiKey/region - Simplify QwenSettingsForVSCode interface: remove modelProviders and model - Improve Onboarding UI: logo above card, better hierarchy, arrow icon on button
Add onDidChangeConfiguration and getConfiguration to the vscode.workspace mock in WebViewProvider.test.ts to fix CI test failures.
… cancel handling, add tests - Clear CODING_PLAN_ENV_KEY and codingPlan metadata when switching to api-key mode - Add authCancelled notification when QuickPick/InputBox is dismissed - ProviderSetupForm resets button state on authCancelled - syncVSCodeSettingsToQwenConfig returns false for api-key mode (no-op) - Fix Onboarding vertical centering (flex-1 min-h-0) - Import from @qwen-code/qwen-code-core top-level instead of deep paths - Add tests: settingsWriter, ProviderSetupForm cancel, AuthMessageHandler cancel, WebViewProvider sync - Fix redundant ternary in pick() helper
…rride Parent container uses [&>*]:items-start and [&>*]:text-left which overrides Tailwind classes. Use inline style for alignItems/justifyContent/textAlign to ensure Onboarding is always centered both horizontally and vertically.
# Conflicts: # packages/vscode-ide-companion/src/webview/App.tsx # packages/vscode-ide-companion/src/webview/providers/WebViewProvider.test.ts
wenshao
left a comment
There was a problem hiding this comment.
packages/webui/src/components/PermissionDrawer.tsx:192-195
[Critical] The new planText extraction accesses index-signature-backed fields with dot notation (item.content, inner.type, inner.text), which violates this repo's strict TypeScript configuration and causes TS4111 on changed code.
Suggested fix: use bracket notation consistently for those fields, for example item['content'], inner['type'], and inner['text'].
— gpt-5.4 via Qwen Code /review
wenshao
left a comment
There was a problem hiding this comment.
I found blocking issues in this PR:
- Critical:
packages/vscode-ide-companion/src/webview/providers/WebViewProvider.tsunconditionally emitsauthSuccessafter reconnecting, even when the reconnect still requires authentication. Invalid or rejected credentials can therefore show a success state and then immediately reopen auth. - Critical:
packages/core/src/tools/swarm.tsonly excludesask_user_questionfrom the default worker blacklist, so workers can still call recursive tools likeagentandswarm, which breaks the non-recursive tool contract and can cause recursive fan-out. - Critical: the changed VS Code companion files still have TypeScript errors (mainly TS4111 index-signature property access in
settingsWriter.ts,SessionMessageHandler.ts, anduseWebViewMessages.ts). - Suggestion:
readQwenSettingsForVSCode()/ settings sync mirrors non-Coding-Plan secrets intoqwen-code.apiKey, even though the setting description says that key is for Coding Plan sync and API-key providers should be configured through the auth flow.
— gpt-5.4 via Qwen Code /review
…state After reconnecting in handleAuthInteractive, doInitializeAgentConnection may return without throwing even when credentials are rejected (it sends authState:false internally and returns early). Previously we unconditionally emitted authSuccess, which contradicted the failed auth state and could briefly show a success toast before re-opening the auth flow. Now we check this.authState after reconnection: only emit authSuccess when authentication actually succeeded, otherwise emit authError with a clear credentials message. Addresses review feedback from PR #3398.
…ync) Merge main branch into feat/vscode-provider-setup, resolving conflicts in WebViewProvider.ts (import section) and WebViewProvider.test.ts (mock setup). Both sides are preserved: - main: parseInsightMessage import, isInsightCommand helper, setupAttachedProvider test helper, availableCommandsCallbackRef, slashCommandNotificationCallbackRef, mockOpenExternal - feat: settings sync mocks, config change handlers, settingsWriter mock, createConfigChangeEvent helper
| : createWebViewProvider(); | ||
|
|
||
| await provider.show(); | ||
| await provider.startInteractiveAuth(); |
There was a problem hiding this comment.
[Critical] This command now calls provider.show() and then immediately calls provider.startInteractiveAuth(). For a newly created provider, show() already runs the startup restoration path, and that path auto-launches the auth flow when authentication is required. A first-time Qwen Code: Auth invocation can therefore open two overlapping auth flows from one command.
Please guard one of the two entry points so a provider instance can only launch the interactive auth flow once per command invocation.
— gpt-5.4 via Qwen Code /review
There was a problem hiding this comment.
Fixed in 4723de1. Added autoAuthTimer + authFlowActive guard to WebViewProvider:
startInteractiveAuth()now clears any pending auto-auth timeout before launching, and uses anauthFlowActivemutex to prevent re-entry.- The deferred auto-auth
setTimeoutindoInitializeAgentConnectionis stored so it can be cancelled, and checksauthFlowActivebefore firing.
This ensures only one interactive auth flow runs per command invocation.
| ([id, baseUrl]) => ({ | ||
| id, | ||
| name: id, | ||
| baseUrl: baseUrl || 'https://api.openai.com/v1', |
There was a problem hiding this comment.
[Critical] writeModelProvidersConfig() replaces the entire modelProviders[AuthType.USE_OPENAI] array with only the models entered in the current VSCode auth flow. Unlike writeCodingPlanConfig(), it does not preserve existing unrelated OpenAI-compatible entries, so reconfiguring one provider here can silently delete other custom providers already stored in ~/.qwen/settings.json.
Please merge the updated models with the existing non-target entries instead of overwriting the whole list.
— gpt-5.4 via Qwen Code /review
There was a problem hiding this comment.
Fixed in 4723de1. writeModelProvidersConfig() now merges new entries with existing non-target models (those with a different envKey) instead of replacing the entire modelProviders[openai] array. This matches the merge pattern already used in writeCodingPlanConfig().
Updated settingsWriter.test.ts to verify that non-target entries (e.g. Coding Plan models) are preserved after reconfiguration.
…roviders - Add authFlowActive mutex and autoAuthTimer to WebViewProvider so startInteractiveAuth() cancels the deferred auto-auth timeout, preventing two overlapping QuickPick flows from a single command. - Change writeModelProvidersConfig() to merge new entries with existing non-target models (different envKey) instead of replacing the entire array, preserving unrelated providers like Coding Plan.
| * | ||
| * @returns true if settings were synced (apiKey is configured), false otherwise | ||
| */ | ||
| private async syncVSCodeSettingsToQwenConfig(): Promise<boolean> { |
There was a problem hiding this comment.
[Critical] Clearing qwen-code.apiKey in VS Code settings does not revoke the persisted Coding Plan credential. syncVSCodeSettingsToQwenConfig() returns early when the key is empty, so the old credential remains in ~/.qwen/settings.json and the existing agent is not disconnected. Users who remove the key in Settings can therefore continue auto-connecting with stale credentials.
Treat an empty qwen-code.apiKey as an explicit de-auth signal: clear the persisted Coding Plan credential/metadata, set auth state to false, and disconnect or reconnect so runtime state matches Settings.
— gpt-5.4 via Qwen Code /review
There was a problem hiding this comment.
Fixed. syncVSCodeSettingsToQwenConfig() returning false with an empty apiKey now triggers a de-auth path in the config change handler:
- Added
clearPersistedAuth()insettingsWriter.ts— removesselectedType, API keys (CODING_PLAN_ENV_KEYandOPENAI_API_KEY), and coding plan metadata from~/.qwen/settings.json. - The
onDidChangeConfigurationhandler now detects the empty-apiKey + active-agent case: callsclearPersistedAuth(), disconnects the agent, setsauthState = false, and notifies the webview.
This ensures clearing qwen-code.apiKey in Settings is treated as an explicit de-auth signal — runtime state, persisted credentials, and UI all reflect the unauthenticated state.
…fix auto-auth race, clean imports - Add clearPersistedAuth() to settingsWriter.ts: removes selectedType, API keys, and coding plan metadata from ~/.qwen/settings.json - Config change handler now detects empty apiKey with active agent and triggers de-auth: clear credentials, disconnect, update authState - Auto-auth timer callback now properly sets authFlowActive mutex to prevent concurrent auth flows with startInteractiveAuth() - Add test covering the de-auth path (clearPersistedAuth + disconnect) - Fix import formatting in 7 CLI files (spacing, trailing commas) - Remove duplicate comment in attemptAuthStateRestoration()
The previous de-auth logic triggered on any auth-related setting change
where syncVSCodeSettingsToQwenConfig() returned false. For api-key
providers this is the normal path (interactive auth owns config), so
changing codingPlanRegion or provider would incorrectly wipe OPENAI_API_KEY.
Now the de-auth branch only fires when e.affectsConfiguration('qwen-code.apiKey')
is true AND the value is empty, preventing false-positive credential clearing.
Add regression test: non-apiKey setting changes on an api-key provider
must not trigger clearPersistedAuth or disconnect.
…check The hoisted mockQwenAgentManagerInstances type was missing the disconnect property, causing TS2339 in the de-auth test assertions.
wenshao
left a comment
There was a problem hiding this comment.
No issues found. LGTM! ✅ — gpt-5.4 via Qwen Code /review
Resolve App.tsx conflict: adopt login→auth rename and keep consistent closeCompletion() call style from the skills picker branch.
…#3398) * refactor(core): move codingPlan constants from cli to core package Extract Coding Plan region configs, model templates, and utility functions into packages/core/src/constants/ so both CLI and VSCode extension can import from a shared source of truth. * refactor(cli): import codingPlan constants from core instead of local path Update all CLI files to import CodingPlanRegion, CODING_PLAN_ENV_KEY, and related utilities from @qwen-code/qwen-code-core, replacing the local ../../constants/codingPlan.js imports. * feat(vscode-ide-companion): replace login flow with provider setup via VSCode Settings Replace the OAuth-based login command with a settings-driven provider configuration flow. Users now configure Coding Plan or API Key providers through VSCode Settings (qwen-code.*), which auto-syncs to ~/.qwen/settings.json. - Rename login command to auth, opening VSCode Settings panel - Add /auth2 interactive flow (QuickPick + InputBox) - Add ProviderSetupForm onboarding component with inline config - Add bidirectional sync between VSCode settings and ~/.qwen/settings.json - Add settingsWriter service for direct settings.json read/write - Add VSCode configuration schema (provider, apiKey, region, model, etc.) - Update all login/session messages to use auth terminology * refactor(vscode-ide-companion): rename auth2→auth, remove dead code, fix sync guard - Rename auth2 to auth for all message types, handlers, and slash command - Remove unused InfoBanner.tsx (128 lines, no references) - Remove dead openProviderSettings handler (no callers) - Remove redundant qwen-code.baseUrl VSCode setting (already in modelProviders) - Replace unreliable setTimeout(500) sync guard with await Promise.all + finally - Clean up old authHandler/setAuthHandler in favor of authInteractiveHandler * refactor(vscode-ide-companion): remove dead VSCode Settings plumbing, simplify sync - Remove qwen-code.modelProviders and qwen-code.model from package.json (model switching handled by chat UI's /model command, not VSCode Settings) - Remove connectWithSettings message handler and plumbing (no webview component sends this message type) - Remove handleConnectWithSettings method from WebViewProvider - Simplify syncVSCodeSettingsToQwenConfig: only sync provider/apiKey/region - Simplify syncQwenConfigToVSCodeSettings: only populate provider/apiKey/region - Simplify QwenSettingsForVSCode interface: remove modelProviders and model - Improve Onboarding UI: logo above card, better hierarchy, arrow icon on button * fix(vscode-ide-companion): add missing vscode.workspace mock in test Add onDidChangeConfiguration and getConfiguration to the vscode.workspace mock in WebViewProvider.test.ts to fix CI test failures. * fix(vscode-ide-companion): clean up stale coding plan state, add auth cancel handling, add tests - Clear CODING_PLAN_ENV_KEY and codingPlan metadata when switching to api-key mode - Add authCancelled notification when QuickPick/InputBox is dismissed - ProviderSetupForm resets button state on authCancelled - syncVSCodeSettingsToQwenConfig returns false for api-key mode (no-op) - Fix Onboarding vertical centering (flex-1 min-h-0) - Import from @qwen-code/qwen-code-core top-level instead of deep paths - Add tests: settingsWriter, ProviderSetupForm cancel, AuthMessageHandler cancel, WebViewProvider sync - Fix redundant ternary in pick() helper * fix(vscode-ide-companion): force center Onboarding against parent override Parent container uses [&>*]:items-start and [&>*]:text-left which overrides Tailwind classes. Use inline style for alignItems/justifyContent/textAlign to ensure Onboarding is always centered both horizontally and vertically. * fix(vscode-ide-companion): bundle onboarding logo * test(vscode-ide-companion): add png loader to bundle test * fix(vscode-ide-companion/webview): avoid redundant auth sync reconnects * fix(vscode-ide-companion/webview): fix auth sync typecheck * docs(vscode-ide-companion): clarify auth restoration flow * fix(webui): use bracket access for permission drawer plan content * fix(vscode-ide-companion): guard authSuccess emission on actual auth state After reconnecting in handleAuthInteractive, doInitializeAgentConnection may return without throwing even when credentials are rejected (it sends authState:false internally and returns early). Previously we unconditionally emitted authSuccess, which contradicted the failed auth state and could briefly show a success toast before re-opening the auth flow. Now we check this.authState after reconnection: only emit authSuccess when authentication actually succeeded, otherwise emit authError with a clear credentials message. Addresses review feedback from PR #3398. * fix(vscode): address auth setup review feedback * fix(vscode-ide-companion): guard concurrent auth flows, merge model providers - Add authFlowActive mutex and autoAuthTimer to WebViewProvider so startInteractiveAuth() cancels the deferred auto-auth timeout, preventing two overlapping QuickPick flows from a single command. - Change writeModelProvidersConfig() to merge new entries with existing non-target models (different envKey) instead of replacing the entire array, preserving unrelated providers like Coding Plan. * fix(vscode-ide-companion): handle apiKey clearing as de-auth signal, fix auto-auth race, clean imports - Add clearPersistedAuth() to settingsWriter.ts: removes selectedType, API keys, and coding plan metadata from ~/.qwen/settings.json - Config change handler now detects empty apiKey with active agent and triggers de-auth: clear credentials, disconnect, update authState - Auto-auth timer callback now properly sets authFlowActive mutex to prevent concurrent auth flows with startInteractiveAuth() - Add test covering the de-auth path (clearPersistedAuth + disconnect) - Fix import formatting in 7 CLI files (spacing, trailing commas) - Remove duplicate comment in attemptAuthStateRestoration() * fix(vscode-ide-companion): scope de-auth to apiKey changes only The previous de-auth logic triggered on any auth-related setting change where syncVSCodeSettingsToQwenConfig() returned false. For api-key providers this is the normal path (interactive auth owns config), so changing codingPlanRegion or provider would incorrectly wipe OPENAI_API_KEY. Now the de-auth branch only fires when e.affectsConfiguration('qwen-code.apiKey') is true AND the value is empty, preventing false-positive credential clearing. Add regression test: non-apiKey setting changes on an api-key provider must not trigger clearPersistedAuth or disconnect. * fix(vscode-ide-companion): add disconnect to mock type to fix CI typecheck The hoisted mockQwenAgentManagerInstances type was missing the disconnect property, causing TS2339 in the de-auth test assertions.
…QwenLM#3398) * refactor(core): move codingPlan constants from cli to core package Extract Coding Plan region configs, model templates, and utility functions into packages/core/src/constants/ so both CLI and VSCode extension can import from a shared source of truth. * refactor(cli): import codingPlan constants from core instead of local path Update all CLI files to import CodingPlanRegion, CODING_PLAN_ENV_KEY, and related utilities from @qwen-code/qwen-code-core, replacing the local ../../constants/codingPlan.js imports. * feat(vscode-ide-companion): replace login flow with provider setup via VSCode Settings Replace the OAuth-based login command with a settings-driven provider configuration flow. Users now configure Coding Plan or API Key providers through VSCode Settings (qwen-code.*), which auto-syncs to ~/.qwen/settings.json. - Rename login command to auth, opening VSCode Settings panel - Add /auth2 interactive flow (QuickPick + InputBox) - Add ProviderSetupForm onboarding component with inline config - Add bidirectional sync between VSCode settings and ~/.qwen/settings.json - Add settingsWriter service for direct settings.json read/write - Add VSCode configuration schema (provider, apiKey, region, model, etc.) - Update all login/session messages to use auth terminology * refactor(vscode-ide-companion): rename auth2→auth, remove dead code, fix sync guard - Rename auth2 to auth for all message types, handlers, and slash command - Remove unused InfoBanner.tsx (128 lines, no references) - Remove dead openProviderSettings handler (no callers) - Remove redundant qwen-code.baseUrl VSCode setting (already in modelProviders) - Replace unreliable setTimeout(500) sync guard with await Promise.all + finally - Clean up old authHandler/setAuthHandler in favor of authInteractiveHandler * refactor(vscode-ide-companion): remove dead VSCode Settings plumbing, simplify sync - Remove qwen-code.modelProviders and qwen-code.model from package.json (model switching handled by chat UI's /model command, not VSCode Settings) - Remove connectWithSettings message handler and plumbing (no webview component sends this message type) - Remove handleConnectWithSettings method from WebViewProvider - Simplify syncVSCodeSettingsToQwenConfig: only sync provider/apiKey/region - Simplify syncQwenConfigToVSCodeSettings: only populate provider/apiKey/region - Simplify QwenSettingsForVSCode interface: remove modelProviders and model - Improve Onboarding UI: logo above card, better hierarchy, arrow icon on button * fix(vscode-ide-companion): add missing vscode.workspace mock in test Add onDidChangeConfiguration and getConfiguration to the vscode.workspace mock in WebViewProvider.test.ts to fix CI test failures. * fix(vscode-ide-companion): clean up stale coding plan state, add auth cancel handling, add tests - Clear CODING_PLAN_ENV_KEY and codingPlan metadata when switching to api-key mode - Add authCancelled notification when QuickPick/InputBox is dismissed - ProviderSetupForm resets button state on authCancelled - syncVSCodeSettingsToQwenConfig returns false for api-key mode (no-op) - Fix Onboarding vertical centering (flex-1 min-h-0) - Import from @qwen-code/qwen-code-core top-level instead of deep paths - Add tests: settingsWriter, ProviderSetupForm cancel, AuthMessageHandler cancel, WebViewProvider sync - Fix redundant ternary in pick() helper * fix(vscode-ide-companion): force center Onboarding against parent override Parent container uses [&>*]:items-start and [&>*]:text-left which overrides Tailwind classes. Use inline style for alignItems/justifyContent/textAlign to ensure Onboarding is always centered both horizontally and vertically. * fix(vscode-ide-companion): bundle onboarding logo * test(vscode-ide-companion): add png loader to bundle test * fix(vscode-ide-companion/webview): avoid redundant auth sync reconnects * fix(vscode-ide-companion/webview): fix auth sync typecheck * docs(vscode-ide-companion): clarify auth restoration flow * fix(webui): use bracket access for permission drawer plan content * fix(vscode-ide-companion): guard authSuccess emission on actual auth state After reconnecting in handleAuthInteractive, doInitializeAgentConnection may return without throwing even when credentials are rejected (it sends authState:false internally and returns early). Previously we unconditionally emitted authSuccess, which contradicted the failed auth state and could briefly show a success toast before re-opening the auth flow. Now we check this.authState after reconnection: only emit authSuccess when authentication actually succeeded, otherwise emit authError with a clear credentials message. Addresses review feedback from PR QwenLM#3398. * fix(vscode): address auth setup review feedback * fix(vscode-ide-companion): guard concurrent auth flows, merge model providers - Add authFlowActive mutex and autoAuthTimer to WebViewProvider so startInteractiveAuth() cancels the deferred auto-auth timeout, preventing two overlapping QuickPick flows from a single command. - Change writeModelProvidersConfig() to merge new entries with existing non-target models (different envKey) instead of replacing the entire array, preserving unrelated providers like Coding Plan. * fix(vscode-ide-companion): handle apiKey clearing as de-auth signal, fix auto-auth race, clean imports - Add clearPersistedAuth() to settingsWriter.ts: removes selectedType, API keys, and coding plan metadata from ~/.qwen/settings.json - Config change handler now detects empty apiKey with active agent and triggers de-auth: clear credentials, disconnect, update authState - Auto-auth timer callback now properly sets authFlowActive mutex to prevent concurrent auth flows with startInteractiveAuth() - Add test covering the de-auth path (clearPersistedAuth + disconnect) - Fix import formatting in 7 CLI files (spacing, trailing commas) - Remove duplicate comment in attemptAuthStateRestoration() * fix(vscode-ide-companion): scope de-auth to apiKey changes only The previous de-auth logic triggered on any auth-related setting change where syncVSCodeSettingsToQwenConfig() returned false. For api-key providers this is the normal path (interactive auth owns config), so changing codingPlanRegion or provider would incorrectly wipe OPENAI_API_KEY. Now the de-auth branch only fires when e.affectsConfiguration('qwen-code.apiKey') is true AND the value is empty, preventing false-positive credential clearing. Add regression test: non-apiKey setting changes on an api-key provider must not trigger clearPersistedAuth or disconnect. * fix(vscode-ide-companion): add disconnect to mock type to fix CI typecheck The hoisted mockQwenAgentManagerInstances type was missing the disconnect property, causing TS2339 in the de-auth test assertions.
Summary
Replace the discontinued Qwen OAuth login flow in the VSCode extension with an interactive provider setup supporting Coding Plan, Alibaba Standard API Key, and Custom API Key.
/auth)~/.qwen/settings.jsonpackages/clitopackages/coreas shared moduleloginterminology toauthacross the extension/authslash command for re-configuration from chatResolves #3397
Resolves #3274
Related: #3203
Demo
The demo shows the onboarding flow after clicking "Get Started": a QuickPick dialog appears with Coding Plan and API Key options, followed by sub-menus for region selection and key input — mirroring the CLI's /auth interactive experience.
Kapture.2026-04-17.at.23.13.57.mp4
Changes
constants/codingPlan.ts,index.tsAuthMessageHandler.tssettingsWriter.ts~/.qwen/settings.jsonWebViewProvider.tspackage.jsonSessionMessageHandler.tsOnboarding.tsx,ProviderSetupForm.tsxTest plan
/authslash command re-triggers the flow from chat~/.qwen/settings.jsonvia CLI → Reload Window → VSCode Settings shows values~/.qwen/settings.jsonupdated → CLI reconnects