From 26c13091d4cd8e7dba6d2bbe93cf6dbb266bdf58 Mon Sep 17 00:00:00 2001 From: tt-a1i <53142663+tt-a1i@users.noreply.github.com> Date: Sun, 21 Jun 2026 02:09:29 +0800 Subject: [PATCH] fix: accept uppercase endpoint URL schemes (#5443) --- .../core/src/qwen/qwenContentGenerator.test.ts | 8 ++++++++ packages/core/src/qwen/qwenContentGenerator.ts | 2 +- .../telemetry/qwen-logger/qwen-logger.test.ts | 11 +++++++++++ .../src/telemetry/qwen-logger/qwen-logger.ts | 2 +- .../__tests__/credential-manager-renew.test.ts | 18 ++++++++++++++++++ .../shared/src/sources/credential-manager.ts | 2 +- 6 files changed, 40 insertions(+), 3 deletions(-) diff --git a/packages/core/src/qwen/qwenContentGenerator.test.ts b/packages/core/src/qwen/qwenContentGenerator.test.ts index 86c4024773..e52ba34caa 100644 --- a/packages/core/src/qwen/qwenContentGenerator.test.ts +++ b/packages/core/src/qwen/qwenContentGenerator.test.ts @@ -1225,6 +1225,14 @@ describe('QwenContentGenerator', () => { input: 'https://api.example.com:443', expected: 'https://api.example.com:443/v1', }, + { + input: 'HTTPS://api.example.com', + expected: 'HTTPS://api.example.com/v1', + }, + { + input: 'HtTp://localhost:8080', + expected: 'HtTp://localhost:8080/v1', + }, { input: 'api.example.com:9000/api', expected: 'https://api.example.com:9000/api/v1', diff --git a/packages/core/src/qwen/qwenContentGenerator.ts b/packages/core/src/qwen/qwenContentGenerator.ts index 9cf8aa49bf..6d67fabf26 100644 --- a/packages/core/src/qwen/qwenContentGenerator.ts +++ b/packages/core/src/qwen/qwenContentGenerator.ts @@ -61,7 +61,7 @@ export class QwenContentGenerator extends OpenAIContentGenerator { const suffix = '/v1'; // Normalize the URL: add protocol if missing, ensure /v1 suffix - const normalizedUrl = baseEndpoint.startsWith('http') + const normalizedUrl = /^https?:\/\//i.test(baseEndpoint) ? baseEndpoint : `https://${baseEndpoint}`; diff --git a/packages/core/src/telemetry/qwen-logger/qwen-logger.test.ts b/packages/core/src/telemetry/qwen-logger/qwen-logger.test.ts index 21d99e6d45..8cc3fc09b6 100644 --- a/packages/core/src/telemetry/qwen-logger/qwen-logger.test.ts +++ b/packages/core/src/telemetry/qwen-logger/qwen-logger.test.ts @@ -138,6 +138,17 @@ describe('QwenLogger', () => { }); }); + describe('getProxyAgent', () => { + it('accepts uppercase proxy URL schemes', () => { + const config = makeFakeConfig({ + getProxy: () => 'HTTPS://proxy.example.com:8080', + }); + const logger = QwenLogger.getInstance(config)!; + + expect(logger.getProxyAgent()).toBeDefined(); + }); + }); + describe('createRumPayload', () => { it('includes os metadata in payload', async () => { const logger = QwenLogger.getInstance(mockConfig)!; diff --git a/packages/core/src/telemetry/qwen-logger/qwen-logger.ts b/packages/core/src/telemetry/qwen-logger/qwen-logger.ts index 0c932de84b..ec740ead2e 100644 --- a/packages/core/src/telemetry/qwen-logger/qwen-logger.ts +++ b/packages/core/src/telemetry/qwen-logger/qwen-logger.ts @@ -1070,7 +1070,7 @@ export class QwenLogger { if (!proxyUrl) return undefined; // undici which is widely used in the repo can only support http & https proxy protocol, // https://github.com/nodejs/undici/issues/2224 - if (proxyUrl.startsWith('http')) { + if (/^https?:\/\//i.test(proxyUrl)) { return new HttpsProxyAgent(proxyUrl); } else { throw new Error('Unsupported proxy type'); diff --git a/packages/desktop/packages/shared/src/sources/__tests__/credential-manager-renew.test.ts b/packages/desktop/packages/shared/src/sources/__tests__/credential-manager-renew.test.ts index ac5c2fad43..799adb6259 100644 --- a/packages/desktop/packages/shared/src/sources/__tests__/credential-manager-renew.test.ts +++ b/packages/desktop/packages/shared/src/sources/__tests__/credential-manager-renew.test.ts @@ -176,6 +176,24 @@ describe('refreshApiRenew via refresh()', () => { expect(fetchCalls[0]!.url).toBe('https://auth.example.com/oauth/token'); }); + test('handles absolute renew URL with uppercase scheme', async () => { + mockGet.mockImplementationOnce(() => Promise.resolve({ + value: 'old-token', expiresAt: Date.now() - 60_000, + })); + mockFetch({ access_token: 'new-token', expires_in: 3600 }); + + const source = createRenewSource({ + api: { + baseUrl: 'https://api.example.com', + authType: 'bearer', + renewEndpoint: { path: 'HTTPS://auth.example.com/oauth/token' }, + }, + }); + + await credManager.refresh(source); + expect(fetchCalls[0]!.url).toBe('HTTPS://auth.example.com/oauth/token'); + }); + test('returns null on 401 response', async () => { mockGet.mockImplementationOnce(() => Promise.resolve({ value: 'old-token', expiresAt: Date.now() - 60_000, diff --git a/packages/desktop/packages/shared/src/sources/credential-manager.ts b/packages/desktop/packages/shared/src/sources/credential-manager.ts index e53dfabaa6..8492a59058 100644 --- a/packages/desktop/packages/shared/src/sources/credential-manager.ts +++ b/packages/desktop/packages/shared/src/sources/credential-manager.ts @@ -975,7 +975,7 @@ export class SourceCredentialManager { try { // 1. Resolve URL - const url = renewConfig.path.startsWith('http') + const url = /^https?:\/\//i.test(renewConfig.path) ? renewConfig.path : new URL(renewConfig.path, baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`).toString();