mirror of
https://github.com/openai/codex.git
synced 2026-11-05 20:10:33 +00:00
Enable Bedrock onboarding through the local daemon (#59068)
## Why Bedrock onboarding previously forced an embedded server and was unavailable when connected to a local daemon. Credential discovery uses the server environment, which can differ from the launching shell. ## What changed - Allow the Bedrock setup wizard to use the local daemon while keeping GCP onboarding on the embedded server. - Clarify that detected AWS credentials come from the server environment. - Direct users who export AWS credentials in their shell to run `codex --no-daemon` in that shell. ## Testing Update wizard eligibility tests and onboarding snapshots, and extend daemon startup coverage to verify discovery of a Bedrock API key available only to an already running daemon. GitOrigin-RevId: 9502ffaa59250210e8f8bc8db6f82b0803cb6cac
This commit is contained in:
@@ -154,7 +154,7 @@ async fn restrictive_launcher_uses_embedded_if_daemon_cannot_start() -> Result<(
|
||||
|
||||
#[tokio::test]
|
||||
#[cfg(unix)]
|
||||
async fn bedrock_onboarding_leaves_a_running_daemon_untouched() -> Result<()> {
|
||||
async fn bedrock_onboarding_discovers_credentials_on_the_running_daemon() -> Result<()> {
|
||||
daemon_startup("bedrock-running").await
|
||||
}
|
||||
|
||||
@@ -250,6 +250,7 @@ async fn daemon_startup(command: &str) -> Result<()> {
|
||||
| "agents-untrusted"
|
||||
| "agents-untrusted-new"
|
||||
| "discovered"
|
||||
| "bedrock"
|
||||
| "bedrock-running"
|
||||
| "restrictive-job"
|
||||
) || mismatch
|
||||
@@ -283,6 +284,14 @@ async fn daemon_startup(command: &str) -> Result<()> {
|
||||
let mut existing_daemon = if matches!(command, "bedrock-running" | "discovered" | "agents-no-daemon")
|
||||
|| (mismatch && !disabling)
|
||||
{
|
||||
if command == "bedrock-running" {
|
||||
// This Unix-only case uses a daemon-only token and no ambient profiles.
|
||||
env.insert("AWS_CONFIG_FILE".into(), "/dev/null".into());
|
||||
env.insert("AWS_SHARED_CREDENTIALS_FILE".into(), "/dev/null".into());
|
||||
env.remove("AWS_ACCESS_KEY_ID");
|
||||
env.remove("AWS_SECRET_ACCESS_KEY");
|
||||
env.insert("AWS_BEARER_TOKEN_BEDROCK".into(), "daemon-only-test-token".into());
|
||||
}
|
||||
let started = Command::new(&codex)
|
||||
.env_clear()
|
||||
.envs(&env)
|
||||
@@ -363,6 +372,11 @@ async fn daemon_startup(command: &str) -> Result<()> {
|
||||
steps.push_back(("GPT-5.6-Terra", b"/status\r"));
|
||||
if restart { "Server:Localbackgroundserver" } else { "Model:" }
|
||||
}
|
||||
} else if command == "bedrock-running" {
|
||||
env.remove("AWS_BEARER_TOKEN_BEDROCK");
|
||||
steps.push_back(("UseanAPIkeyoranotherprovider", b"3"));
|
||||
steps.push_back(("UseAmazonBedrock", b"2"));
|
||||
"Continuewithdetectedcredentials"
|
||||
} else if bedrock_onboarding {
|
||||
// With GCP disabled, only Bedrock makes the sign-in menu advertise another provider.
|
||||
"UseanAPIkeyoranotherprovider"
|
||||
@@ -445,7 +459,18 @@ async fn daemon_startup(command: &str) -> Result<()> {
|
||||
// A fresh disable request includes both cold daemon startup and its confirmed restart.
|
||||
let timeout = Duration::from_secs(if disabling { 90 } else { 45 });
|
||||
let result = tokio::time::timeout(timeout, async {
|
||||
while let Some(bytes) = stdout.recv().await {
|
||||
loop {
|
||||
let bytes = tokio::select! {
|
||||
bytes = stdout.recv() => bytes,
|
||||
// The first onboarding draw precedes its typeahead drain. Retry the
|
||||
// main menu shortcut until the submenu appears (it has no option 3).
|
||||
() = tokio::time::sleep(Duration::from_millis(/*millis*/ 250)),
|
||||
if steps.front().is_some_and(|(ready, _)| *ready == "UseAmazonBedrock") => {
|
||||
writer.send(b"3".to_vec()).await?;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let Some(bytes) = bytes else { break };
|
||||
output.push_str(&String::from_utf8_lossy(&bytes));
|
||||
screen.process(&bytes);
|
||||
for (query, reply) in &mut queries {
|
||||
@@ -564,9 +589,11 @@ async fn daemon_startup(command: &str) -> Result<()> {
|
||||
} else if elevated_launch {
|
||||
ensure!(!home.path().join("app-server-daemon").exists());
|
||||
ensure!(!home.path().join("packages/app-server-daemon").exists());
|
||||
} else if bedrock_onboarding || command == "restrictive-job" {
|
||||
} else if bedrock_onboarding {
|
||||
ensure!(pid_file.exists() != elevated);
|
||||
} else if command == "restrictive-job" {
|
||||
ensure!(!pid_file.exists());
|
||||
if command == "restrictive-job" && !elevated {
|
||||
if !elevated {
|
||||
let contents = screen.screen().contents();
|
||||
let warning = contents.lines()
|
||||
.find(|line| line.contains("Running without the shared background server:"))
|
||||
|
||||
@@ -1899,8 +1899,7 @@ async fn run_ratatui_app(
|
||||
if config.model_provider_id != startup_model_provider {
|
||||
startup_account = None;
|
||||
if matches!(&app_server_target, AppServerTarget::Embedded) {
|
||||
// App-server providers are fixed at startup, so onboarding cannot
|
||||
// reuse a server initialized before it persisted another provider.
|
||||
// Rebuild the embedded server with the final local configuration.
|
||||
shutdown_app_server_if_present(app_server.take()).await;
|
||||
}
|
||||
}
|
||||
@@ -2520,7 +2519,12 @@ fn should_show_provider_setup_wizard(
|
||||
app_server_target: &AppServerTarget,
|
||||
feature: Feature,
|
||||
) -> bool {
|
||||
matches!(app_server_target, AppServerTarget::Embedded)
|
||||
let supported_target = match app_server_target {
|
||||
AppServerTarget::Embedded => true,
|
||||
AppServerTarget::LocalDaemon { .. } => feature == Feature::BedrockSetupWizard,
|
||||
AppServerTarget::Remote { .. } => false,
|
||||
};
|
||||
supported_target
|
||||
&& should_show_login_screen(login_status, requires_openai_auth)
|
||||
&& config.features.enabled(feature)
|
||||
&& config.model_provider_id == "openai"
|
||||
|
||||
@@ -165,7 +165,7 @@ gcp_setup_wizard = true
|
||||
allow_embedded_fallback: true,
|
||||
endpoint: shared_endpoint.clone(),
|
||||
},
|
||||
[false, false],
|
||||
[true, false],
|
||||
),
|
||||
(
|
||||
"remote app server",
|
||||
|
||||
@@ -392,7 +392,7 @@ impl BedrockState {
|
||||
} else if *list == BedrockMethodList::Detected
|
||||
&& !self.environment_credentials.is_empty()
|
||||
{
|
||||
lines.push(" AWS credentials detected in your environment.".into());
|
||||
lines.push(" AWS credentials detected in the server environment.".into());
|
||||
} else {
|
||||
if *list == BedrockMethodList::Detected {
|
||||
lines.push(" No AWS credentials found.".into());
|
||||
@@ -454,7 +454,7 @@ impl BedrockState {
|
||||
}
|
||||
BedrockView::EnvironmentInstructions => {
|
||||
lines.push(
|
||||
" Configure AWS credentials in your environment, then restart Codex.".into(),
|
||||
" Export AWS credentials, then run `codex --no-daemon` in that shell.".into(),
|
||||
);
|
||||
lines.push("".into());
|
||||
lines.push(Line::from(vec![
|
||||
@@ -587,9 +587,9 @@ impl BedrockState {
|
||||
let description = if self.environment_credentials.iter().any(|credential| {
|
||||
credential.credential_type == AwsCredentialType::BedrockApiKey
|
||||
}) {
|
||||
"Use your existing Amazon Bedrock API key"
|
||||
"Use the Bedrock API key from the server environment"
|
||||
} else {
|
||||
"Use your existing AWS credentials"
|
||||
"Use AWS access keys from the server environment"
|
||||
};
|
||||
(
|
||||
"Continue with detected credentials".to_string(),
|
||||
@@ -620,7 +620,7 @@ impl BedrockState {
|
||||
),
|
||||
BedrockMethod::EnvironmentInstructions => (
|
||||
"Environment variables".to_string(),
|
||||
"Configure AWS credentials in your environment, then return here".to_string(),
|
||||
"Use AWS credentials exported in your shell".to_string(),
|
||||
),
|
||||
BedrockMethod::ApiKey => (
|
||||
"Bedrock API key".to_string(),
|
||||
|
||||
@@ -193,7 +193,7 @@ fn discovery_prioritizes_profiles_and_groups_other_sign_in_methods() {
|
||||
Use your existing AWS credentials
|
||||
|
||||
2. Continue with detected credentials
|
||||
Use your existing Amazon Bedrock API key
|
||||
Use the Bedrock API key from the server environment
|
||||
|
||||
3. Other AWS sign-in methods
|
||||
Use another profile, access keys, environment variables, or a
|
||||
@@ -225,7 +225,7 @@ fn discovery_prioritizes_profiles_and_groups_other_sign_in_methods() {
|
||||
Enter an access key ID and secret access key
|
||||
|
||||
3. Environment variables
|
||||
Configure AWS credentials in your environment, then return here
|
||||
Use AWS credentials exported in your shell
|
||||
|
||||
4. Bedrock API key
|
||||
Enter a Bedrock API key
|
||||
@@ -300,7 +300,7 @@ fn discovery_prioritizes_profiles_and_groups_other_sign_in_methods() {
|
||||
insta::assert_snapshot!(render_visible(&environment_state), @r###"
|
||||
> Set up Amazon Bedrock
|
||||
|
||||
AWS credentials detected in your environment.
|
||||
AWS credentials detected in the server environment.
|
||||
|
||||
> 1. Continue with detected credentials
|
||||
|
||||
@@ -328,7 +328,7 @@ fn discovery_prioritizes_profiles_and_groups_other_sign_in_methods() {
|
||||
Enter an access key ID and secret access key
|
||||
|
||||
3. Environment variables
|
||||
Configure AWS credentials in your environment, then return here
|
||||
Use AWS credentials exported in your shell
|
||||
|
||||
4. Bedrock API key
|
||||
Enter a Bedrock API key
|
||||
@@ -515,7 +515,7 @@ fn credential_entries_share_region_and_keep_aws_secrets_hidden() {
|
||||
insta::assert_snapshot!(render_visible(&access_keys), @r###"
|
||||
> Set up Amazon Bedrock
|
||||
|
||||
Configure AWS credentials in your environment, then restart Codex.
|
||||
Export AWS credentials, then run `codex --no-daemon` in that shell.
|
||||
|
||||
Setup guide: https://learn.chatgpt.com/docs/amazon-bedrock
|
||||
|
||||
|
||||
@@ -573,17 +573,13 @@ pub(super) async fn run_main_inner(
|
||||
&& !app_server_target.uses_remote_workspace();
|
||||
if auto_start_daemon
|
||||
&& daemon_exclusion.is_none()
|
||||
&& [Feature::BedrockSetupWizard, Feature::GcpSetupWizard]
|
||||
.into_iter()
|
||||
.any(|feature| {
|
||||
should_show_provider_setup_wizard(
|
||||
LoginStatus::NotAuthenticated,
|
||||
config.model_provider.requires_openai_auth,
|
||||
&config,
|
||||
&AppServerTarget::Embedded,
|
||||
feature,
|
||||
)
|
||||
})
|
||||
&& should_show_provider_setup_wizard(
|
||||
LoginStatus::NotAuthenticated,
|
||||
config.model_provider.requires_openai_auth,
|
||||
&config,
|
||||
&AppServerTarget::Embedded,
|
||||
Feature::GcpSetupWizard,
|
||||
)
|
||||
&& startup_draft
|
||||
.run_until(
|
||||
config
|
||||
@@ -595,8 +591,8 @@ pub(super) async fn run_main_inner(
|
||||
.flatten()
|
||||
.is_none()
|
||||
{
|
||||
// Provider setup wizards configure their provider through the embedded server.
|
||||
daemon_exclusion = Some("Cloud provider sign-in");
|
||||
// GCP setup resolves credential paths and runs gcloud in the launch environment.
|
||||
daemon_exclusion = Some("GCP sign-in");
|
||||
app_server_target = AppServerTarget::Embedded;
|
||||
}
|
||||
if auto_start_daemon
|
||||
@@ -803,7 +799,7 @@ pub(super) async fn run_main_inner(
|
||||
"config_override"
|
||||
}
|
||||
}
|
||||
Some("Bedrock sign-in") => "bedrock_sign_in",
|
||||
Some("GCP sign-in") => "gcp_sign_in",
|
||||
Some(daemon_startup::WSL_DRVFS_EXCLUSION) => "wsl_drvfs",
|
||||
Some("this Windows launcher") => "windows_detach_restricted",
|
||||
Some("daemon feature settings") => "daemon_compatibility",
|
||||
|
||||
Reference in New Issue
Block a user