feat(vscode): replace OAuth with Coding Plan / API Key provider setup (#3398)
Some checks are pending
Qwen Code CI / Lint (push) Waiting to run
Qwen Code CI / Test (push) Blocked by required conditions
Qwen Code CI / Test-1 (push) Blocked by required conditions
Qwen Code CI / Test-2 (push) Blocked by required conditions
Qwen Code CI / Test-3 (push) Blocked by required conditions
Qwen Code CI / Test-4 (push) Blocked by required conditions
Qwen Code CI / Test-5 (push) Blocked by required conditions
Qwen Code CI / Test-6 (push) Blocked by required conditions
Qwen Code CI / Test-7 (push) Blocked by required conditions
Qwen Code CI / Test-8 (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run

* 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.
This commit is contained in:
易良 2026-04-21 22:20:58 +08:00 committed by GitHub
parent ebe364d0b8
commit e49867a762
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
37 changed files with 2284 additions and 282 deletions

View file

@ -17,7 +17,7 @@ import {
isCodingPlanConfig,
CodingPlanRegion,
CODING_PLAN_ENV_KEY,
} from '../../constants/codingPlan.js';
} from '@qwen-code/qwen-code-core';
import { getPersistScopeForModelSelection } from '../../config/modelProvidersScope.js';
import { backupSettingsFile } from '../../utils/settingsUtils.js';
import { loadSettings, type LoadedSettings } from '../../config/settings.js';

View file

@ -6,8 +6,7 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { showAuthStatus } from './handler.js';
import { AuthType } from '@qwen-code/qwen-code-core';
import { CODING_PLAN_ENV_KEY } from '../../constants/codingPlan.js';
import { AuthType, CODING_PLAN_ENV_KEY } from '@qwen-code/qwen-code-core';
import type { LoadedSettings } from '../../config/settings.js';
vi.mock('../../config/settings.js', () => ({

View file

@ -6,7 +6,11 @@
import type React from 'react';
import { useState } from 'react';
import { AuthType } from '@qwen-code/qwen-code-core';
import {
AuthType,
CodingPlanRegion,
isCodingPlanConfig,
} from '@qwen-code/qwen-code-core';
import { Box, Text } from 'ink';
import Link from 'ink-link';
import { theme } from '../semantic-colors.js';
@ -18,10 +22,6 @@ import { useUIState } from '../contexts/UIStateContext.js';
import { useUIActions } from '../contexts/UIActionsContext.js';
import { useConfig } from '../contexts/ConfigContext.js';
import { t } from '../../i18n/index.js';
import {
CodingPlanRegion,
isCodingPlanConfig,
} from '../../constants/codingPlan.js';
import {
ALIBABA_STANDARD_API_KEY_ENDPOINTS,
type AlibabaStandardRegion,

View file

@ -15,6 +15,10 @@ import {
AuthType,
getErrorMessage,
logAuth,
getCodingPlanConfig,
isCodingPlanConfig,
CodingPlanRegion,
CODING_PLAN_ENV_KEY,
} from '@qwen-code/qwen-code-core';
import { useCallback, useEffect, useState } from 'react';
import type { LoadedSettings } from '../../config/settings.js';
@ -29,12 +33,6 @@ import { useQwenAuth } from '../hooks/useQwenAuth.js';
import { AuthState, MessageType } from '../types.js';
import type { HistoryItem } from '../types.js';
import { t } from '../../i18n/index.js';
import {
getCodingPlanConfig,
isCodingPlanConfig,
CodingPlanRegion,
CODING_PLAN_ENV_KEY,
} from '../../constants/codingPlan.js';
import { backupSettingsFile } from '../../utils/settingsUtils.js';
import {
ALIBABA_STANDARD_API_KEY_ENDPOINTS,

View file

@ -11,7 +11,7 @@ import { TextInput } from './shared/TextInput.js';
import { theme } from '../semantic-colors.js';
import { useKeypress } from '../hooks/useKeypress.js';
import { t } from '../../i18n/index.js';
import { CodingPlanRegion } from '../../constants/codingPlan.js';
import { CodingPlanRegion } from '@qwen-code/qwen-code-core';
import Link from 'ink-link';
interface ApiKeyInputProps {

View file

@ -5,13 +5,12 @@
*/
import { Box } from 'ink';
import { AuthType } from '@qwen-code/qwen-code-core';
import { AuthType, isCodingPlanConfig } from '@qwen-code/qwen-code-core';
import { Header, AuthDisplayType } from './Header.js';
import { Tips } from './Tips.js';
import { useSettings } from '../contexts/SettingsContext.js';
import { useConfig } from '../contexts/ConfigContext.js';
import { useUIState } from '../contexts/UIStateContext.js';
import { isCodingPlanConfig } from '../../constants/codingPlan.js';
interface AppHeaderProps {
version: string;

View file

@ -13,9 +13,9 @@ import {
type AuthType,
type EditorType,
type ApprovalMode,
type CodingPlanRegion,
} from '@qwen-code/qwen-code-core';
import { type SettingScope } from '../../config/settings.js';
import { type CodingPlanRegion } from '../../constants/codingPlan.js';
import { type AlibabaStandardRegion } from '../../constants/alibabaStandardApiKey.js';
import type { AuthState } from '../types.js';
import { type ArenaDialogType } from '../hooks/useArenaCommand.js';

View file

@ -11,8 +11,8 @@ import {
CODING_PLAN_ENV_KEY,
getCodingPlanConfig,
CodingPlanRegion,
} from '../../constants/codingPlan.js';
import { AuthType } from '@qwen-code/qwen-code-core';
AuthType,
} from '@qwen-code/qwen-code-core';
// Get region configs for testing
const chinaConfig = getCodingPlanConfig(CodingPlanRegion.CHINA);

View file

@ -6,15 +6,15 @@
import { useCallback, useEffect, useState } from 'react';
import type { Config, ModelProvidersConfig } from '@qwen-code/qwen-code-core';
import { AuthType } from '@qwen-code/qwen-code-core';
import type { LoadedSettings } from '../../config/settings.js';
import { getPersistScopeForModelSelection } from '../../config/modelProvidersScope.js';
import {
AuthType,
isCodingPlanConfig,
getCodingPlanConfig,
CodingPlanRegion,
CODING_PLAN_ENV_KEY,
} from '../../constants/codingPlan.js';
} from '@qwen-code/qwen-code-core';
import type { LoadedSettings } from '../../config/settings.js';
import { getPersistScopeForModelSelection } from '../../config/modelProvidersScope.js';
import { t } from '../../i18n/index.js';
export interface CodingPlanUpdateRequest {

View file

@ -6,7 +6,7 @@
import type { ExtendedSystemInfo } from './systemInfo.js';
import { t } from '../i18n/index.js';
import { isCodingPlanConfig } from '../constants/codingPlan.js';
import { isCodingPlanConfig } from '@qwen-code/qwen-code-core';
/**
* Field configuration for system information display