feat: implement Codex OAuth flow with loopback callback server and management UI
- Add localCallbackServer for handling OAuth callback requests. - Implement service functions for managing OAuth sessions and accounts. - Create CodexAdapter for platform-specific interactions. - Develop OAuthManagement page for managing OAuth connections and providers. - Add tests for localCallbackServer and OAuthManagement components.
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
import { BasePlatformAdapter, type BalanceInfo, type CheckinResult, type UserInfo } from './base.js';
|
||||
|
||||
function normalizeBaseUrl(baseUrl: string): string {
|
||||
return (baseUrl || '').replace(/\/+$/, '');
|
||||
}
|
||||
|
||||
export class CodexAdapter extends BasePlatformAdapter {
|
||||
readonly platformName = 'codex';
|
||||
|
||||
async detect(url: string): Promise<boolean> {
|
||||
const normalized = normalizeBaseUrl(url).toLowerCase();
|
||||
return normalized.includes('chatgpt.com/backend-api/codex');
|
||||
}
|
||||
|
||||
override async login(_baseUrl: string, _username: string, _password: string) {
|
||||
return { success: false as const, message: 'codex oauth login is managed via OAuth flow' };
|
||||
}
|
||||
|
||||
override async getUserInfo(_baseUrl: string, _accessToken: string): Promise<UserInfo | null> {
|
||||
return null;
|
||||
}
|
||||
|
||||
async checkin(_baseUrl: string, _accessToken: string): Promise<CheckinResult> {
|
||||
return { success: false, message: 'codex oauth connections do not support checkin' };
|
||||
}
|
||||
|
||||
async getBalance(_baseUrl: string, _accessToken: string): Promise<BalanceInfo> {
|
||||
return { balance: 0, used: 0, quota: 0 };
|
||||
}
|
||||
|
||||
async getModels(_baseUrl: string, _token: string): Promise<string[]> {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -58,6 +58,11 @@ describe('getAdapter platform aliases', () => {
|
||||
expect(getAdapter('gemini')?.platformName).toBe('gemini');
|
||||
});
|
||||
|
||||
it('supports dedicated codex adapter and aliases', () => {
|
||||
expect(getAdapter('codex')?.platformName).toBe('codex');
|
||||
expect(getAdapter('chatgpt-codex')?.platformName).toBe('codex');
|
||||
});
|
||||
|
||||
it('detects anyrouter URL before generic new-api adapter', async () => {
|
||||
const adapter = await detectPlatform('https://anyrouter.top');
|
||||
expect(adapter?.platformName).toBe('anyrouter');
|
||||
|
||||
@@ -7,6 +7,7 @@ import { OneHubAdapter } from './oneHub.js';
|
||||
import { DoneHubAdapter } from './doneHub.js';
|
||||
import { Sub2ApiAdapter } from './sub2api.js';
|
||||
import { OpenAiAdapter } from './openai.js';
|
||||
import { CodexAdapter } from './codex.js';
|
||||
import { ClaudeAdapter } from './claude.js';
|
||||
import { GeminiAdapter } from './gemini.js';
|
||||
import { CliProxyApiAdapter } from './cliproxyapi.js';
|
||||
@@ -15,6 +16,7 @@ import { detectPlatformByTitle } from './titleHint.js';
|
||||
const adapters: PlatformAdapter[] = [
|
||||
// Specific forks before generic adapters for better auto-detection.
|
||||
new OpenAiAdapter(),
|
||||
new CodexAdapter(),
|
||||
new ClaudeAdapter(),
|
||||
new GeminiAdapter(),
|
||||
new CliProxyApiAdapter(),
|
||||
@@ -49,6 +51,9 @@ const platformAliases: Record<string, string> = {
|
||||
sub2api: 'sub2api',
|
||||
// Official upstream APIs
|
||||
openai: 'openai',
|
||||
codex: 'codex',
|
||||
'chatgpt-codex': 'codex',
|
||||
'chatgpt codex': 'codex',
|
||||
anthropic: 'claude',
|
||||
claude: 'claude',
|
||||
gemini: 'gemini',
|
||||
@@ -83,6 +88,7 @@ function detectPlatformByUrlHint(url: string): string | undefined {
|
||||
|
||||
// Official upstream endpoints.
|
||||
if (normalized.includes('api.openai.com')) return 'openai';
|
||||
if (normalized.includes('chatgpt.com/backend-api/codex')) return 'codex';
|
||||
if (normalized.includes('api.anthropic.com') || normalized.includes('anthropic.com/v1')) return 'claude';
|
||||
if (
|
||||
normalized.includes('generativelanguage.googleapis.com')
|
||||
|
||||
Reference in New Issue
Block a user