Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion codex-rs/backend-client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,14 +351,14 @@ impl Client {
fn map_plan_type(plan_type: crate::types::PlanType) -> AccountPlanType {
match plan_type {
crate::types::PlanType::Free => AccountPlanType::Free,
crate::types::PlanType::Go => AccountPlanType::Go,
crate::types::PlanType::Plus => AccountPlanType::Plus,
crate::types::PlanType::Pro => AccountPlanType::Pro,
crate::types::PlanType::Team => AccountPlanType::Team,
crate::types::PlanType::Business => AccountPlanType::Business,
crate::types::PlanType::Enterprise => AccountPlanType::Enterprise,
crate::types::PlanType::Edu | crate::types::PlanType::Education => AccountPlanType::Edu,
crate::types::PlanType::Guest
| crate::types::PlanType::Go
| crate::types::PlanType::FreeWorkspace
| crate::types::PlanType::Quorum
| crate::types::PlanType::K12 => AccountPlanType::Unknown,
Expand Down
1 change: 1 addition & 0 deletions codex-rs/core/src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ impl CodexAuth {
pub fn account_plan_type(&self) -> Option<AccountPlanType> {
let map_known = |kp: &InternalKnownPlan| match kp {
InternalKnownPlan::Free => AccountPlanType::Free,
InternalKnownPlan::Go => AccountPlanType::Go,
InternalKnownPlan::Plus => AccountPlanType::Plus,
InternalKnownPlan::Pro => AccountPlanType::Pro,
InternalKnownPlan::Team => AccountPlanType::Team,
Expand Down
23 changes: 19 additions & 4 deletions codex-rs/core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,9 +371,11 @@ impl std::fmt::Display for UsageLimitReachedError {
retry_suffix_after_or(self.resets_at.as_ref())
)
}
Some(PlanType::Known(KnownPlan::Free)) => {
"You've hit your usage limit. Upgrade to Plus to continue using Codex (https://openai.com/chatgpt/pricing)."
.to_string()
Some(PlanType::Known(KnownPlan::Free)) | Some(PlanType::Known(KnownPlan::Go)) => {
format!(
"You've hit your usage limit. Upgrade to Plus to continue using Codex (https://openai.com/chatgpt/pricing),{}",
retry_suffix_after_or(self.resets_at.as_ref())
)
}
Some(PlanType::Known(KnownPlan::Pro)) => format!(
"You've hit your usage limit. Visit https://chatgpt.com/codex/settings/usage to purchase more credits{}",
Expand Down Expand Up @@ -734,7 +736,20 @@ mod tests {
};
assert_eq!(
err.to_string(),
"You've hit your usage limit. Upgrade to Plus to continue using Codex (https://openai.com/chatgpt/pricing)."
"You've hit your usage limit. Upgrade to Plus to continue using Codex (https://openai.com/chatgpt/pricing), or try again later."
);
Comment on lines 737 to +740
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Update usage-limit tests to match new URL

The expected string in usage_limit_reached_error_formats_free_plan (and the new Go-plan test right after it) still asserts https://openai.com/chatgpt/pricing, but UsageLimitReachedError::fmt now emits https://chatgpt.com/codex/settings/usage for Free/Go plans. As written, these unit tests will fail whenever the core error tests are run, masking the intended message update.

Useful? React with 👍 / 👎.

}

#[test]
fn usage_limit_reached_error_formats_go_plan() {
let err = UsageLimitReachedError {
plan_type: Some(PlanType::Known(KnownPlan::Go)),
resets_at: None,
rate_limits: Some(rate_limit_snapshot()),
};
assert_eq!(
err.to_string(),
"You've hit your usage limit. Upgrade to Plus to continue using Codex (https://openai.com/chatgpt/pricing), or try again later."
);
}

Expand Down
33 changes: 33 additions & 0 deletions codex-rs/core/src/token_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ pub(crate) enum PlanType {
#[serde(rename_all = "lowercase")]
pub(crate) enum KnownPlan {
Free,
Go,
Plus,
Pro,
Team,
Expand Down Expand Up @@ -174,6 +175,38 @@ mod tests {
assert_eq!(info.get_chatgpt_plan_type().as_deref(), Some("Pro"));
}

#[test]
fn id_token_info_parses_go_plan() {
#[derive(Serialize)]
struct Header {
alg: &'static str,
typ: &'static str,
}
let header = Header {
alg: "none",
typ: "JWT",
};
let payload = serde_json::json!({
"email": "user@example.com",
"https://api.openai.com/auth": {
"chatgpt_plan_type": "go"
}
});

fn b64url_no_pad(bytes: &[u8]) -> String {
base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(bytes)
}

let header_b64 = b64url_no_pad(&serde_json::to_vec(&header).unwrap());
let payload_b64 = b64url_no_pad(&serde_json::to_vec(&payload).unwrap());
let signature_b64 = b64url_no_pad(b"sig");
let fake_jwt = format!("{header_b64}.{payload_b64}.{signature_b64}");

let info = parse_id_token(&fake_jwt).expect("should parse");
assert_eq!(info.email.as_deref(), Some("user@example.com"));
assert_eq!(info.get_chatgpt_plan_type().as_deref(), Some("Go"));
}

#[test]
fn id_token_info_handles_missing_fields() {
#[derive(Serialize)]
Expand Down
1 change: 1 addition & 0 deletions codex-rs/protocol/src/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use ts_rs::TS;
pub enum PlanType {
#[default]
Free,
Go,
Plus,
Pro,
Team,
Expand Down
Loading