forked from boos/BoosAPI
feat: expand gemini proxy compatibility matrix
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
import { BasePlatformAdapter, type BalanceInfo, type CheckinResult, type UserInfo } from './base.js';
|
||||
import {
|
||||
ANTIGRAVITY_CLIENT_METADATA,
|
||||
ANTIGRAVITY_GOOGLE_API_CLIENT,
|
||||
ANTIGRAVITY_UPSTREAM_BASE_URL,
|
||||
ANTIGRAVITY_USER_AGENT,
|
||||
} from '../oauth/antigravityProvider.js';
|
||||
|
||||
function normalizeBaseUrl(baseUrl: string): string {
|
||||
return (baseUrl || '').replace(/\/+$/, '');
|
||||
}
|
||||
|
||||
function extractAntigravityModelNames(payload: unknown): string[] {
|
||||
if (!payload || typeof payload !== 'object') return [];
|
||||
const record = payload as { models?: unknown };
|
||||
if (record.models && typeof record.models === 'object' && !Array.isArray(record.models)) {
|
||||
return Object.keys(record.models).map((name) => name.trim()).filter(Boolean);
|
||||
}
|
||||
if (Array.isArray(record.models)) {
|
||||
return record.models.flatMap((item) => {
|
||||
if (typeof item === 'string') return item.trim() ? [item.trim()] : [];
|
||||
if (!item || typeof item !== 'object') return [];
|
||||
const id = typeof (item as { id?: unknown }).id === 'string'
|
||||
? (item as { id: string }).id.trim()
|
||||
: (typeof (item as { name?: unknown }).name === 'string'
|
||||
? (item as { name: string }).name.trim()
|
||||
: '');
|
||||
return id ? [id] : [];
|
||||
});
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
export class AntigravityAdapter extends BasePlatformAdapter {
|
||||
readonly platformName = 'antigravity';
|
||||
|
||||
async detect(url: string): Promise<boolean> {
|
||||
const normalized = (url || '').toLowerCase();
|
||||
return normalized.includes('antigravity');
|
||||
}
|
||||
|
||||
override async login(_baseUrl: string, _username: string, _password: string) {
|
||||
return { success: false, message: 'login endpoint not supported' };
|
||||
}
|
||||
|
||||
override async getUserInfo(_baseUrl: string, _accessToken: string): Promise<UserInfo | null> {
|
||||
return null;
|
||||
}
|
||||
|
||||
async checkin(_baseUrl: string, _accessToken: string): Promise<CheckinResult> {
|
||||
return { success: false, message: 'checkin endpoint not supported' };
|
||||
}
|
||||
|
||||
async getBalance(_baseUrl: string, _accessToken: string): Promise<BalanceInfo> {
|
||||
return { balance: 0, used: 0, quota: 0 };
|
||||
}
|
||||
|
||||
async getModels(baseUrl: string, accessToken: string): Promise<string[]> {
|
||||
try {
|
||||
const payload = await this.fetchJson<{ models?: unknown }>(
|
||||
`${normalizeBaseUrl(baseUrl || ANTIGRAVITY_UPSTREAM_BASE_URL)}/v1internal:fetchAvailableModels`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: 'application/json',
|
||||
'User-Agent': ANTIGRAVITY_USER_AGENT,
|
||||
'X-Goog-Api-Client': ANTIGRAVITY_GOOGLE_API_CLIENT,
|
||||
'Client-Metadata': ANTIGRAVITY_CLIENT_METADATA,
|
||||
},
|
||||
body: JSON.stringify({}),
|
||||
},
|
||||
);
|
||||
return extractAntigravityModelNames(payload);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,6 +58,11 @@ describe('getAdapter platform aliases', () => {
|
||||
expect(getAdapter('gemini')?.platformName).toBe('gemini');
|
||||
});
|
||||
|
||||
it('supports antigravity adapter aliases', () => {
|
||||
expect(getAdapter('antigravity')?.platformName).toBe('antigravity');
|
||||
expect(getAdapter('anti-gravity')?.platformName).toBe('antigravity');
|
||||
});
|
||||
|
||||
it('supports dedicated codex adapter and aliases', () => {
|
||||
expect(getAdapter('codex')?.platformName).toBe('codex');
|
||||
expect(getAdapter('chatgpt-codex')?.platformName).toBe('codex');
|
||||
|
||||
@@ -11,6 +11,7 @@ import { CodexAdapter } from './codex.js';
|
||||
import { ClaudeAdapter } from './claude.js';
|
||||
import { GeminiAdapter } from './gemini.js';
|
||||
import { GeminiCliAdapter } from './geminiCli.js';
|
||||
import { AntigravityAdapter } from './antigravity.js';
|
||||
import { CliProxyApiAdapter } from './cliproxyapi.js';
|
||||
import { detectPlatformByTitle } from './titleHint.js';
|
||||
|
||||
@@ -21,6 +22,7 @@ const adapters: PlatformAdapter[] = [
|
||||
new ClaudeAdapter(),
|
||||
new GeminiAdapter(),
|
||||
new GeminiCliAdapter(),
|
||||
new AntigravityAdapter(),
|
||||
new CliProxyApiAdapter(),
|
||||
new AnyRouterAdapter(),
|
||||
new DoneHubAdapter(),
|
||||
@@ -60,6 +62,8 @@ const platformAliases: Record<string, string> = {
|
||||
claude: 'claude',
|
||||
gemini: 'gemini',
|
||||
'gemini-cli': 'gemini-cli',
|
||||
antigravity: 'antigravity',
|
||||
'anti-gravity': 'antigravity',
|
||||
google: 'gemini',
|
||||
// CLIProxyAPI aliases
|
||||
cliproxyapi: 'cliproxyapi',
|
||||
|
||||
@@ -44,10 +44,7 @@ function extractHtmlTitle(html: string): string {
|
||||
return match[1].replace(/\s+/g, ' ').trim();
|
||||
}
|
||||
|
||||
export async function detectPlatformByTitle(url: string): Promise<TitleHintPlatform | undefined> {
|
||||
const base = normalizeBaseUrl(url);
|
||||
if (!base) return undefined;
|
||||
|
||||
async function detectPlatformByTitleOnce(base: string): Promise<TitleHintPlatform | undefined> {
|
||||
try {
|
||||
const { fetch } = await import('undici');
|
||||
const res = await fetch(`${base}/`, {
|
||||
@@ -71,3 +68,16 @@ export async function detectPlatformByTitle(url: string): Promise<TitleHintPlatf
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export async function detectPlatformByTitle(url: string): Promise<TitleHintPlatform | undefined> {
|
||||
const base = normalizeBaseUrl(url);
|
||||
if (!base) return undefined;
|
||||
|
||||
const first = await detectPlatformByTitleOnce(base);
|
||||
if (first) return first;
|
||||
|
||||
// Under heavy parallel test load, local title probes can occasionally race
|
||||
// with just-started ephemeral HTTP servers. Retry once before giving up.
|
||||
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||
return detectPlatformByTitleOnce(base);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user