Merge branch 'dev/winston/mitm-command-ca-bundles' into dev/winston/credentialed-routes-e2e

# Conflicts:
#	codex-rs/backend-client/src/client.rs
#	codex-rs/backend-client/src/lib.rs
This commit is contained in:
Winston Howes
2026-06-03 19:39:58 -07:00
329 changed files with 15716 additions and 5353 deletions
+53 -19
View File
@@ -1,6 +1,5 @@
use crate::types::CodeTaskDetailsResponse;
use crate::types::ConfigBundleResponse;
use crate::types::ConfigFileResponse;
use crate::types::ListCredentialRoutesResponse;
use crate::types::PaginatedListTaskListItem;
use crate::types::RateLimitReachedKind as BackendRateLimitReachedKind;
@@ -17,6 +16,7 @@ use codex_protocol::protocol::CreditsSnapshot;
use codex_protocol::protocol::RateLimitReachedType;
use codex_protocol::protocol::RateLimitSnapshot;
use codex_protocol::protocol::RateLimitWindow;
use codex_protocol::protocol::SpendControlLimitSnapshot;
use reqwest::StatusCode;
use reqwest::header::CONTENT_TYPE;
use reqwest::header::HeaderMap;
@@ -398,23 +398,6 @@ impl Client {
self.decode_json::<TurnAttemptsSiblingTurnsResponse>(&url, &ct, &body)
}
/// Fetch the managed requirements file from codex-backend.
///
/// `GET /api/codex/config/requirements` (Codex API style) or
/// `GET /wham/config/requirements` (ChatGPT backend-api style).
pub async fn get_config_requirements_file(
&self,
) -> std::result::Result<ConfigFileResponse, RequestError> {
let url = match self.path_style {
PathStyle::CodexApi => format!("{}/api/codex/config/requirements", self.base_url),
PathStyle::ChatGptApi => format!("{}/wham/config/requirements", self.base_url),
};
let req = self.http.get(&url).headers(self.headers());
let (body, ct) = self.exec_request_detailed(req, "GET", &url).await?;
self.decode_json::<ConfigFileResponse>(&url, &ct, &body)
.map_err(RequestError::from)
}
pub async fn list_credential_routes(&self) -> Result<ListCredentialRoutesResponse> {
let url = self.credential_routes_url();
let req = self.http.get(&url).headers(self.headers());
@@ -432,7 +415,6 @@ impl Client {
pub fn credential_routes_proxy_auth_headers(&self) -> HeaderMap {
self.auth_headers()
}
/// Fetch the selected cloud-managed config bundle from codex-backend.
///
/// `GET /api/codex/config/bundle` (Codex API style) or
@@ -494,11 +476,17 @@ impl Client {
.rate_limit_reached_type
.flatten()
.and_then(|details| Self::map_rate_limit_reached_type(details.kind));
let individual_limit = payload
.spend_control
.flatten()
.and_then(|details| details.individual_limit.flatten())
.map(|details| Self::map_individual_limit(*details));
let mut snapshots = vec![Self::make_rate_limit_snapshot(
Some("codex".to_string()),
/*limit_name*/ None,
payload.rate_limit.flatten().map(|details| *details),
payload.credits.flatten().map(|details| *details),
individual_limit,
plan_type,
rate_limit_reached_type,
)];
@@ -509,6 +497,7 @@ impl Client {
Some(details.limit_name),
details.rate_limit.flatten().map(|rate_limit| *rate_limit),
/*credits*/ None,
/*individual_limit*/ None,
plan_type,
/*rate_limit_reached_type*/ None,
)
@@ -522,6 +511,7 @@ impl Client {
limit_name: Option<String>,
rate_limit: Option<crate::types::RateLimitStatusDetails>,
credits: Option<crate::types::CreditStatusDetails>,
individual_limit: Option<SpendControlLimitSnapshot>,
plan_type: Option<AccountPlanType>,
rate_limit_reached_type: Option<RateLimitReachedType>,
) -> RateLimitSnapshot {
@@ -538,6 +528,7 @@ impl Client {
primary,
secondary,
credits: Self::map_credits(credits),
individual_limit,
plan_type,
rate_limit_reached_type,
}
@@ -613,6 +604,17 @@ impl Client {
})
}
fn map_individual_limit(
details: crate::types::SpendControlLimitDetails,
) -> SpendControlLimitSnapshot {
SpendControlLimitSnapshot {
limit: details.limit,
used: details.used,
remaining_percent: details.remaining_percent,
resets_at: i64::from(details.reset_at),
}
}
fn map_plan_type(plan_type: crate::types::PlanType) -> AccountPlanType {
match plan_type {
crate::types::PlanType::Free => AccountPlanType::Free,
@@ -707,6 +709,23 @@ mod tests {
balance: Some(Some("9.99".to_string())),
..Default::default()
}))),
spend_control: Some(Some(Box::new(
codex_backend_openapi_models::models::SpendControlStatusDetails {
reached: false,
individual_limit: Some(Some(Box::new(
crate::types::SpendControlLimitDetails {
source: None,
limit: "25000".to_string(),
used: "8000".to_string(),
remaining: "17000".to_string(),
used_percent: 32,
remaining_percent: 68,
reset_after_seconds: 3600,
reset_at: 789,
},
))),
},
))),
rate_limit_reached_type: Some(Some(BackendRateLimitReachedType {
kind: RateLimitReachedKind::WorkspaceMemberCreditsDepleted,
})),
@@ -738,6 +757,15 @@ mod tests {
snapshots[0].rate_limit_reached_type,
Some(RateLimitReachedType::WorkspaceMemberCreditsDepleted)
);
assert_eq!(
snapshots[0].individual_limit,
Some(SpendControlLimitSnapshot {
limit: "25000".to_string(),
used: "8000".to_string(),
remaining_percent: 68,
resets_at: 789,
})
);
assert_eq!(snapshots[1].limit_id.as_deref(), Some("codex_other"));
assert_eq!(snapshots[1].limit_name.as_deref(), Some("codex_other"));
@@ -746,6 +774,7 @@ mod tests {
Some(70.0)
);
assert_eq!(snapshots[1].credits, None);
assert_eq!(snapshots[1].individual_limit, None);
assert_eq!(snapshots[1].plan_type, Some(AccountPlanType::Pro));
assert_eq!(snapshots[1].rate_limit_reached_type, None);
}
@@ -761,6 +790,7 @@ mod tests {
rate_limit: None,
}])),
credits: None,
spend_control: None,
rate_limit_reached_type: None,
};
@@ -786,6 +816,7 @@ mod tests {
}),
secondary: None,
credits: None,
individual_limit: None,
plan_type: Some(AccountPlanType::Pro),
rate_limit_reached_type: None,
},
@@ -799,6 +830,7 @@ mod tests {
}),
secondary: None,
credits: None,
individual_limit: None,
plan_type: Some(AccountPlanType::Pro),
rate_limit_reached_type: None,
},
@@ -843,6 +875,7 @@ mod tests {
plan_type: crate::types::PlanType::Plus,
rate_limit: None,
credits: None,
spend_control: None,
additional_rate_limits: None,
rate_limit_reached_type: Some(Some(BackendRateLimitReachedType { kind })),
};
@@ -858,6 +891,7 @@ mod tests {
plan_type: crate::types::PlanType::Plus,
rate_limit: None,
credits: None,
spend_control: None,
additional_rate_limits: None,
rate_limit_reached_type: None,
};
-1
View File
@@ -7,7 +7,6 @@ pub use client::RequestError;
pub use types::CodeTaskDetailsResponse;
pub use types::CodeTaskDetailsResponseExt;
pub use types::ConfigBundleResponse;
pub use types::ConfigFileResponse;
pub use types::CredentialRouteAuthType;
pub use types::DeliveredConfigToml;
pub use types::DeliveredRequirementsToml;
+1 -1
View File
@@ -1,5 +1,4 @@
pub use codex_backend_openapi_models::models::ConfigBundleResponse;
pub use codex_backend_openapi_models::models::ConfigFileResponse;
pub use codex_backend_openapi_models::models::CreditStatusDetails;
pub use codex_backend_openapi_models::models::DeliveredConfigToml;
pub use codex_backend_openapi_models::models::DeliveredRequirementsToml;
@@ -10,6 +9,7 @@ pub use codex_backend_openapi_models::models::RateLimitReachedKind;
pub use codex_backend_openapi_models::models::RateLimitStatusDetails;
pub use codex_backend_openapi_models::models::RateLimitStatusPayload;
pub use codex_backend_openapi_models::models::RateLimitWindowSnapshot;
pub use codex_backend_openapi_models::models::SpendControlLimitDetails;
pub use codex_backend_openapi_models::models::TaskListItem;
use serde::Deserialize;