mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
Support Amazon Bedrock login in the app server (#33170)
## What changed - Handle experimental `account/login/start` requests with `type: "amazonBedrock"`. - Validate the API key and Mantle region, persist the managed credential, select the `amazon-bedrock` model provider, and emit the standard login and account-update notifications. - Reload configuration for account and authentication status reads so the newly selected provider is visible immediately, with the startup configuration as a fallback. - Extend the app-server test client with an Amazon Bedrock login mode that redacts API keys from request logs. ## Testing - Add app-server coverage for successful login, credential and provider persistence, notifications, refreshed account state, invalid inputs, conflicting configuration, forced ChatGPT login, external authentication, and configuration reload failure. GitOrigin-RevId: bf3ae98375caee41e78d2cdc677127138184c17a
This commit is contained in:
1 parent
a2fb3f048d
commit
518085387b
11 files changed
+816
-22
No files matched your search
@@ -230,11 +230,20 @@ enum CliCommand {
|
||||
#[arg(long)]
|
||||
abort_on: Option<usize>,
|
||||
},
|
||||
/// Trigger the ChatGPT login flow and wait for completion.
|
||||
/// Trigger a ChatGPT or Amazon Bedrock login flow.
|
||||
TestLogin {
|
||||
/// Use the device-code login flow instead of the browser callback flow.
|
||||
#[arg(long, default_value_t = false)]
|
||||
#[arg(long, default_value_t = false, conflicts_with = "amazon_bedrock")]
|
||||
device_code: bool,
|
||||
/// Use a Codex-managed Amazon Bedrock API key.
|
||||
#[arg(long, default_value_t = false, conflicts_with = "device_code")]
|
||||
amazon_bedrock: bool,
|
||||
/// Amazon Bedrock API key.
|
||||
#[arg(long, value_name = "API_KEY")]
|
||||
api_key: Option<String>,
|
||||
/// AWS Region for the Amazon Bedrock Mantle endpoint.
|
||||
#[arg(long, value_name = "REGION")]
|
||||
region: Option<String>,
|
||||
},
|
||||
/// Fetch the current account rate limits from the Codex app-server.
|
||||
GetAccountRateLimits,
|
||||
@@ -313,6 +322,12 @@ enum CliCommand {
|
||||
},
|
||||
}
|
||||
|
||||
enum TestLoginMode {
|
||||
ChatgptBrowser,
|
||||
ChatgptDeviceCode,
|
||||
AmazonBedrock { api_key: String, region: String },
|
||||
}
|
||||
|
||||
pub async fn run() -> Result<()> {
|
||||
let Cli {
|
||||
codex_bin,
|
||||
@@ -415,10 +430,24 @@ pub async fn run() -> Result<()> {
|
||||
)
|
||||
.await
|
||||
}
|
||||
CliCommand::TestLogin { device_code } => {
|
||||
CliCommand::TestLogin {
|
||||
device_code,
|
||||
amazon_bedrock,
|
||||
api_key,
|
||||
region,
|
||||
} => {
|
||||
ensure_dynamic_tools_unused(&dynamic_tools, "test-login")?;
|
||||
let endpoint = resolve_endpoint(codex_bin, url)?;
|
||||
test_login(&endpoint, &config_overrides, device_code).await
|
||||
let mode = if amazon_bedrock {
|
||||
let api_key = api_key.context("--api-key is required with --amazon-bedrock")?;
|
||||
let region = region.context("--region is required with --amazon-bedrock")?;
|
||||
TestLoginMode::AmazonBedrock { api_key, region }
|
||||
} else if device_code {
|
||||
TestLoginMode::ChatgptDeviceCode
|
||||
} else {
|
||||
TestLoginMode::ChatgptBrowser
|
||||
};
|
||||
test_login(&endpoint, &config_overrides, mode).await
|
||||
}
|
||||
CliCommand::GetAccountRateLimits => {
|
||||
ensure_dynamic_tools_unused(&dynamic_tools, "get-account-rate-limits")?;
|
||||
@@ -1128,16 +1157,45 @@ async fn send_follow_up_v2(
|
||||
async fn test_login(
|
||||
endpoint: &Endpoint,
|
||||
config_overrides: &[String],
|
||||
device_code: bool,
|
||||
mode: TestLoginMode,
|
||||
) -> Result<()> {
|
||||
with_client("test-login", endpoint, config_overrides, |client| {
|
||||
let initialize = client.initialize()?;
|
||||
println!("< initialize response: {initialize:?}");
|
||||
|
||||
let login_response = if device_code {
|
||||
client.login_account_chatgpt_device_code()?
|
||||
} else {
|
||||
client.login_account_chatgpt()?
|
||||
let login_response = match mode {
|
||||
TestLoginMode::ChatgptBrowser => client.login_account_chatgpt()?,
|
||||
TestLoginMode::ChatgptDeviceCode => client.login_account_chatgpt_device_code()?,
|
||||
TestLoginMode::AmazonBedrock { api_key, region } => {
|
||||
let request_id = client.request_id();
|
||||
let login_response: LoginAccountResponse = client.send_request(
|
||||
ClientRequest::LoginAccount {
|
||||
request_id: request_id.clone(),
|
||||
params: codex_app_server_protocol::LoginAccountParams::AmazonBedrock {
|
||||
api_key,
|
||||
region,
|
||||
},
|
||||
},
|
||||
request_id,
|
||||
"account/login/start",
|
||||
)?;
|
||||
println!("< account/login/start response: {login_response:?}");
|
||||
|
||||
let completion =
|
||||
client.wait_for_account_login_completion(/*expected_login_id*/ None)?;
|
||||
println!("< account/login/completed notification: {completion:?}");
|
||||
|
||||
loop {
|
||||
let notification = client.next_notification()?;
|
||||
if let Ok(ServerNotification::AccountUpdated(account_updated)) =
|
||||
ServerNotification::try_from(notification)
|
||||
{
|
||||
println!("< account/updated notification: {account_updated:?}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
println!("< account/login/start response: {login_response:?}");
|
||||
let login_id = match login_response {
|
||||
@@ -1158,7 +1216,7 @@ async fn test_login(
|
||||
_ => bail!("expected chatgpt login response"),
|
||||
};
|
||||
|
||||
let completion = client.wait_for_account_login_completion(&login_id)?;
|
||||
let completion = client.wait_for_account_login_completion(Some(&login_id))?;
|
||||
println!("< account/login/completed notification: {completion:?}");
|
||||
|
||||
if completion.success {
|
||||
@@ -1799,7 +1857,7 @@ impl CodexClient {
|
||||
|
||||
fn wait_for_account_login_completion(
|
||||
&mut self,
|
||||
expected_login_id: &str,
|
||||
expected_login_id: Option<&str>,
|
||||
) -> Result<AccountLoginCompletedNotification> {
|
||||
loop {
|
||||
let notification = self.next_notification()?;
|
||||
@@ -1807,7 +1865,7 @@ impl CodexClient {
|
||||
if let Ok(server_notification) = ServerNotification::try_from(notification) {
|
||||
match server_notification {
|
||||
ServerNotification::AccountLoginCompleted(completion) => {
|
||||
if completion.login_id.as_deref() == Some(expected_login_id) {
|
||||
if completion.login_id.as_deref() == expected_login_id {
|
||||
return Ok(completion);
|
||||
}
|
||||
|
||||
@@ -1956,7 +2014,13 @@ impl CodexClient {
|
||||
.context("client request was not a valid JSON-RPC request")?;
|
||||
request.trace = current_span_w3c_trace_context();
|
||||
let request_json = serde_json::to_string(&request)?;
|
||||
let request_pretty = serde_json::to_string_pretty(&request)?;
|
||||
let mut request_for_logging = serde_json::to_value(&request)?;
|
||||
if request.method == "account/login/start"
|
||||
&& let Some(api_key) = request_for_logging.pointer_mut("/params/apiKey")
|
||||
{
|
||||
*api_key = Value::String("<redacted>".to_string());
|
||||
}
|
||||
let request_pretty = serde_json::to_string_pretty(&request_for_logging)?;
|
||||
print_multiline_with_prefix("> ", &request_pretty);
|
||||
self.write_payload(&request_json)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user