From bd1636eca1e8790d664063d84d694444719b2ff0 Mon Sep 17 00:00:00 2001 From: Ryder Sun Date: Sat, 7 Nov 2026 19:38:11 +0800 Subject: [PATCH] Follow Qwen native environment authentication selection Detect inferred OAuth and provider modes using the CLI precedence so credential filtering and readiness match invocation. Checks: 114 native configuration and readiness tests; git diff --check. --- desktop/src/agents/cli/qwen-native-config.cjs | 7 +++++++ .../test/agents/cli/qwen-native-config.test.cjs | 14 ++++++++++++++ desktop/test/agents/native-config-summary.test.cjs | 1 + 3 files changed, 22 insertions(+) diff --git a/desktop/src/agents/cli/qwen-native-config.cjs b/desktop/src/agents/cli/qwen-native-config.cjs index 2413cc5..5d168d8 100644 --- a/desktop/src/agents/cli/qwen-native-config.cjs +++ b/desktop/src/agents/cli/qwen-native-config.cjs @@ -65,6 +65,13 @@ function qwenNativeSelection(options = {}) { } } } + if (!auth) { + if (env.QWEN_OAUTH) auth = 'qwen-oauth' + else if (env.OPENAI_API_KEY && env.OPENAI_MODEL && env.OPENAI_BASE_URL) auth = 'openai' + else if (env.GEMINI_API_KEY && env.GEMINI_MODEL) auth = 'gemini' + else if (env.GOOGLE_API_KEY && env.GOOGLE_MODEL) auth = 'vertex-ai' + else if (env.ANTHROPIC_API_KEY && env.ANTHROPIC_MODEL && env.ANTHROPIC_BASE_URL) auth = 'anthropic' + } auth ||= typeof env.QWEN_DEFAULT_AUTH_TYPE === 'string' ? env.QWEN_DEFAULT_AUTH_TYPE : '' if (!auth) return { keys: [], unsupported: false, authType: '' } const selected = model ? (providers.get(auth) || []).filter(entry => entry?.id === model) : [] diff --git a/desktop/test/agents/cli/qwen-native-config.test.cjs b/desktop/test/agents/cli/qwen-native-config.test.cjs index bfaa5f5..1cfaab1 100644 --- a/desktop/test/agents/cli/qwen-native-config.test.cjs +++ b/desktop/test/agents/cli/qwen-native-config.test.cjs @@ -135,3 +135,17 @@ test('Qwen OAuth readiness ignores unrelated provider keys', t => { fs.writeFileSync(path.join(root, 'oauth_creds.json'), JSON.stringify({ access_token: 'oauth-token' })) assert.deepEqual(nativeCredentialState('qwen', { home, env }), { state: 'ready', source: 'native-credential' }) }) + +test('Qwen infers native auth from complete environment combinations', t => { + const home = fs.mkdtempSync(path.join(os.tmpdir(), 'meldwork-qwen-env-auth-')) + t.after(() => fs.rmSync(home, { recursive: true, force: true })) + const env = { HOME: home, QWEN_CODE_SYSTEM_SETTINGS_PATH: path.join(home, 'missing.json'), + OPENAI_API_KEY: 'unrelated', OPENAI_MODEL: 'model', OPENAI_BASE_URL: 'https://example.test' } + assert.deepEqual(qwenDeclaredCredentialKeys({ home, env }), ['OPENAI_API_KEY']) + const oauthEnv = { ...env, QWEN_OAUTH: '1' } + assert.deepEqual(qwenDeclaredCredentialKeys({ home, env: oauthEnv }), []) + assert.deepEqual(nativeCredentialState('qwen', { home, env: oauthEnv }), { state: 'unknown', source: 'unverified' }) + fs.mkdirSync(path.join(home, '.qwen')) + fs.writeFileSync(path.join(home, '.qwen', 'settings.json'), JSON.stringify({ security: { auth: { selectedType: 'openai' } } })) + assert.deepEqual(qwenDeclaredCredentialKeys({ home, env: oauthEnv }), ['OPENAI_API_KEY']) +}) diff --git a/desktop/test/agents/native-config-summary.test.cjs b/desktop/test/agents/native-config-summary.test.cjs index 7dd78fc..9af8490 100644 --- a/desktop/test/agents/native-config-summary.test.cjs +++ b/desktop/test/agents/native-config-summary.test.cjs @@ -42,6 +42,7 @@ test('Qwen summary follows native system, user, default and environment preceden QWEN_CODE_SYSTEM_DEFAULTS_PATH: defaults, QWEN_DEFAULT_AUTH_TYPE: 'environment' } } const write = (file, selectedType) => fs.writeFileSync(file, JSON.stringify({ security: { auth: { selectedType } } })) assert.deepEqual(nativeUserConfigSummary('qwen', options), { authType: 'environment' }) + assert.deepEqual(nativeUserConfigSummary('qwen', { ...options, env: { ...options.env, QWEN_OAUTH: '1' } }), { authType: 'qwen-oauth' }) write(defaults, 'default') assert.deepEqual(nativeUserConfigSummary('qwen', options), { authType: 'default' }) write(user, 'user')