backend-client: add authenticated user settings

This commit is contained in:
jayp-oai
2026-07-10 03:03:13 +00:00
parent 1f0566d3f5
commit f5d324b3e7
3 changed files with 86 additions and 0 deletions

View File

@@ -1,5 +1,6 @@
use crate::types::AccountsCheckResponse;
use crate::types::CodeTaskDetailsResponse;
use crate::types::CodexUserSettingsResponse;
use crate::types::CodexWorkspaceMessagesResponse;
use crate::types::ConfigBundleResponse;
use crate::types::PaginatedListTaskListItem;
@@ -432,6 +433,24 @@ impl Client {
.map_err(RequestError::from)
}
/// Fetch authenticated Codex user settings from the active backend route.
///
/// Uses `GET /api/codex/settings/user` for Codex API hosts and
/// `GET /wham/settings/user` for ChatGPT `backend-api` hosts.
pub async fn get_user_settings(
&self,
) -> std::result::Result<CodexUserSettingsResponse, RequestError> {
let url = self.user_settings_url();
let req = self
.http
.get(&url)
.headers(self.headers())
.header(CACHE_CONTROL, HeaderValue::from_static("no-store"));
let (body, ct) = self.exec_request_detailed(req, "GET", &url).await?;
self.decode_json::<CodexUserSettingsResponse>(&url, &ct, &body)
.map_err(RequestError::from)
}
pub async fn list_workspace_messages(
&self,
) -> std::result::Result<CodexWorkspaceMessagesResponse, RequestError> {
@@ -593,6 +612,13 @@ impl Client {
}
}
fn user_settings_url(&self) -> String {
match self.path_style {
PathStyle::CodexApi => format!("{}/api/codex/settings/user", self.base_url),
PathStyle::ChatGptApi => format!("{}/wham/settings/user", self.base_url),
}
}
fn map_rate_limit_window(
window: Option<Option<Box<crate::types::RateLimitWindowSnapshot>>>,
) -> Option<RateLimitWindow> {
@@ -974,6 +1000,55 @@ mod tests {
);
}
#[test]
fn user_settings_uses_expected_paths() {
let codex_client = test_client("https://example.test", PathStyle::CodexApi);
assert_eq!(
codex_client.user_settings_url(),
"https://example.test/api/codex/settings/user"
);
let chatgpt_client = test_client("https://chatgpt.com/backend-api", PathStyle::ChatGptApi);
assert_eq!(
chatgpt_client.user_settings_url(),
"https://chatgpt.com/backend-api/wham/settings/user"
);
}
#[test]
fn user_settings_missing_attribution_policy_defaults_to_disabled() {
assert_eq!(
serde_json::from_value::<CodexUserSettingsResponse>(serde_json::json!({})).unwrap(),
CodexUserSettingsResponse {
commit_attribution_enabled: false,
}
);
}
#[test]
fn authenticated_user_settings_client_uses_active_workspace_headers() {
let auth = CodexAuth::from_external_chatgpt_tokens(
"e30.e30.c2ln",
"workspace-123",
Some("enterprise"),
)
.unwrap();
let client = Client::from_auth("https://chatgpt.com/backend-api", &auth).unwrap();
let headers = client.headers();
assert_eq!(
[
headers
.get("authorization")
.and_then(|value| value.to_str().ok()),
headers
.get("chatgpt-account-id")
.and_then(|value| value.to_str().ok()),
],
[Some("Bearer e30.e30.c2ln"), Some("workspace-123")]
);
}
fn test_client(base_url: &str, path_style: PathStyle) -> Client {
Client {
base_url: base_url.to_string(),

View File

@@ -8,6 +8,7 @@ pub use types::AccountEntry;
pub use types::AccountsCheckResponse;
pub use types::CodeTaskDetailsResponse;
pub use types::CodeTaskDetailsResponseExt;
pub use types::CodexUserSettingsResponse;
pub use types::CodexWorkspaceMessage;
pub use types::CodexWorkspaceMessageType;
pub use types::CodexWorkspaceMessagesResponse;

View File

@@ -60,6 +60,16 @@ pub struct CodexWorkspaceMessagesResponse {
pub messages: Vec<CodexWorkspaceMessage>,
}
/// Authenticated Codex user settings used by CLI runtime policy.
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Eq)]
pub struct CodexUserSettingsResponse {
/// Server-computed effective commit-attribution policy.
///
/// Older backend responses omit this field, which safely defaults to disabled.
#[serde(default)]
pub commit_attribution_enabled: bool,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
pub struct CodexWorkspaceMessage {
pub message_id: String,