From eb5aafca86558297f14b6465dfa3f2675fa0ba6d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 18 Oct 2026 07:58:41 +0000 Subject: [PATCH] chore: sync public mirror from internal --- .repository-projection.json | 6 +++--- .../src/init_cli/device_login.rs | 19 ++++++++++++++----- packages/local-host-rs/src/mcp/oauth.rs | 17 ++++++++++++----- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/.repository-projection.json b/.repository-projection.json index b57968ed5..4bcac2b9a 100644 --- a/.repository-projection.json +++ b/.repository-projection.json @@ -3,11 +3,11 @@ "projection": "deixic-code", "projectionSchemaVersion": 1, "sourceRepository": "dx-corp/mono", - "sourceSha": "21d8ab717599b75abf5279f6355d9c6859c3864e", + "sourceSha": "6d7beb47d3e0244dd4e04003a7a5602b2127918b", "destinationRepository": "dx-corp/code", - "priorProjectedBase": "38552eec05ca20df647223c02943d4e5bd243293", + "priorProjectedBase": "8906b7723671a37f5c472ea26c53bfedffa63a4f", "definitionDigest": "93eb3fb9be07d3644538c5519cf38973b4246cdcf2f8d7b60ccd593d7ca73033", "toolDigest": "5a30b31c7f35befc8be0e48bd91bdd65167ce110fbc393702b4ad9ac7defe7d7", - "contentDigest": "fdffd8698fb9408671db2b75360f5047a8d76847495b12db8b303d2c3a797d91", + "contentDigest": "0b756db4041a4e9e9853dbbb890ce49452fa5db044cb0940bef7f4b7f9842994", "publicationEligible": true } diff --git a/packages/local-host-rs/src/init_cli/device_login.rs b/packages/local-host-rs/src/init_cli/device_login.rs index 9562b064c..0009a42e6 100644 --- a/packages/local-host-rs/src/init_cli/device_login.rs +++ b/packages/local-host-rs/src/init_cli/device_login.rs @@ -439,6 +439,15 @@ mod tests { use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::TcpListener; + /// Same request bound as the production device-login client, so a stalled + /// fixture fails the test instead of hanging it. + fn test_http_client() -> Client { + Client::builder() + .timeout(Duration::from_secs(30)) + .build() + .expect("test HTTP client") + } + fn device_authorization() -> DeviceAuthorization { DeviceAuthorization { device_code: "device-secret".into(), @@ -700,7 +709,7 @@ mod tests { .await; let started = std::time::Instant::now(); let credentials = device_login( - &Client::new(), + &test_http_client(), &identity, "deixic-code-cli", "llm_gateway:invoke sessions:read", @@ -766,7 +775,7 @@ mod tests { ) .await; let error = device_login( - &Client::new(), + &test_http_client(), &identity, "deixic-code-cli", "s", @@ -785,7 +794,7 @@ mod tests { spawn_device_identity((404, "404 Not Found".into()), Vec::new(), true) .await; let error = device_login( - &Client::new(), + &test_http_client(), &identity, "deixic-code-cli", "s", @@ -808,7 +817,7 @@ mod tests { ) .await; let error = device_login( - &Client::new(), + &test_http_client(), &identity, "deixic-code-cli", "s", @@ -830,7 +839,7 @@ mod tests { ) .await; let error = device_login( - &Client::new(), + &test_http_client(), &identity, "deixic-code-cli", "s", diff --git a/packages/local-host-rs/src/mcp/oauth.rs b/packages/local-host-rs/src/mcp/oauth.rs index 1b4ca6f89..b431a5074 100644 --- a/packages/local-host-rs/src/mcp/oauth.rs +++ b/packages/local-host-rs/src/mcp/oauth.rs @@ -88,10 +88,7 @@ async fn login_with_output( .url .as_deref() .context("HTTP MCP server is missing its URL")?; - let client = reqwest::Client::builder() - .timeout(Duration::from_secs(20)) - .build() - .context("build OAuth client")?; + let client = oauth_http_client().context("build OAuth client")?; let resource_metadata_url = discover_resource_metadata_url(&client, server_url).await?; require_secure_endpoint(&resource_metadata_url, "protected-resource metadata")?; let resource: ProtectedResourceMetadata = client @@ -297,6 +294,15 @@ fn save_credential(name: &str, credential: &StoredCredential) -> Result<()> { .context("store MCP OAuth credential") } +/// Bounded client for OAuth discovery, authorization-code exchange, and +/// refresh. Refresh runs on the MCP connect path, so an unresponsive token +/// endpoint must fail the connection rather than hang it. +fn oauth_http_client() -> reqwest::Result { + reqwest::Client::builder() + .timeout(Duration::from_secs(20)) + .build() +} + async fn refresh_credential( config: &McpServerConfig, credential: &mut StoredCredential, @@ -307,7 +313,8 @@ async fn refresh_credential( config.name )) })?; - let token = reqwest::Client::new() + let token = oauth_http_client() + .map_err(|error| McpError::ConnectionFailed(format!("OAuth refresh failed: {error}")))? .post(&credential.token_endpoint) .form(&[ ("grant_type", "refresh_token"),