mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-05-05 23:42:03 +00:00
fix(acp): add authMethods in set_model response errors
This commit is contained in:
parent
6fa11a7bae
commit
06b37bd6bf
7 changed files with 166 additions and 24 deletions
|
|
@ -9,6 +9,7 @@
|
|||
import { z } from 'zod';
|
||||
import * as schema from './schema.js';
|
||||
import { ACP_ERROR_CODES } from './errorCodes.js';
|
||||
import { pickAuthMethodsForDetails } from './authMethods.js';
|
||||
export * from './schema.js';
|
||||
|
||||
import type { WritableStream, ReadableStream } from 'node:stream/web';
|
||||
|
|
@ -180,6 +181,7 @@ type ErrorResponse = {
|
|||
code: number;
|
||||
message: string;
|
||||
data?: unknown;
|
||||
authMethods?: schema.AuthMethod[];
|
||||
};
|
||||
|
||||
type PendingResponse = {
|
||||
|
|
@ -282,8 +284,11 @@ class Connection {
|
|||
details = error.message;
|
||||
}
|
||||
|
||||
if (errorName === 'TokenManagerError') {
|
||||
return RequestError.authRequired(details).toResult();
|
||||
if (errorName === 'TokenManagerError' || details?.includes('/auth')) {
|
||||
return RequestError.authRequired(
|
||||
details,
|
||||
pickAuthMethodsForDetails(details),
|
||||
).toResult();
|
||||
}
|
||||
|
||||
if (details?.includes('/auth')) {
|
||||
|
|
@ -339,17 +344,24 @@ class Connection {
|
|||
}
|
||||
|
||||
export class RequestError extends Error {
|
||||
data?: { details?: string };
|
||||
data?: { details?: string; authMethods?: schema.AuthMethod[] };
|
||||
|
||||
constructor(
|
||||
public code: number,
|
||||
message: string,
|
||||
details?: string,
|
||||
authMethods?: schema.AuthMethod[],
|
||||
) {
|
||||
super(message);
|
||||
this.name = 'RequestError';
|
||||
if (details) {
|
||||
this.data = { details };
|
||||
if (details || authMethods) {
|
||||
this.data = {};
|
||||
if (details) {
|
||||
this.data.details = details;
|
||||
}
|
||||
if (authMethods) {
|
||||
this.data.authMethods = authMethods;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -393,11 +405,15 @@ export class RequestError extends Error {
|
|||
);
|
||||
}
|
||||
|
||||
static authRequired(details?: string): RequestError {
|
||||
static authRequired(
|
||||
details?: string,
|
||||
authMethods?: schema.AuthMethod[],
|
||||
): RequestError {
|
||||
return new RequestError(
|
||||
ACP_ERROR_CODES.AUTH_REQUIRED,
|
||||
'Authentication required',
|
||||
details,
|
||||
authMethods,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import {
|
|||
} from '@qwen-code/qwen-code-core';
|
||||
import type { ApprovalModeValue } from './schema.js';
|
||||
import * as acp from './acp.js';
|
||||
import { buildAuthMethods } from './authMethods.js';
|
||||
import { AcpFileSystemService } from './service/filesystem.js';
|
||||
import { Readable, Writable } from 'node:stream';
|
||||
import type { LoadedSettings } from '../config/settings.js';
|
||||
|
|
@ -73,20 +74,7 @@ class GeminiAgent {
|
|||
args: acp.InitializeRequest,
|
||||
): Promise<acp.InitializeResponse> {
|
||||
this.clientCapabilities = args.clientCapabilities;
|
||||
const authMethods = [
|
||||
{
|
||||
id: AuthType.USE_OPENAI,
|
||||
name: 'Use OpenAI API key',
|
||||
description:
|
||||
'Requires setting the `OPENAI_API_KEY` environment variable',
|
||||
},
|
||||
{
|
||||
id: AuthType.QWEN_OAUTH,
|
||||
name: 'Qwen OAuth',
|
||||
description:
|
||||
'OAuth authentication for Qwen models with 2000 daily requests',
|
||||
},
|
||||
];
|
||||
const authMethods = buildAuthMethods();
|
||||
|
||||
// Get current approval mode from config
|
||||
const currentApprovalMode = this.config.getApprovalMode();
|
||||
|
|
@ -290,7 +278,7 @@ class GeminiAgent {
|
|||
`Session not found for id: ${params.sessionId}`,
|
||||
);
|
||||
}
|
||||
return session.setModel(params);
|
||||
return await session.setModel(params);
|
||||
}
|
||||
|
||||
private async ensureAuthenticated(config: Config): Promise<void> {
|
||||
|
|
@ -298,6 +286,7 @@ class GeminiAgent {
|
|||
if (!selectedType) {
|
||||
throw acp.RequestError.authRequired(
|
||||
'Use Qwen Code CLI to authenticate first.',
|
||||
this.pickAuthMethodsForAuthRequired(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -308,10 +297,55 @@ class GeminiAgent {
|
|||
console.error(`Authentication failed: ${e}`);
|
||||
throw acp.RequestError.authRequired(
|
||||
'Authentication failed: ' + (e as Error).message,
|
||||
this.pickAuthMethodsForAuthRequired(selectedType, e),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private pickAuthMethodsForAuthRequired(
|
||||
selectedType?: AuthType | string,
|
||||
error?: unknown,
|
||||
): acp.AuthMethod[] {
|
||||
const authMethods = buildAuthMethods();
|
||||
const errorMessage = this.extractErrorMessage(error);
|
||||
if (
|
||||
errorMessage?.includes('qwen-oauth') ||
|
||||
errorMessage?.includes('Qwen OAuth')
|
||||
) {
|
||||
const qwenOAuthMethods = authMethods.filter(
|
||||
(method) => method.id === AuthType.QWEN_OAUTH,
|
||||
);
|
||||
return qwenOAuthMethods.length ? qwenOAuthMethods : authMethods;
|
||||
}
|
||||
|
||||
if (selectedType) {
|
||||
const matchedMethods = authMethods.filter(
|
||||
(method) => method.id === selectedType,
|
||||
);
|
||||
return matchedMethods.length ? matchedMethods : authMethods;
|
||||
}
|
||||
|
||||
return authMethods;
|
||||
}
|
||||
|
||||
private extractErrorMessage(error?: unknown): string | undefined {
|
||||
if (error instanceof Error) {
|
||||
return error.message;
|
||||
}
|
||||
if (
|
||||
typeof error === 'object' &&
|
||||
error != null &&
|
||||
'message' in error &&
|
||||
typeof error.message === 'string'
|
||||
) {
|
||||
return error.message;
|
||||
}
|
||||
if (typeof error === 'string') {
|
||||
return error;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private setupFileSystem(config: Config): void {
|
||||
if (!this.clientCapabilities?.fs) {
|
||||
return;
|
||||
|
|
|
|||
47
packages/cli/src/acp-integration/authMethods.ts
Normal file
47
packages/cli/src/acp-integration/authMethods.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2025 Qwen Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { AuthType } from '@qwen-code/qwen-code-core';
|
||||
import type { AuthMethod } from './schema.js';
|
||||
|
||||
export function buildAuthMethods(): AuthMethod[] {
|
||||
return [
|
||||
{
|
||||
id: AuthType.USE_OPENAI,
|
||||
name: 'Use OpenAI API key',
|
||||
description: 'Requires setting the `OPENAI_API_KEY` environment variable',
|
||||
type: 'terminal',
|
||||
args: ['--auth-type=openai'],
|
||||
},
|
||||
{
|
||||
id: AuthType.QWEN_OAUTH,
|
||||
name: 'Qwen OAuth',
|
||||
description:
|
||||
'OAuth authentication for Qwen models with free daily requests',
|
||||
type: 'terminal',
|
||||
args: ['--auth-type=qwen-oauth'],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export function filterAuthMethodsById(
|
||||
authMethods: AuthMethod[],
|
||||
authMethodId: string,
|
||||
): AuthMethod[] {
|
||||
return authMethods.filter((method) => method.id === authMethodId);
|
||||
}
|
||||
|
||||
export function pickAuthMethodsForDetails(details?: string): AuthMethod[] {
|
||||
const authMethods = buildAuthMethods();
|
||||
if (!details) {
|
||||
return authMethods;
|
||||
}
|
||||
if (details.includes('qwen-oauth') || details.includes('Qwen OAuth')) {
|
||||
const narrowed = filterAuthMethodsById(authMethods, AuthType.QWEN_OAUTH);
|
||||
return narrowed.length ? narrowed : authMethods;
|
||||
}
|
||||
return authMethods;
|
||||
}
|
||||
|
|
@ -406,9 +406,12 @@ export const agentCapabilitiesSchema = z.object({
|
|||
});
|
||||
|
||||
export const authMethodSchema = z.object({
|
||||
args: z.array(z.string()).optional(),
|
||||
description: z.string().nullable(),
|
||||
env: z.record(z.string()).optional(),
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
type: z.string().optional(),
|
||||
});
|
||||
|
||||
export const clientResponseSchema = z.union([
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue