Spaces:
Runtime error
Runtime error
| import { OAuthService } from "./oauth"; | |
| import { OPENAI_CONFIG } from "../constants/oauth"; | |
| import { getServerCredentials } from "../config/index"; | |
| import { spinner as createSpinner } from "../utils/ui"; | |
| /** | |
| * OpenAI OAuth Service (Native) | |
| * Uses Authorization Code Flow with PKCE (similar to Codex) | |
| */ | |
| export class OpenAIService extends OAuthService { | |
| constructor() { | |
| super(OPENAI_CONFIG); | |
| } | |
| /** | |
| * Build OpenAI authorization URL | |
| */ | |
| buildOpenAIAuthUrl(redirectUri, state, codeChallenge) { | |
| const params = new URLSearchParams({ | |
| client_id: OPENAI_CONFIG.clientId, | |
| response_type: "code", | |
| redirect_uri: redirectUri, | |
| scope: OPENAI_CONFIG.scope, | |
| state: state, | |
| code_challenge: codeChallenge, | |
| code_challenge_method: OPENAI_CONFIG.codeChallengeMethod, | |
| ...OPENAI_CONFIG.extraParams, | |
| }); | |
| return `${OPENAI_CONFIG.authorizeUrl}?${params.toString()}`; | |
| } | |
| /** | |
| * Exchange OpenAI authorization code for tokens | |
| */ | |
| async exchangeOpenAICode(code, redirectUri, codeVerifier) { | |
| const response = await fetch(OPENAI_CONFIG.tokenUrl, { | |
| method: "POST", | |
| headers: { | |
| "Content-Type": "application/x-www-form-urlencoded", | |
| Accept: "application/json", | |
| }, | |
| body: new URLSearchParams({ | |
| grant_type: "authorization_code", | |
| client_id: OPENAI_CONFIG.clientId, | |
| code: code, | |
| redirect_uri: redirectUri, | |
| code_verifier: codeVerifier, | |
| }), | |
| }); | |
| if (!response.ok) { | |
| const error = await response.text(); | |
| throw new Error(`Token exchange failed: ${error}`); | |
| } | |
| return await response.json(); | |
| } | |
| /** | |
| * Save OpenAI tokens to server | |
| */ | |
| async saveTokens(tokens) { | |
| const { server, token, userId } = getServerCredentials(); | |
| const response = await fetch(`${server}/api/cli/providers/openai`, { | |
| method: "POST", | |
| headers: { | |
| "Content-Type": "application/json", | |
| Authorization: `Bearer ${token}`, | |
| "X-User-Id": userId, | |
| }, | |
| body: JSON.stringify({ | |
| accessToken: tokens.access_token, | |
| refreshToken: tokens.refresh_token, | |
| expiresIn: tokens.expires_in, | |
| idToken: tokens.id_token, | |
| scope: tokens.scope, | |
| }), | |
| }); | |
| if (!response.ok) { | |
| const error = await response.json(); | |
| throw new Error(error.error || "Failed to save tokens"); | |
| } | |
| return await response.json(); | |
| } | |
| /** | |
| * Complete OpenAI OAuth flow | |
| */ | |
| async connect() { | |
| const spinner = createSpinner("Starting OpenAI OAuth...").start(); | |
| try { | |
| spinner.text = "Starting local server..."; | |
| // Authenticate and get authorization code | |
| const { code, codeVerifier, redirectUri } = await this.authenticate( | |
| "OpenAI", | |
| this.buildOpenAIAuthUrl.bind(this) | |
| ); | |
| spinner.start("Exchanging code for tokens..."); | |
| // Exchange code for tokens | |
| const tokens = await this.exchangeOpenAICode(code, redirectUri, codeVerifier); | |
| spinner.text = "Saving tokens to server..."; | |
| // Save tokens to server | |
| await this.saveTokens(tokens); | |
| spinner.succeed("OpenAI connected successfully!"); | |
| return true; | |
| } catch (error) { | |
| spinner.fail(`Failed: ${error.message}`); | |
| throw error; | |
| } | |
| } | |
| } | |