mirror of
https://github.com/openai/codex.git
synced 2026-11-13 21:30:47 +00:00
## Why A lost Cloud reply stream previously required reopening the conversation, even when it was idle. Recover the displayed conversation automatically while holding queued input until its reply stream is usable. ## What changed - Add bounded retries for transient stream failures in the displayed, idle conversation, with credential and network-policy checks. Keep explicit reopen behavior when recovery is unavailable or fails. - Enable HTTP/2 keepalive pings for Cloud connections. - Queue input while disconnected and resume ordinary submission after recovery. Mark recovered history as potentially incomplete so an explicit reopen reloads missed turns while preserving drafts and reconciling pending submissions. - Store retained input per backend and apply background settings updates to the owning backend's drafts. ## Testing Add regression coverage for queued input during disconnection and extend resume and backend-handoff tests to cover history refresh, draft preservation, and retained settings. GitOrigin-RevId: 3d753e53d3c0d594680a90cac8a75fe33b91e219
63 lines
2.4 KiB
Rust
63 lines
2.4 KiB
Rust
//! Opens the internal Cloud preview with the selected ChatGPT credentials and local transport policy.
|
|
|
|
use super::start_worker;
|
|
use crate::AppServerClient;
|
|
use crate::legacy_core::config::Config;
|
|
use codex_app_server_protocol as protocol;
|
|
use codex_cloud_client as cloud;
|
|
use codex_login::token_data::parse_jwt_expiration;
|
|
use codex_protocol::account::PlanType;
|
|
use color_eyre::eyre::Result;
|
|
use color_eyre::eyre::bail;
|
|
use color_eyre::eyre::eyre;
|
|
|
|
const ORIGIN: &str = "https://codex-cloud-backend.chatgpt.com";
|
|
|
|
pub async fn connect(config: &Config) -> Result<AppServerClient> {
|
|
// The preview borrows Local's policy without owning credential updates.
|
|
let factory = config.http_client_factory();
|
|
let policy = factory.network_policy().clone().for_current_account();
|
|
let factory = factory.with_network_policy(policy);
|
|
let storage = config.auth_config();
|
|
let auth = storage
|
|
.load_auth(/*enable_codex_api_key_env*/ false)
|
|
.await?
|
|
.ok_or_else(|| eyre!("Sign in with ChatGPT and reopen Codex to use Cloud."))?;
|
|
if !auth.is_chatgpt_auth() || auth.is_fedramp_account() {
|
|
bail!("This account is not supported by the cloud conversation preview.");
|
|
}
|
|
let account_id = auth
|
|
.get_account_id()
|
|
.ok_or_else(|| eyre!("Cloud requires a selected account."))?;
|
|
let token = auth.get_token()?;
|
|
if let Ok(Some(expires)) = parse_jwt_expiration(&token)
|
|
&& std::time::SystemTime::from(expires) <= std::time::SystemTime::now()
|
|
{
|
|
bail!("Your ChatGPT credential expired. Reopen Codex to reconnect.");
|
|
}
|
|
let origin = ORIGIN.parse()?;
|
|
let permit = factory.network_policy().acquire(&origin)?;
|
|
let credentials = cloud::Credentials::new(&token, &account_id)?;
|
|
let client = cloud::Client::new(&factory, ORIGIN, credentials)?;
|
|
Ok(start_worker(
|
|
client,
|
|
super::idle::Login {
|
|
storage,
|
|
mode: auth.api_auth_mode(),
|
|
token,
|
|
account: account_id,
|
|
permit,
|
|
},
|
|
protocol::GetAccountResponse {
|
|
user_id: auth.get_chatgpt_user_id(),
|
|
account: Some(protocol::Account::Chatgpt {
|
|
email: auth.get_account_email(),
|
|
plan_type: auth.account_plan_type().unwrap_or(PlanType::Unknown),
|
|
workspace_name: None,
|
|
}),
|
|
requires_openai_auth: true,
|
|
workspace_routing: None,
|
|
},
|
|
))
|
|
}
|