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 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, }, #[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, }, } #[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, pub sessions: Vec, } #[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, pub user_id: Option, pub display_name: Option, pub image_url: Option, pub last_used_at: i64, pub is_active: bool, pub selected_workspace_account_id: Option, pub workspaces: Vec, } #[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, pub image_url: Option, pub kind: Option, } #[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, } #[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, } #[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>, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct GetAccountTokenUsageResponse { pub summary: AccountTokenUsageSummary, pub daily_usage_buckets: Option>, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct AccountTokenUsageSummary { pub lifetime_tokens: Option, pub peak_daily_tokens: Option, pub longest_running_turn_sec: Option, pub current_streak_days: Option, pub longest_streak_days: Option, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct AccountTokenUsageDailyBucket { pub start_date: String, pub tokens: i64, } #[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, 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, pub plan_type: Option, } #[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, pub limit_name: Option, pub primary: Option, pub secondary: Option, pub credits: Option, pub individual_limit: Option, pub plan_type: Option, pub rate_limit_reached_type: Option, } impl From 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 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 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, #[ts(type = "number | null")] pub resets_at: Option, } impl From 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, } impl From 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 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, pub success: bool, pub error: Option, }