mirror of
https://github.com/modelstudioai/cli.git
synced 2026-09-14 19:49:23 +08:00
refactor(auth): unify console callback persistence — validate apiKey with callback's baseUrl
Move apiKey validation into login-console.ts so it uses the baseUrl from the same callback (not stale config). All fields are now persisted in one place: config fields first, then apiKey validated + written. Remove onApiKey callback indirection from runConsoleLogin signature. Clean up debug logging. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,15 +5,18 @@ import http from "node:http";
|
||||
import {
|
||||
BailianError,
|
||||
ExitCode,
|
||||
chatEndpoint,
|
||||
getConfigPath,
|
||||
readConfigFile,
|
||||
requestJson,
|
||||
writeConfigFile,
|
||||
type Config,
|
||||
} from "bailian-cli-core";
|
||||
|
||||
const CONSOLE_LOGIN_TIMEOUT_MS = 15 * 60 * 1000;
|
||||
const MAX_AUTH_CALLBACK_BODY = 65536;
|
||||
|
||||
const DEFAULT_CONSOLE_ORIGIN = "https://pre-bailian.console.aliyun.com";
|
||||
const DEFAULT_CONSOLE_ORIGIN = "https://bailian.console.aliyun.com";
|
||||
|
||||
export function resolveConsoleOrigin(): string {
|
||||
return process.env.BAILIAN_CONSOLE_ORIGIN || DEFAULT_CONSOLE_ORIGIN;
|
||||
@@ -366,12 +369,69 @@ function openInBrowser(url: string): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
const RETRY_DELAY_BASE_MS = 500;
|
||||
|
||||
function canRetry(err: unknown): boolean {
|
||||
if (err instanceof BailianError) {
|
||||
if (err.exitCode === ExitCode.NETWORK || err.exitCode === ExitCode.TIMEOUT) return true;
|
||||
const status = err.api?.httpStatus;
|
||||
return status === 401 || (status !== undefined && status >= 500);
|
||||
}
|
||||
if (err instanceof Error) {
|
||||
return (
|
||||
err.name === "AbortError" ||
|
||||
err.name === "TimeoutError" ||
|
||||
err.message.includes("timed out") ||
|
||||
err.message === "fetch failed"
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async function validateAndPersistApiKey(
|
||||
config: Config,
|
||||
key: string,
|
||||
baseUrl: string,
|
||||
): Promise<void> {
|
||||
process.stderr.write("Testing key... ");
|
||||
const testConfig = { ...config, apiKey: key, baseUrl };
|
||||
const requestOpts = {
|
||||
url: chatEndpoint(testConfig.baseUrl),
|
||||
method: "POST",
|
||||
timeout: Math.min(config.timeout, 30),
|
||||
body: {
|
||||
model: "qwen-max",
|
||||
messages: [{ role: "user", content: "hi" }],
|
||||
max_tokens: 1,
|
||||
},
|
||||
};
|
||||
|
||||
for (let attempt = 1; attempt <= 3; attempt++) {
|
||||
try {
|
||||
await requestJson<unknown>(testConfig, requestOpts);
|
||||
break;
|
||||
} catch (err) {
|
||||
if (attempt >= 3 || !canRetry(err)) {
|
||||
process.stderr.write("Failed\n");
|
||||
throw new BailianError("API key validation failed", ExitCode.AUTH, "Invalid API key.", {
|
||||
cause: err,
|
||||
});
|
||||
}
|
||||
const delayMs = RETRY_DELAY_BASE_MS * 2 ** (attempt - 1);
|
||||
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
||||
}
|
||||
}
|
||||
|
||||
process.stderr.write("Valid\n");
|
||||
const existing = readConfigFile() as Record<string, unknown>;
|
||||
existing.api_key = key;
|
||||
await writeConfigFile(existing);
|
||||
}
|
||||
|
||||
export async function runConsoleLogin(
|
||||
consoleOrigin: string,
|
||||
opts?: {
|
||||
needApiKey?: boolean;
|
||||
onApiKey?: ({ apiKey, baseUrl }: { apiKey: string; baseUrl?: string }) => Promise<void>;
|
||||
},
|
||||
config: Config,
|
||||
opts?: { needApiKey?: boolean },
|
||||
): Promise<void> {
|
||||
const state = randomBytes(16).toString("hex");
|
||||
let callbackError: unknown;
|
||||
@@ -397,7 +457,6 @@ export async function runConsoleLogin(
|
||||
const { accessToken, apiKey, baseUrl, consoleSite, consoleRegion, consoleSwitchAgent } =
|
||||
await extractCredentialsFromRequest(req);
|
||||
|
||||
console.log({ accessToken, apiKey, baseUrl, consoleSite, consoleRegion, consoleSwitchAgent });
|
||||
const hasConfig =
|
||||
accessToken || baseUrl || consoleSite || consoleRegion || consoleSwitchAgent;
|
||||
|
||||
@@ -413,8 +472,9 @@ export async function runConsoleLogin(
|
||||
await writeConfigFile(existing);
|
||||
process.stderr.write(`Config saved to ${getConfigPath()}\n`);
|
||||
}
|
||||
if (apiKey && opts?.onApiKey) {
|
||||
await opts.onApiKey({ apiKey, baseUrl: baseUrl ?? undefined });
|
||||
if (apiKey) {
|
||||
const testBaseUrl = baseUrl || config.baseUrl;
|
||||
await validateAndPersistApiKey(config, apiKey, testBaseUrl);
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
callbackError = err;
|
||||
|
||||
@@ -41,7 +41,6 @@ function canRetry(err: unknown): boolean {
|
||||
|
||||
async function validateKeyAndPersist(config: Config, key: string): Promise<void> {
|
||||
process.stderr.write("Testing key... ");
|
||||
process.stderr.write("\r\n" + JSON.stringify(config));
|
||||
|
||||
const testConfig = { ...config, apiKey: key };
|
||||
const requestOpts = {
|
||||
@@ -102,11 +101,8 @@ export default defineCommand({
|
||||
return;
|
||||
}
|
||||
const hasApiKey = !!(config.apiKey || config.fileApiKey);
|
||||
await runConsoleLogin(resolveConsoleOrigin(), {
|
||||
await runConsoleLogin(resolveConsoleOrigin(), config, {
|
||||
needApiKey: !hasApiKey,
|
||||
onApiKey: ({ apiKey, baseUrl }) => {
|
||||
return validateKeyAndPersist({ ...config, ...(baseUrl ? { baseUrl } : {}) }, apiKey);
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user