mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-12 20:15:40 +00:00
fix(cliproxy): probe Gemini OAuth support
This commit is contained in:
1 parent
af2cb4b9b3
commit
19c6c3f457
4 files changed
+307
-62
No files matched your search
@@ -0,0 +1,93 @@
|
|||||||
|
import { ValidationError, AuthError } from '../../errors/error-types';
|
||||||
|
import { getUnsupportedAuthStartReason } from '../provider-capabilities';
|
||||||
|
import { CLIProxyBackend, CLIProxyProvider } from '../types';
|
||||||
|
import {
|
||||||
|
getKiroCLIAuthFlag,
|
||||||
|
getOAuthConfig,
|
||||||
|
isKiroCLIAuthMethod,
|
||||||
|
normalizeKiroAuthMethod,
|
||||||
|
normalizeKiroIDCFlow,
|
||||||
|
type OAuthOptions,
|
||||||
|
} from './auth-types';
|
||||||
|
import {
|
||||||
|
getOAuthFlagCandidatesForProvider,
|
||||||
|
resolveAdvertisedAuthFlag,
|
||||||
|
} from './oauth-cli-capabilities';
|
||||||
|
|
||||||
|
export function buildOAuthArgs(
|
||||||
|
provider: CLIProxyProvider,
|
||||||
|
configPath: string,
|
||||||
|
headless: boolean,
|
||||||
|
noIncognito: boolean,
|
||||||
|
options: {
|
||||||
|
advertisedFlags?: ReadonlySet<string>;
|
||||||
|
backend?: CLIProxyBackend;
|
||||||
|
kiroMethod?: OAuthOptions['kiroMethod'];
|
||||||
|
kiroIDCStartUrl?: string;
|
||||||
|
kiroIDCRegion?: string;
|
||||||
|
kiroIDCFlow?: OAuthOptions['kiroIDCFlow'];
|
||||||
|
} = {}
|
||||||
|
): string[] {
|
||||||
|
const unsupportedReason = getUnsupportedAuthStartReason(provider);
|
||||||
|
if (unsupportedReason) {
|
||||||
|
throw new AuthError(unsupportedReason, provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
const args = ['--config', configPath];
|
||||||
|
const advertisedFlags = options.advertisedFlags;
|
||||||
|
|
||||||
|
if (provider === 'kiro') {
|
||||||
|
const method = normalizeKiroAuthMethod(options.kiroMethod);
|
||||||
|
if (!isKiroCLIAuthMethod(method)) {
|
||||||
|
throw new AuthError(`Kiro auth method '${method}' is not supported by CLI flow.`, 'kiro');
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedKiroFlag = advertisedFlags
|
||||||
|
? resolveAdvertisedAuthFlag(
|
||||||
|
provider,
|
||||||
|
getOAuthFlagCandidatesForProvider(provider, method),
|
||||||
|
advertisedFlags,
|
||||||
|
{ backend: options.backend }
|
||||||
|
)
|
||||||
|
: getKiroCLIAuthFlag(method);
|
||||||
|
|
||||||
|
if (method !== 'idc') {
|
||||||
|
args.push(selectedKiroFlag);
|
||||||
|
} else {
|
||||||
|
const startUrl = options.kiroIDCStartUrl?.trim();
|
||||||
|
if (!startUrl) {
|
||||||
|
throw new ValidationError(
|
||||||
|
'Kiro IDC login requires --kiro-idc-start-url',
|
||||||
|
'kiroIDCStartUrl'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
args.push(selectedKiroFlag, '--kiro-idc-start-url', startUrl);
|
||||||
|
const region = options.kiroIDCRegion?.trim();
|
||||||
|
if (region) {
|
||||||
|
args.push('--kiro-idc-region', region);
|
||||||
|
}
|
||||||
|
args.push('--kiro-idc-flow', normalizeKiroIDCFlow(options.kiroIDCFlow));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
args.push(
|
||||||
|
advertisedFlags
|
||||||
|
? resolveAdvertisedAuthFlag(
|
||||||
|
provider,
|
||||||
|
getOAuthFlagCandidatesForProvider(provider),
|
||||||
|
advertisedFlags,
|
||||||
|
{ backend: options.backend }
|
||||||
|
)
|
||||||
|
: getOAuthConfig(provider).authFlag
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (headless) {
|
||||||
|
args.push('--no-browser');
|
||||||
|
}
|
||||||
|
if (provider === 'kiro' && noIncognito) {
|
||||||
|
args.push('--no-incognito');
|
||||||
|
}
|
||||||
|
|
||||||
|
return args;
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
import * as childProcess from 'child_process';
|
||||||
|
import { AuthError, BinaryError } from '../../errors/error-types';
|
||||||
|
import type { CLIProxyBackend, CLIProxyProvider } from '../types';
|
||||||
|
import { getKiroCLIAuthFlag, getOAuthConfig, type KiroCLIAuthMethod } from './auth-types';
|
||||||
|
|
||||||
|
const HELP_FLAG_PATTERN = /(?:^|\n)\s+-([a-z0-9][a-z0-9-]*)\b/gi;
|
||||||
|
export const OAUTH_HELP_PROBE_TIMEOUT_MS = 5000;
|
||||||
|
|
||||||
|
export function extractAdvertisedCliFlags(helpText: string): Set<string> {
|
||||||
|
const flags = new Set<string>();
|
||||||
|
|
||||||
|
for (const match of helpText.matchAll(HELP_FLAG_PATTERN)) {
|
||||||
|
const flagName = match[1]?.trim();
|
||||||
|
if (!flagName) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
flags.add(`--${flagName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getOAuthFlagCandidatesForProvider(
|
||||||
|
provider: CLIProxyProvider,
|
||||||
|
kiroMethod?: KiroCLIAuthMethod
|
||||||
|
): readonly string[] {
|
||||||
|
if (provider !== 'kiro') {
|
||||||
|
return [getOAuthConfig(provider).authFlag];
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (kiroMethod) {
|
||||||
|
case 'google':
|
||||||
|
return ['--kiro-google-login', '--kiro-login'];
|
||||||
|
case 'aws':
|
||||||
|
case 'aws-authcode':
|
||||||
|
case 'idc':
|
||||||
|
return [getKiroCLIAuthFlag(kiroMethod)];
|
||||||
|
default:
|
||||||
|
return [getKiroCLIAuthFlag('aws')];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function selectAdvertisedAuthFlag(
|
||||||
|
candidates: readonly string[],
|
||||||
|
advertisedFlags: ReadonlySet<string>
|
||||||
|
): string | null {
|
||||||
|
for (const candidate of candidates) {
|
||||||
|
if (advertisedFlags.has(candidate)) {
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveAdvertisedAuthFlag(
|
||||||
|
provider: CLIProxyProvider,
|
||||||
|
candidates: readonly string[],
|
||||||
|
advertisedFlags: ReadonlySet<string>,
|
||||||
|
options: { backend?: CLIProxyBackend } = {}
|
||||||
|
): string {
|
||||||
|
const selected = selectAdvertisedAuthFlag(candidates, advertisedFlags);
|
||||||
|
if (selected) {
|
||||||
|
return selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
const oauthConfig = getOAuthConfig(provider);
|
||||||
|
if (provider === 'gemini' && options.backend === 'original') {
|
||||||
|
throw new AuthError(
|
||||||
|
'Installed CLIProxy binary does not advertise Google Gemini login support (--login). The active `cliproxy.backend: original` runtime cannot start Gemini OAuth from CCS. To use Gemini OAuth, switch `cliproxy.backend` to `plus`, set CLIPROXY_GEMINI_OAUTH_CLIENT_ID and CLIPROXY_GEMINI_OAUTH_CLIENT_SECRET before starting CLIProxy Plus, reinstall the maintained Plus fork, and retry auth.',
|
||||||
|
provider
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new AuthError(
|
||||||
|
`Installed CLIProxy binary does not advertise a supported ${oauthConfig.displayName} login flag (${candidates.join(' or ')}). Run \`ccs cliproxy status\`, then reinstall the active backend binary before retrying auth.`,
|
||||||
|
provider
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function probeCliProxyAdvertisedFlags(binaryPath: string): Set<string> {
|
||||||
|
const result = childProcess.spawnSync(binaryPath, ['--help'], {
|
||||||
|
encoding: 'utf8',
|
||||||
|
shell: false,
|
||||||
|
timeout: OAUTH_HELP_PROBE_TIMEOUT_MS,
|
||||||
|
windowsHide: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.error) {
|
||||||
|
const errorCode = (result.error as NodeJS.ErrnoException).code;
|
||||||
|
if (errorCode === 'ETIMEDOUT') {
|
||||||
|
throw new BinaryError(
|
||||||
|
`Timed out after ${OAUTH_HELP_PROBE_TIMEOUT_MS}ms while inspecting CLIProxy login capabilities`,
|
||||||
|
binaryPath
|
||||||
|
);
|
||||||
|
}
|
||||||
|
throw result.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.signal) {
|
||||||
|
throw new BinaryError(
|
||||||
|
`CLIProxy capability probe was interrupted while inspecting login capabilities (${result.signal})`,
|
||||||
|
binaryPath
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return extractAdvertisedCliFlags(`${result.stdout ?? ''}\n${result.stderr ?? ''}`);
|
||||||
|
}
|
||||||
@@ -14,9 +14,9 @@ import * as fs from 'fs';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { fail, info, warn, color, ok } from '../../utils/ui';
|
import { fail, info, warn, color, ok } from '../../utils/ui';
|
||||||
import { createLogger } from '../../services/logging';
|
import { createLogger } from '../../services/logging';
|
||||||
import { ensureCLIProxyBinary, getStoredConfiguredBackend } from '../binary-manager';
|
import { ensureCLIProxyBinary, getConfiguredBackend } from '../binary-manager';
|
||||||
import { generateConfig } from '../config/config-generator';
|
import { generateConfig } from '../config/config-generator';
|
||||||
import { AuthError, ConfigError } from '../../errors/error-types';
|
import { AuthError, BinaryError, ConfigError } from '../../errors/error-types';
|
||||||
import { CLIProxyBackend, CLIProxyProvider } from '../types';
|
import { CLIProxyBackend, CLIProxyProvider } from '../types';
|
||||||
import {
|
import {
|
||||||
AccountInfo,
|
AccountInfo,
|
||||||
@@ -37,8 +37,6 @@ import {
|
|||||||
DEFAULT_KIRO_AUTH_METHOD,
|
DEFAULT_KIRO_AUTH_METHOD,
|
||||||
DEFAULT_KIRO_IDC_FLOW,
|
DEFAULT_KIRO_IDC_FLOW,
|
||||||
getKiroCallbackPort,
|
getKiroCallbackPort,
|
||||||
getKiroCLIAuthArgs,
|
|
||||||
isKiroCLIAuthMethod,
|
|
||||||
isKiroDeviceCodeMethod,
|
isKiroDeviceCodeMethod,
|
||||||
getOAuthConfig,
|
getOAuthConfig,
|
||||||
ProviderOAuthConfig,
|
ProviderOAuthConfig,
|
||||||
@@ -47,6 +45,7 @@ import {
|
|||||||
getManagementOAuthCallbackPath,
|
getManagementOAuthCallbackPath,
|
||||||
normalizeKiroAuthMethod,
|
normalizeKiroAuthMethod,
|
||||||
normalizeKiroIDCFlow,
|
normalizeKiroIDCFlow,
|
||||||
|
isKiroCLIAuthMethod,
|
||||||
} from './auth-types';
|
} from './auth-types';
|
||||||
import { isHeadlessEnvironment, killProcessOnPort, showStep } from './environment-detector';
|
import { isHeadlessEnvironment, killProcessOnPort, showStep } from './environment-detector';
|
||||||
import {
|
import {
|
||||||
@@ -60,6 +59,12 @@ import {
|
|||||||
import { executeOAuthProcess } from './oauth-process';
|
import { executeOAuthProcess } from './oauth-process';
|
||||||
import { importKiroToken } from './kiro-import';
|
import { importKiroToken } from './kiro-import';
|
||||||
import { parseGitLabPatAuthResponse } from './gitlab-pat-response';
|
import { parseGitLabPatAuthResponse } from './gitlab-pat-response';
|
||||||
|
import {
|
||||||
|
getOAuthFlagCandidatesForProvider,
|
||||||
|
probeCliProxyAdvertisedFlags,
|
||||||
|
selectAdvertisedAuthFlag,
|
||||||
|
} from './oauth-cli-capabilities';
|
||||||
|
import { buildOAuthArgs } from './oauth-cli-args';
|
||||||
import {
|
import {
|
||||||
buildOAuthStartFailureGuidance,
|
buildOAuthStartFailureGuidance,
|
||||||
formatOAuthStartFailureForCli,
|
formatOAuthStartFailureForCli,
|
||||||
@@ -144,14 +149,27 @@ function buildPlusOAuthCredentialMessage(
|
|||||||
displayName: string,
|
displayName: string,
|
||||||
idEnv: string,
|
idEnv: string,
|
||||||
secretEnv: string,
|
secretEnv: string,
|
||||||
missing?: string[]
|
missing?: string[],
|
||||||
|
options?: { originalFallbackSupported?: boolean }
|
||||||
): string {
|
): string {
|
||||||
const missingText = missing?.length ? ` Missing: ${missing.join(', ')}.` : '';
|
const missingText = missing?.length ? ` Missing: ${missing.join(', ')}.` : '';
|
||||||
|
const fallbackText =
|
||||||
|
options?.originalFallbackSupported === false
|
||||||
|
? ' Current `cliproxy.backend: original` releases do not advertise Gemini login, so switching back to original will not restore Gemini OAuth.'
|
||||||
|
: ` or switch \`cliproxy.backend\` to \`original\` for ${displayName}.`;
|
||||||
return (
|
return (
|
||||||
`${displayName} OAuth from CLIProxy Plus is missing Google OAuth client credentials.` +
|
`${displayName} OAuth from CLIProxy Plus is missing Google OAuth client credentials.` +
|
||||||
missingText +
|
missingText +
|
||||||
` Set ${idEnv} and ${secretEnv} before starting CLIProxy Plus,` +
|
` Set ${idEnv} and ${secretEnv} before starting CLIProxy Plus,` +
|
||||||
` or switch \`cliproxy.backend\` to \`original\` for ${displayName}.`
|
fallbackText
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGeminiOriginalBackendOAuthMessage(): string {
|
||||||
|
return (
|
||||||
|
'Installed CLIProxy binary does not advertise Google Gemini login support (--login). ' +
|
||||||
|
'The active `cliproxy.backend: original` runtime cannot start Gemini OAuth from CCS. ' +
|
||||||
|
`To use Gemini OAuth, switch \`cliproxy.backend\` to \`plus\`, set ${GEMINI_PLUS_CLIENT_ID_ENV} and ${GEMINI_PLUS_CLIENT_SECRET_ENV} before starting CLIProxy Plus, reinstall the maintained Plus fork, and retry auth.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,7 +195,9 @@ export function getPlusOAuthCredentialError(
|
|||||||
|
|
||||||
const missing = [entry.idEnv, entry.secretEnv].filter((name) => !env[name]?.trim());
|
const missing = [entry.idEnv, entry.secretEnv].filter((name) => !env[name]?.trim());
|
||||||
return missing.length > 0
|
return missing.length > 0
|
||||||
? buildPlusOAuthCredentialMessage(entry.displayName, entry.idEnv, entry.secretEnv, missing)
|
? buildPlusOAuthCredentialMessage(entry.displayName, entry.idEnv, entry.secretEnv, missing, {
|
||||||
|
originalFallbackSupported: provider !== 'gemini',
|
||||||
|
})
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,7 +225,15 @@ export function getPlusAuthUrlCredentialError(
|
|||||||
const clientId = parsed.searchParams.get('client_id')?.trim();
|
const clientId = parsed.searchParams.get('client_id')?.trim();
|
||||||
return clientId
|
return clientId
|
||||||
? null
|
? null
|
||||||
: buildPlusOAuthCredentialMessage(entry.displayName, entry.idEnv, entry.secretEnv);
|
: buildPlusOAuthCredentialMessage(
|
||||||
|
entry.displayName,
|
||||||
|
entry.idEnv,
|
||||||
|
entry.secretEnv,
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
originalFallbackSupported: provider !== 'gemini',
|
||||||
|
}
|
||||||
|
);
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -579,12 +607,21 @@ async function runPreflightChecks(
|
|||||||
*/
|
*/
|
||||||
async function prepareBinary(
|
async function prepareBinary(
|
||||||
provider: CLIProxyProvider,
|
provider: CLIProxyProvider,
|
||||||
verbose: boolean
|
verbose: boolean,
|
||||||
): Promise<{ binaryPath: string; tokenDir: string; configPath: string } | null> {
|
backend: CLIProxyBackend
|
||||||
|
): Promise<{
|
||||||
|
binaryPath: string;
|
||||||
|
tokenDir: string;
|
||||||
|
configPath: string;
|
||||||
|
backend: CLIProxyBackend;
|
||||||
|
} | null> {
|
||||||
showStep(1, 4, 'progress', 'Preparing CLIProxy binary...');
|
showStep(1, 4, 'progress', 'Preparing CLIProxy binary...');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const binaryPath = await ensureCLIProxyBinary(verbose, { skipAutoUpdate: true });
|
const binaryPath = await ensureCLIProxyBinary(verbose, {
|
||||||
|
backend,
|
||||||
|
skipAutoUpdate: true,
|
||||||
|
});
|
||||||
process.stdout.write('\x1b[1A\x1b[2K');
|
process.stdout.write('\x1b[1A\x1b[2K');
|
||||||
showStep(1, 4, 'ok', 'CLIProxy binary ready');
|
showStep(1, 4, 'ok', 'CLIProxy binary ready');
|
||||||
|
|
||||||
@@ -596,7 +633,7 @@ async function prepareBinary(
|
|||||||
console.error(`[auth] Config generated: ${configPath}`);
|
console.error(`[auth] Config generated: ${configPath}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { binaryPath, tokenDir, configPath };
|
return { binaryPath, tokenDir, configPath, backend };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
process.stdout.write('\x1b[1A\x1b[2K');
|
process.stdout.write('\x1b[1A\x1b[2K');
|
||||||
showStep(1, 4, 'fail', 'Failed to prepare CLIProxy binary');
|
showStep(1, 4, 'fail', 'Failed to prepare CLIProxy binary');
|
||||||
@@ -605,49 +642,31 @@ async function prepareBinary(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildOAuthArgs(
|
async function prepareOAuthRuntime(
|
||||||
provider: CLIProxyProvider,
|
provider: CLIProxyProvider,
|
||||||
configPath: string,
|
verbose: boolean,
|
||||||
headless: boolean,
|
backend: CLIProxyBackend
|
||||||
noIncognito: boolean,
|
): Promise<{
|
||||||
options: {
|
advertisedFlags: ReadonlySet<string>;
|
||||||
kiroMethod?: OAuthOptions['kiroMethod'];
|
backend: CLIProxyBackend;
|
||||||
kiroIDCStartUrl?: string;
|
binaryPath: string;
|
||||||
kiroIDCRegion?: string;
|
configPath: string;
|
||||||
kiroIDCFlow?: OAuthOptions['kiroIDCFlow'];
|
tokenDir: string;
|
||||||
} = {}
|
}> {
|
||||||
): string[] {
|
const prepared = await prepareBinary(provider, verbose, backend);
|
||||||
const unsupportedReason = getUnsupportedAuthStartReason(provider);
|
if (!prepared) {
|
||||||
if (unsupportedReason) {
|
throw new BinaryError('CLIProxy binary preparation returned no runtime');
|
||||||
throw new AuthError(unsupportedReason, provider);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const args = ['--config', configPath];
|
return {
|
||||||
|
...prepared,
|
||||||
|
advertisedFlags: probeCliProxyAdvertisedFlags(prepared.binaryPath),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (provider === 'kiro') {
|
function formatOAuthRuntimePreparationError(error: unknown): string {
|
||||||
const method = normalizeKiroAuthMethod(options.kiroMethod);
|
const message = error instanceof Error ? error.message : String(error);
|
||||||
if (!isKiroCLIAuthMethod(method)) {
|
return `Unable to prepare CLIProxy OAuth runtime: ${message}`;
|
||||||
throw new AuthError(`Kiro auth method '${method}' is not supported by CLI flow.`, 'kiro');
|
|
||||||
}
|
|
||||||
args.push(
|
|
||||||
...getKiroCLIAuthArgs(method, {
|
|
||||||
idcStartUrl: options.kiroIDCStartUrl,
|
|
||||||
idcRegion: options.kiroIDCRegion,
|
|
||||||
idcFlow: options.kiroIDCFlow,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
args.push(getOAuthConfig(provider).authFlag);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (headless) {
|
|
||||||
args.push('--no-browser');
|
|
||||||
}
|
|
||||||
if (provider === 'kiro' && noIncognito) {
|
|
||||||
args.push('--no-incognito');
|
|
||||||
}
|
|
||||||
|
|
||||||
return args;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function usesKiroLocalCallbackReplay(
|
export function usesKiroLocalCallbackReplay(
|
||||||
@@ -1223,12 +1242,10 @@ export async function triggerOAuth(
|
|||||||
usesKiroLocalCallbackReplay(resolvedKiroMethod, resolvedKiroIDCFlow);
|
usesKiroLocalCallbackReplay(resolvedKiroMethod, resolvedKiroIDCFlow);
|
||||||
const useSelectedKiroDirectCliFlow =
|
const useSelectedKiroDirectCliFlow =
|
||||||
provider === 'kiro' && (isDeviceCodeFlow || useSelectedKiroLocalPasteCallback);
|
provider === 'kiro' && (isDeviceCodeFlow || useSelectedKiroLocalPasteCallback);
|
||||||
|
const activeBackend = getConfiguredBackend();
|
||||||
|
|
||||||
if (!(selectedPasteCallback && !useSelectedKiroDirectCliFlow)) {
|
if (!(selectedPasteCallback && !useSelectedKiroDirectCliFlow)) {
|
||||||
const credentialError = getGeminiPlusOAuthCredentialError(
|
const credentialError = getGeminiPlusOAuthCredentialError(provider, activeBackend);
|
||||||
provider,
|
|
||||||
getStoredConfiguredBackend()
|
|
||||||
);
|
|
||||||
if (credentialError) {
|
if (credentialError) {
|
||||||
console.log(fail(credentialError));
|
console.log(fail(credentialError));
|
||||||
return null;
|
return null;
|
||||||
@@ -1266,7 +1283,26 @@ export async function triggerOAuth(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (selectedPasteCallback && !useSelectedKiroDirectCliFlow) {
|
if (selectedPasteCallback && !useSelectedKiroDirectCliFlow) {
|
||||||
const tokenDir = getProviderTokenDir(provider);
|
const target = getProxyTarget();
|
||||||
|
let tokenDir = getProviderTokenDir(provider);
|
||||||
|
if (provider === 'gemini' && activeBackend === 'original' && !target.isRemote) {
|
||||||
|
try {
|
||||||
|
const runtime = await prepareOAuthRuntime(provider, verbose, activeBackend);
|
||||||
|
tokenDir = runtime.tokenDir;
|
||||||
|
const selectedFlag = selectAdvertisedAuthFlag(
|
||||||
|
getOAuthFlagCandidatesForProvider(provider),
|
||||||
|
runtime.advertisedFlags
|
||||||
|
);
|
||||||
|
if (!selectedFlag) {
|
||||||
|
console.log(fail(getGeminiOriginalBackendOAuthMessage()));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(fail(formatOAuthRuntimePreparationError(error)));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return handlePasteCallbackMode(
|
return handlePasteCallbackMode(
|
||||||
provider,
|
provider,
|
||||||
oauthConfig,
|
oauthConfig,
|
||||||
@@ -1289,11 +1325,16 @@ export async function triggerOAuth(
|
|||||||
|
|
||||||
console.log('');
|
console.log('');
|
||||||
|
|
||||||
// Prepare binary
|
let runtime;
|
||||||
const prepared = await prepareBinary(provider, verbose);
|
let advertisedFlags: ReadonlySet<string>;
|
||||||
if (!prepared) return null;
|
try {
|
||||||
|
runtime = await prepareOAuthRuntime(provider, verbose, activeBackend);
|
||||||
const { binaryPath, tokenDir, configPath } = prepared;
|
advertisedFlags = runtime.advertisedFlags;
|
||||||
|
} catch (error) {
|
||||||
|
console.log(fail(formatOAuthRuntimePreparationError(error)));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const { binaryPath, tokenDir, configPath } = runtime;
|
||||||
|
|
||||||
// Free callback port if needed (only for authorization code flows)
|
// Free callback port if needed (only for authorization code flows)
|
||||||
const localCallbackPort = callbackPort;
|
const localCallbackPort = callbackPort;
|
||||||
@@ -1308,6 +1349,8 @@ export async function triggerOAuth(
|
|||||||
let args: string[];
|
let args: string[];
|
||||||
try {
|
try {
|
||||||
args = buildOAuthArgs(provider, configPath, processHeadless, noIncognito, {
|
args = buildOAuthArgs(provider, configPath, processHeadless, noIncognito, {
|
||||||
|
advertisedFlags,
|
||||||
|
backend: activeBackend,
|
||||||
kiroMethod: provider === 'kiro' ? resolvedKiroMethod : undefined,
|
kiroMethod: provider === 'kiro' ? resolvedKiroMethod : undefined,
|
||||||
kiroIDCStartUrl: options.kiroIDCStartUrl,
|
kiroIDCStartUrl: options.kiroIDCStartUrl,
|
||||||
kiroIDCRegion: options.kiroIDCRegion,
|
kiroIDCRegion: options.kiroIDCRegion,
|
||||||
|
|||||||
@@ -244,6 +244,7 @@ export class BinaryManager {
|
|||||||
|
|
||||||
export interface EnsureCLIProxyBinaryOptions {
|
export interface EnsureCLIProxyBinaryOptions {
|
||||||
allowInstall?: boolean;
|
allowInstall?: boolean;
|
||||||
|
backend?: CLIProxyBackend;
|
||||||
skipAutoUpdate?: boolean;
|
skipAutoUpdate?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,7 +253,7 @@ export async function ensureCLIProxyBinary(
|
|||||||
verbose = false,
|
verbose = false,
|
||||||
options: EnsureCLIProxyBinaryOptions = {}
|
options: EnsureCLIProxyBinaryOptions = {}
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const configuredBackend = getConfiguredOrDefaultBackend();
|
const configuredBackend = options.backend ?? getConfiguredOrDefaultBackend();
|
||||||
const backend = resolveLocalBackend(configuredBackend, { notifyOnPlus: true });
|
const backend = resolveLocalBackend(configuredBackend, { notifyOnPlus: true });
|
||||||
|
|
||||||
// Migrate old shared pin to backend-specific location (one-time migration)
|
// Migrate old shared pin to backend-specific location (one-time migration)
|
||||||
|
|||||||
Reference in new issue
Block a user