mirror of
https://github.com/openai/codex.git
synced 2026-10-28 18:58:46 +00:00
490 lines
18 KiB
Rust
490 lines
18 KiB
Rust
use crate::protocol::common::AuthMode;
|
|
use codex_experimental_api_macros::ExperimentalApi;
|
|
use codex_protocol::account::PlanType;
|
|
use codex_protocol::account::ProviderAccount;
|
|
use codex_protocol::protocol::CreditsSnapshot as CoreCreditsSnapshot;
|
|
use codex_protocol::protocol::RateLimitReachedType as CoreRateLimitReachedType;
|
|
use codex_protocol::protocol::RateLimitSnapshot as CoreRateLimitSnapshot;
|
|
use codex_protocol::protocol::RateLimitWindow as CoreRateLimitWindow;
|
|
use codex_protocol::protocol::SpendControlLimitSnapshot as CoreSpendControlLimitSnapshot;
|
|
use schemars::JsonSchema;
|
|
use serde::Deserialize;
|
|
use serde::Serialize;
|
|
use std::collections::HashMap;
|
|
use ts_rs::TS;
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(tag = "type", rename_all = "camelCase")]
|
|
#[ts(tag = "type")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum Account {
|
|
#[serde(rename = "apiKey", rename_all = "camelCase")]
|
|
#[ts(rename = "apiKey", rename_all = "camelCase")]
|
|
ApiKey {},
|
|
|
|
#[serde(rename = "chatgpt", rename_all = "camelCase")]
|
|
#[ts(rename = "chatgpt", rename_all = "camelCase")]
|
|
Chatgpt { email: String, plan_type: PlanType },
|
|
|
|
#[serde(rename = "amazonBedrock", rename_all = "camelCase")]
|
|
#[ts(rename = "amazonBedrock", rename_all = "camelCase")]
|
|
AmazonBedrock {},
|
|
}
|
|
|
|
impl From<ProviderAccount> for Account {
|
|
fn from(account: ProviderAccount) -> Self {
|
|
match account {
|
|
ProviderAccount::ApiKey => Self::ApiKey {},
|
|
ProviderAccount::Chatgpt { email, plan_type } => Self::Chatgpt { email, plan_type },
|
|
ProviderAccount::AmazonBedrock => Self::AmazonBedrock {},
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS, ExperimentalApi)]
|
|
#[serde(tag = "type")]
|
|
#[ts(tag = "type")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum LoginAccountParams {
|
|
#[serde(rename = "apiKey", rename_all = "camelCase")]
|
|
#[ts(rename = "apiKey", rename_all = "camelCase")]
|
|
ApiKey {
|
|
#[serde(rename = "apiKey")]
|
|
#[ts(rename = "apiKey")]
|
|
api_key: String,
|
|
},
|
|
#[serde(rename = "chatgpt", rename_all = "camelCase")]
|
|
#[ts(rename = "chatgpt", rename_all = "camelCase")]
|
|
Chatgpt {
|
|
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
|
codex_streamlined_login: bool,
|
|
/// Optional auth session logging ID propagated to authapi for analytics correlation.
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
#[ts(optional)]
|
|
auth_session_logging_id: Option<String>,
|
|
},
|
|
#[serde(rename = "chatgptDeviceCode")]
|
|
#[ts(rename = "chatgptDeviceCode")]
|
|
ChatgptDeviceCode,
|
|
/// [UNSTABLE] FOR OPENAI INTERNAL USE ONLY - DO NOT USE.
|
|
/// The access token must contain the same scopes that Codex-managed ChatGPT auth tokens have.
|
|
#[experimental("account/login/start.chatgptAuthTokens")]
|
|
#[serde(rename = "chatgptAuthTokens", rename_all = "camelCase")]
|
|
#[ts(rename = "chatgptAuthTokens", rename_all = "camelCase")]
|
|
ChatgptAuthTokens {
|
|
/// Access token (JWT) supplied by the client.
|
|
/// This token is used for backend API requests and email extraction.
|
|
access_token: String,
|
|
/// Workspace/account identifier supplied by the client.
|
|
chatgpt_account_id: String,
|
|
/// Optional plan type supplied by the client.
|
|
///
|
|
/// When `null`, Codex attempts to derive the plan type from access-token
|
|
/// claims. If unavailable, the plan defaults to `unknown`.
|
|
#[ts(optional = nullable)]
|
|
chatgpt_plan_type: Option<String>,
|
|
},
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(tag = "type", rename_all = "camelCase")]
|
|
#[ts(tag = "type")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum LoginAccountResponse {
|
|
#[serde(rename = "apiKey", rename_all = "camelCase")]
|
|
#[ts(rename = "apiKey", rename_all = "camelCase")]
|
|
ApiKey {},
|
|
#[serde(rename = "chatgpt", rename_all = "camelCase")]
|
|
#[ts(rename = "chatgpt", rename_all = "camelCase")]
|
|
Chatgpt {
|
|
// Use plain String for identifiers to avoid TS/JSON Schema quirks around uuid-specific types.
|
|
// Convert to/from UUIDs at the application layer as needed.
|
|
login_id: String,
|
|
/// URL the client should open in a browser to initiate the OAuth flow.
|
|
auth_url: String,
|
|
},
|
|
#[serde(rename = "chatgptDeviceCode", rename_all = "camelCase")]
|
|
#[ts(rename = "chatgptDeviceCode", rename_all = "camelCase")]
|
|
ChatgptDeviceCode {
|
|
// Use plain String for identifiers to avoid TS/JSON Schema quirks around uuid-specific types.
|
|
// Convert to/from UUIDs at the application layer as needed.
|
|
login_id: String,
|
|
/// URL the client should open in a browser to complete device code authorization.
|
|
verification_url: String,
|
|
/// One-time code the user must enter after signing in.
|
|
user_code: String,
|
|
},
|
|
#[serde(rename = "chatgptAuthTokens", rename_all = "camelCase")]
|
|
#[ts(rename = "chatgptAuthTokens", rename_all = "camelCase")]
|
|
ChatgptAuthTokens {},
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct CancelLoginAccountParams {
|
|
pub login_id: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum CancelLoginAccountStatus {
|
|
Canceled,
|
|
NotFound,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct CancelLoginAccountResponse {
|
|
pub status: CancelLoginAccountStatus,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct AccountSessionsAddParams {
|
|
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
|
pub switch_to_added_account: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct AccountSessionsListParams {
|
|
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
|
pub refresh_workspace_metadata: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct AccountSessionsLogoutParams {
|
|
pub session_id: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct AccountSessionsSwitchParams {
|
|
pub session_id: String,
|
|
pub account_id: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct AccountSessionsResponse {
|
|
pub active_session_id: Option<String>,
|
|
pub sessions: Vec<AccountSession>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct AccountSession {
|
|
pub session_id: String,
|
|
pub email: Option<String>,
|
|
pub user_id: Option<String>,
|
|
pub display_name: Option<String>,
|
|
pub image_url: Option<String>,
|
|
pub last_used_at: i64,
|
|
pub is_active: bool,
|
|
pub selected_workspace_account_id: Option<String>,
|
|
pub workspaces: Vec<AccountSessionWorkspace>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct AccountSessionWorkspace {
|
|
pub account_id: String,
|
|
pub name: Option<String>,
|
|
pub image_url: Option<String>,
|
|
pub kind: Option<AccountSessionWorkspaceKind>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum AccountSessionWorkspaceKind {
|
|
Personal,
|
|
Workspace,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct LogoutAccountResponse {}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum ChatgptAuthTokensRefreshReason {
|
|
/// Codex attempted a backend request and received `401 Unauthorized`.
|
|
Unauthorized,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ChatgptAuthTokensRefreshParams {
|
|
pub reason: ChatgptAuthTokensRefreshReason,
|
|
/// Workspace/account identifier that Codex was previously using.
|
|
///
|
|
/// Clients that manage multiple accounts/workspaces can use this as a hint
|
|
/// to refresh the token for the correct workspace.
|
|
///
|
|
/// This may be `null` when the prior auth state did not include a workspace
|
|
/// identifier (`chatgpt_account_id`).
|
|
#[ts(optional = nullable)]
|
|
pub previous_account_id: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ChatgptAuthTokensRefreshResponse {
|
|
pub access_token: String,
|
|
pub chatgpt_account_id: String,
|
|
pub chatgpt_plan_type: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct GetAccountRateLimitsResponse {
|
|
/// Backward-compatible single-bucket view; mirrors the historical payload.
|
|
pub rate_limits: RateLimitSnapshot,
|
|
/// Multi-bucket view keyed by metered `limit_id` (for example, `codex`).
|
|
pub rate_limits_by_limit_id: Option<HashMap<String, RateLimitSnapshot>>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct SendAddCreditsNudgeEmailParams {
|
|
pub credit_type: AddCreditsNudgeCreditType,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "snake_case")]
|
|
#[ts(export_to = "v2/", rename_all = "snake_case")]
|
|
pub enum AddCreditsNudgeCreditType {
|
|
Credits,
|
|
UsageLimit,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct SendAddCreditsNudgeEmailResponse {
|
|
pub status: AddCreditsNudgeEmailStatus,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "snake_case")]
|
|
#[ts(export_to = "v2/", rename_all = "snake_case")]
|
|
pub enum AddCreditsNudgeEmailStatus {
|
|
Sent,
|
|
CooldownActive,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct GetAccountParams {
|
|
/// When `true`, requests a proactive token refresh before returning.
|
|
///
|
|
/// In managed auth mode this triggers the normal refresh-token flow. In
|
|
/// external auth mode this flag is ignored. Clients should refresh tokens
|
|
/// themselves and call `account/login/start` with `chatgptAuthTokens`.
|
|
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
|
pub refresh_token: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct GetAccountResponse {
|
|
pub account: Option<Account>,
|
|
pub requires_openai_auth: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct AccountUpdatedNotification {
|
|
pub auth_mode: Option<AuthMode>,
|
|
pub plan_type: Option<PlanType>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
/// Sparse rolling rate-limit update.
|
|
///
|
|
/// Clients should merge available values into the most recent `account/rateLimits/read` response
|
|
/// or refetch that snapshot. Nullable account metadata may be unavailable in a rolling update and
|
|
/// does not clear a previously observed value.
|
|
pub struct AccountRateLimitsUpdatedNotification {
|
|
pub rate_limits: RateLimitSnapshot,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct RateLimitSnapshot {
|
|
pub limit_id: Option<String>,
|
|
pub limit_name: Option<String>,
|
|
pub primary: Option<RateLimitWindow>,
|
|
pub secondary: Option<RateLimitWindow>,
|
|
pub credits: Option<CreditsSnapshot>,
|
|
pub individual_limit: Option<SpendControlLimitSnapshot>,
|
|
pub plan_type: Option<PlanType>,
|
|
pub rate_limit_reached_type: Option<RateLimitReachedType>,
|
|
}
|
|
|
|
impl From<CoreRateLimitSnapshot> for RateLimitSnapshot {
|
|
fn from(value: CoreRateLimitSnapshot) -> Self {
|
|
Self {
|
|
limit_id: value.limit_id,
|
|
limit_name: value.limit_name,
|
|
primary: value.primary.map(RateLimitWindow::from),
|
|
secondary: value.secondary.map(RateLimitWindow::from),
|
|
credits: value.credits.map(CreditsSnapshot::from),
|
|
individual_limit: value.individual_limit.map(SpendControlLimitSnapshot::from),
|
|
plan_type: value.plan_type,
|
|
rate_limit_reached_type: value
|
|
.rate_limit_reached_type
|
|
.map(RateLimitReachedType::from),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "snake_case")]
|
|
#[ts(export_to = "v2/", rename_all = "snake_case")]
|
|
pub enum RateLimitReachedType {
|
|
RateLimitReached,
|
|
WorkspaceOwnerCreditsDepleted,
|
|
WorkspaceMemberCreditsDepleted,
|
|
WorkspaceOwnerUsageLimitReached,
|
|
WorkspaceMemberUsageLimitReached,
|
|
}
|
|
|
|
impl From<CoreRateLimitReachedType> for RateLimitReachedType {
|
|
fn from(value: CoreRateLimitReachedType) -> Self {
|
|
match value {
|
|
CoreRateLimitReachedType::RateLimitReached => Self::RateLimitReached,
|
|
CoreRateLimitReachedType::WorkspaceOwnerCreditsDepleted => {
|
|
Self::WorkspaceOwnerCreditsDepleted
|
|
}
|
|
CoreRateLimitReachedType::WorkspaceMemberCreditsDepleted => {
|
|
Self::WorkspaceMemberCreditsDepleted
|
|
}
|
|
CoreRateLimitReachedType::WorkspaceOwnerUsageLimitReached => {
|
|
Self::WorkspaceOwnerUsageLimitReached
|
|
}
|
|
CoreRateLimitReachedType::WorkspaceMemberUsageLimitReached => {
|
|
Self::WorkspaceMemberUsageLimitReached
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<RateLimitReachedType> for CoreRateLimitReachedType {
|
|
fn from(value: RateLimitReachedType) -> Self {
|
|
match value {
|
|
RateLimitReachedType::RateLimitReached => Self::RateLimitReached,
|
|
RateLimitReachedType::WorkspaceOwnerCreditsDepleted => {
|
|
Self::WorkspaceOwnerCreditsDepleted
|
|
}
|
|
RateLimitReachedType::WorkspaceMemberCreditsDepleted => {
|
|
Self::WorkspaceMemberCreditsDepleted
|
|
}
|
|
RateLimitReachedType::WorkspaceOwnerUsageLimitReached => {
|
|
Self::WorkspaceOwnerUsageLimitReached
|
|
}
|
|
RateLimitReachedType::WorkspaceMemberUsageLimitReached => {
|
|
Self::WorkspaceMemberUsageLimitReached
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct RateLimitWindow {
|
|
pub used_percent: i32,
|
|
#[ts(type = "number | null")]
|
|
pub window_duration_mins: Option<i64>,
|
|
#[ts(type = "number | null")]
|
|
pub resets_at: Option<i64>,
|
|
}
|
|
|
|
impl From<CoreRateLimitWindow> for RateLimitWindow {
|
|
fn from(value: CoreRateLimitWindow) -> Self {
|
|
Self {
|
|
used_percent: value.used_percent.round() as i32,
|
|
window_duration_mins: value.window_minutes,
|
|
resets_at: value.resets_at,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct CreditsSnapshot {
|
|
pub has_credits: bool,
|
|
pub unlimited: bool,
|
|
pub balance: Option<String>,
|
|
}
|
|
|
|
impl From<CoreCreditsSnapshot> for CreditsSnapshot {
|
|
fn from(value: CoreCreditsSnapshot) -> Self {
|
|
Self {
|
|
has_credits: value.has_credits,
|
|
unlimited: value.unlimited,
|
|
balance: value.balance,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct SpendControlLimitSnapshot {
|
|
pub limit: String,
|
|
pub used: String,
|
|
pub remaining_percent: i32,
|
|
#[ts(type = "number")]
|
|
pub resets_at: i64,
|
|
}
|
|
|
|
impl From<CoreSpendControlLimitSnapshot> for SpendControlLimitSnapshot {
|
|
fn from(value: CoreSpendControlLimitSnapshot) -> Self {
|
|
Self {
|
|
limit: value.limit,
|
|
used: value.used,
|
|
remaining_percent: value.remaining_percent,
|
|
resets_at: value.resets_at,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct AccountLoginCompletedNotification {
|
|
// Use plain String for identifiers to avoid TS/JSON Schema quirks around uuid-specific types.
|
|
// Convert to/from UUIDs at the application layer as needed.
|
|
pub login_id: Option<String>,
|
|
pub success: bool,
|
|
pub error: Option<String>,
|
|
}
|